考虑以下合约:
contract;
use std::{
asset::{
mint_to,
transfer,
},
call_frames::{
msg_asset_id,
},
constants::ZERO_B256,
context::msg_amount,
};
abi LiquidityPool {
#[payable]
fn deposit(recipient: Identity);
#[payable]
fn withdraw(recipient: Identity);
}
const BASE_TOKEN: AssetId = AssetId::from(0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c);
impl LiquidityPool for Contract {
#[payable]
fn deposit(recipient: Identity) {
assert(BASE_TOKEN == msg_asset_id());
assert(0 < msg_amount());
// Mint two times the amount.
let amount_to_mint = msg_amount() * 2;
// Mint some LP token based upon the amount of the base token.
mint_to(recipient, ZERO_B256, amount_to_mint);
}
#[payable]
fn withdraw(recipient: Identity) {
assert(0 < msg_amount());
// Amount to withdraw.
let amount_to_transfer = msg_amount() / 2;
// Transfer base token to recipient.
transfer(recipient, BASE_TOKEN, amount_to_transfer);
}
}
顾名思义,它代表了一个简化的流动性池合约示例。deposit()
方法期望您提供任意数量的 BASE_TOKEN
。作为结果,它会向调用地址铸造双倍数量的流动性资产。类似地,如果您调用 withdraw()
并提供流动性资产,它将把一半数量的 BASE_TOKEN
转移回调用地址,而不是从合约余额中扣除,而是从铸造中扣除。
与 Rust SDK 中与任何合约交互的第一步是调用 abigen!
宏以为合约方法生成类型安全的 Rust 绑定:
abigen!(Contract(
name = "MyContract",
abi = "e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool-abi.json"
));
接下来,我们使用自定义定义的资产设置一个钱包。我们为我们的钱包提供了一些合约的 BASE_TOKEN
和默认资产(部署合约所需的资产):
let base_asset_id: AssetId =
"0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c".parse()?;
let asset_ids = [AssetId::zeroed(), base_asset_id];
let asset_configs = asset_ids
.map(|id| AssetConfig {
id,
num_coins: 1,
coin_amount: 1_000_000,
})
.into();
let wallet_config = WalletsConfig::new_multiple_assets(1, asset_configs);
let wallets = launch_custom_provider_and_get_wallets(wallet_config, None, None).await?;
let wallet = &wallets[0];
启动提供程序并创建钱包后,我们可以部署我们的合约并创建其方法的实例:
let contract_id = Contract::load_from(
"../../e2e/sway/contracts/liquidity_pool/out/release/liquidity_pool.bin",
LoadConfiguration::default(),
)?
.deploy(wallet, TxPolicies::default())
.await?;
let contract_methods = MyContract::new(contract_id.clone(), wallet.clone()).methods();
准备工作完成后,我们最终可以通过合约实例调用 deposit()
来存入流动性池。由于我们必须向合约转移资产,我们创建适当的 CallParameters
并将它们链接到方法调用上。为了接收铸造的流动性池资产,我们必须将一个变量输出附加到我们的合约调用中。
let deposit_amount = 1_000_000;
let call_params = CallParameters::default()
.with_amount(deposit_amount)
.with_asset_id(base_asset_id);
contract_methods
.deposit(wallet.address().into())
.call_params(call_params)?
.append_variable_outputs(1)
.call()
.await?;
作为最后的演示,让我们使用我们所有的流动性资产余额从池中提款,并确认我们检索到了初始金额。为此,我们获取我们的流动性资产余额,并通过 CallParameters
将其提供给 withdraw()
调用。
let lp_asset_id = contract_id.asset_id(&Bits256::zeroed());
let lp_token_balance = wallet.get_asset_balance(&lp_asset_id).await?;
let call_params = CallParameters::default()
.with_amount(lp_token_balance)
.with_asset_id(lp_asset_id);
contract_methods
.withdraw(wallet.address().into())
.call_params(call_params)?
.append_variable_outputs(1)
.call()
.await?;
let base_balance = wallet.get_asset_balance(&base_asset_id).await?;
assert_eq!(base_balance, deposit_amount);