Icon HelpCircleForumIcon Link

⌘K

Icon HelpCircleForumIcon Link
Deposit and Withdraw

Icon LinkDeposit And Withdraw

Consider the following contract:

contract;
 
use std::{
    asset::{
        mint_to_address,
        transfer_to_address,
    },
    call_frames::{
        msg_asset_id,
    },
    context::msg_amount,
};
use std::constants::ZERO_B256;
 
abi LiquidityPool {
    #[payable]
    fn deposit(recipient: Address);
    #[payable]
    fn withdraw(recipient: Address);
}
 
const BASE_TOKEN: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000);
 
impl LiquidityPool for Contract {
    #[payable]
    fn deposit(recipient: Address) {
        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_address(recipient, ZERO_B256, amount_to_mint);
    }
 
    #[payable]
    fn withdraw(recipient: Address) {
        assert(0 < msg_amount());
 
        // Amount to withdraw.
        let amount_to_transfer = msg_amount() / 2;
 
        // Transfer base token to recipient.
        transfer_to_address(recipient, BASE_TOKEN, amount_to_transfer);
    }
}

As the name implies, this contract represents a simplified version of a liquidity pool. The deposit() method allows you to supply an arbitrary amount of BASE_TOKEN. In response, it mints twice the amount of the liquidity asset to the caller's address. Similarly, the withdraw() method transfers half the amount of the BASE_TOKEN back to the caller's address.

Now, let's deposit some tokens into the liquidity pool contract. Since this requires forwarding assets to the contract, we need to pass the appropriate values to callParams when creating a contract call.

const depositAmount = 100_000;
const liquidityOwner = Wallet.generate({ provider });
 
// the subId used to mint the new asset is a zero b256 on the contract
const subId = ZeroBytes32;
const contractId = liquidityPoolContract.id.toB256();
 
const assetId = getAssetId(contractId, subId);
 
await liquidityPoolContract.functions
  .deposit({ value: liquidityOwner.address.toB256() })
  .callParams({ forward: [depositAmount, BaseAssetId] })
  .txParams({ variableOutputs: 1 })
  .call();
 
const liquidityAmount = await liquidityOwner.getBalance(assetId);
 
expect(liquidityAmount.toNumber()).toBe(depositAmount * 2);

As a final demonstration, let's use all our liquidity asset balance to withdraw from the pool and confirm we retrieved the initial amount. For this, we get our liquidity asset balance and supply it to the withdraw() function via callParams.

await liquidityPoolContract.functions
  .withdraw({ value: liquidityOwner.address.toB256() })
  .callParams({ forward: [depositAmount, BaseAssetId] })
  .txParams({ variableOutputs: 1 })
  .call();
 
const baseAssetAfterWithdraw = await liquidityOwner.getBalance(BaseAssetId);
 
expect(baseAssetAfterWithdraw.toNumber()).toBe(depositAmount / 2);