From 7163a5e619e54fe35c5b8e10afd6d6fbbbe1eff6 Mon Sep 17 00:00:00 2001 From: mpoplavkov Date: Mon, 26 Aug 2024 15:32:01 +0100 Subject: [PATCH] Swap exact output tests --- Cargo.lock | 9 + Cargo.toml | 1 + .../tests/cases/success.rs | 2 +- scripts/swap_exact_output_script/Cargo.toml | 14 + .../tests/cases/mod.rs | 1 + .../tests/cases/success.rs | 259 ++++++++++++++++++ .../swap_exact_output_script/tests/harness.rs | 2 + .../tests/utils/mod.rs | 114 ++++++++ test-harness/src/interface.rs | 4 + test-harness/src/paths.rs | 2 + 10 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 scripts/swap_exact_output_script/Cargo.toml create mode 100644 scripts/swap_exact_output_script/tests/cases/mod.rs create mode 100644 scripts/swap_exact_output_script/tests/cases/success.rs create mode 100644 scripts/swap_exact_output_script/tests/harness.rs create mode 100644 scripts/swap_exact_output_script/tests/utils/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 23885fe..87ab65e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5662,6 +5662,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "swap-exact-output-script" +version = "0.1.0" +dependencies = [ + "fuels", + "test-harness", + "tokio", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 024ca69..651b94a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "./test-harness", "./scripts/remove_liquidity_script", "./scripts/swap_exact_input_script", + "./scripts/swap_exact_output_script", ] [workspace.dependencies] diff --git a/scripts/swap_exact_input_script/tests/cases/success.rs b/scripts/swap_exact_input_script/tests/cases/success.rs index 91c4373..8dc636a 100644 --- a/scripts/swap_exact_input_script/tests/cases/success.rs +++ b/scripts/swap_exact_input_script/tests/cases/success.rs @@ -14,7 +14,7 @@ async fn swap_between_two_volatile_tokens() { _, wallet, deadline, - (token_0_id, token_1_id, token_2_id), + (token_0_id, token_1_id, _), ) = setup().await; let amount_0_desired: u64 = 1_000_000; diff --git a/scripts/swap_exact_output_script/Cargo.toml b/scripts/swap_exact_output_script/Cargo.toml new file mode 100644 index 0000000..4bedf35 --- /dev/null +++ b/scripts/swap_exact_output_script/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "swap-exact-output-script" +version.workspace = true +edition.workspace = true + +[dev-dependencies] +fuels = { workspace = true } +test-harness = { path = "../../test-harness" } +tokio = { workspace = true } + +[[test]] +harness = true +name = "tests" +path = "tests/harness.rs" diff --git a/scripts/swap_exact_output_script/tests/cases/mod.rs b/scripts/swap_exact_output_script/tests/cases/mod.rs new file mode 100644 index 0000000..4d8e510 --- /dev/null +++ b/scripts/swap_exact_output_script/tests/cases/mod.rs @@ -0,0 +1 @@ +pub mod success; diff --git a/scripts/swap_exact_output_script/tests/cases/success.rs b/scripts/swap_exact_output_script/tests/cases/success.rs new file mode 100644 index 0000000..87c0b95 --- /dev/null +++ b/scripts/swap_exact_output_script/tests/cases/success.rs @@ -0,0 +1,259 @@ +use crate::utils::setup; +use fuels::prelude::VariableOutputPolicy; +use test_harness::interface::amm::pool_metadata; +use test_harness::interface::scripts::get_transaction_inputs_outputs; +use test_harness::utils::common::{pool_assets_balance, MINIMUM_LIQUIDITY}; + +#[tokio::test] +async fn swap_between_two_volatile_tokens() { + let ( + add_liquidity_script, + swap_exact_output_script, + amm, + pool_id, + _, + wallet, + deadline, + (token_0_id, token_1_id, _), + ) = setup().await; + + let amount_0_desired: u64 = 1_000_000; + let amount_1_desired: u64 = 1_000_000; + let expected_liquidity: u64 = 1_000_000 - MINIMUM_LIQUIDITY; + + let (inputs, outputs) = get_transaction_inputs_outputs( + &wallet, + &vec![ + (token_0_id, amount_0_desired), + (token_1_id, amount_1_desired), + ], + ) + .await; + + // adds initial liquidity + let added_liquidity = add_liquidity_script + .main( + pool_id, + amount_0_desired, + amount_1_desired, + 0, + 0, + wallet.address().into(), + deadline, + ) + .with_contracts(&[&amm.instance]) + .with_inputs(inputs) + .with_outputs(outputs) + .with_variable_output_policy(VariableOutputPolicy::Exactly(2)) + .call() + .await + .unwrap() + .value; + + assert_eq!(added_liquidity.amount, expected_liquidity); + + let token_1_output = 1_000; + let token_0_input_expected = 1006; + + let (inputs, outputs) = + get_transaction_inputs_outputs(&wallet, &vec![(token_0_id, token_0_input_expected)]).await; + + let wallet_balances_before = pool_assets_balance(&wallet, &pool_id, amm.id).await; + let pool_metadata_before = pool_metadata(&amm.instance, pool_id).await.value.unwrap(); + let amounts_in = swap_exact_output_script + .main( + token_1_output, + token_1_id, + token_0_input_expected * 2, + vec![pool_id], + wallet.address().into(), + deadline, + ) + .with_contracts(&[&amm.instance]) + .with_inputs(inputs) + .with_outputs(outputs) + .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) + .call() + .await + .unwrap() + .value; + + assert_eq!( + amounts_in, + vec![ + (token_1_output, token_1_id), + (token_0_input_expected, token_0_id), + ] + ); + let wallet_balances_after = pool_assets_balance(&wallet, &pool_id, amm.id).await; + let pool_metadata_after = pool_metadata(&amm.instance, pool_id).await.value.unwrap(); + + assert_eq!( + wallet_balances_after.asset_a, + wallet_balances_before.asset_a - token_0_input_expected + ); + assert_eq!( + wallet_balances_after.asset_b, + wallet_balances_before.asset_b + token_1_output + ); + assert_eq!( + pool_metadata_after.reserve_0, + pool_metadata_before.reserve_0 + token_0_input_expected + ); + assert_eq!( + pool_metadata_after.reserve_1, + pool_metadata_before.reserve_1 - token_1_output + ); +} + +#[tokio::test] +async fn swap_between_three_volatile_tokens() { + let ( + add_liquidity_script, + swap_exact_output_script, + amm, + pool_id_0, + pool_id_1, + wallet, + deadline, + (token_0_id, token_1_id, token_2_id), + ) = setup().await; + + let amount_0_desired: u64 = 1_000_000; + let amount_1_desired: u64 = 1_000_000; + let expected_liquidity: u64 = 1_000_000 - MINIMUM_LIQUIDITY; + + let (inputs, outputs) = get_transaction_inputs_outputs( + &wallet, + &vec![ + (token_0_id, amount_0_desired), + (token_1_id, amount_1_desired), + ], + ) + .await; + + // adds initial liquidity + let added_liquidity = add_liquidity_script + .main( + pool_id_0, + amount_0_desired, + amount_1_desired, + 0, + 0, + wallet.address().into(), + deadline, + ) + .with_contracts(&[&amm.instance]) + .with_inputs(inputs) + .with_outputs(outputs) + .with_variable_output_policy(VariableOutputPolicy::Exactly(2)) + .call() + .await + .unwrap() + .value; + + assert_eq!(added_liquidity.amount, expected_liquidity); + + let (inputs, outputs) = get_transaction_inputs_outputs( + &wallet, + &vec![ + (token_1_id, amount_0_desired), + (token_2_id, amount_1_desired), + ], + ) + .await; + + // adds initial liquidity + let added_liquidity = add_liquidity_script + .main( + pool_id_1, + amount_0_desired, + amount_1_desired, + 0, + 0, + wallet.address().into(), + deadline, + ) + .with_contracts(&[&amm.instance]) + .with_inputs(inputs) + .with_outputs(outputs) + .with_variable_output_policy(VariableOutputPolicy::Exactly(2)) + .call() + .await + .unwrap() + .value; + + assert_eq!(added_liquidity.amount, expected_liquidity); + + let token_0_input_expected = 1012; + let token_1_input_expected = 1006; + let token_2_output = 1000; + + let (inputs, outputs) = + get_transaction_inputs_outputs(&wallet, &vec![(token_0_id, token_0_input_expected)]).await; + + let wallet_balances_0_before = pool_assets_balance(&wallet, &pool_id_0, amm.id).await; + let wallet_balances_1_before = pool_assets_balance(&wallet, &pool_id_1, amm.id).await; + let pool_metadata_0_before = pool_metadata(&amm.instance, pool_id_0).await.value.unwrap(); + let pool_metadata_1_before = pool_metadata(&amm.instance, pool_id_1).await.value.unwrap(); + let amounts_in = swap_exact_output_script + .main( + token_2_output, + token_2_id, + token_0_input_expected * 2, + vec![pool_id_0, pool_id_1], + wallet.address().into(), + deadline, + ) + .with_contracts(&[&amm.instance]) + .with_inputs(inputs) + .with_outputs(outputs) + .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) + .call() + .await + .unwrap() + .value; + let pool_metadata_0_after = pool_metadata(&amm.instance, pool_id_0).await.value.unwrap(); + let pool_metadata_1_after = pool_metadata(&amm.instance, pool_id_1).await.value.unwrap(); + let wallet_balances_0_after = pool_assets_balance(&wallet, &pool_id_0, amm.id).await; + let wallet_balances_1_after = pool_assets_balance(&wallet, &pool_id_1, amm.id).await; + + assert_eq!( + amounts_in, + vec![ + (token_2_output, token_2_id), + (token_1_input_expected, pool_id_0.1), + (token_0_input_expected, pool_id_0.0), + ] + ); + + assert_eq!( + wallet_balances_0_after.asset_a, + wallet_balances_0_before.asset_a - token_0_input_expected + ); + assert_eq!( + wallet_balances_0_after.asset_b, + wallet_balances_0_before.asset_b + ); + assert_eq!( + wallet_balances_1_after.asset_b, + wallet_balances_1_before.asset_b + token_2_output + ); + + assert_eq!( + pool_metadata_0_after.reserve_0, + pool_metadata_0_before.reserve_0 + token_0_input_expected + ); + assert_eq!( + pool_metadata_0_after.reserve_1, + pool_metadata_0_before.reserve_1 - token_1_input_expected + ); + assert_eq!( + pool_metadata_1_after.reserve_0, + pool_metadata_1_before.reserve_0 + token_1_input_expected + ); + assert_eq!( + pool_metadata_1_after.reserve_1, + pool_metadata_1_before.reserve_1 - token_2_output + ); +} diff --git a/scripts/swap_exact_output_script/tests/harness.rs b/scripts/swap_exact_output_script/tests/harness.rs new file mode 100644 index 0000000..8b04d05 --- /dev/null +++ b/scripts/swap_exact_output_script/tests/harness.rs @@ -0,0 +1,2 @@ +pub mod cases; +pub mod utils; diff --git a/scripts/swap_exact_output_script/tests/utils/mod.rs b/scripts/swap_exact_output_script/tests/utils/mod.rs new file mode 100644 index 0000000..ae77599 --- /dev/null +++ b/scripts/swap_exact_output_script/tests/utils/mod.rs @@ -0,0 +1,114 @@ +use std::str::FromStr; + +use fuels::accounts::wallet::WalletUnlocked; +use fuels::types::{AssetId, ContractId, Identity}; +use test_harness::data_structures::{MiraAMMContract, WalletAssetConfiguration}; +use test_harness::interface::amm::{create_pool, initialize_ownership}; +use test_harness::interface::mock::{ + add_token, deploy_mock_token_contract, get_sub_id, mint_tokens, +}; +use test_harness::interface::{ + AddLiquidityScript, AddLiquidityScriptConfigurables, SwapExactOutputScript, + SwapExactOutputScriptConfigurables, +}; +use test_harness::paths::{ADD_LIQUIDITY_SCRIPT_BINARY_PATH, SWAP_EXACT_OUTPUT_SCRIPT_BINARY_PATH}; +use test_harness::setup::common::{deploy_amm, setup_wallet_and_provider}; +use test_harness::types::PoolId; +use test_harness::utils::common::order_sub_ids; + +pub async fn setup() -> ( + AddLiquidityScript, + SwapExactOutputScript, + MiraAMMContract, + PoolId, + PoolId, + WalletUnlocked, + u32, + (AssetId, AssetId, AssetId), +) { + let (wallet, _asset_ids, provider) = + setup_wallet_and_provider(&WalletAssetConfiguration::default()).await; + let amm = deploy_amm(&wallet).await; + initialize_ownership(&amm.instance, Identity::Address(wallet.address().into())).await; + let (token_contract_id, token_contract) = deploy_mock_token_contract(&wallet).await; + + let token_0_id = add_token(&token_contract, "TOKEN_A".to_string(), "TKA".to_string(), 9) + .await + .value; + let token_1_id = add_token(&token_contract, "TOKEN_B".to_string(), "TKB".to_string(), 9) + .await + .value; + let token_2_id = add_token(&token_contract, "TOKEN_C".to_string(), "TKC".to_string(), 9) + .await + .value; + let mut all_assets = vec![token_0_id, token_1_id, token_2_id]; + all_assets.sort(); + let [token_0_id, token_1_id, token_2_id] = all_assets[..] else { + todo!() + }; + + let token_0_sub_id = get_sub_id(&token_contract, token_0_id).await.value.unwrap(); + let token_1_sub_id = get_sub_id(&token_contract, token_1_id).await.value.unwrap(); + let token_2_sub_id = get_sub_id(&token_contract, token_2_id).await.value.unwrap(); + + mint_tokens(&token_contract, token_0_id, 1_000_000_000).await; + mint_tokens(&token_contract, token_1_id, 1_000_000_000).await; + mint_tokens(&token_contract, token_2_id, 1_000_000_000).await; + + let (token_a_sub_id, token_b_sub_id) = + order_sub_ids((token_0_id, token_1_id), (token_0_sub_id, token_1_sub_id)); + + let pool_id_0 = create_pool( + &amm.instance, + &token_contract, + token_contract_id, + token_a_sub_id, + token_contract_id, + token_b_sub_id, + false, + ) + .await + .value; + + let (token_c_sub_id, token_d_sub_id) = + order_sub_ids((token_1_id, token_2_id), (token_1_sub_id, token_2_sub_id)); + + let pool_id_1 = create_pool( + &amm.instance, + &token_contract, + token_contract_id, + token_c_sub_id, + token_contract_id, + token_d_sub_id, + false, + ) + .await + .value; + + let deadline = provider.latest_block_height().await.unwrap() + 10; + + let add_liquidity_script_configurables = AddLiquidityScriptConfigurables::default() + .with_AMM_CONTRACT_ID(ContractId::from_str(&amm.id.to_string()).unwrap()) + .unwrap(); + let add_liquidity_script_instance = + AddLiquidityScript::new(wallet.clone(), ADD_LIQUIDITY_SCRIPT_BINARY_PATH) + .with_configurables(add_liquidity_script_configurables); + + let swap_exact_output_script_configurables = SwapExactOutputScriptConfigurables::default() + .with_AMM_CONTRACT_ID(ContractId::from_str(&amm.id.to_string()).unwrap()) + .unwrap(); + let swap_exact_output_script_instance = + SwapExactOutputScript::new(wallet.clone(), SWAP_EXACT_OUTPUT_SCRIPT_BINARY_PATH) + .with_configurables(swap_exact_output_script_configurables); + + ( + add_liquidity_script_instance, + swap_exact_output_script_instance, + amm, + pool_id_0, + pool_id_1, + wallet, + deadline, + (token_0_id, token_1_id, token_2_id), + ) +} diff --git a/test-harness/src/interface.rs b/test-harness/src/interface.rs index 478779f..c2ced90 100644 --- a/test-harness/src/interface.rs +++ b/test-harness/src/interface.rs @@ -21,6 +21,10 @@ abigen!( name = "SwapExactInputScript", abi = "scripts/swap_exact_input_script/out/debug/swap_exact_input_script-abi.json" ), + Script( + name = "SwapExactOutputScript", + abi = "scripts/swap_exact_output_script/out/debug/swap_exact_output_script-abi.json" + ), Contract( name = "MiraAMM", abi = "fixtures/mira-amm/mira_amm_contract-abi.json" diff --git a/test-harness/src/paths.rs b/test-harness/src/paths.rs index cb50cf4..38a651d 100644 --- a/test-harness/src/paths.rs +++ b/test-harness/src/paths.rs @@ -4,6 +4,8 @@ pub const REMOVE_LIQUIDITY_SCRIPT_BINARY_PATH: &str = "../../scripts/remove_liquidity_script/out/debug/remove_liquidity_script.bin"; pub const SWAP_EXACT_INPUT_SCRIPT_BINARY_PATH: &str = "../../scripts/swap_exact_input_script/out/debug/swap_exact_input_script.bin"; +pub const SWAP_EXACT_OUTPUT_SCRIPT_BINARY_PATH: &str = + "../../scripts/swap_exact_output_script/out/debug/swap_exact_output_script.bin"; pub const AMM_CONTRACT_BINARY_PATH: &str = "../../fixtures/mira-amm/mira_amm_contract.bin"; pub const MOCK_TOKEN_CONTRACT_BINARY_PATH: &str = "../../fixtures/mock-token/mock_token.bin";