Skip to content
On this page

Mint TIP-3 Tokens

In previous section we have learned to deploy a token root and wallet using the our custom contract.

In this section we will learn the how to mint TIP-3 tokens for a deployed token wallet, it's pretty easy and straight forward, you just need to pay attention to a few small points.

TIP

  • Notice that the owner of the deployed Token Wallet is the MultiWalletTIP3 contract that we deployed earlier.
  • the notify parameter is always true in MultiWalletTIP3 in order to receive callback function from the token root and update the state of the MultiWalletTIP3 contract.
  • We send the mint transaction to the token root contract, so how do we update the MultiWalletTIP3 contract state ?
    There is a callback function named onAcceptTokensMint which will be called on the MultiWalletTIP-3 by the token wallet and updates its state when the tokens are minted if we set the parameter notify to true !

Step 1: Write Minting Script

We can mint TIP-3 tokens for the target Token Wallet, as shown in the code samples below using the locklift tool.

We use the previously written script stats from the deploy token wallet section for the following script.

INFO

  • Before we start to write our scripts we need to make sure that there is a file named 05-mint-tip3.ts in the script folder in the project root.

Minting TIP-3 tokens using everscale-inpage-provider is pretty easy as well:

INFO

  • You may find the following code sample a bit complex, that's because we want to get familiar with the multi wallet functionalities and how to use them.

typescript
/* Minting TIP-3 tokens using multi wallet contract */

// Defining the parameters for minting tip-3 tokens
const mintAmount: number =
  50 * 10 ** deployRootFromDeployerParams.decimals;

console.log(
  'balance before mint:',
  (
    await getWalletData(
      aliceMultiWalletContract,
      tokenRootContract.address
    )
  ).balance /
    10 ** deployRootFromDeployerParams.decimals
);

// Minting tokens for receiver
await tokenRootContract.methods
  .mint({
    amount: mintAmount,
    recipient: aliceMultiWalletContract.address, // the owner of the token wallet is the MW contract
    deployWalletValue: 0,
    notify: true, // To update the Multi Wallet contract
    payload: '',
    remainingGasTo: aliceAccount.address,
  })
  .send({
    from: aliceAccount.address,
    amount: locklift.utils.toNano(5),
  });

// confirming that its minted
console.log(
  'balance after mint:',
  (
    await getWalletData(
      aliceMultiWalletContract,
      tokenRootContract.address
    )
  ).balance /
    10 ** deployRootFromDeployerParams.decimals
);
typescript
import {
  ProviderRpcClient,
  Address,
  Transaction,
  Contract,
} from 'everscale-inpage-provider';
import * as tip3Artifacts from 'tip3-docs-artifacts';
import { provider, providerAddress } from './useProvider';

// We use the getWalletData function to extract the token wallet data from the multi wallet contract
async function getWalletData(
  MWContract: Contract<
    tip3Artifacts.FactorySource['MultiWalletTIP3']
  >,
  tokenRootAddress: Address
): Promise<{ tokenWallet: Address; balance: number }> {
  // Returned value of the wallets mapping on the multi wallet tip-3 contract
  const walletData = (
    await MWContract.methods.wallets().call()
  ).wallets.map(item => {
    if (item[0].toString() == tokenRootAddress.toString()) {
      return item[1];
    }
  });
  let balance: number = 0;
  let tokenWallet: Address = tip3Artifacts.zeroAddress;
  if (walletData.length != 0) {
    balance = Number(walletData[0]!.balance);
    tokenWallet = walletData[0]!.tokenWallet;
  }
  return { tokenWallet: tokenWallet, balance: balance };
}

async function main() {
  // Required contracts addresses
  const tokenRootAddress: Address = new Address(
    '<YOUR_TOKEN_ROOT_ADDRESS>'
  );
  const multiWalletAddress: Address = new Address(
    '<YOUR_MULTI_WALLET_ADDRESS>'
  );

  try {
    // creating an instance of the required contracts
    const tokenRootContract: Contract<
      tip3Artifacts.FactorySource['TokenRoot']
    > = new provider.Contract(
      tip3Artifacts.factorySource['TokenRoot'],
      tokenRootAddress
    );
    const MultiWalletContract: Contract<
      tip3Artifacts.FactorySource['MultiWalletTIP3']
    > = new provider.Contract(
      tip3Artifacts.factorySource['MultiWalletTIP3'],
      multiWalletAddress
    );

    // Fetching the decimals and symbol
    const [decimals, symbol] = await Promise.all([
      Number(
        (
          await tokenRootContract.methods
            .decimals({ answerId: 0 })
            .call()
        ).value0
      ),
      (await tokenRootContract.methods.symbol({ answerId: 0 }).call())
        .value0,
    ]);

    // Defining the mint amount
    const mintAmount: number = 50 * 10 ** decimals;

    // Checking if the user already has a token wallet associated with the token root
    let tokenWalletData = await getWalletData(
      MultiWalletContract,
      tokenRootContract.address
    );

    // Defining the deployWalletValue
    let deployWalletValue: number = 0;

    // Fetching the balance before minting tokens
    const oldBal: number =
      Number(tokenWalletData.balance) / 10 ** decimals;

    // Checking the if the user already has an token wallet of the target token root and accordingly setting the deployWalletValue to deploy onw for the user if doesn't have any
    if (
      tokenWalletData.tokenWallet.toString() ==
      tip3Artifacts.zeroAddress.toString()
    ) {
      deployWalletValue = 2 * 10 ** 9;
    }

    // Defining the transaction fee
    const txFee: string = String(2 * 10 ** 9 + deployWalletValue);

    // Minting tokens fo the receiver
    const mintRes: Transaction = await tokenRootContract.methods
      .mint({
        amount: mintAmount,
        deployWalletValue: deployWalletValue,
        remainingGasTo: providerAddress,
        recipient: multiWalletAddress,
        notify: true, /// @dev it's very important to update the MW contract state
        payload: '',
      })
      .send({
        from: providerAddress,
        amount: txFee,
        bounce: true,
      });

    // Throwing an error if the transaction was aborted
    if (mintRes.aborted) {
      throw new Error(
        `Transaction aborted ! ${
          (mintRes.exitCode, mintRes.resultCode)
        }`
      );
    }

    // Fetching the wallet data and balance after the mint is done
    tokenWalletData = await getWalletData(
      MultiWalletContract,
      tokenRootContract.address
    );
    const newBal: number =
      Number(tokenWalletData.balance) / 10 ** decimals;

    // Checking if the tokens are minted successfully for th receiver
    if (newBal >= oldBal) {
      console.log(`${mintAmount} ${symbol}'s minted successfully `);

      return `Old balance: ${oldBal} \n New balance: ${newBal}`;
    } else {
      throw new Error(
        `Failed ${(mintRes.exitCode, mintRes.resultCode)}`
      );
    }
  } catch (e: any) {
    throw new Error(`Failed ${e.message}`);
  }
}

Step 2: Mint TIP-3 tokens

Use this command to mint TIP-3 tokens:

shell
npx locklift run -s ./scripts/05-mint-tip3.ts -n local
buildStructure

Congratulations, you have successfully minted TIP-3 tokens for a token wallet deployed by a custom contract 🎉

Token Root address

Multi Wallet (recipient) address

Amount

GIF