use tari_engine::fees::FeeTable;
use tari_engine_types::{fees::FeeSource, limits::FREE_COMPUTE_GRACE_POINTS};
use tari_ootle_common_types::substate_type::SubstateType;
use tari_ootle_transaction::{Transaction, args};
use tari_template_lib::types::{Amount, ComponentAddress, ResourceAddress, constants::TARI_TOKEN};
use tari_template_test_tooling::TemplateTest;
const CRATE_PATH: &str = env!("CARGO_MANIFEST_DIR");
const SAFETY_FACTOR: u64 = 2;
const POOL_LIQUIDITY: u64 = 100_000_000;
const SWAP_INPUT: u64 = 20_000_000;
fn create_faucet(test: &mut TemplateTest, symbol: &str) -> (ComponentAddress, ResourceAddress) {
let component: ComponentAddress = test.call_function(
"TestFaucet",
"mint_with_symbol",
args![Amount::from(1_000_000_000_000u64), symbol.to_string()],
vec![],
);
let resource = test
.get_previous_output_address(SubstateType::Resource)
.as_resource_address()
.unwrap();
(component, resource)
}
fn create_pool(test: &mut TemplateTest, a: ResourceAddress, b: ResourceAddress) -> ComponentAddress {
let template = test.get_template_address("TariSwapPool");
let result = test.execute_expect_success(
test.transaction()
.call_function(template, "new", args![a, b, 5u16])
.build_and_seal(test.secret_key()),
vec![],
);
let (addr, _) = result
.expect_success()
.up_iter()
.find(|(address, substate)| {
address.is_component() && *substate.substate_value().component().unwrap().template_address() == template
})
.unwrap();
addr.as_component_address().unwrap()
}
#[test]
fn fee_paid_via_amm_swap_fits_within_grace() {
let mut test = TemplateTest::new(CRATE_PATH, ["tests/templates/tariswap", "tests/templates/faucet"]);
let (account, owner, key) = test.create_funded_account();
let (token_faucet, token) = create_faucet(&mut test, "SWAP");
test.build_and_execute(
Transaction::builder_localnet()
.call_method(token_faucet, "take_free_coins_custom", args![Amount::from(
1_000_000_000u64
)])
.put_last_instruction_output_on_workspace("coins")
.call_method(account, "deposit", args![Workspace("coins")]),
vec![],
)
.expect_success();
let pool = create_pool(&mut test, TARI_TOKEN, token);
test.build_and_execute(
Transaction::builder_localnet()
.call_method(account, "withdraw", args![TARI_TOKEN, Amount::from(POOL_LIQUIDITY)])
.put_last_instruction_output_on_workspace("tari")
.call_method(account, "withdraw", args![token, Amount::from(POOL_LIQUIDITY)])
.put_last_instruction_output_on_workspace("token")
.call_method(pool, "add_liquidity", args![Workspace("tari"), Workspace("token")])
.put_last_instruction_output_on_workspace("lp")
.call_method(account, "deposit", args![Workspace("lp")]),
vec![owner.clone(), test.owner_proof()],
)
.expect_success();
let mut fee_table = FeeTable::zero_rated();
fee_table.per_wasm_point_cost = 1;
fee_table.wasm_points_cost_divisor = 1;
test.set_fee_table(fee_table);
test.enable_fees();
let tx = Transaction::builder_localnet()
.with_fee_instructions_builder(|builder| {
builder
.call_method(account, "withdraw", args![token, Amount::from(SWAP_INPUT)])
.put_last_instruction_output_on_workspace("input")
.call_method(pool, "swap", args![Workspace("input"), TARI_TOKEN])
.put_last_instruction_output_on_workspace("fee_tari")
.pay_fee_from_bucket("fee_tari")
})
.build_and_seal(&key);
let result = test.execute_expect_success(tx, vec![owner]);
let worst_case_points = result
.finalize
.fee_receipt
.fee_breakdown()
.iter()
.find_map(|(s, a)| (*s == FeeSource::WasmExecution).then_some(*a))
.expect("WasmExecution charge present");
eprintln!(
"worst-case fee-sourcing compute (AMM swap to TARI): {worst_case_points} points; FREE_COMPUTE_GRACE_POINTS = \
{FREE_COMPUTE_GRACE_POINTS}",
);
assert!(
worst_case_points.saturating_mul(SAFETY_FACTOR) <= FREE_COMPUTE_GRACE_POINTS,
"FREE_COMPUTE_GRACE_POINTS ({FREE_COMPUTE_GRACE_POINTS}) must stay at least {SAFETY_FACTOR}x above the \
worst-case fee-sourcing compute ({worst_case_points}); re-measure and adjust the grace constant",
);
}