Skip to content
On this page

Transfer TIP-3 Tokens

We have already learned how to send messages to a contract through an account.

Therefore, making a transfer is as easy as shelling pears!

TIP

TIP-3 Token Wallet has 2 transfer methods:

  • Transfer - Transfer tokens and optionally deploy TokenWallet for recipient account address.
  • Transfer tokens using another TokenWallet address, that wallet must be deployed previously.

In the code sample provided below, we assume that Alice does not have a token wallet. Therefore, we begin by deploying a token wallet for Alice which will be accomplished when using the transfer function. Subsequently, we can employ the transferToWallet function to transfer a certain amount of TIP3 tokens to Alice.

Step 1: Write Transfer Script

In the code sample below, we will demonstrate how to transfer TIP-3 tokens using locklift:

Notice that we utilize the stats of the previously written script in the mint tip-3 section.

INFO

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

Transferring TIP-3 tokens is considered one of the easier steps depended to previous steps. let's look at the code samples below to see how its done using everscale-inpage-provider:

WARNING

  • Notice that if the notify parameter be true for the transaction, the change will be sent back to the sender accounts tokenWallet contract !!
    So if you want the change back into your account contract leave the Notify unchecked !!

typescript
/* Transferring tip-3 tokens using transfer function */

console.log(
  "Bob's balance before transfer: ",
  Number(
    (
      await bobTokenWallet.methods
        .balance({
          answerId: 0,
        })
        .call()
    ).value0
  ) /
    10 ** decimals
);

console.log('Alice balance before transfer: 0');

// Amount to transfer
const transferAmount: number = 30 * 10 ** decimals;

/*
    Transfer with the deployment of a wallet for the recipient account.

    Don't pay attention to notify and payload yet, we'll get back to them.
  */
await bobTokenWallet.methods
  .transfer({
    amount: transferAmount,
    recipient: aliceAccount.address,
    deployWalletValue: locklift.utils.toNano(2), // assume alice doesn't have any token wallet
    remainingGasTo: bobAccount.address,
    notify: false,
    payload: '',
  })
  .send({
    from: bobAccount.address,
    amount: locklift.utils.toNano('5'),
  });

/*
     Creating the alice's token wallet and Checking Alice's balance
   */
const aliceTokenWallet: Contract<FactorySource['TokenWallet']> =
  locklift.factory.getDeployedContract(
    'TokenWallet',
    (
      await tokenRootContract.methods
        .walletOf({
          answerId: 0,
          walletOwner: aliceAccount.address,
        })
        .call()
    ).value0
  );

console.log(
  "Bob's balance after transfer: ",
  Number(
    (
      await bobTokenWallet.methods
        .balance({
          answerId: 0,
        })
        .call()
    ).value0
  ) /
    10 ** decimals
);

console.log(
  "Alice's balance after transfer: ",
  Number(
    (
      await aliceTokenWallet.methods
        .balance({
          answerId: 0,
        })
        .call()
    ).value0
  ) /
    10 ** decimals
);

/* Transferring tip-3 tokens using transferToWallet function */

await bobTokenWallet.methods
  .transferToWallet({
    amount: transferAmount,
    recipientTokenWallet: aliceTokenWallet.address,
    remainingGasTo: bobAccount.address,
    notify: false,
    payload: '',
  })
  .send({
    from: bobAccount.address,
    amount: locklift.utils.toNano('3'),
  });

console.log(
  "Bob's balance after transfer to wallet: ",
  Number(
    (
      await bobTokenWallet.methods
        .balance({
          answerId: 0,
        })
        .call()
    ).value0
  ) /
    10 ** decimals
);

console.log(
  "Alice's balance after transfer to wallet: ",

  Number(
    (
      await aliceTokenWallet.methods
        .balance({
          answerId: 0,
        })
        .call()
    ).value0
  ) /
    10 ** decimals
);
typescript
// Import the required libraries
import {
  ProviderRpcClient as PRC,
  Address,
  Contract,
  Transaction,
} from 'everscale-inpage-provider';
import * as tip3Artifacts from 'tip3-docs-artifacts';
import { provider, providerAddress } from './useProvider';

async function main() {
  // Preparing the required addresses
  const tokenRootAddress: Address = new Address(
    '<YOUR_TOKEN_ROOT_ADDRESS>'
  );
  const recipientAddress: Address = new Address(
    '<RECIPIENT_ACCOUNT_ADDRESS>'
  );

  // creating an instance of the target token root contract
  const tokenRootContract: Contract<
    tip3Artifacts.FactorySource['TokenRoot']
  > = new provider.Contract(
    tip3Artifacts.factorySource['TokenRoot'],
    tokenRootAddress
  );

  // getting the decimals of the token
  const decimals = Number(
    (await tokenRootContract.methods.decimals({ answerId: 0 }).call())
      .value0
  );

  // creating an instance of the sender token wallet contract
  const tokenWallet: Contract<
    tip3Artifacts.FactorySource['TokenWallet']
  > = new provider.Contract(
    tip3Artifacts.factorySource['TokenWallet'],
    (
      await tokenRootContract.methods
        .walletOf({ answerId: 0, walletOwner: providerAddress })
        .call()
    ).value0
  );

  /**
   * we will make an instance of the recipient token wallet contract and we assign value to it if the token wallet was already deployed
   * the amount attached to the tx varies based on the mentioned subject.
   */
  let recipientTokenWallet:
    | Contract<tip3Artifacts.FactorySource['TokenWallet']>
    | undefined = undefined;

  const receiverTokenWalletAddress = (
    await tokenRootContract.methods
      .walletOf({ answerId: 0, walletOwner: recipientAddress })
      .call()
  ).value0;

  // Defining the transfer parameters
  let txFee: number = 3 * 10 ** 9;
  let oldBal: number = 0;
  let deployWalletValue: number = 0;

  // Setting the deployWalletValue and transaction fee based on the recipient token wallet deployment status
  if (
    !(
      await provider.getFullContractState({
        address: receiverTokenWalletAddress,
      })
    ).state?.isDeployed
  ) {
    txFee = 5 * 10 ** 9;
    deployWalletValue = 2 * 10 ** 9;
  } else {
    recipientTokenWallet = new provider.Contract(
      // Transferring the token
      tip3Artifacts.factorySource['TokenWallet'],
      receiverTokenWalletAddress
    );

    oldBal = Number(
      (
        await recipientTokenWallet.methods
          .balance({ answerId: 0 })
          .call({})
      ).value0
    );
  }

  // Defining the transfer amount
  let transferAmount: number = 10 ** (10 ** decimals);

  // Transferring token
  const transferRes: Transaction = await tokenWallet.methods
    .transfer({
      amount: transferAmount,
      recipient: recipientAddress,
      deployWalletValue: deployWalletValue,
      remainingGasTo: providerAddress,
      notify: false, // true if the change must be sent back to the sender wallet account not the sender token wallet
      payload: '',
    })
    .send({
      from: providerAddress,
      amount: String(txFee),
      bounce: true,
    });

  // Checking of the transaction is aborted or not
  if (transferRes.aborted) {
    throw new Error(
      `Transaction aborted !: ${
        (transferRes.exitCode, transferRes.resultCode)
      }`
    );
  }

  // In this case the recipient didn't have any token wallet and one is deployed during the transfer, so we fetch it since we haven't before
  recipientTokenWallet = new provider.Contract(
    // Transferring the token
    tip3Artifacts.factorySource['TokenWallet'],
    receiverTokenWalletAddress
  );
  // recipient balance after transfer
  const newBal: number = Number(
    (
      await recipientTokenWallet.methods
        .balance({ answerId: 0 })
        .call({})
    ).value0
  );

  // Checking if the tokens are received successfully
  if (newBal >= Number(transferAmount) * 10 ** decimals + oldBal) {
    console.log('tokens transferred successfully');
  } else {
    throw new Error(
      ` Transferring tokens failed \n tx Hash: ${
        (transferRes.exitCode, transferRes.resultCode)
      }`
    );
  }

  // transferring token to wallet
  const transferToWalletRes: Transaction = await tokenWallet.methods
    .transferToWallet({
      amount: transferAmount * 10 ** decimals,
      recipientTokenWallet: recipientTokenWallet.address,
      remainingGasTo: providerAddress,
      notify: false,
      payload: '',
    })
    .send({
      from: providerAddress,
      amount: String(3 * 10 ** 9),
      bounce: true,
    });

  // Throwing an error if the transaction was aborted
  if (transferToWalletRes.aborted) {
    throw new Error(`Transaction aborted !: ${transferRes.exitCode}`);
  }

  // newBal is actually the old balance and its fetched before utilizing the "transferToWallet" the function
  if (
    Number(
      (
        await recipientTokenWallet.methods
          .balance({ answerId: 0 })
          .call({})
      ).value0
    ) > newBal
  ) {
    console.log('tokens transferred successfully');
    return `tx Hash: ${transferRes.id.hash}`;
  } else {
    throw new Error(
      `Transferring tokens failed, tx Hash: ${transferRes.id.hash}`
    );
  }
}

Step 2: Transfer TIP-3 Tokens

Use this command to transfer TIP-3 tokens:

shell
npx locklift run -s ./scripts/04-transfer-tip3.ts -n local
transferTip3Output

Congratulations, you have successfully transferred TIP-3 tokens from one to another Wallet 🎉

Transfer TIP-3 tokens

Token Root address

Recipient address

Amount

GIF

Transfer TIP-3 tokens to Token Wallet

Token Wallet address

Amount

GIF