From 1ec0d2c13023d988712792deba8f9956f41a01b6 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Fri, 22 Nov 2024 11:47:23 +0700 Subject: [PATCH] chore: withdraw stuck asset --- contracts/entry-point/src/contract.rs | 16 +- contracts/entry-point/src/execute.rs | 18 +++ .../entry-point/tests/test_withdraw_asset.rs | 139 ++++++++++++++++++ packages/skip/src/entry_point.rs | 4 + 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 contracts/entry-point/tests/test_withdraw_asset.rs diff --git a/contracts/entry-point/src/contract.rs b/contracts/entry-point/src/contract.rs index 21281a6e..8bc10b72 100644 --- a/contracts/entry-point/src/contract.rs +++ b/contracts/entry-point/src/contract.rs @@ -2,7 +2,8 @@ use crate::{ error::{ContractError, ContractResult}, execute::{ execute_post_swap_action, execute_swap_and_action, execute_swap_and_action_with_recover, - execute_universal_swap, execute_update_config, execute_user_swap, receive_cw20, + execute_universal_swap, execute_update_config, execute_user_swap, execute_withdraw_asset, + receive_cw20, }, query::{query_ibc_transfer_adapter_contract, query_swap_venue_adapter_contract}, reply::{reply_swap_and_action_with_recover, RECOVER_REPLY_ID}, @@ -16,6 +17,7 @@ use cosmwasm_std::{ StdResult, }; use cw2::set_contract_version; +use oraiswap::universal_swap_memo::Memo; use skip::entry_point::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; /////////////// @@ -220,6 +222,9 @@ pub fn execute( ibc_wasm_contract_address, ), ExecuteMsg::UniversalSwap { memo } => execute_universal_swap(deps, env, info, None, memo), + ExecuteMsg::WithdrawAsset { coin, receiver } => { + execute_withdraw_asset(deps, info, coin, receiver) + } } } @@ -246,3 +251,12 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } } } + +#[test] +fn test_memo() { + let memo = "CpcBCgdvcmFpZGV4EosBCogBCj9vcmFpMTl0dGcwajd3NWtyODNqczMydG13bnd4eGRxOXJrbXc0bTNkN21uMmoyaGtwdWd3d2E0dHN6d3Nua2cSP29yYWkxNXVuOG1zeDNuNXpmOWFobHhtZmVxZDJrd2E1d20wbnJweGVyMzA0bTluZDVxNnFxMGc2c2t1NXBkZBoEb3JhaRIBMBiA/I+Y1c7ygxgiLyItCitvcmFpMW15Y21oeXJtZDZkdXNwNDA4cnRqZ3psazc3Mzh2aHRncXloeHh0KitvcmFpMW15Y21oeXJtZDZkdXNwNDA4cnRqZ3psazc3Mzh2aHRncXloeHh0"; + println!( + "{:?}", + Memo::decode_memo(Binary::from_base64(memo).unwrap()).unwrap() + ); +} diff --git a/contracts/entry-point/src/execute.rs b/contracts/entry-point/src/execute.rs index 2ac8ac62..0db4465d 100644 --- a/contracts/entry-point/src/execute.rs +++ b/contracts/entry-point/src/execute.rs @@ -93,6 +93,24 @@ pub fn receive_cw20( /// EXECUTE ENTRYPOINTS /// /////////////////////////// +// withdraw stuck coin and transfer back to user +// only owner can execute +pub fn execute_withdraw_asset( + deps: DepsMut, + info: MessageInfo, + coin: Asset, + receiver: Option, +) -> Result { + OWNER.assert_admin(deps.as_ref(), &info.sender)?; + let receiver = receiver.unwrap_or(info.sender); + + let msg = coin.transfer(receiver.as_str()); + + Ok(Response::new() + .add_attributes(vec![("action", "withdraw_asset")]) + .add_message(msg)) +} + // update config pub fn execute_update_config( deps: DepsMut, diff --git a/contracts/entry-point/tests/test_withdraw_asset.rs b/contracts/entry-point/tests/test_withdraw_asset.rs new file mode 100644 index 00000000..89534afa --- /dev/null +++ b/contracts/entry-point/tests/test_withdraw_asset.rs @@ -0,0 +1,139 @@ +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + Addr, BankMsg, Coin, CosmosMsg, SubMsg, Uint128, +}; +use skip::{asset::Asset, entry_point::ExecuteMsg, swap::SwapVenue}; +use skip_api_entry_point::{ + error::ContractError, + state::{BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT_ADDRESS, OWNER, SWAP_VENUE_MAP}, +}; +use test_case::test_case; + +/* +Test Cases: + +Expect Response + - Happy Path (tests the adapter and blocked contract addresses are stored correctly) + +Expect Error: + - Unauthorized +Expect Error + - Duplicate Swap Venue Names + */ + +// Define test parameters +struct Params { + owner: Option, + coin: Asset, + sender: String, + expected_error: Option, + expected_msgs: Vec, +} + +// Test instantiate +#[test_case( + Params { + owner: None, + coin: Asset::Native(Coin::new(1_000_000, "os")), + sender: "creator".to_string(), + expected_error: None, + expected_msgs: vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { + to_address: "creator".to_string(), + amount: vec![Coin { + denom: "os".to_string(), + amount: Uint128::new(1000000) + }] + }))] + }; + "Happy Path")] +#[test_case( + Params { + owner: None, + coin: Asset::Native(Coin::new(1_000_000, "os")), + sender: "sender".to_string(), + expected_error: Some(ContractError::AdminError(cw_controllers::AdminError::NotAdmin{})), + expected_msgs: vec![] + }; + + "Unauthorized")] +fn test_withdraw_asset(params: Params) { + // Create mock dependencies + let mut deps = mock_dependencies(); + + // Create mock info + let info = mock_info(¶ms.sender, &[]); + + // Create mock env with the entry point contract address + let mut env = mock_env(); + env.contract.address = Addr::unchecked("entry_point"); + + // init owner + OWNER + .set(deps.as_mut(), Some(Addr::unchecked("creator"))) + .unwrap(); + + // Call instantiate with the given test parameters + let res = skip_api_entry_point::contract::execute( + deps.as_mut(), + env, + info, + ExecuteMsg::WithdrawAsset { + coin: params.coin, + receiver: None, + }, + ); + + match res { + Ok(res) => { + // Assert the test did not expect an error + assert!( + params.expected_error.is_none(), + "expected test to error with {:?}, but it succeeded", + params.expected_error + ); + assert_eq!( + res.messages, + params.expected_msgs + ); + + // // Get stored ibc transfer adapter contract address + // let stored_ibc_transfer_contract_address = IBC_TRANSFER_CONTRACT_ADDRESS + // .load(deps.as_ref().storage) + // .unwrap(); + + // // Assert the ibc transfer adapter contract address exists in the blocked contract addresses map + // assert!(BLOCKED_CONTRACT_ADDRESSES + // .has(deps.as_ref().storage, &stored_ibc_transfer_contract_address)); + + // params.swap_venues.into_iter().for_each(|swap_venue| { + // // Get stored swap venue adapter contract address + // let stored_swap_venue_contract_address = SWAP_VENUE_MAP + // .may_load(deps.as_ref().storage, &swap_venue.name) + // .unwrap() + // .unwrap(); + + // // Assert the swap venue name exists in the map and that + // // the adapter contract address stored is correct + // assert_eq!( + // &stored_swap_venue_contract_address, + // &Addr::unchecked(&swap_venue.adapter_contract_address) + // ); + + // // Assert the swap adapter contract address exists in the blocked contract addresses map + // assert!(BLOCKED_CONTRACT_ADDRESSES + // .has(deps.as_ref().storage, &stored_swap_venue_contract_address)); + // }); + } + Err(err) => { + // Assert the test expected an error + assert!( + params.expected_error.is_some(), + "expected test to succeed, but it errored with {:?}", + err + ); + + // Assert the error is correct + assert_eq!(err, params.expected_error.unwrap()); + } + } +} diff --git a/packages/skip/src/entry_point.rs b/packages/skip/src/entry_point.rs index 5cd2b502..54d1fb71 100644 --- a/packages/skip/src/entry_point.rs +++ b/packages/skip/src/entry_point.rs @@ -78,6 +78,10 @@ pub enum ExecuteMsg { UniversalSwap { memo: String, }, + WithdrawAsset { + coin: Asset, + receiver: Option, + }, } /// This structure describes a CW20 hook message.