From 58f89b26b46c45bd91ada51a320a9287f4f3cbd5 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sun, 12 Nov 2023 22:19:51 +0100 Subject: [PATCH] Happy path test, make notes --- contracts/external/cw-abc/src/abc.rs | 2 + contracts/external/cw-abc/src/commands.rs | 5 +- contracts/external/cw-abc/src/contract.rs | 2 +- .../cw-abc/src/test_tube/integration_tests.rs | 156 +++++++++++++++--- .../external/cw-abc/src/test_tube/test_env.rs | 58 ++----- 5 files changed, 149 insertions(+), 74 deletions(-) diff --git a/contracts/external/cw-abc/src/abc.rs b/contracts/external/cw-abc/src/abc.rs index 55cd3e62b..0adb044ce 100644 --- a/contracts/external/cw-abc/src/abc.rs +++ b/contracts/external/cw-abc/src/abc.rs @@ -40,6 +40,7 @@ pub struct HatchConfig { pub initial_raise: MinMax, /// The initial price (p0) per reserve token /// TODO: initial price is not implemented yet + /// TODO: do we need this or is it just calculated? pub initial_price: Uint128, /// The initial allocation (θ), percentage of the initial raise allocated to the Funding Pool pub initial_allocation_ratio: StdDecimal, @@ -86,6 +87,7 @@ impl HatchConfig { #[cw_serde] pub struct OpenConfig { + // TODO isn't this the same as initial_allocation_ratio? Maybe clearer to just call it an entrance fee? /// Percentage of capital put into the Reserve Pool during the Open phase pub allocation_percentage: StdDecimal, /// Exit taxation ratio diff --git a/contracts/external/cw-abc/src/commands.rs b/contracts/external/cw-abc/src/commands.rs index 77892388e..e807091d8 100644 --- a/contracts/external/cw-abc/src/commands.rs +++ b/contracts/external/cw-abc/src/commands.rs @@ -39,7 +39,7 @@ pub fn execute_buy( // Check if the initial_raise max has been met if curve_state.reserve + payment >= hatch_config.initial_raise.max { - // Transition to the Open phase, the hatchers' tokens are now vesting + // Transition to the Open phase phase = CommonsPhase::Open; PHASE.save(deps.storage, &phase)?; } @@ -54,10 +54,11 @@ pub fn execute_buy( } }; - // calculate how many tokens can be purchased with this and mint them + // Calculate how many tokens can be purchased with this and mint them let curve = curve_fn(curve_state.clone().decimals); curve_state.reserve += reserved; curve_state.funding += funded; + // Calculate the supply based on the reserve let new_supply = curve.supply(curve_state.reserve); let minted = new_supply diff --git a/contracts/external/cw-abc/src/contract.rs b/contracts/external/cw-abc/src/contract.rs index 4eb098cdb..8afb920e3 100644 --- a/contracts/external/cw-abc/src/contract.rs +++ b/contracts/external/cw-abc/src/contract.rs @@ -89,7 +89,7 @@ pub fn instantiate( PHASE_CONFIG.save(deps.storage, &phase_config)?; - // TODO don't hardcode this? + // TODO don't hardcode this? Make it configurable? Hatch config can be optional PHASE.save(deps.storage, &CommonsPhase::Hatch)?; // Initialize owner to sender diff --git a/contracts/external/cw-abc/src/test_tube/integration_tests.rs b/contracts/external/cw-abc/src/test_tube/integration_tests.rs index 921d8a55b..0f3b4280f 100644 --- a/contracts/external/cw-abc/src/test_tube/integration_tests.rs +++ b/contracts/external/cw-abc/src/test_tube/integration_tests.rs @@ -1,12 +1,14 @@ -use crate::msg::{ - CommonsPhaseConfigResponse, CurveInfoResponse, DenomResponse, ExecuteMsg, QueryMsg, +use crate::{ + abc::{ClosedConfig, CommonsPhase, CommonsPhaseConfig, HatchConfig, MinMax, OpenConfig}, + msg::{CommonsPhaseConfigResponse, CurveInfoResponse, DenomResponse, ExecuteMsg, QueryMsg}, }; -use super::test_env::{TestEnv, TestEnvBuilder}; +use super::test_env::{TestEnv, TestEnvBuilder, RESERVE}; -use cosmwasm_std::coins; +use cosmwasm_std::{coins, Decimal, Uint128}; use cw_tokenfactory_issuer::msg::QueryMsg as IssuerQueryMsg; -use osmosis_test_tube::{Account, OsmosisTestApp}; +use osmosis_std::types::cosmos::bank::v1beta1::QueryBalanceRequest; +use osmosis_test_tube::{osmosis_std::types::cosmos::base::v1beta1::Coin, Account, OsmosisTestApp}; #[test] fn test_happy_path() { @@ -22,7 +24,7 @@ fn test_happy_path() { } = env; // Buy tokens - abc.execute(&ExecuteMsg::Buy {}, &coins(1000000, "uosmo"), &accounts[0]) + abc.execute(&ExecuteMsg::Buy {}, &coins(1000, RESERVE), &accounts[0]) .unwrap(); // Query denom @@ -30,37 +32,137 @@ fn test_happy_path() { .query::(&IssuerQueryMsg::Denom {}) .unwrap() .denom; - println!("Denom {:?}", denom); // Query balances - let balances = env.bank().query_all_balances( - &osmosis_test_tube::osmosis_std::types::cosmos::bank::v1beta1::QueryAllBalancesRequest { + let user_balance = env + .bank() + .query_balance(&QueryBalanceRequest { address: accounts[0].address(), - pagination: None, - }, - ).unwrap(); - println!("{:?}", balances); + denom: denom.clone(), + }) + .unwrap(); + let contract_balance = env + .bank() + .query_balance(&QueryBalanceRequest { + address: abc.contract_addr.to_string(), + denom: RESERVE.to_string(), + }) + .unwrap(); + + // Check balances + assert_eq!( + user_balance.balance, + Some(Coin { + denom: denom.clone(), + amount: "9000".to_string(), + }) + ); + assert_eq!( + contract_balance.balance, + Some(Coin { + denom: RESERVE.to_string(), + amount: "1000".to_string(), + }) + ); // Query curve let curve_info: CurveInfoResponse = abc.query(&QueryMsg::CurveInfo {}).unwrap(); - println!("Curve {:?}", curve_info); + assert_eq!( + curve_info, + CurveInfoResponse { + reserve: Uint128::new(900), + supply: Uint128::new(9000), + funding: Uint128::new(100), + spot_price: Decimal::percent(10u64), + reserve_denom: RESERVE.to_string(), + } + ); + // Query phase let phase: CommonsPhaseConfigResponse = abc.query(&QueryMsg::PhaseConfig {}).unwrap(); - println!("Phase {:?}", phase); - - // Contract balances - let balances = env.bank().query_all_balances( - &osmosis_test_tube::osmosis_std::types::cosmos::bank::v1beta1::QueryAllBalancesRequest { - address: abc.contract_addr.to_string(), - pagination: None, - }, - ).unwrap(); - println!("{:?}", balances); + assert_eq!(phase.phase, CommonsPhase::Hatch); + assert_eq!( + phase.phase_config, + CommonsPhaseConfig { + hatch: HatchConfig { + initial_raise: MinMax { + min: Uint128::one(), + max: Uint128::from(1000000u128), + }, + initial_price: Uint128::one(), + initial_allocation_ratio: Decimal::percent(10u64), + exit_tax: Decimal::percent(10u64), + }, + open: OpenConfig { + allocation_percentage: Decimal::percent(10u64), + exit_tax: Decimal::percent(10u64), + }, + closed: ClosedConfig {}, + } + ); // Burn - abc.execute(&ExecuteMsg::Burn {}, &coins(10000, denom), &accounts[0]) - .unwrap(); + abc.execute( + &ExecuteMsg::Burn {}, + &coins(100, denom.clone()), + &accounts[0], + ) + .unwrap(); + // Query curve let curve_info: CurveInfoResponse = abc.query(&QueryMsg::CurveInfo {}).unwrap(); - println!("Curve {:?}", curve_info); + assert_eq!( + curve_info, + CurveInfoResponse { + reserve: Uint128::new(890), + supply: Uint128::new(8900), + funding: Uint128::new(110), + spot_price: Decimal::percent(10u64), + reserve_denom: RESERVE.to_string(), + } + ); + + // Query balances + let user_balance = env + .bank() + .query_balance(&QueryBalanceRequest { + address: accounts[0].address(), + denom: denom.clone(), + }) + .unwrap(); + let contract_balance = env + .bank() + .query_balance(&QueryBalanceRequest { + address: abc.contract_addr.to_string(), + denom: RESERVE.to_string(), + }) + .unwrap(); + + // Check balances + assert_eq!( + user_balance.balance, + Some(Coin { + denom: denom.clone(), + amount: "8800".to_string(), + }) + ); + assert_eq!( + contract_balance.balance, + Some(Coin { + denom: RESERVE.to_string(), + amount: "990".to_string(), + }) + ); + + // Buy enough tokens to end the hatch phase + abc.execute( + &ExecuteMsg::Buy {}, + &coins(1000000000, RESERVE), + &accounts[0], + ) + .unwrap(); + + // Contract is now in open phase + let phase: CommonsPhaseConfigResponse = abc.query(&QueryMsg::PhaseConfig {}).unwrap(); + assert_eq!(phase.phase, CommonsPhase::Open); } diff --git a/contracts/external/cw-abc/src/test_tube/test_env.rs b/contracts/external/cw-abc/src/test_tube/test_env.rs index b68f16ac3..2dd020c24 100644 --- a/contracts/external/cw-abc/src/test_tube/test_env.rs +++ b/contracts/external/cw-abc/src/test_tube/test_env.rs @@ -23,6 +23,7 @@ use std::fmt::Debug; use std::path::PathBuf; pub const DENOM: &str = "ucat"; + // Needs to match what's configured for test-tube pub const RESERVE: &str = "uosmo"; @@ -87,22 +88,16 @@ impl<'a> TestEnv<'a> { } } -pub struct TestEnvBuilder { - pub accounts: Vec, - pub instantiate_msg: Option, -} +pub struct TestEnvBuilder {} impl TestEnvBuilder { pub fn new() -> Self { - Self { - accounts: vec![], - instantiate_msg: None, - } + Self {} } pub fn default_setup(self, app: &'_ OsmosisTestApp) -> TestEnv<'_> { let accounts = app - .init_accounts(&[Coin::new(1000000000000000u128, "uosmo")], 10) + .init_accounts(&[Coin::new(1000000000000000u128, RESERVE)], 10) .unwrap(); let issuer_id = TokenfactoryIssuer::upload(app, &accounts[0]).unwrap(); @@ -128,7 +123,7 @@ impl TestEnvBuilder { }, initial_price: Uint128::one(), initial_allocation_ratio: Decimal::percent(10u64), - exit_tax: Decimal::zero(), + exit_tax: Decimal::percent(10u64), }, open: OpenConfig { allocation_percentage: Decimal::percent(10u64), @@ -158,28 +153,18 @@ impl TestEnvBuilder { } } - pub fn build(self, app: &'_ OsmosisTestApp) -> TestEnv<'_> { - let accounts = self.accounts; + pub fn setup(self, app: &'_ OsmosisTestApp, msg: InstantiateMsg) -> TestEnv<'_> { + let accounts = app + .init_accounts(&[Coin::new(1000000000000000u128, RESERVE)], 10) + .unwrap(); - let abc = CwAbc::deploy( - app, - self.instantiate_msg - .as_ref() - .expect("instantiate msg not set"), - &accounts[0], - ) - .unwrap(); + let issuer_id = TokenfactoryIssuer::upload(app, &accounts[0]).unwrap(); + + let abc = CwAbc::deploy(app, &msg, &accounts[0]).unwrap(); let issuer_addr = CwAbc::query(&abc, &QueryMsg::TokenContract {}).unwrap(); - let tf_issuer = TokenfactoryIssuer::new_with_values( - app, - self.instantiate_msg - .expect("instantiate msg not set") - .token_issuer_code_id, - issuer_addr, - ) - .unwrap(); + let tf_issuer = TokenfactoryIssuer::new_with_values(app, issuer_id, issuer_addr).unwrap(); TestEnv { app, @@ -192,21 +177,6 @@ impl TestEnvBuilder { pub fn upload_issuer(self, app: &'_ OsmosisTestApp, signer: &SigningAccount) -> u64 { TokenfactoryIssuer::upload(app, signer).unwrap() } - - pub fn set_accounts(mut self, accounts: Vec) -> Self { - self.accounts = accounts; - self - } - - pub fn with_account(mut self, account: SigningAccount) -> Self { - self.accounts.push(account); - self - } - - pub fn with_instantiate_msg(mut self, msg: InstantiateMsg) -> Self { - self.instantiate_msg = Some(msg); - self - } } pub fn assert_contract_err(expected: ContractError, actual: RunnerError) { @@ -237,7 +207,7 @@ impl<'a> CwAbc<'a> { signer: &SigningAccount, ) -> Result { let wasm = Wasm::new(app); - let token_creation_fee = Coin::new(10000000, "uosmo"); + let token_creation_fee = Coin::new(10000000, RESERVE); let code_id = wasm .store_code(&Self::get_wasm_byte_code(), None, signer)?