Skip to content

Commit

Permalink
⚡️ [API-2349] Preserve Pre Swap Out Asset Contract Balance (#83)
Browse files Browse the repository at this point in the history
* add admin address to deploy script configs

* reformat / fix lints to clean up script

* add admin param to instantiate/instantiate2 msgs

* bump contract versions to 0.3.0

* set contract version in entrypoint

* add contract version to ibc adapter contracts

* add contract version to swap adapter contracts

* remove unused python import

* remove another unused import in python

* add migrate message to entry point contract

* run make fmt

* add migrate msg to adapter contract

* run make schema

* use correct var name

* run make upgrade

* run make update

* update neutron sdk to 0.8

* update deps, remove deprecated functions, update tests after upgrade

* limit out asset to only what is obtained from the swap

* update existing tests with pre swap amount 0

* add test ensuring pre swap out asset contract balance preserved

* ensure PRE_SWAP_OUT_ASSET_AMOUNT is set properly in unit test

* run make fmt

* tidy
  • Loading branch information
NotJeremyLiu authored Jan 4, 2024
1 parent c12b6ef commit 11a778e
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 7 deletions.
26 changes: 22 additions & 4 deletions contracts/entry-point/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::{
error::{ContractError, ContractResult},
reply::{RecoverTempStorage, RECOVER_REPLY_ID},
state::{
BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT_ADDRESS, RECOVER_TEMP_STORAGE,
SWAP_VENUE_MAP,
BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT,
RECOVER_TEMP_STORAGE, SWAP_VENUE_MAP,
},
};
use cosmwasm_std::{
Expand Down Expand Up @@ -116,6 +116,11 @@ pub fn execute_swap_and_action(
return Err(ContractError::Timeout);
}

// Save the current out asset amount to storage as the pre swap out asset amount
let pre_swap_out_asset_amount =
get_current_asset_available(&deps, &env, min_asset.denom())?.amount();
PRE_SWAP_OUT_ASSET_AMOUNT.save(deps.storage, &pre_swap_out_asset_amount)?;

// Already validated at entrypoints (both direct and cw20_receive)
let mut remaining_asset = sent_asset;

Expand Down Expand Up @@ -432,9 +437,22 @@ pub fn execute_post_swap_action(
let mut response: Response =
Response::new().add_attribute("action", "execute_post_swap_action");

// Get contract balance of min out asset immediately after swap
// Get the pre swap out asset amount from storage
let pre_swap_out_asset_amount = PRE_SWAP_OUT_ASSET_AMOUNT.load(deps.storage)?;

// Get contract balance of min out asset post swap
// for fee deduction and transfer out amount enforcement
let transfer_out_asset = get_current_asset_available(&deps, &env, min_asset.denom())?;
let post_swap_out_asset = get_current_asset_available(&deps, &env, min_asset.denom())?;

// Set the transfer out asset to the post swap out asset amount minus the pre swap out asset amount
// Since we only want to transfer out the amount received from the swap
let transfer_out_asset = Asset::new(
deps.api,
min_asset.denom(),
post_swap_out_asset
.amount()
.checked_sub(pre_swap_out_asset_amount)?,
);

// Error if the contract balance is less than the min asset out amount
if transfer_out_asset.amount() < min_asset.amount() {
Expand Down
7 changes: 6 additions & 1 deletion contracts/entry-point/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::reply::RecoverTempStorage;
use cosmwasm_std::Addr;
use cosmwasm_std::{Addr, Uint128};
use cw_storage_plus::{Item, Map};

pub const BLOCKED_CONTRACT_ADDRESSES: Map<&Addr, ()> = Map::new("blocked_contract_addresses");
Expand All @@ -9,3 +9,8 @@ pub const IBC_TRANSFER_CONTRACT_ADDRESS: Item<Addr> = Item::new("ibc_transfer_co
// Temporary state to save variables to be used in
// reply handling in case of recovering from an error
pub const RECOVER_TEMP_STORAGE: Item<RecoverTempStorage> = Item::new("recover_temp_storage");

// Temporary state to save the amount of the out asset the contract
// has pre swap so that we can ensure the amount transferred out does not
// exceed the amount the contract obtained from the current swap/call
pub const PRE_SWAP_OUT_ASSET_AMOUNT: Item<Uint128> = Item::new("pre_swap_out_asset_amount");
50 changes: 49 additions & 1 deletion contracts/entry-point/tests/test_execute_post_swap_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use skip::{
};
use skip_api_entry_point::{
error::ContractError,
state::{BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT_ADDRESS},
state::{BLOCKED_CONTRACT_ADDRESSES, IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT},
};
use test_case::test_case;

Expand All @@ -38,6 +38,9 @@ Expect Response
- Ibc Transfer w/ IBC Fees of same denom as min coin With Exact Out Set To True
- Contract Call With Exact Out Set To True
// Invariants
- Pre Swap Out Asset Contract Balance Preserved
Expect Error
- Transfer Timeout
- Received Less Native Asset From Swap Than Min Asset
Expand All @@ -53,6 +56,7 @@ struct Params {
min_asset: Asset,
post_swap_action: Action,
exact_out: bool,
pre_swap_out_asset_amount: Uint128,
expected_messages: Vec<SubMsg>,
expected_error: Option<ContractError>,
}
Expand All @@ -66,6 +70,7 @@ struct Params {
to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(),
},
exact_out: true,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: BankMsg::Send {
Expand All @@ -87,6 +92,7 @@ struct Params {
to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: BankMsg::Send {
Expand All @@ -111,6 +117,7 @@ struct Params {
to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -144,6 +151,7 @@ struct Params {
fee_swap: None,
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -186,6 +194,7 @@ struct Params {
fee_swap: None,
},
exact_out: true,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -232,6 +241,7 @@ struct Params {
fee_swap: None,
},
exact_out: true,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -284,6 +294,7 @@ struct Params {
fee_swap: None,
},
exact_out: true,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -323,6 +334,7 @@ struct Params {
msg: to_json_binary(&"contract_call_msg").unwrap(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand All @@ -349,6 +361,7 @@ struct Params {
msg: to_json_binary(&"contract_call_msg").unwrap(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -376,6 +389,7 @@ struct Params {
msg: to_json_binary(&"contract_call_msg").unwrap(),
},
exact_out: true,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -410,6 +424,7 @@ struct Params {
fee_swap: None,
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -462,6 +477,7 @@ struct Params {
fee_swap: None,
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![SubMsg {
id: 0,
msg: WasmMsg::Execute {
Expand Down Expand Up @@ -492,6 +508,28 @@ struct Params {
expected_error: None,
};
"Ibc Transfer w/ IBC Fees of same denom as min coin")]
#[test_case(
Params {
caller: "entry_point".to_string(),
min_asset: Asset::Native(Coin::new(700_000, "os")),
post_swap_action: Action::Transfer {
to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(200_000),
expected_messages: vec![SubMsg {
id: 0,
msg: BankMsg::Send {
to_address: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5".to_string(),
amount: vec![Coin::new(800_000, "os")],
}
.into(),
gas_limit: None,
reply_on: Never,
}],
expected_error: None,
};
"Pre Swap Out Asset Contract Balance Preserved")]
#[test_case(
Params {
caller: "entry_point".to_string(),
Expand All @@ -511,6 +549,7 @@ struct Params {
fee_swap: None,
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![],
expected_error: Some(ContractError::NonNativeIbcTransfer),
};
Expand All @@ -523,6 +562,7 @@ struct Params {
to_address: "swapper".to_string(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![],
expected_error: Some(ContractError::ReceivedLessAssetFromSwapsThanMinAsset),
};
Expand All @@ -538,6 +578,7 @@ struct Params {
to_address: "swapper".to_string(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![],
expected_error: Some(ContractError::ReceivedLessAssetFromSwapsThanMinAsset),
};
Expand All @@ -550,6 +591,7 @@ struct Params {
to_address: "swapper".to_string(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![],
expected_error: Some(ContractError::Unauthorized),
};
Expand All @@ -563,6 +605,7 @@ struct Params {
msg: to_json_binary(&"contract_call_msg").unwrap(),
},
exact_out: false,
pre_swap_out_asset_amount: Uint128::new(0),
expected_messages: vec![],
expected_error: Some(ContractError::ContractCallAddressBlocked),
};
Expand Down Expand Up @@ -609,6 +652,11 @@ fn test_execute_post_swap_action(params: Params) {
.save(deps.as_mut().storage, &Addr::unchecked("entry_point"), &())
.unwrap();

// Store the pre swap out asset amount
PRE_SWAP_OUT_ASSET_AMOUNT
.save(deps.as_mut().storage, &params.pre_swap_out_asset_amount)
.unwrap();

// Call execute_post_swap_action with the given test parameters
let res = skip_api_entry_point::contract::execute(
deps.as_mut(),
Expand Down
6 changes: 5 additions & 1 deletion contracts/entry-point/tests/test_execute_swap_and_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use skip::{
};
use skip_api_entry_point::{
error::ContractError,
state::{IBC_TRANSFER_CONTRACT_ADDRESS, SWAP_VENUE_MAP},
state::{IBC_TRANSFER_CONTRACT_ADDRESS, PRE_SWAP_OUT_ASSET_AMOUNT, SWAP_VENUE_MAP},
};
use test_case::test_case;

Expand Down Expand Up @@ -1753,6 +1753,10 @@ fn test_execute_swap_and_action(params: Params) {

// Assert the messages in the response are correct
assert_eq!(res.messages, params.expected_messages,);

// Assert the pre swap out asset amount set is correct
let pre_swap_out_asset_amount = PRE_SWAP_OUT_ASSET_AMOUNT.load(&deps.storage).unwrap();
assert_eq!(pre_swap_out_asset_amount, Uint128::from(1_000_000u128));
}
Err(err) => {
// Assert the test expected an error
Expand Down

0 comments on commit 11a778e

Please sign in to comment.