# Building Multichain Stablecoins: Part One

As the web3 ecosystem continues to grow and more blockchains enter the industry, ensuring that your token is available on multiple chains is essential for the success of your application. What is also essential is that your token can be sent across these different chains to one another, rather than having siloed instances of your token across different chains. Axelar's Interchain Token Service is built to provide a solution for this.

This blog assumes you have an understanding of how to use Hardhat, a basic understanding of upgradable contracts, and how ERC20s operate.

Note: The token being built here contains many of the properties that can be used for a stablecoin such as transaction fee redistribution to token holders and transaction burning to contain inflation. The token will not contain all the components of a complete stablecoin. Rather the blog will focus more on the integration of the cross-chain components and testing of the token.

### What is Interchain Token Service?

The [Interchain Token Service](https://www.axelar.network/its) (ITS) allows for the integration of tokens across many different blockchains. It can support the deployment of fresh new Interchain Tokens across multiple chains and it can also connect pre-existing custom ERC20s. ITS also comes with an easy-to-use [frontend](https://interchain.axelar.dev/), which offers a no-code solution for deploying your token across any connected chain that you choose.

### Objective

In this blog, you will be focusing on custom ERC20 token integrations to ITS. Custom tokens can have more advanced functionality than the [InterchainTokens](https://github.com/axelarnetwork/interchain-token-service/blob/main/contracts/interchain-token/InterchainToken.sol) that ITS deploys out of the box in its no-code solution. The token that you will be building will be an upgradable token that will be deployed using Axelar's `create3` service. The token will also have transaction fee redistribution to token holders and a burning mechanism, that burns a small percentage of each transaction to simulate inflation control.

In addition to the token itself, you will be using a `TokenFactory` contract that you will build out to deploy your token across different chains. There will be two types of tokens that will be built out, `NativeToken` and `SemiNativeToken`. The `NativeToken` will be the main token with the previously discussed features. The `SemiNativeToken` on the other hand will be a simpler ERC20 that will also be deployed through the factory. The reason for the two different tokens is to simulate a common issue, which teams in the stablecoin space face. Oftentimes, teams may be restricted to only officially operate on a certain group of chains, perhaps due to regulations or other restrictions. For the chains, that a team is regulated to operate on they can use the `NativeToken` but for those chains where they are not regulated, the simpler `SemiNativeToken` can be deployed by anyone via the `TokenFactory`. The `SemiNativeToken` will eventually be replaced by a `NativeToken` once the team is eligible to fully operate on that chain.

### Architecture

This project will be built using [Hardhat](https://hardhat.org/) (but it can of course be built out with [Foundry](https://book.getfoundry.sh/) as well). The five contracts you'll be interacting with are the `TokenFactory`, `TokenDeployer`, `NativeToken`, `SemiNativeToken`, and `AccessControl`.

Let's build!

Start by cloning the [starter code](https://github.com/benjamin852/Advanced-ITS-Token).

### NativeToken

You can begin by writing up the `NativeToken` contract.

Start by giving the token a name and importing the required `OpenZeppelin` helper contracts.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import '@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PausableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';

contract NativeToken is  Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, ERC20PermitUpgradeable {}
```

Next, go ahead and create the `initializer` for the token.

```solidity
 /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize() public initializer {
        __ERC20_init('USD Token', 'USD');
        __ERC20Burnable_init();
        __ERC20Pausable_init();
        __ERC20Permit_init('USD Token');
    }
```

Once the initializer has been implemented you can implement two publicly available `mint()` and `burn()` functions.

```solidity
 function mint(address _to, uint256 _amount) public whenNotPaused isBlacklisted(_to) {
     _mint(_to, _amount);
 }

 function burn(address _from, uint256 _amount) public whenNotPaused {
     _burn(_from, _amount);
 }
```

Note the `whenNotPaused` modifier on both the `mint()` and `burn()` functions which will be activated in case of an emergency.

You also need to override the `_update()` function. This function is written out in the `ERC20Upgradeable` contract that you're inheriting from. The `_update()` function is used when transferring a `value` amount of tokens from one address to another. You will need to eventually implement the custom burning and transaction fee logic in this function.

For now, implement it as follows:

```solidity
    function _update(address from, address to, uint256 value)
        internal
        override(ERC20Upgradeable, ERC20PausableUpgradeable) whenNotPaused
    {
        ERC20Upgradeable._update(from, to, value);
    }
```

Great! At this point, you should have a working ERC20 token that can be upgradable!

Let's build this out a bit further. As mentioned before we want the token to have a `burnRate` for every transaction that is sent and a `fee` which will be paid out to token holders.

Set the `burnRate` and `txFeeRate` in storage as two public `uint256` variables. You can set a value for them in your `initialize()` function

Your contract should now look like this.

```solidity
    uint256 public s_burnRate;
    uint256 public s_txFeeRate;


    /*************\
     INITIALIZATION
    /*************/

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(
        uint256 _burnRate,
        uint256 _txFeeRate
    ) public initializer {
        __ERC20_init('USD Token', 'USD');
        __ERC20Burnable_init();
        __ERC20Pausable_init();
        __ERC20Permit_init('USD Token USD');

        s_burnRate = _burnRate;
        s_txFeeRate = _txFeeRate;
    }
```

Now, back in your `_update()` function you can include some logic that will occur on every token transfer.

First, you set the `burnAmount` by multiplying the `value` of the transfer by the `s_burnRate` you set and then divide that by `1e18` (assuming your token has 18 decimal points). The same can be done for the transaction fee.

Now that you have the `burnAmount` and the `fee` you can subtract those two values from the actual amount being sent.

This can be written out as follows

```solidity
     uint256 burnAmount = (_value * s_burnRate) / 1e18;
     uint256 fee = (_value * s_txFeeRate) / 1e18;

     uint256 amountToSend = _value - fee - burnAmount;
```

Once you have the `amountToSend` you can burn the `burnAmount` and update a new storage variable called `s_rewardPool` with the fee.

Worth noting, is you only want to deduct these fees when a token is being transferred from one address to another as opposed to when a new token is being minted. The way to do this is to check if the `_from` address is `address(0)`. If it is, then `return` the function before calling the aforementioned functionality. The completed `_update()` function should be as follows.

```solidity
    function _update(
        address _from,
        address _to,
        uint256 _value
    ) internal override(ERC20Upgradeable, ERC20PausableUpgradeable) whenNotPaused {
        if (_from == address(0)) {
            // Minting case, do not apply burn and fee
            ERC20Upgradeable._update(_from, _to, _value);
            return;
        }
        uint256 burnAmount = (_value * s_burnRate) / 1e18;
        uint256 fee = (_value * s_txFeeRate) / 1e18;

        uint256 amountToSend = _value - fee - burnAmount;

        if (burnAmount > 0) _burn(_from, burnAmount);

        if (amountToSend + burnAmount + fee != _value) revert InvalidSendAmount();
        s_rewardPool += fee;
        ERC20Upgradeable._update(_from, _to, amountToSend);
        emit RewardAdded(fee);
    }
```

Now that there is a reward pool accruing for token holders, you can include a simple `claimRewards()` function that allows token holders to claim a proportional reward for the number of tokens that they hold.

To calculate the reward you can check the balance of tokens that they have multiplied by the reward pool divided by the total supply of the token. Let's extract this into its own helper function called `_calculateReward()`

```solidity

    function _calculateReward(address _account) internal view returns (uint256) {
        if (totalSupply() == 0) return 0;
        return (s_rewardPool * balanceOf(_account)) / totalSupply();
    }
```

Now the `claimRewards()` function can call this function and mint an appropriate reward based on the output of the `calculateReward()` function. The `claimRewards()` function can be written as follows

```solidity
   function claimRewards() external whenNotPaused {
        uint256 reward = _calculateReward(msg.sender);
        s_rewardPool -= reward;
        _mint(msg.sender, reward);
        emit RewardClaimed(msg.sender, reward);
    }
```

The final bit of functionality this token needs is the ability to alter these rates going forward. Let's add those functions as follows

```solidity
  function setBurnRate(uint256 newBurnRate) external whenNotPaused  {
        s_burnRate = newBurnRate;
  }

  function setTxFee(uint256 newRewardRate) external whenNotPaused  {
        s_txFeeRate = newRewardRate;
  }
```

Great! At this point, most of the token logic is now complete. The only issue you might want to restrict is who can call critical functions such as `setBurnRate` and `setTxFee()`.

### AccessControl

To address this you will add a new contract called AccessControl. The AccessControl will inherit from OpenZeppelin's `AccessControlUpgradeable` contract.

The AccessControl contract will allow for several roles including `admin` role, `minter` role, and `blacklisted` role. You will need a `mapping` to track the different addresses that have been assigned to specific roles. The completed AccessControl will look something like this.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';

contract AccessControl is AccessControlUpgradeable {
    bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE');
    bytes32 public constant PAUSER_ROLE = keccak256('PAUSER_ROLE');
    bytes32 public constant BLACKLIST_ADMIN_ROLE = keccak256('BLACKLIST_ADMIN_ROLE');

    // eligible minters
    mapping(address => bool) private _minterAddresses;

    // blacklisted (receiver) addresses
    mapping(address => bool) private _blacklistedAddresses;

    function initialize(address _defaultAdmin) public initializer {
        __AccessControl_init();
        _grantRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
        _grantRole(MINTER_ROLE, _defaultAdmin);
        _grantRole(BLACKLIST_ADMIN_ROLE, _defaultAdmin);
    }

    function addAdminRole(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
        grantRole(DEFAULT_ADMIN_ROLE, _address);
    }

    function removeAdminRole(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
        revokeRole(DEFAULT_ADMIN_ROLE, _address);
    }

    function addNewMinter(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _minterAddresses[_address] = true;
    }

    function removeMinter(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _minterAddresses[_address] = false;
    }

    function addToBlacklist(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _blacklistedAddresses[_address] = true;
    }

    function removeFromBlacklist(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _blacklistedAddresses[_address] = false;
    }

    function isAdmin(address _address) external view returns (bool) {
        return hasRole(DEFAULT_ADMIN_ROLE, _address);
    }

    function isWhitelistedMinter(address _address) external view returns (bool) {
        return _minterAddresses[_address];
    }

    function isBlacklistedReceiver(address _address) external view returns (bool) {
        return _blacklistedAddresses[_address];
    }
}
```

Note: For blacklisting, we included a simplified `mapping` to collect any potential blacklisted addresses from receiving your token. In production, it would be recommended to use a service such as ChainAnalysis, which has an easy-to-use [Oracle](https://go.chainalysis.com/chainalysis-oracle-docs.html) that you can integrate with your contract that tracks sanctioned addresses that regulators would prohibit your token from being sent to.

Now with your `AccessControl` set you can return to your `NativeToken` contract to integrate with the `AccessControl` contract.

First, you need to import the `AccessControl` contract to your token.

```solidity
import './AccessControl.sol';
```

Next, in the `initialize` function of your Token contract, make sure to pass in the address of your `AccessControl` contract and then store in a storage variable.

Your `initialize` function should now look like this.

```solidity
  function initialize(
        AccessControl _accessControl,
        uint256 _burnRate,
        uint256 _txFeeRate
    ) public initializer {
        __ERC20_init('USD Token', 'USD');
        __ERC20Burnable_init();
        __ERC20Pausable_init();
        __ERC20Permit_init('USD Token');

        s_accessControl = _accessControl;

        s_burnRate = _burnRate;
        s_txFeeRate = _txFeeRate;
    }
```

Now you can add a `modifier` to use the `AccessControl` contract to restrict certain functionality to specific addresses granted a given role.

For example, `isAdmin` can restrict the `msg.sender` to be a specific whitelisted admin.

```solidity
 modifier isAdmin() {
        if (s_accessControl.isAdmin(msg.sender)) revert OnlyAdmin();
        _;
    }
```

You can use the `isAdmin` modifier for your `setBurnRate()` and `setTxFeeRate()` modifiers.

Great! At this point, you should have all the functionality you need for your `NativeToken`! Now you need to deploy your token. For this, you can use a `Factory` contract to deploy the token on your home chain as well as other remote chains.

### Token Factory

In this section, you will build the `TokenFactory` which will deploy the NativeToken you have just written on the home chain and also send a [General Message Passing (GMP message)](https://docs.axelar.dev/dev/general-message-passing/overview) to a remote blockchain where you can deploy your token as well!

The factory will deploy a [TransparentProxy](https://blog.openzeppelin.com/the-transparent-proxy-pattern) for your token so that your token is upgradable. It will also use [create3](https://www.axelar.network/blog/same-address-cross-chain-tutorial) so that your token is at the same address across all chains that you choose to deploy on.

Much like the `NativeToken` contract, this contract will also be upgradable. So let's start with the `initialize` function.

The `initialize` function will take in several params including the address of the `InterchainTokenService`, the addresses of Axelar's `Gateway` and `GasService` contracts (which you will need for sending GMP messages), and the address of your `AccessControl` contract.

You will also need the appropriate storage variables to store these parameters in. Your `TokenFactory` contract should look like this now.

```solidity
import '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import '@axelar-network/interchain-token-service/contracts/interfaces/IInterchainTokenService.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';  
contract TokenFactory {   
    IInterchainTokenService public s_its;
    AccessControl public s_accessControl;
    IAxelarGasService public s_gasService;
    IAxelarGateway public s_gateway;    

    /*************\
     INITIALIZATION
    /*************/
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(
        IInterchainTokenService _its,
        IAxelarGasService _gasService,
        IAxelarGateway _gateway,
        AccessControl _accessControl,
    ) external initializer {
        s_its = _its;
        s_gasService = _gasService;
        s_gateway = _gateway;
        s_accessControl = _accessControl;
    }
}
```

With your `initializer` being built you can now move on to actually deploying the token.

Let's write a specific function for deploying the token on your *home* chain called `deployHomeNative()`

Since your NativeToken requires a `burnRate` and `txFeeRate` you must make sure to pass that into your `deployHomeNative()` function to set those rates for the token. The function should look like this

```solidity
function deployHomeNative(uint256 _burnRate, uint256 _txFeeRate) external onlyAdmin returns (address newTokenProxy) {}
```

Note the `onlyAdmin` modifier can be defined exactly as you did previously in the Token contract so that only your team can deploy the original token on the home chain.

Before writing up the rest of this function you will need to import the `Create3.sol` contract from the `axelar-gmp-sdk` dependency. This will give you access to the `_create3()` function that you will use to deploy the contract. Import the dependency as follows

```solidity
import '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3.sol';
```

Now with the dependency imported, you can have your contract inherit from the `Create3` contract.

When deploying with `create3()` you will need to pas in a `bytes32` salt, which will be used to determine the address of your contract. For simplicity you can defined to salts in the beginning of the `deployHomeNative()` function. The first salt can be for the deployment of your proxy and the second for the deployment of your implementation.

```solidity

bytes32 SALT_PROXY = 0x000000000000000000000000000000000000000000000000000000000000007B; //123
bytes32 SALT_IMPL = 0x00000000000000000000000000000000000000000000000000000000000004D2; //1234
```

These hexes correspond to the numbers `123` for the proxy and `1234` for the implementation.

Next, you can deploy the `implementation` of the `NativeToken` contract. (Recall since the contract is going to be an upgradable contract you need to deploy both a proxy and implementation).

To deploy the `implementation` simply call the `_create3()` internal function, which is defined in the `Create3` contract that you inherited from before.

```solidity
// Deploy implementation
address newTokenImpl = _create3(type(NativeToken).creationCode, SALT_IMPL);
if (newTokenImpl == address(0)) revert DeploymentFailed();
```

The `_create3()` takes the deployment bytecode of the `NativeToken` contract and the salt that you already defined. You can then run a sanity check to make sure that the deployment was in fact successful and did not return a 0 address.

Now with your implementation contract deployed, you can move on to the `proxy`. Deploying the `proxy` will be a bit different than deploying the `implementation`, as you will technically be deploying a [TransparentUpgradeableProxy](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) that is pointing to your implementation. To do this you can define a helper function called `_getEncodedCreationCodeNative()`.

This function will take in your [ProxyAdmin](https://docs.openzeppelin.com/contracts/4.x/api/proxy#ProxyAdmin), your `implementation` address, your `_burnRate`, and your `_txFeeRate`.

Your function signature should look like this

```solidity

    function _getEncodedCreationCodeNative(
        address _proxyAdmin,
        address _implAddr,
        uint256 _burnRate,
        uint256 _txFeeRate
    ) internal view returns (bytes memory proxyCreationCode) {}
```

Now in this function, you can set your `initData` for the Token itself. Recall these are the three params that you required in your `initialize` function for the `NativeToken` contract (`accessControl`, `burnRate`, and `txFeeRate`). You can encode these three params into a single `bytes` variable that will be used when initializing your proxy

```solidity
bytes memory initData = abi.encodeWithSelector(NativeToken.initialize.selector, s_accessControl, s_its, _burnRate, _txFeeRate);
```

Now (still in your `_getEncodedCreationCodeNative()` function). You can encode the `initData` you just defined along with the `_proxyAdmin` and the `_implAddr` that the `TransparentProxy` will require when it's being deployed. You can then encode that with the creation code of the `TransparentProxy` itself. This all translates into the following line of code

```solidity
proxyCreationCode = abi.encodePacked(type(TransparentUpgradeableProxy).creationCode, abi.encode(_implAddr, _proxyAdmin, initData));
```

So your completed `_getEncodedCreationCodeNative()` function now looks like this:

```solidity
    function _getEncodedCreationCodeNative(
        address _proxyAdmin,
        address _implAddr,
        uint256 _burnRate,
        uint256 _txFeeRate
    ) internal view returns (bytes memory proxyCreationCode) {
        bytes memory initData = abi.encodeWithSelector(NativeToken.initialize.selector, s_accessControl, s_its, _burnRate, _txFeeRate);

        proxyCreationCode = abi.encodePacked(type(TransparentUpgradeableProxy).creationCode, abi.encode(_implAddr, _proxyAdmin, initData));
    }
```

Back to your `deployHomeNative()` function you can now trigger the `_getEncodedCreationCodeNative()` function underneath where your deployed the `implementation` contract. The output of the `_getEncodedCreationCodeNative()` function will be the creation code of your proxy contract.

```solidity
bytes memory proxyCreationCode = _getEncodedCreationCodeNative(msg.sender, newTokenImpl, _burnRate, _txFeeRate);
```

With the `proxyCreationCode` now available, you can call `_create3()` once again (exactly as you did for the implementation contract) and pass in the `proxyCreationCode` as the first parameter and the `SALT_PROXY` as the salt value. Let's also store the address of the deployed proxy in a storage variable called `s_nativeProxy`.

```solidity
// Deploy the proxy
newTokenProxy = _create3(proxyCreationCode, SALT_PROXY);
if (newTokenProxy == address(0)) revert DeploymentFailed();

s_nativeToken = newTokenProxy;
```

Now that you have stored the address of the proxy you can go back to the top of the `deployHomeNative()` function and add in a quick check to make sure that no native token has been deployed before. If one has been deployed then you can `revert` the transaction.

```solidity
if (s_nativeToken != address(0)) revert TokenAlreadyDeployed();
```

Awesome! At this point once you call the `deployHomeNativeFunction()` you should now be able to deploy an upgradable version of the `NativeToken` stablecoin. But there is still more to do! Recall, that you want to be able to send cross-chain transactions with this token via ITS.

#### ITS Integration

To integrate your token with ITS you will need to deploy a token manager. A [Token Manager](https://github.com/axelarnetwork/interchain-token-service/blob/main/contracts/token-manager/TokenManager.sol) is a separate contract that will help facilitate the integration of your token with ITS. Some of its responsibilities include setting flow limits (akin to rate limits) for your token, locking, burning, and minting tokens for cross-chain transactions. A token cannot send cross-chain transactions via ITS unless it has been successfully registered with its own unique token manager.

To deploy a token manager for your token you can simply call the function `deployTokenManager()` which is defined on the ITS contract that you passed into your `initialize` function.

The `deployTokenManager()` takes the [following params](https://github.com/axelarnetwork/interchain-token-service/blob/main/contracts/InterchainTokenService.sol#L270). First, it needs a salt, this salt will be used to generate your tokens unique `interchainTokenId`. This token will be used to track your authentic token deployment across all chains that your token is wired up to do cross-chain transfers. Next, you need to pass in the destination chain in case you're doing a cross-chain deployment of the token manager. In this case, you're deploying the token manager for the chain you're already on since there is no destination chain needed you can simply pass in an empty string here. You then need to pass in the [type](https://github.com/axelarnetwork/interchain-token-service/blob/main/contracts/interfaces/ITokenManagerType.sol) of token manager you want. For this demo you can use the `LOCK_UNLOCK` token manager, this will lock your token on the home chain and unlock that token when it's bridged back from one of the remote chains. Next, you need to pass in the `params`. This is a `bytes` encoding of the address that will be the `operator` of the token and the address of the token itself. The `operator` is a role that allows a given address to adjust flow limits for the token. The fifth and final parameter is the `gasValue` this will be used to pay for the cost of a cross-chain token transfer, since you are doing a cross-chain deployment you can keep this as zero.

Still, in your `deployHomeNative()` function, you can call the `deployTokenManager()` function like this

```solidity
s_its.deployTokenManager(
    S_SALT_ITS_TOKEN,
    '',
    ITokenManagerType.TokenManagerType.LOCK_UNLOCK,
    abi.encode(msg.sender.toBytes(), newTokenProxy),
    0
);
```

You can add a new event at the very bottom of the function to mark a successful token deployment and registration and that is it! The completed function should now look like this.

```solidity
    function deployHomeNative(uint256 _burnRate, uint256 _txFeeRate) external onlyAdmin returns (address newTokenProxy) {
        if (s_nativeToken != address(0)) revert TokenAlreadyDeployed();

        bytes32 SALT_PROXY = 0x000000000000000000000000000000000000000000000000000000000000007B; //123
        bytes32 SALT_IMPL = 0x00000000000000000000000000000000000000000000000000000000000004D2; //1234

        // Deploy implementation
        address newTokenImpl = _create3(type(NativeToken).creationCode, SALT_IMPL);
        if (newTokenImpl == address(0)) revert DeploymentFailed();

        // Generate Proxy Creation Code
        bytes memory proxyCreationCode = _getEncodedCreationCodeNative(msg.sender, newTokenImpl, _burnRate, _txFeeRate);

        // Deploy proxy
        newTokenProxy = _create3(proxyCreationCode, SALT_PROXY);
        if (newTokenProxy == address(0)) revert DeploymentFailed();
        s_nativeToken = newTokenProxy;

        // Deploy ITS
        bytes32 tokenId = s_its.deployTokenManager(
            S_SALT_ITS_TOKEN,
            '',
            ITokenManagerType.TokenManagerType.LOCK_UNLOCK,
            abi.encode(msg.sender.toBytes(), newTokenProxy),
            0
        );

        emit NativeTokenDeployed(newTokenProxy, tokenId);
    }
```

Great! At this point when this function is called you will have successfully deployed an upgradeable `NativeToken` AND connected it to the Interchain Token Service!

### Conclusion

In this section, you have built a custom ERC20 token that can burn tokens at a set burn rate for each token transfer and accrue proportional rewards for token holders. The token's roles are controlled by the Access Control contract. Finally, you wrote up a factory contract to deploy your custom token as an upgradable token using `create3` to have a predictable contract address and integrated your newly deployed token with ITS.

In [part two](https://blog.axelar.dev/building-multichain-stablecoins-part-two), you will continue to build out your Token Factory so that you can deploy your custom token on other blockchains.

The complete code for this section can be found [here](https://github.com/benjamin852/Multichain-Stablecoin/tree/complete-p1).
