Skip to content

Commit

Permalink
fix bug in swap adapter contract and add tests
Browse files Browse the repository at this point in the history
- must switch info.sender after the sent_asset is created
  • Loading branch information
NotJeremyLiu committed Nov 8, 2023
1 parent c0f809d commit 845f8de
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 4 deletions.
8 changes: 4 additions & 4 deletions contracts/adapters/swap/astroport/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,16 @@ pub fn receive_cw20(
mut info: MessageInfo,
cw20_msg: Cw20ReceiveMsg,
) -> ContractResult<Response> {
// Set the sender to the originating address that triggered the cw20 send call
// This is later validated / enforced to be the entry point contract address
info.sender = deps.api.addr_validate(&cw20_msg.sender)?;

let sent_asset = Asset::Cw20(Cw20Coin {
address: info.sender.to_string(),
amount: cw20_msg.amount,
});
sent_asset.validate(&deps, &env, &info)?;

// Set the sender to the originating address that triggered the cw20 send call
// This is later validated / enforced to be the entry point contract address
info.sender = deps.api.addr_validate(&cw20_msg.sender)?;

match from_binary(&cw20_msg.msg)? {
Cw20HookMsg::Swap { operations } => execute_swap(deps, env, info, sent_asset, operations),
}
Expand Down
271 changes: 271 additions & 0 deletions contracts/adapters/swap/astroport/tests/test_execute_receive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
use astroport::{
asset::AssetInfo,
router::{ExecuteMsg as RouterExecuteMsg, SwapOperation as AstroportSwapOperation},
};
use core::panic;
use cosmwasm_std::{
testing::{mock_dependencies, mock_env, mock_info},
to_binary, Addr, Coin, ContractResult as SystemContractResult, QuerierResult,
ReplyOn::Never,
SubMsg, SystemResult, Uint128, WasmMsg, WasmQuery,
};
use cw20::{BalanceResponse, Cw20Coin, Cw20ExecuteMsg, Cw20ReceiveMsg};
use cw_utils::PaymentError::NonPayable;
use skip::{
asset::Asset,
error::SkipError::Payment,
swap::{ExecuteMsg, SwapOperation},
};
use skip_api_swap_adapter_astroport::{
error::{ContractError, ContractResult},
state::{ENTRY_POINT_CONTRACT_ADDRESS, ROUTER_CONTRACT_ADDRESS},
};
use test_case::test_case;

/*
Test Cases:
Expect Success
- One Swap Operation - Cw20 In
- One Swap Operation - Cw20 In And Out
Expect Error
- Coin sent with cw20
*/

// Define test parameters
struct Params {
caller: String,
info_funds: Vec<Coin>,
sent_asset: Asset,
swap_operations: Vec<SwapOperation>,
expected_messages: Vec<SubMsg>,
expected_error: Option<ContractError>,
}

// Test execute_swap
#[test_case(
Params {
caller: "entry_point".to_string(),
info_funds: vec![],
sent_asset: Asset::Cw20(Cw20Coin {
address: "neutron123".to_string(),
amount: Uint128::from(100u128)
}),
swap_operations: vec![
SwapOperation {
pool: "pool_1".to_string(),
denom_in: "neutron123".to_string(),
denom_out: "ua".to_string(),
}
],
expected_messages: vec![
SubMsg {
id: 0,
msg: WasmMsg::Execute {
contract_addr: "neutron123".to_string(),
msg: to_binary(&Cw20ExecuteMsg::Send {
contract: "router_contract".to_string(),
amount: Uint128::from(100u128),
msg: to_binary(&RouterExecuteMsg::ExecuteSwapOperations {
operations: vec![
AstroportSwapOperation::AstroSwap {
offer_asset_info: AssetInfo::Token {
contract_addr: Addr::unchecked("neutron123"),
},
ask_asset_info: AssetInfo::NativeToken {
denom: "ua".to_string(),
},
}
],
minimum_receive: None,
to: None,
max_spread: None,
})?,
})?,
funds: vec![],
}.into(),
gas_limit: None,
reply_on: Never,
},
SubMsg {
id: 0,
msg: WasmMsg::Execute {
contract_addr: "swap_contract_address".to_string(),
msg: to_binary(&ExecuteMsg::TransferFundsBack {
return_denom: "ua".to_string(),
swapper: Addr::unchecked("entry_point"),
})?,
funds: vec![],
}
.into(),
gas_limit: None,
reply_on: Never,
},
],
expected_error: None,
};
"One Swap Operation - Cw20 In")]
#[test_case(
Params {
caller: "entry_point".to_string(),
info_funds: vec![],
sent_asset: Asset::Cw20(Cw20Coin {
address: "neutron123".to_string(),
amount: Uint128::from(100u128)
}),
swap_operations: vec![
SwapOperation {
pool: "pool_1".to_string(),
denom_in: "neutron123".to_string(),
denom_out: "neutron987".to_string(),
}
],
expected_messages: vec![
SubMsg {
id: 0,
msg: WasmMsg::Execute {
contract_addr: "neutron123".to_string(),
msg: to_binary(&Cw20ExecuteMsg::Send {
contract: "router_contract".to_string(),
amount: Uint128::from(100u128),
msg: to_binary(&RouterExecuteMsg::ExecuteSwapOperations {
operations: vec![
AstroportSwapOperation::AstroSwap {
offer_asset_info: AssetInfo::Token {
contract_addr: Addr::unchecked("neutron123"),
},
ask_asset_info: AssetInfo::Token {
contract_addr: Addr::unchecked("neutron987"),
},
}
],
minimum_receive: None,
to: None,
max_spread: None,
})?,
})?,
funds: vec![],
}.into(),
gas_limit: None,
reply_on: Never,
},
SubMsg {
id: 0,
msg: WasmMsg::Execute {
contract_addr: "swap_contract_address".to_string(),
msg: to_binary(&ExecuteMsg::TransferFundsBack {
return_denom: "neutron987".to_string(),
swapper: Addr::unchecked("entry_point"),
})?,
funds: vec![],
}
.into(),
gas_limit: None,
reply_on: Never,
},
],
expected_error: None,
};
"One Swap Operation - Cw20 In And Out")]
#[test_case(
Params {
caller: "entry_point".to_string(),
info_funds: vec![
Coin::new(100, "un"),
],
sent_asset: Asset::Cw20(Cw20Coin {
address: "neutron123".to_string(),
amount: Uint128::from(100u128)
}),
swap_operations: vec![],
expected_messages: vec![],
expected_error: Some(ContractError::Skip(Payment(NonPayable{}))),
};
"Coin sent with cw20 - Expect Error")]
fn test_execute_swap(params: Params) -> ContractResult<()> {
// Create mock dependencies
let mut deps = mock_dependencies();

// Create mock wasm handler to handle the swap adapter contract query
let wasm_handler = |query: &WasmQuery| -> QuerierResult {
match query {
WasmQuery::Smart { contract_addr, .. } => {
if contract_addr == "neutron123" {
SystemResult::Ok(SystemContractResult::Ok(
to_binary(&BalanceResponse {
balance: Uint128::from(100u128),
})
.unwrap(),
))
} else {
panic!("Unsupported contract: {:?}", query);
}
}
_ => panic!("Unsupported query: {:?}", query),
}
};

// Update querier with mock wasm handler
deps.querier.update_wasm(wasm_handler);

// Create mock env
let mut env = mock_env();
env.contract.address = Addr::unchecked("swap_contract_address");

// Convert info funds vector into a slice of Coin objects
let info_funds: &[Coin] = &params.info_funds;

// Create mock info with entry point contract address
let info = mock_info(params.sent_asset.denom(), info_funds);

// Store the entry point contract address
ENTRY_POINT_CONTRACT_ADDRESS.save(deps.as_mut().storage, &Addr::unchecked("entry_point"))?;

// Store the router contract address
ROUTER_CONTRACT_ADDRESS.save(deps.as_mut().storage, &Addr::unchecked("router_contract"))?;

// Call execute_swap with the given test parameters
let res = skip_api_swap_adapter_astroport::contract::execute(
deps.as_mut(),
env,
info,
ExecuteMsg::Receive(Cw20ReceiveMsg {
sender: params.caller,
amount: params.sent_asset.amount(),
msg: to_binary(&ExecuteMsg::Swap {
operations: params.swap_operations,
})
.unwrap(),
}),
);

// Assert the behavior is correct
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 the messages are correct
assert_eq!(res.messages, params.expected_messages);
}
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());
}
}

Ok(())
}

0 comments on commit 845f8de

Please sign in to comment.