diff --git a/Cargo.lock b/Cargo.lock index 51d32ef..94ced16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1473,6 +1473,13 @@ dependencies = [ "uint", ] +[[package]] +name = "mock-burrowland" +version = "0.1.0" +dependencies = [ + "near-sdk", +] + [[package]] name = "mock-price-oracle" version = "0.1.0" @@ -2348,10 +2355,11 @@ dependencies = [ [[package]] name = "ref-exchange" -version = "1.9.18" +version = "1.9.19" dependencies = [ "hex", "mock-boost-farming", + "mock-burrowland", "mock-price-oracle", "mock-pyth", "mock-wnear", diff --git a/Cargo.toml b/Cargo.toml index aecb8da..3815160 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "./ref-farming", "./test-rated-token", "./mock-boost-farming", + "./mock-burrowland", "./mock-price-oracle", "./mock-wnear", "./mock-pyth" diff --git a/Makefile b/Makefile index dbcc836..4f4fcb5 100644 --- a/Makefile +++ b/Makefile @@ -21,20 +21,20 @@ else RUSTFLAGS=$(RFLAGS) cargo test -p ref-exchange --lib -- --nocapture endif -test: build-exchange mock-ft mock-rated mock-farming test-wnear test-price-oracle test-pyth +test: build-exchange mock-ft mock-rated mock-farming test-wnear test-price-oracle test-pyth mock-burrow ifdef TF RUSTFLAGS=$(RFLAGS) cargo test -p ref-exchange --test $(TF) -- --nocapture else RUSTFLAGS=$(RFLAGS) cargo test -p ref-exchange --tests endif -test-exchange: build-exchange mock-ft mock-rated mock-farming test-wnear test-price-oracle test-pyth +test-exchange: build-exchange mock-ft mock-rated mock-farming test-wnear test-price-oracle test-pyth mock-burrow RUSTFLAGS=$(RFLAGS) cargo test -p ref-exchange test-farm: build-farm mock-ft RUSTFLAGS=$(RFLAGS) cargo test -p ref_farming -test-release: mock-ft mock-rated mock-farming test-wnear test-price-oracle test-pyth +test-release: mock-ft mock-rated mock-farming test-wnear test-price-oracle test-pyth mock-burrow mkdir -p res cp ./releases/ref_exchange_release.wasm ./res/ref_exchange.wasm RUSTFLAGS=$(RFLAGS) cargo test -p ref-exchange @@ -57,6 +57,12 @@ mock-farming: mock-boost-farming mkdir -p res cp target/wasm32-unknown-unknown/release/mock_boost_farming.wasm ./res/mock_boost_farming.wasm +mock-burrow: mock-burrowland + rustup target add wasm32-unknown-unknown + RUSTFLAGS=$(RFLAGS) cargo build -p mock-burrowland --target wasm32-unknown-unknown --release + mkdir -p res + cp target/wasm32-unknown-unknown/release/mock_burrowland.wasm ./res/mock_burrowland.wasm + test-wnear: mock-wnear rustup target add wasm32-unknown-unknown RUSTFLAGS=$(RFLAGS) cargo build -p mock-wnear --target wasm32-unknown-unknown --release diff --git a/mock-burrowland/Cargo.toml b/mock-burrowland/Cargo.toml new file mode 100644 index 0000000..d91d5dd --- /dev/null +++ b/mock-burrowland/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "mock-burrowland" +version = "0.1.0" +edition = "2018" +publish = false + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +near-sdk = "3.1.0" diff --git a/mock-burrowland/src/lib.rs b/mock-burrowland/src/lib.rs new file mode 100644 index 0000000..82b55b8 --- /dev/null +++ b/mock-burrowland/src/lib.rs @@ -0,0 +1,35 @@ +use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::json_types::U128; +use near_sdk::{near_bindgen, AccountId, PanicOnDefault}; + +#[near_bindgen] +#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)] +pub struct Contract; + +#[near_bindgen] +impl Contract { + #[init] + pub fn new() -> Self { + Self + } + + #[allow(unused_variables)] + pub fn on_cast_shadow( + &mut self, + account_id: AccountId, + shadow_id: String, + amount: U128, + msg: String, + ) { + } + + #[allow(unused_variables)] + pub fn on_remove_shadow( + &mut self, + account_id: AccountId, + shadow_id: String, + amount: U128, + msg: String, + ) { + } +} diff --git a/ref-exchange/Cargo.toml b/ref-exchange/Cargo.toml index cf516ec..1c3c8c2 100644 --- a/ref-exchange/Cargo.toml +++ b/ref-exchange/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ref-exchange" -version = "1.9.18" +version = "1.9.19" authors = ["Illia Polosukhin "] edition = "2018" publish = false @@ -20,8 +20,9 @@ near-sdk-sim = "3.1.0" test-token = { path = "../test-token" } test-rated-token = { path = "../test-rated-token" } mock-boost-farming = { path = "../mock-boost-farming" } +mock-burrowland = { path = "../mock-burrowland" } mock-wnear = { path = "../mock-wnear" } mock-price-oracle = { path = "../mock-price-oracle" } mock-pyth = { path = "../mock-pyth" } rand = "0.8" -rand_pcg = "0.3" \ No newline at end of file +rand_pcg = "0.3" diff --git a/ref-exchange/release_notes.md b/ref-exchange/release_notes.md index 70fcf12..f32a369 100644 --- a/ref-exchange/release_notes.md +++ b/ref-exchange/release_notes.md @@ -1,5 +1,12 @@ # Release Notes +### Version 1.9.19 +``` +AEAm8sQ5KYZRgnpq2CN2NhX6d8QV76iRtiDpy5Ai6zDX +``` +1. prevent donation_share from donating shadow-locked shares. +2. validate token prices on all add_liquidity calls. + ### Version 1.9.18 ``` AqbqDX2n5AJ6zh7VEL7R49yPejgAE5YUySxWM6RepTiX diff --git a/ref-exchange/src/degen_swap/mod.rs b/ref-exchange/src/degen_swap/mod.rs index d405ddc..e32a17f 100644 --- a/ref-exchange/src/degen_swap/mod.rs +++ b/ref-exchange/src/degen_swap/mod.rs @@ -219,11 +219,10 @@ impl DegenSwapPool { // make amounts into comparable-amounts let c_amounts = self.amounts_to_c_amounts(amounts); - + if self.shares_total_supply == 0 { // Bootstrapping the pool, request providing all non-zero balances, // and all fee free. - self.assert_degens_valid(); for c_amount in &c_amounts { assert!(*c_amount > 0, "{}", ERR65_INIT_TOKEN_BALANCE); @@ -258,9 +257,17 @@ impl DegenSwapPool { let n_coins = self.token_account_ids.len(); assert_eq!(amounts.len(), n_coins, "{}", ERR64_TOKENS_COUNT_ILLEGAL); + let degens = match degens { + Some(d) => d.clone(), + None => { + self.assert_degens_valid(); + self.get_degens() + } + }; + let (new_shares, _) = self.calc_add_liquidity_with_degens( amounts, - degens.as_ref().unwrap_or(&self.get_degens()), + °ens, fees ); @@ -282,6 +289,7 @@ impl DegenSwapPool { let n_coins = self.token_account_ids.len(); assert_eq!(amounts.len(), n_coins, "{}", ERR64_TOKENS_COUNT_ILLEGAL); + self.assert_degens_valid(); let (new_shares, fee_part) = self.calc_add_liquidity_with_degens(amounts, &self.get_degens(), fees); //slippage check on the LP tokens. diff --git a/ref-exchange/src/donation.rs b/ref-exchange/src/donation.rs index 8c8193e..079d7a4 100644 --- a/ref-exchange/src/donation.rs +++ b/ref-exchange/src/donation.rs @@ -18,8 +18,15 @@ impl Contract { let account_id = env::predecessor_account_id(); let prev_storage = env::storage_usage(); let mut pool = self.pools.get(pool_id).expect(ERR85_NO_POOL); - let donation_amount = amount.map(|v| v.0).unwrap_or(pool.share_balances(&account_id)); - assert!(donation_amount > 0, "Invalid amount"); + let total_shares = pool.share_balances(&account_id); + let donation_amount = amount.map(|v| v.0).unwrap_or(total_shares); + assert!(donation_amount > 0 && donation_amount <= total_shares, "Invalid amount"); + if let Some(account) = self.internal_get_account(&account_id) { + if let Some(record) = account.get_shadow_record(pool_id) { + let available_shares = record.free_shares(total_shares); + assert!(available_shares >= donation_amount, "Cannot donate shadow-locked shares"); + } + } pool.share_transfer(&account_id, &env::current_account_id(), donation_amount); if unregister == Some(true) { pool.share_unregister(&account_id); diff --git a/ref-exchange/src/rated_swap/mod.rs b/ref-exchange/src/rated_swap/mod.rs index f011f83..ce54d09 100644 --- a/ref-exchange/src/rated_swap/mod.rs +++ b/ref-exchange/src/rated_swap/mod.rs @@ -221,7 +221,6 @@ impl RatedSwapPool { if self.shares_total_supply == 0 { // Bootstrapping the pool, request providing all non-zero balances, // and all fee free. - self.assert_rates_valid(); for c_amount in &c_amounts { assert!(*c_amount > 0, "{}", ERR65_INIT_TOKEN_BALANCE); @@ -256,9 +255,17 @@ impl RatedSwapPool { let n_coins = self.token_account_ids.len(); assert_eq!(amounts.len(), n_coins, "{}", ERR64_TOKENS_COUNT_ILLEGAL); + let rates = match rates { + Some(r) => r.clone(), + None => { + self.assert_rates_valid(); + self.get_rates() + } + }; + let (new_shares, _) = self.calc_add_liquidity_with_rates( amounts, - rates.as_ref().unwrap_or(&self.get_rates()), + &rates, fees ); @@ -280,6 +287,7 @@ impl RatedSwapPool { let n_coins = self.token_account_ids.len(); assert_eq!(amounts.len(), n_coins, "{}", ERR64_TOKENS_COUNT_ILLEGAL); + self.assert_rates_valid(); let (new_shares, fee_part) = self.calc_add_liquidity_with_rates(amounts, &self.get_rates(), fees); //slippage check on the LP tokens. diff --git a/ref-exchange/tests/test_degen_pool.rs b/ref-exchange/tests/test_degen_pool.rs index 4bf5f7c..0098cf4 100644 --- a/ref-exchange/tests/test_degen_pool.rs +++ b/ref-exchange/tests/test_degen_pool.rs @@ -636,3 +636,183 @@ fn batch_update_degen_token_same_price_id() { This test verifies the fix for the issue where only one token was updated." ); } + +#[test] +fn test_add_liquidity_fails_when_prices_expired() { + let (root, owner, pool, tokens) = + setup_degen_pool( + vec![eth(), near()], + vec![100000*ONE_ETH, 100000*ONE_NEAR], + vec![18, 24], + 25, + 10000, + ); + + let eth_token = tokens.get(0).unwrap(); + let near_token = tokens.get(1).unwrap(); + let pyth_contract = setup_pyth_oracle(&root); + let price_oracle_contract = setup_price_oracle(&root); + + // Register oracle configs with 1 hour expiration + call!( + owner, + pool.register_degen_oracle_config(DegenOracleConfig::PriceOracle(PriceOracleConfig { + oracle_id: price_oracle(), + expire_ts: 3600 * 10u64.pow(9), // 1 hour + maximum_recency_duration_sec: 90, + maximum_staleness_duration_sec: 90 + })), + deposit = 1 + ).assert_success(); + + call!( + owner, + pool.register_degen_oracle_config(DegenOracleConfig::PythOracle(PythOracleConfig { + oracle_id: pyth_oracle(), + expire_ts: 3600 * 10u64.pow(9), // 1 hour + pyth_price_valid_duration_sec: 60 + })), + deposit = 1 + ).assert_success(); + + // Register degen tokens + call!( + owner, + pool.register_degen_token(to_va(eth()), DegenType::PriceOracle { decimals: 18 }), + deposit = 1 + ).assert_success(); + + call!( + owner, + pool.register_degen_token(to_va(near()), DegenType::PythOracle { + price_identifier: ref_exchange::pyth_oracle::PriceIdentifier( + hex::decode("27e867f0f4f61076456d1a73b14c7edc1cf5cef4f4d6193a33424288f11bd0f4") + .unwrap() + .try_into() + .unwrap() + ) + }), + deposit = 1 + ).assert_success(); + + // Set initial prices + let block_timestamp = root.borrow_runtime().current_block().block_timestamp; + call!( + root, + pyth_contract.set_price( + mock_pyth::PriceIdentifier( + hex::decode("27e867f0f4f61076456d1a73b14c7edc1cf5cef4f4d6193a33424288f11bd0f4") + .unwrap() + .try_into() + .unwrap() + ), + PythPrice { + price: 100000000.into(), + conf: 397570.into(), + expo: -8, + publish_time: nano_to_sec(block_timestamp) as i64, + } + ) + ).assert_success(); + + call!( + root, + price_oracle_contract.set_price_data(eth(), Price { + multiplier: 10000, + decimals: 22, + }) + ).assert_success(); + + // Update prices to make them valid + call!(root, pool.update_degen_token_price(to_va(eth())), deposit = 0).assert_success(); + call!(root, pool.update_degen_token_price(to_va(near())), deposit = 0).assert_success(); + + // First add liquidity should succeed with valid prices + let user1 = root.create_user("user1".to_string(), to_yocto("100")); + mint_and_deposit_token(&user1, eth_token, &pool, 50000*ONE_ETH); + mint_and_deposit_token(&user1, near_token, &pool, 50000*ONE_NEAR); + + let out_come = call!( + user1, + pool.add_stable_liquidity( + 0, + vec![U128(50000*ONE_ETH), U128(50000*ONE_NEAR)], + U128(1) + ), + deposit = to_yocto("0.0007") + ); + out_come.assert_success(); + + // Simulate time passing (2 hours to exceed the 1-hour expiration) + root.borrow_runtime_mut().cur_block.block_timestamp += 2 * 3600 * 10u64.pow(9); + + // Prepare for second add liquidity + let user2 = root.create_user("user2".to_string(), to_yocto("100")); + mint_and_deposit_token(&user2, eth_token, &pool, 25000*ONE_ETH); + mint_and_deposit_token(&user2, near_token, &pool, 25000*ONE_NEAR); + + // Try to add liquidity with expired prices - should fail + let out_come = call!( + user2, + pool.add_stable_liquidity( + 0, + vec![U128(25000*ONE_ETH), U128(25000*ONE_NEAR)], + U128(1) + ), + deposit = to_yocto("0.0007") + ); + assert_eq!(get_error_count(&out_come), 1); + assert!(get_error_status(&out_come).contains("E129: Degens expired")); + + // Update only ETH price + call!(root, pool.update_degen_token_price(to_va(eth())), deposit = 0).assert_success(); + + // Try again with only one price updated - should still fail because NEAR price is expired + let out_come = call!( + user2, + pool.add_stable_liquidity( + 0, + vec![U128(25000*ONE_ETH), U128(25000*ONE_NEAR)], + U128(1) + ), + deposit = to_yocto("0.0007") + ); + assert_eq!(get_error_count(&out_come), 1); + assert!(get_error_status(&out_come).contains("E129: Degens expired")); + + // Update NEAR price as well (need to update pyth oracle timestamp) + let new_block_timestamp = root.borrow_runtime().current_block().block_timestamp; + call!( + root, + pyth_contract.set_price( + mock_pyth::PriceIdentifier( + hex::decode("27e867f0f4f61076456d1a73b14c7edc1cf5cef4f4d6193a33424288f11bd0f4") + .unwrap() + .try_into() + .unwrap() + ), + PythPrice { + price: 100000000.into(), + conf: 397570.into(), + expo: -8, + publish_time: nano_to_sec(new_block_timestamp) as i64, + } + ) + ).assert_success(); + call!(root, pool.update_degen_token_price(to_va(near())), deposit = 0).assert_success(); + + // Now with both prices updated, add liquidity should succeed + let out_come = call!( + user2, + pool.add_stable_liquidity( + 0, + vec![U128(25000*ONE_ETH), U128(25000*ONE_NEAR)], + U128(1) + ), + deposit = to_yocto("0.0007") + ); + out_come.assert_success(); + // user2 added 25000 each, which is 1/4 of initial liquidity (100000 each) + // Initial LPT = 200000, so user2 should get 50000 LPT + assert_eq!(mft_balance_of(&pool, ":0", &user2.account_id()), 50000*ONE_LPT); +} diff --git a/ref-exchange/tests/test_donation.rs b/ref-exchange/tests/test_donation.rs index ba2ac52..afee02b 100644 --- a/ref-exchange/tests/test_donation.rs +++ b/ref-exchange/tests/test_donation.rs @@ -1,11 +1,22 @@ use std::collections::HashMap; use near_sdk::json_types::U128; +use near_sdk::serde_json::json; use near_sdk::AccountId; -use near_sdk_sim::{call, to_yocto, view}; +use near_sdk_sim::{call, deploy, to_yocto, view}; + +use mock_burrowland::ContractContract as MockBurrowland; use crate::common::utils::*; pub mod common; +near_sdk_sim::lazy_static_include::lazy_static_include_bytes! { + MOCK_BURROWLAND_WASM_BYTES => "../res/mock_burrowland.wasm", +} + +fn burrowland() -> AccountId { + "burrowland".to_string() +} + #[test] fn donation_share() { let (root, _owner, pool, _token1, _token2, _token3) = setup_pool_with_liquidity(); @@ -68,6 +79,54 @@ fn donation_share() { assert!(deposit_before_donation_share < get_storage_state(&pool, to_va(root.account_id.clone())).unwrap().deposit.0); } +#[test] +fn donation_share_respects_burrow_locked_shares() { + let (root, _owner, pool, _token1, _token2, _token3) = setup_pool_with_liquidity(); + let _mock_burrowland = deploy!( + contract: MockBurrowland, + contract_id: burrowland(), + bytes: &MOCK_BURROWLAND_WASM_BYTES, + signer_account: root, + init_method: new() + ); + + let total_shares = mft_balance_of(&pool, ":0", &root.account_id()); + let lock_amount = total_shares / 2; + assert!(lock_amount > 0); + + let args = json!({ + "action": "ToBurrowland", + "pool_id": 0, + "amount": U128(lock_amount), + "msg": "lock", + }); + root.call( + pool.user_account.account_id.clone(), + "shadow_action", + &near_sdk::serde_json::to_vec(&args).unwrap(), + near_sdk_sim::DEFAULT_GAS, + to_yocto("0.01"), + ) + .assert_success(); + + let free_shares = total_shares - lock_amount; + let outcome = call!( + root, + pool.donation_share(0, Some(U128(free_shares + 1)), None), + deposit = 1 + ); + assert_eq!(get_error_count(&outcome), 1); + assert!(get_error_status(&outcome).contains("Cannot donate shadow-locked shares")); + + call!( + root, + pool.donation_share(0, Some(U128(free_shares)), None), + deposit = 1 + ) + .assert_success(); + assert_eq!(mft_balance_of(&pool, ":0", &root.account_id()), lock_amount); +} + #[test] fn donation_token() { let (root, owner, pool, token1, _token2, _token3) = setup_pool_with_liquidity(); @@ -135,4 +194,4 @@ fn donation_token() { let balances = balances.get(&token1.account_id()).unwrap().0; assert_eq!(balances, to_yocto("600")); -} \ No newline at end of file +} diff --git a/ref-exchange/tests/test_migrate.rs b/ref-exchange/tests/test_migrate.rs index 4971900..fd29a37 100644 --- a/ref-exchange/tests/test_migrate.rs +++ b/ref-exchange/tests/test_migrate.rs @@ -51,7 +51,7 @@ fn test_upgrade() { .assert_success(); let metadata = get_metadata(&pool); // println!("{:#?}", metadata); - assert_eq!(metadata.version, "1.9.18".to_string()); + assert_eq!(metadata.version, "1.9.19".to_string()); assert_eq!(metadata.admin_fee_bps, 5); assert_eq!(metadata.boost_farm_id, "boost_farm".to_string()); assert_eq!(metadata.burrowland_id, "burrowland".to_string()); diff --git a/ref-exchange/tests/test_rated_pool.rs b/ref-exchange/tests/test_rated_pool.rs index 70bfea3..131a286 100644 --- a/ref-exchange/tests/test_rated_pool.rs +++ b/ref-exchange/tests/test_rated_pool.rs @@ -2131,6 +2131,131 @@ fn sim_sfrax_rated_swap_liquidity_two_pyth() { } +#[test] +fn test_add_liquidity_fails_when_rates_expired() { + let (root, owner, pool, tokens, token_rated_contracts) = + setup_rated_pool_with_liquidity( + vec![near()], + vec![stnear(), linear()], + vec![100000*ONE_NEAR], + vec![100000*ONE_STNEAR, 100000*ONE_LINEAR], + vec![24, 24, 24], + 25, + 10000, + ); + + let stnear_contract = &token_rated_contracts[0]; + let linear_contract = &token_rated_contracts[1]; + + call!( + owner, + pool.register_rated_token( + "STNEAR".to_string(), + token_rated_contracts[0].valid_account_id(), + None + ), + deposit = 1 + ).assert_success(); + + call!( + owner, + pool.register_rated_token( + "LINEAR".to_string(), + token_rated_contracts[1].valid_account_id(), + None + ), + deposit = 1 + ).assert_success(); + + // Update both token rates to make them valid initially + call!( + owner, + pool.update_token_rate( + stnear_contract.valid_account_id() + ), + deposit = 1 + ).assert_success(); + + call!( + owner, + pool.update_token_rate( + linear_contract.valid_account_id() + ), + deposit = 1 + ).assert_success(); + + // First add liquidity should succeed with valid rates + let user1 = root.create_user("user1".to_string(), to_yocto("100")); + mint_and_deposit_token(&user1, &tokens[0], &pool, 100000*ONE_NEAR); + mint_and_deposit_rated_token(&user1, &token_rated_contracts[0], &pool, 100000*ONE_STNEAR); + mint_and_deposit_rated_token(&user1, &token_rated_contracts[1], &pool, 100000*ONE_LINEAR); + + let out_come = call!( + user1, + pool.add_stable_liquidity(0, vec![ + U128(100000*ONE_NEAR), U128(100000*ONE_STNEAR), U128(100000*ONE_LINEAR)], U128(1)), + deposit = to_yocto("0.0007") + ); + out_come.assert_success(); + + // Simulate time passing (25 hours in nanoseconds to exceed the 24-hour expiration) + root.borrow_runtime_mut().cur_block.block_timestamp += 25 * 3600 * 10u64.pow(9); + + // Now prepare for second add liquidity but with expired rates + let user2 = root.create_user("user2".to_string(), to_yocto("100")); + mint_and_deposit_token(&user2, &tokens[0], &pool, 50000*ONE_NEAR); + mint_and_deposit_rated_token(&user2, &token_rated_contracts[0], &pool, 50000*ONE_STNEAR); + mint_and_deposit_rated_token(&user2, &token_rated_contracts[1], &pool, 50000*ONE_LINEAR); + + // Try to add liquidity with expired rates (not updated) - should fail + let out_come = call!( + user2, + pool.add_stable_liquidity(0, vec![ + U128(50000*ONE_NEAR), U128(50000*ONE_STNEAR), U128(50000*ONE_LINEAR)], U128(1)), + deposit = to_yocto("0.0007") + ); + assert_eq!(get_error_count(&out_come), 1); + assert!(get_error_status(&out_come).contains("E120: Rates expired")); + + // Update only stnear rate + call!( + owner, + pool.update_token_rate( + stnear_contract.valid_account_id() + ), + deposit = 1 + ).assert_success(); + + // Try again with only one rate updated - should still fail because linear is expired + let out_come = call!( + user2, + pool.add_stable_liquidity(0, vec![ + U128(50000*ONE_NEAR), U128(50000*ONE_STNEAR), U128(50000*ONE_LINEAR)], U128(1)), + deposit = to_yocto("0.0007") + ); + assert_eq!(get_error_count(&out_come), 1); + assert!(get_error_status(&out_come).contains("E120: Rates expired")); + + // Update linear rate as well + call!( + owner, + pool.update_token_rate( + linear_contract.valid_account_id() + ), + deposit = 1 + ).assert_success(); + + // Now with both rates updated, add liquidity should succeed + let out_come = call!( + user2, + pool.add_stable_liquidity(0, vec![ + U128(50000*ONE_NEAR), U128(50000*ONE_STNEAR), U128(50000*ONE_LINEAR)], U128(1)), + deposit = to_yocto("0.0007") + ); + out_come.assert_success(); + assert_eq!(mft_balance_of(&pool, ":0", &user2.account_id()), 150000*ONE_LPT); +} + #[test] fn sim_sfrax_rated_swap_rate_one_pyth() { let (root, owner, pool, tokens, token_rated_contracts) = diff --git a/releases/ref_exchange_release.wasm b/releases/ref_exchange_release.wasm index f8168c5..6f8960c 100644 Binary files a/releases/ref_exchange_release.wasm and b/releases/ref_exchange_release.wasm differ diff --git a/releases/ref_exchange_release_v1918.wasm b/releases/ref_exchange_release_v1918.wasm new file mode 100644 index 0000000..f8168c5 Binary files /dev/null and b/releases/ref_exchange_release_v1918.wasm differ