⌘K

Deposit and Withdraw

Icon Link存款和提款

考虑以下合约:

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);