Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reproduce simulate issue #24

Merged
merged 3 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions contracts/tfi-pair/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ thiserror = { version = "1.0.20" }
[dev-dependencies]
cosmwasm-schema = "0.14.0"
cosmwasm-storage = { version = "0.14.0" }
cw20-base = { version = "0.6.0", feautres = ["library"] }
cw-multi-test = { version = "0.6.0" }
tfi-mocks = { path = "../../packages/mocks", version = "0.0.5"}
7 changes: 7 additions & 0 deletions contracts/tfi-pair/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ pub fn provide_liquidity(
}

let pair_info: PairInfo = PAIR_INFO.load(deps.storage)?;
// we really should do this locally...
let mut pools: [Asset; 2] =
pair_info.query_pools(&deps.querier, env.contract.address.clone())?;
let deposits: [Uint128; 2] = [
Expand Down Expand Up @@ -558,6 +559,12 @@ fn compute_swap(
ask_pool.checked_sub(cp.multiply_ratio(1u128, offer_pool + offer_amount))?;

// calculate spread & commission
if offer_pool.is_zero() {
// return Err(StdError::divide_by_zero(ask_pool.to_string()).into());
return Err(StdError::generic_err(
"Divide by zero error computing the swap",
));
}
let spread_amount: Uint128 = (offer_amount * Decimal::from_ratio(ask_pool, offer_pool))
.checked_sub(return_amount)
.unwrap_or_else(|_| Uint128::zero());
Expand Down
2 changes: 2 additions & 0 deletions contracts/tfi-pair/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ pub mod state;

mod error;

#[cfg(test)]
mod multitest;
#[cfg(test)]
mod testing;
176 changes: 176 additions & 0 deletions contracts/tfi-pair/src/multitest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use cosmwasm_std::testing::{mock_env, MockApi, MockStorage};
use cosmwasm_std::{coins, Addr, Empty, StdError, Uint128};
use cw20::{Cw20Coin, Cw20ExecuteMsg};
use cw_multi_test::{App, Contract, ContractWrapper, SimpleBank};

use crate::error::ContractError;
use tfi::asset::{Asset, AssetInfo};
use tfi::pair::{ExecuteMsg, InstantiateMsg, QueryMsg, SimulationResponse};

fn mock_app() -> App {
let env = mock_env();
let api = Box::new(MockApi::default());
let bank = SimpleBank {};

App::new(api, env.block, bank, || Box::new(MockStorage::new()))
}

pub fn contract_pair() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
crate::contract::execute,
crate::contract::instantiate,
crate::contract::query,
);
Box::new(contract)
}

pub fn contract_cw20() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
cw20_base::contract::execute,
cw20_base::contract::instantiate,
cw20_base::contract::query,
);
Box::new(contract)
}

#[test]
// just do basic setup
fn setup_liquidity_pool() {
let mut app = mock_app();

// set personal balance
let owner = Addr::unchecked("owner");
let init_funds = coins(20000, "btc");
app.set_bank_balance(&owner, init_funds).unwrap();

// set up cw20 contract with some tokens
let cw20_id = app.store_code(contract_cw20());
let msg = cw20_base::msg::InstantiateMsg {
name: "Cash Money".to_string(),
symbol: "CASH".to_string(),
decimals: 2,
initial_balances: vec![Cw20Coin {
address: owner.to_string(),
amount: Uint128::new(50000),
}],
mint: None,
};
let cash_addr = app
.instantiate_contract(cw20_id, owner.clone(), &msg, &[], "CASH")
.unwrap();

// set up pair contract
let pair_id = app.store_code(contract_pair());
let msg = InstantiateMsg {
asset_infos: [
AssetInfo::Native("btc".into()),
AssetInfo::Token(cash_addr.clone()),
],
token_code_id: cw20_id,
};
let pair_addr = app
.instantiate_contract(pair_id, owner.clone(), &msg, &[], "Pair")
.unwrap();

// run a simulate query with wrong token
let query_msg = QueryMsg::Simulation {
offer_asset: Asset {
info: AssetInfo::Native("foobar".into()),
amount: Uint128::new(1000),
},
};
let err = app
.wrap()
.query_wasm_smart::<SimulationResponse, _, _>(&pair_addr, &query_msg)
.unwrap_err();
let expected_err = ContractError::AssetMismatch(AssetInfo::Native("foobar".into()).to_string());
assert!(
err.to_string().ends_with(&expected_err.to_string()),
"got: {}, expected: {}",
err.to_string(),
expected_err.to_string()
);

// simulate with proper token
let query_msg = QueryMsg::Simulation {
offer_asset: Asset {
info: AssetInfo::Token(cash_addr.clone()),
amount: Uint128::new(7000),
},
};
let err = app
.wrap()
.query_wasm_smart::<SimulationResponse, _, _>(&pair_addr, &query_msg)
.unwrap_err();
let expected_err = StdError::generic_err("Divide by zero error computing the swap");
assert!(
err.to_string().ends_with(&expected_err.to_string()),
"got: {}, expected: {}",
err.to_string(),
expected_err.to_string()
);

// provide an allowance to pay into LP
// let cash = Cw20Contract(cash_addr.clone());
let allow_msg = Cw20ExecuteMsg::IncreaseAllowance {
spender: pair_addr.to_string(),
amount: Uint128::new(10000),
expires: None,
};
let _ = app
.execute_contract(owner.clone(), cash_addr.clone(), &allow_msg, &[])
.unwrap();

// provide liquidity with proper tokens
let msg = ExecuteMsg::ProvideLiquidity {
assets: [
Asset {
info: AssetInfo::Native("btc".into()),
amount: Uint128::new(10),
},
Asset {
info: AssetInfo::Token(cash_addr),
amount: Uint128(7000),
},
],
slippage_tolerance: None,
};
// This is failing due to multitest limitations in 0.6
let _ = app
.execute_contract(owner, pair_addr, &msg, &coins(10, "btc"))
.unwrap_err();

// // simulate again
// let res: SimulationResponse = app.wrap().query_wasm_smart(&pair_addr, &query_msg).unwrap();
// // doubling the amount of cash should return half the BTC from the LP
// assert_eq!(res.return_amount, Uint128::new(5));

// // send some tokens to create an escrow
// let arb = Addr::unchecked("arbiter");
// let ben = String::from("beneficiary");
// let id = "demo".to_string();
// let create_msg = ReceiveMsg::Create(CreateMsg {
// id: id.clone(),
// arbiter: arb.to_string(),
// recipient: ben.clone(),
// end_height: None,
// end_time: None,
// cw20_whitelist: None,
// });
// let send_msg = Cw20ExecuteMsg::Send {
// contract: escrow_addr.to_string(),
// amount: Uint128::new(1200),
// msg: to_binary(&create_msg).unwrap(),
// };
// let res = router
// .execute_contract(owner.clone(), cash_addr.clone(), &send_msg, &[])
// .unwrap();
// assert_eq!(2, res.events.len());
// println!("{:?}", res.events);
// let cw20_attr = res.custom_attrs(0);
// println!("{:?}", cw20_attr);
// assert_eq!(4, cw20_attr.len());
// let escrow_attr = res.custom_attrs(1);
// println!("{:?}", escrow_attr);
// assert_eq!(2, escrow_attr.len());
}