Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: update deposit-and-withdraw page #1591

Merged
merged 12 commits into from
Dec 23, 2023
Merged
5 changes: 5 additions & 0 deletions .changeset/kind-hotels-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/transactions": minor
---

update deposit and withdraw doc page
64 changes: 64 additions & 0 deletions apps/docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Contract, WalletUnlocked, Provider } from 'fuels';
import { ContractFactory, BaseAssetId, Wallet, ZeroBytes32, getAssetId } from 'fuels';

import {
DocSnippetProjectsEnum,
getDocsSnippetsForcProject,
} from '../../../test/fixtures/forc-projects';
import { getTestWallet } from '../../utils';

/**
* @group node
*/
describe(__filename, () => {
let sender: WalletUnlocked;
let liquidityPoolContract: Contract;
let provider: Provider;

beforeAll(async () => {
sender = await getTestWallet();

const { abiContents, binHexlified } = getDocsSnippetsForcProject(
DocSnippetProjectsEnum.LIQUIDITY_POOL
);
provider = sender.provider;
const factory = new ContractFactory(binHexlified, abiContents, sender);
const { minGasPrice } = sender.provider.getGasConfig();
liquidityPoolContract = await factory.deployContract({ gasPrice: minGasPrice });
});

it('deposit and withdraw cookbook guide', async () => {
// #region deposit-and-withdraw-cookbook-2
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({ gasLimit: 1_000, variableOutputs: 1, gasPrice: 1 })
.call();

const liquidityAmount = await liquidityOwner.getBalance(assetId);

expect(liquidityAmount.toNumber()).toBe(depositAmount * 2);
// #endregion deposit-and-withdraw-cookbook-2

// #region deposit-and-withdraw-cookbook-3
await liquidityPoolContract.functions
.withdraw({ value: liquidityOwner.address.toB256() })
.callParams({ forward: [depositAmount, BaseAssetId] })
.txParams({ gasLimit: 1_000, variableOutputs: 1, gasPrice: 1 })
.call();

const baseAssetAfterWithdraw = await liquidityOwner.getBalance(BaseAssetId);

expect(baseAssetAfterWithdraw.toNumber()).toBe(depositAmount / 2);
// #endregion deposit-and-withdraw-cookbook-3
});
});
1 change: 1 addition & 0 deletions apps/docs-snippets/test/fixtures/forc-projects/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"counter",
"echo-enum",
"liquidity-pool",
"log-values",
"sum-script",
"echo-values",
Expand Down
1 change: 1 addition & 0 deletions apps/docs-snippets/test/fixtures/forc-projects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export enum DocSnippetProjectsEnum {
ECHO_U64_ARRAY = 'echo-u64-array',
RETURN_CONTEXT = 'return-context',
TOKEN_DEPOSITOR = 'token-depositor',
LIQUIDITY_POOL = 'liquidity-pool',
SIMPLE_PREDICATE = 'simple-predicate',
ECHO_CONFIGURABLES = 'echo-configurables',
TRANSFER_TO_ADDRESS = 'transfer-to-address',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "liquidity-pool"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// #region deposit-and-withdraw-cookbook-1
contract;

use std::{
call_frames::{
msg_asset_id,
},
context::msg_amount,
token::{
mint_to_address,
transfer_to_address,
},
};
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);
}
}
// #endregion deposit-and-withdraw-cookbook-1
34 changes: 6 additions & 28 deletions apps/docs/src/guide/cookbook/deposit-and-withdraw.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,15 @@
# Deposit And Withdraw

Consider the following contracts:
Consider the following contract:

<<< @/../../../packages/fuel-gauge/test/fixtures/forc-projects/token_contract/src/main.sw#token-contract{rust:line-numbers}
<<< @/../../docs-snippets/test/fixtures/forc-projects/liquidity-pool/src/main.sw#deposit-and-withdraw-cookbook-1{rust:line-numbers}

<<< @/../../../packages/fuel-gauge/test/fixtures/forc-projects/liquidity-pool/src/main.sw#liquidity-pool-contract{rust:line-numbers}
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.

The first contract is a contract that represents a simple token.
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 out contract call.
Torres-ssf marked this conversation as resolved.
Show resolved Hide resolved

The second contract, as its name suggests, represents a simplified example of a liquidity pool contract. The method deposit() expects you to supply an arbitrary amount of the `base_token`. As a result, it mints double the amount of the liquidity asset to the calling address. Analogously, if you call `withdraw()` supplying it with the liquidity asset, it will transfer half that amount of the `base_token` back to the calling address except for deducting it from the contract balance instead of minting it.

The first step towards interacting with any contract in the TypeScript SDK is using the `typegen` CLI utility to generate type-safe bindings for the contract methods:

```sh
$ npx fuels typegen -i ./contract/out/debug/*-abi.json -o ./contract-types
```

Next, let's setup a [`Wallet`](../wallets/index.md) and seed it with some coins. We will need these coins to deploy the contracts and to interact with them.

<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-wallet-setup{ts:line-numbers}

Let's now deploy both the contracts and set them up.

<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-contract-deployments{ts:line-numbers}

Next, let's mint some tokens and transfer them to our wallet.

<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-mint-and-transfer{ts:line-numbers}

Now, let's deposit some tokens into the liquidity pool contract. Since we have to transfer assets to the contract, we create the appropriate [`callParams`](../contracts/call-parameters.md) and chain them to the method call.

<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-deposit{ts:line-numbers}
<<< @/../../docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts#deposit-and-withdraw-cookbook-2{ts:line-numbers}

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`.

<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-withdraw{ts:line-numbers}
<<< @/../../docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts#deposit-and-withdraw-cookbook-3{ts:line-numbers}
94 changes: 0 additions & 94 deletions packages/fuel-gauge/src/doc-examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
Wallet,
WalletUnlocked,
Signer,
ContractFactory,
ZeroBytes32,
BaseAssetId,
FUEL_NETWORK_URL,
Expand All @@ -35,9 +34,6 @@ const { abiContents: callTestAbi } = getFuelGaugeForcProject(
FuelGaugeProjectsEnum.CALL_TEST_CONTRACT
);

const { binHexlified: liquidityPoolContractBytecode, abiContents: liquidityPoolABI } =
getFuelGaugeForcProject(FuelGaugeProjectsEnum.LIQUIDITY_POOL);

const { binHexlified: predicateTriple } = getFuelGaugeForcProject(
FuelGaugeProjectsEnum.PREDICATE_TRIPLE_SIG
);
Expand All @@ -46,9 +42,6 @@ const { binHexlified: testPredicateTrue } = getFuelGaugeForcProject(
FuelGaugeProjectsEnum.PREDICATE_TRUE
);

const { binHexlified: tokenContractBytecode, abiContents: tokenContractABI } =
getFuelGaugeForcProject(FuelGaugeProjectsEnum.TOKEN_CONTRACT);

const PUBLIC_KEY =
'0x2f34bc0df4db0ec391792cedb05768832b49b1aa3a2dd8c30054d1af00f67d00b74b7acbbf3087c8e0b1a4c343db50aa471d21f278ff5ce09f07795d541fb47e';

Expand Down Expand Up @@ -472,91 +465,4 @@ describe('Doc Examples', () => {
// assert that predicate funds now belong to the receiver
expect(bn(receiverBalance).gte(bn(amountToReceiver))).toBeTruthy();
});

test.skip('deposit and withdraw cookbook guide', async () => {
// #region deposit-and-withdraw-cookbook-wallet-setup
const provider = await Provider.create(FUEL_NETWORK_URL);
const PRIVATE_KEY = '0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301';
const wallet = Wallet.fromPrivateKey(PRIVATE_KEY, provider);
await seedTestWallet(wallet, [{ assetId: BaseAssetId, amount: bn(100_000) }]);
// #endregion deposit-and-withdraw-cookbook-wallet-setup

// #region deposit-and-withdraw-cookbook-contract-deployments
const tokenContractFactory = new ContractFactory(
tokenContractBytecode,
tokenContractABI,
wallet
);
const tokenContract = await tokenContractFactory.deployContract({ gasPrice });
const tokenContractID = tokenContract.id;

const liquidityPoolContractFactory = new ContractFactory(
liquidityPoolContractBytecode,
liquidityPoolABI,
wallet
);
const liquidityPoolContract = await liquidityPoolContractFactory.deployContract({ gasPrice });
const liquidityPoolContractID = liquidityPoolContract.id;
await liquidityPoolContract.functions.set_base_token(tokenContractID).call();
// #endregion deposit-and-withdraw-cookbook-contract-deployments

// mint some base tokens to the current wallet
// #region deposit-and-withdraw-cookbook-mint-and-transfer
await tokenContract.functions.mint_coins(500, 1).call();
await tokenContract.functions
.transfer_coins_to_output(
200,
{
value: tokenContract.id,
},
{
value: wallet.address.toB256(),
}
)
.txParams({
variableOutputs: 1,
gasPrice,
})
.call();
// #endregion deposit-and-withdraw-cookbook-mint-and-transfer

// deposit base tokens into the liquidity pool
// #region deposit-and-withdraw-cookbook-deposit
await liquidityPoolContract.functions
.deposit({
value: wallet.address.toB256(),
})
.callParams({
forward: {
amount: bn(100),
assetId: tokenContractID.toB256(),
},
})
.call();
// #endregion deposit-and-withdraw-cookbook-deposit

// verify balances
expect(await wallet.getBalance(tokenContractID.toB256())).toEqual(bn(100));
expect(await wallet.getBalance(liquidityPoolContractID.toB256())).toEqual(bn(200));

// withdraw base tokens from the liquidity pool
// #region deposit-and-withdraw-cookbook-withdraw
const lpTokenBalance = await wallet.getBalance(liquidityPoolContractID.toB256());
await liquidityPoolContract.functions
.withdraw({
value: wallet.address.toB256(),
})
.callParams({
forward: {
amount: lpTokenBalance,
assetId: liquidityPoolContractID.toB256(),
},
})
.call();
// #endregion deposit-and-withdraw-cookbook-withdraw

// verify balances again
expect(await wallet.getBalance(tokenContractID.toB256())).toEqual(bn(200));
expect(await wallet.getBalance(liquidityPoolContractID.toB256())).toEqual(bn(0));
});
});
41 changes: 11 additions & 30 deletions packages/fuel-gauge/src/predicate/predicate-with-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ describe('Predicate', () => {
const { binHexlified: contractBytes, abiContents: contractAbi } = getFuelGaugeForcProject(
FuelGaugeProjectsEnum.CALL_TEST_CONTRACT
);
const { binHexlified: liquidityPoolBytes, abiContents: liquidityPoolAbi } =
getFuelGaugeForcProject(FuelGaugeProjectsEnum.LIQUIDITY_POOL);
const { binHexlified: tokenPoolBytes, abiContents: tokenPoolAbi } = getFuelGaugeForcProject(
FuelGaugeProjectsEnum.TOKEN_CONTRACT
);

const { abiContents: predicateAbiMainArgsStruct } = getFuelGaugeForcProject(
FuelGaugeProjectsEnum.PREDICATE_MAIN_ARGS_STRUCT
Expand Down Expand Up @@ -86,8 +87,8 @@ describe('Predicate', () => {

it('calls a predicate and uses proceeds for a contract call', async () => {
const contract = await new ContractFactory(
liquidityPoolBytes,
liquidityPoolAbi,
tokenPoolBytes,
tokenPoolAbi,
wallet
).deployContract({ gasPrice });

Expand All @@ -97,15 +98,10 @@ describe('Predicate', () => {
contract.account = receiver;
await expect(
contract.functions
.deposit({
value: receiver.address.toB256(),
})
.callParams({
forward: [100, BaseAssetId],
})
.mint_coins(200)
.txParams({
gasPrice,
gasLimit: 10_000,
gasLimit: 1_000,
})
.call()
).rejects.toThrow(/not enough coins to fit the target/);
Expand Down Expand Up @@ -139,35 +135,20 @@ describe('Predicate', () => {
// calling the contract with the receiver account (with resources)
const contractAmount = 10;
const {
transactionResult: { fee: receiverTxFee1 },
} = await contract.functions
.set_base_token(BaseAssetId)
.txParams({ gasPrice, gasLimit: 10_000 })
.call();
const {
transactionResult: { fee: receiverTxFee2 },
transactionResult: { fee: receiverTxFee },
} = await contract.functions
.deposit({
value: receiver.address.toB256(),
})
.callParams({
forward: [contractAmount, BaseAssetId],
})
.mint_coins(200)
.txParams({
gasPrice,
gasLimit: 10_000,
gasLimit: 1_000,
})
.call();

const finalReceiverBalance = toNumber(await receiver.getBalance());
const remainingPredicateBalance = toNumber(await predicate.getBalance());

const expectedFinalReceiverBalance =
initialReceiverBalance +
amountToReceiver -
contractAmount -
receiverTxFee1.toNumber() -
receiverTxFee2.toNumber();
initialReceiverBalance + amountToReceiver - contractAmount - receiverTxFee.toNumber();

expectToBeInRange({
value: finalReceiverBalance,
Expand Down
1 change: 0 additions & 1 deletion packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ members = [
"configurable-contract",
"coverage-contract",
"generic-types-contract",
"liquidity-pool",
"multi-token-contract",
"payable-annotation",
"predicate-address",
Expand Down
Loading
Loading