Skip to content

Deposit And Withdraw

Consider the following contract:

rust
contract;

use std::{asset::{mint_to, transfer,}, 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);
}

configurable {
    TOKEN: AssetId = AssetId::from(0x0000000000000000000000000000000000000000000000000000000000000000),
}

impl LiquidityPool for Contract {
    #[payable]
    fn deposit(recipient: Address) {
        assert(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(Identity::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(Identity::Address(recipient), TOKEN, amount_to_transfer);
    }
}
See code in context

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.

ts
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 = getMintedAssetId(contractId, subId);

const call1 = await liquidityPoolContract.functions
  .deposit({ bits: liquidityOwner.address.toB256() })
  .callParams({ forward: [depositAmount, provider.getBaseAssetId()] })
  .txParams({ variableOutputs: 1 })
  .call();

await call1.waitForResult();

const liquidityAmount = await liquidityOwner.getBalance(assetId);

expect(liquidityAmount.toNumber()).toBe(depositAmount * 2);
See code in context

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.

ts
const call2 = await liquidityPoolContract.functions
  .withdraw({ bits: liquidityOwner.address.toB256() })
  .callParams({ forward: [depositAmount, provider.getBaseAssetId()] })
  .txParams({ variableOutputs: 1 })
  .call();

await call2.waitForResult();

const baseAssetAfterWithdraw = await liquidityOwner.getBalance(provider.getBaseAssetId());

expect(baseAssetAfterWithdraw.toNumber()).toBe(depositAmount / 2);
See code in context