Skip to content
On this page

Deploy Token Root

In this section, we will learn a little more about the memory structure and StateInit in the TVM (Ton Virtual Machine), and deploy our token root contract through a smart contract.

Memory Structure and State Init

TVM memory and persistent storage consist of cells. Remember that the TVM memory and persistent storage consist of (TVM) cells.

tvm.buildStateInit - Generates a StateInit from code and data TvmCells. Member splitDepth of the tree of cell StateInit:

  1. is not set. Has no value.
  2. is set. 0 <= splitDepth <= 31
  3. Arguments can also be set with names. List of possible names:
  4. code (TvmCell) - defines the code field of the StateInit. Must be specified.
  5. data (TvmCell) - defines the data field of the StateInit. Conflicts with pubkey and varInit. Can be omitted, in this case data field would be build from pubkey and varInit.
  6. splitDepth (uint8) - splitting depth. 0 <= splitDepth <= 31. Can be omitted. By default, it has no value.
  7. pubkey (uint256) - defines the public key of the new contract. Conflicts with data. Can be omitted, default value is 0.
  8. varInit (initializer list) - used to set static variables of the contract. Conflicts with data and requires contr to be set. Can be omitted.
  9. contr (contract) - defines the contract whose StateInit is being built. Mandatory to be set if the option varInit is specified.

Step 1: Write Deployment Script

Follow the instructions below to deploy a TokenRoot using the rootDeployer contract with the locklift tool and the state of the previously written script for deploying the RootDeployer.

INFO

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

Deploying a token root using everscale-inpage-provider is now made easier with the Multi Wallet contract, as demonstrated in the code samples below:

WARNING

The parameter initialSupply must be set to zero if the initialSupplyTo is zero address.


typescript
/* Deploying Token Root contract using root Deployer */

// Defining an interface for tokens root deployment using the root deployer contract
interface deployRootParams {
  initialSupplyTo: Address;
  rootOwner: Address;
  name: string;
  symbol: string;
  decimals: number;
  mintDisabled: boolean;
  burnByRootDisabled: boolean;
  burnPaused: boolean;
  initialSupply: number;
  deployWalletValue: number;
  randomNonce: number;
  remainingGasTo: Address;
}

// Preparing the deployment params
const deployRootFromDeployerParams: deployRootParams = {
  name: 'Tip3OnboardingToken',
  decimals: 6,
  initialSupplyTo: zeroAddress,
  initialSupply: 0,
  deployWalletValue: 0,
  symbol: 'TOT',
  mintDisabled: false,
  rootOwner: aliceAccount.address,
  randomNonce: locklift.utils.getRandomNonce(),
  burnByRootDisabled: false,
  burnPaused: false,
  remainingGasTo: aliceAccount.address,
};

// Deploying the token root utilizing an external message to the root deployer contract
await rootDeployer.methods
  .deployTokenRoot(deployRootFromDeployerParams)
  .sendExternal({
    publicKey: signerAlice.publicKey,
  });

// Confirming tha that the token root is deployed by calling its name method
const tokenRootContract: Contract<FactorySource['TokenRoot']> =
  locklift.factory.getDeployedContract(
    'TokenRoot',
    (
      await rootDeployer.methods
        .getExpectedTokenRootAddress({
          name: deployRootFromDeployerParams.name,
          decimals: deployRootFromDeployerParams.decimals,
          symbol: deployRootFromDeployerParams.symbol,
          rootOwner: deployRootFromDeployerParams.rootOwner,
          randomNonce: deployRootFromDeployerParams.randomNonce,
        })
        .call()
    ).value0
  );

console.log(
  `Token Root address : ${tokenRootContract.address.toString()} \n Token Root name: ${
    (await tokenRootContract.methods.name({ answerId: 0 }).call())
      .value0
  }`
); // >> Tip3OnboardingToken
typescript
import {
  ProviderRpcClient,
  Address,
  GetExpectedAddressParams,
  Contract,
  Transaction,
  FullContractState,
} from 'everscale-inpage-provider';
import * as tip3Artifacts from 'tip3-docs-artifacts';
import { provider, providerAddress } from './useProvider';

/**
 * We develop two more methods in order to reduce the mass of the script
 */

// THis function will extract the public key of the sender
async function extractPubkey(
  provider: ProviderRpcClient,
  senderAddress: Address
): Promise<string> {
  // Fetching the user public key
  const accountFullState: FullContractState = (
    await provider.getFullContractState({ address: senderAddress })
  ).state!;

  const senderPublicKey: string = await provider.extractPublicKey(
    accountFullState.boc
  );

  return senderPublicKey;
}

async function main() {
  // Initiate the TVM provider

  // Token Root contracts abi
  const tokenRootAbi: tip3Artifacts.FactorySource['TokenRoot'] =
    tip3Artifacts.factorySource['TokenRoot'];

  // Creating an instance of the  root deployer contract
  const rootDeployerAddress: Address = new Address(
    '<YOUR_ROOT_DEPLOYER_ADDRESS>'
  );

  const rootDeployerAbi: tip3Artifacts.FactorySource['RootDeployer'] =
    tip3Artifacts.factorySource['RootDeployer'];

  const rootDeployerContract: Contract<
    tip3Artifacts.FactorySource['RootDeployer']
  > = new provider.Contract(rootDeployerAbi, rootDeployerAddress);

  // Defining an interface for tokens root deployment using the root deployer contract
  interface deployRootParams {
    initialSupplyTo: Address;
    rootOwner: Address;
    name: string;
    symbol: string;
    decimals: number;
    mintDisabled: boolean;
    burnByRootDisabled: boolean;
    burnPaused: boolean;
    initialSupply: number;
    deployWalletValue: number;
    randomNonce: number;
    remainingGasTo: Address;
  }

  // Preparing the parameters
  const params: deployRootParams = {
    initialSupplyTo: tip3Artifacts.zeroAddress,
    rootOwner: providerAddress,
    randomNonce: (Math.random() * 6400) | 0,
    deployWalletValue: 0,
    name: 'Tip3OnboardingToken',
    symbol: 'TOT',
    decimals: 6,
    mintDisabled: false,
    burnByRootDisabled: false,
    burnPaused: false,
    initialSupply: 0,
    remainingGasTo: providerAddress,
  };

  // Deploying the tokenRoot
  const { transaction: deployRes } =
    await rootDeployerContract.methods
      .deployTokenRoot(params)
      .sendExternal({
        publicKey: await extractPubkey(provider, providerAddress),
      });

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

  // Fetching the address of the token root
  const tokenRootAddr: Address = (
    await rootDeployerContract.methods
      .getExpectedTokenRootAddress({
        name: params.name,
        decimals: params.decimals,
        symbol: params.symbol,
        rootOwner: params.rootOwner,
        randomNonce: params.randomNonce,
      })
      .call()
  ).value0;

  // making an instance of the token root
  const tokenRootContract: Contract<
    tip3Artifacts.FactorySource['TokenRoot']
  > = new provider.Contract(tokenRootAbi, tokenRootAddr);

  // checking if the token root is deployed successfully by calling one of its methods
  const tokenName: string = (
    await tokenRootContract.methods.name({ answerId: 0 }).call({})
  ).value0;

  if (tokenName == params.name) {
    console.log(`${params.symbol} Token deployed successfully`);
    return `${params.symbol} deployed to ${tokenRootAddr.toString()}`;
  } else {
    throw new Error(
      `${params.symbol} Token deployment failed !${deployRes.exitCode}`
    );
  }
}

Step 2: Deploy Token Root

Let's run our script using locklift

shell
npx locklift run -s ./scripts/03-deploy-token.ts -n local
buildStructure

Congratulations, you have deployed a TIP3 Token Root through the Root Deployer contract 🎉

Root deployer

initialSupplyTo

rootOwner

name

symbol

decimals

initialSupply

GIF