use tari_ootle_common_types::substate_type::SubstateType;
use tari_ootle_transaction::{Transaction, args};
use tari_template_lib::types::{Amount, ComponentAddress, NonFungibleAddress, ResourceAddress};
use tari_template_test_tooling::TemplateTest;
const CRATE_PATH: &str = env!("CARGO_MANIFEST_DIR");
struct TariSwapTest {
template_test: TemplateTest,
a_resource: ResourceAddress,
b_resource: ResourceAddress,
lp_resource: ResourceAddress,
tariswap: ComponentAddress,
account_address: ComponentAddress,
account_proof: NonFungibleAddress,
}
fn setup(fee: u16) -> TariSwapTest {
let mut template_test = TemplateTest::new(CRATE_PATH, ["tests/templates/tariswap", "tests/templates/faucet"]);
let (a_faucet, a_resource) = create_faucet_component(&mut template_test, "A".to_string());
let (b_faucet, b_resource) = create_faucet_component(&mut template_test, "B".to_string());
let (tariswap, lp_resource) = create_tariswap_component(&mut template_test, a_resource, b_resource, fee);
let (account_address, account_proof, _) = template_test.create_funded_account();
fund_account(&mut template_test, account_address, a_faucet);
fund_account(&mut template_test, account_address, b_faucet);
TariSwapTest {
template_test,
a_resource,
b_resource,
lp_resource,
tariswap,
account_address,
account_proof,
}
}
fn create_faucet_component(template_test: &mut TemplateTest, symbol: String) -> (ComponentAddress, ResourceAddress) {
let initial_supply = Amount::from(1_000_000_000_000u64);
let component_address: ComponentAddress =
template_test.call_function("TestFaucet", "mint_with_symbol", args![initial_supply, symbol], vec![]);
let resource_address = template_test
.get_previous_output_address(SubstateType::Resource)
.as_resource_address()
.unwrap();
(component_address, resource_address)
}
fn create_tariswap_component(
template_test: &mut TemplateTest,
a_resource: ResourceAddress,
b_resource: ResourceAddress,
fee: u16,
) -> (ComponentAddress, ResourceAddress) {
let module_name = "TariSwapPool";
let tariswap_template = template_test.get_template_address(module_name);
let res = template_test.execute_expect_success(
template_test
.transaction()
.call_function(tariswap_template, "new", args![a_resource, b_resource, fee])
.build_and_seal(template_test.secret_key()),
vec![],
);
let (substate_addr, _) = res
.expect_success()
.up_iter()
.find(|(address, substate)| {
address.is_component() &&
*substate.substate_value().component().unwrap().template_address() == tariswap_template
})
.unwrap();
let component_address = substate_addr.as_component_address().unwrap();
let (substate_addr, _) = res
.expect_success()
.up_iter()
.find(|(address, _)| address.is_resource())
.unwrap();
let lp_resource = substate_addr.as_resource_address().unwrap();
(component_address, lp_resource)
}
fn fund_account(
template_test: &mut TemplateTest,
account_address: ComponentAddress,
faucet_component: ComponentAddress,
) {
template_test
.build_and_execute(
Transaction::builder_localnet()
.call_method(faucet_component, "take_free_coins", args![])
.put_last_instruction_output_on_workspace("free_coins")
.call_method(account_address, "deposit", args![Workspace("free_coins")]),
vec![],
)
.expect_success();
}
fn swap(test: &mut TariSwapTest, input_resource: &ResourceAddress, output_resource: &ResourceAddress, amount: Amount) {
test.template_test
.build_and_execute(
Transaction::builder_localnet()
.call_method(test.account_address, "withdraw", args![input_resource, amount])
.put_last_instruction_output_on_workspace("input_bucket")
.call_method(test.tariswap, "swap", args![Workspace("input_bucket"), output_resource])
.put_last_instruction_output_on_workspace("output_bucket")
.call_method(test.account_address, "deposit", args![Workspace("output_bucket")]),
vec![test.account_proof.clone()],
)
.expect_success();
}
fn add_liquidity(test: &mut TariSwapTest, a_amount: Amount, b_amount: Amount) {
test.template_test
.build_and_execute(
Transaction::builder_localnet()
.call_method(test.account_address, "withdraw", args![test.a_resource, a_amount])
.put_last_instruction_output_on_workspace("a_bucket")
.call_method(test.account_address, "withdraw", args![test.b_resource, b_amount])
.put_last_instruction_output_on_workspace("b_bucket")
.call_method(test.tariswap, "add_liquidity", args![
Workspace("a_bucket"),
Workspace("b_bucket")
])
.put_last_instruction_output_on_workspace("lp_bucket")
.call_method(test.account_address, "deposit", args![Workspace("lp_bucket")]),
vec![test.account_proof.clone(), test.template_test.owner_proof()],
)
.expect_success();
}
fn remove_liquidity(test: &mut TariSwapTest, lp_amount: Amount) {
test.template_test
.build_and_execute(
Transaction::builder_localnet()
.call_method(test.account_address, "withdraw", args![test.lp_resource, lp_amount])
.put_last_instruction_output_on_workspace("lp_bucket")
.call_method(test.tariswap, "remove_liquidity", args![Workspace("lp_bucket")])
.put_last_instruction_output_on_workspace("pool_buckets")
.call_method(test.account_address, "deposit", args![Workspace("pool_buckets.0")])
.call_method(test.account_address, "deposit", args![Workspace("pool_buckets.1")]),
vec![test.account_proof.clone(), test.template_test.owner_proof()],
)
.expect_success();
}
fn get_pool_balance(test: &mut TariSwapTest, resource_address: ResourceAddress) -> Amount {
test.template_test
.call_method(test.tariswap, "get_pool_balance", args![resource_address], vec![])
}
fn get_account_balance(test: &mut TariSwapTest, resource_address: ResourceAddress) -> Amount {
test.template_test
.call_method(test.account_address, "balance", args![resource_address], vec![])
}
fn assert_swap(
test: &mut TariSwapTest,
input_resource: &ResourceAddress,
input_amount: u64,
output_resource: &ResourceAddress,
expected_output_amount: u64,
) {
let input_amount = Amount::from(input_amount);
let expected_output_amount = Amount::from(expected_output_amount);
let input_pool_balance = get_pool_balance(test, *input_resource);
let output_pool_balance = get_pool_balance(test, *output_resource);
swap(test, input_resource, output_resource, input_amount);
let new_input_pool_balance = get_pool_balance(test, *input_resource);
let new_output_pool_balance = get_pool_balance(test, *output_resource);
assert_eq!(new_input_pool_balance, input_pool_balance + input_amount);
assert_eq!(new_output_pool_balance, output_pool_balance - expected_output_amount);
}
fn assert_add_liquidity(test: &mut TariSwapTest, a_amount: u64, b_amount: u64, expected_lp_amount: u64) {
let a_amount = Amount::from(a_amount);
let b_amount = Amount::from(b_amount);
let expected_lp_amount = Amount::from(expected_lp_amount);
let a_resource = test.a_resource;
let b_resource = test.b_resource;
let lp_resource = test.lp_resource;
let pool_a_balance = get_pool_balance(test, a_resource);
let pool_b_balance = get_pool_balance(test, b_resource);
let account_lp_balance = get_account_balance(test, lp_resource);
add_liquidity(test, a_amount, b_amount);
let new_account_lp_balance = get_account_balance(test, lp_resource);
assert_eq!(new_account_lp_balance, account_lp_balance + expected_lp_amount);
let new_pool_a_balance = get_pool_balance(test, a_resource);
let new_pool_b_balance = get_pool_balance(test, b_resource);
assert_eq!(new_pool_a_balance, pool_a_balance + a_amount);
assert_eq!(new_pool_b_balance, pool_b_balance + b_amount);
}
fn assert_remove_liquidity(
test: &mut TariSwapTest,
lp_amount_to_remove: u64,
expected_a_amount: u64,
expected_b_amount: u64,
) {
let lp_amount_to_remove = Amount::from(lp_amount_to_remove);
let expected_a_amount = Amount::from(expected_a_amount);
let expected_b_amount = Amount::from(expected_b_amount);
let a_resource = test.a_resource;
let b_resource = test.b_resource;
let lp_resource = test.lp_resource;
let lp_balance = get_account_balance(test, lp_resource);
let account_a_balance = get_account_balance(test, a_resource);
let account_b_balance = get_account_balance(test, b_resource);
remove_liquidity(test, lp_amount_to_remove);
assert_eq!(get_account_balance(test, lp_resource), lp_balance - lp_amount_to_remove);
let new_account_a_balance = get_account_balance(test, a_resource);
let new_account_b_balance = get_account_balance(test, b_resource);
assert_eq!(new_account_a_balance, account_a_balance + expected_a_amount);
assert_eq!(new_account_b_balance, account_b_balance + expected_b_amount);
}
#[test]
fn tariswap() {
let fee = 50; let mut test = setup(fee);
let a_resource = test.a_resource;
let b_resource = test.b_resource;
let liquidity_amount = 500;
let expected_lp_amount = liquidity_amount * 2; assert_add_liquidity(&mut test, liquidity_amount, liquidity_amount, expected_lp_amount);
let a_amount = 50;
let expected_b_amount = 44; assert_swap(&mut test, &a_resource, a_amount, &b_resource, expected_b_amount);
let b_amount = 50;
let expected_a_amount = 53; assert_swap(&mut test, &b_resource, b_amount, &a_resource, expected_a_amount);
let lp_amount_to_remove = 100;
let expected_a_amount = 50;
let expected_b_amount = 51;
assert_remove_liquidity(&mut test, lp_amount_to_remove, expected_a_amount, expected_b_amount);
}