# Building Multichain Stablecoins: Part Two

### Objective

In [section one](https://blog.axelar.dev/building-multichain-stablecoins-part-one), you built an upgradable custom ERC20 stablecoin, that accrues value for token holders and burns a percentage of each token transfer to address token inflation. You also began building a factory contract that integrated with Axelar's [Interchain Token Service](https://www.axelar.network/its). The factory deployed your custom ERC20 as an upgradable token using [create3](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/contracts/deploy/Create3.sol). In this section, you will build out the functionality to deploy your token from your home chain onto a remote chain via your factory contract. You will also build out the functionality to upgrade your token to a simple V2 version. Finally, you will be able to test the token by actually sending a cross-chain transaction!

### Deploy On Remote Chain

Let's build out a separate function called `deployRemoteSemiNativeToken()` to handle this deployment. This function will be used to deploy an instance of the `SemiNativeToken` discussed earlier. Recall, Semi Native tokens will not have the transaction fee redistribution and burning functionality that Native tokens have, these will be simpler tokens merely to provide some pseudo presence for your stablecoin on a chain you may not want to operate your official token on yet.

Since there is no `burnRate` and `txFeeRate` needed here, all you need to pass into this function is the name of the destination chain you want to deploy your token on.

```solidity
function deployRemoteSemiNativeToken(string calldata _destChain) external payable {}
```

To start you will want to compute the `interchainTokenId` that ITS will use to register the token. To compute your token's `interchainTokenId` you need to hash the string `interchain-token-id` with the address that is deploying the token with a unique salt. This can be done as follows

```solidity
bytes32 computedTokenId = keccak256(abi.encode(keccak256('its-interchain-token-id'), address(this), S_SALT_ITS_TOKEN));
```

Next, you will need to construct and send your [GMP message](https://docs.axelar.dev/dev/general-message-passing/overview#at-the-source-chain) to the destination chain.

Your GMP message needs to be of type `bytes` so let's encode the different components of your GMP message in a `bytes` type object. The three unique messages you will be encoding here a first your `computedTokenId`, second the Semi Native Token's `creationCode`, and last the Semi Native Token's `selector`. This can be done as follows

```solidity
bytes memory gmpPayload = abi.encode(computedTokenId, type(SemiNativeToken).creationCode, SemiNativeToken.initialize.selector);
```

Now that you have your encoded message you can interact with the Axelar `GasService` and `Gateway` to send your GMP message. If this is new to you, feel free to [watch this tutorial](https://www.youtube.com/watch?v=htMVIYzGA34&t=1s) for the basics of sending a GMP message.

To pay for the transaction with the [Axelar Gas Service](https://github.com/axelarnetwork/axelar-cgp-solidity/blob/main/contracts/gas-service/AxelarGasService.sol) you must call the `payNativeGasForContractCall()` function. This function requires the address making the payment, the destination chain, the destination address, the gmp message, and the refund address (in the event of an overly high gas payment) as its parameters.

This can be written out as follows:

```solidity
   s_gasService.payNativeGasForContractCall{ value: msg.value }(
            address(this),
            _destChain,
            address(s_deployer).toString(),
            gmpPayload,
            msg.sender
        );
```

Note the `s_deployer` variable that is referenced as the destination address. This is pointing to a storage variable called `s_deployer` that has yet to be defined.

It can be defined up top with the rest of your storage variable and passed in as the fifth parameter to your `initialize` function so that your `initialize` function now looks like this.

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

        S_SALT_ITS_TOKEN = 0x0000000000000000000000000000000000000000000000000000000000003039; //12345
    }
```

Now that your `s_deployer` function is available you can send your GMP message to the destination chain you need to call the `callContract()` function on the [Axelar Gateway](https://github.com/axelarnetwork/axelar-cgp-solidity/blob/main/contracts/AxelarGateway.sol) contract. It requires similar parameters to the previous function, namely the destination chain, destination address, and GMP message.

```solidity
s_gateway.callContract(_destChain, address(s_deployer).toString(), gmpPayload);
```

Your `deployRemoteSemiNativeToken()` should now be complete. Note the conditional that was added at the beginning of the function that references a new mapping you need to add in storage called `s_semiNativeTokens`, this will be where you register all your remote `SemiNativeTokens` that you want your factory contract to be aware of. In this `if` statement, you are making sure that the `SemiNativeToken` has not yet been deployed on the remote chain you're attempting to deploy to.

```solidity
    function deployRemoteSemiNativeToken(string calldata _destChain) external payable {
        if (s_semiNativeTokens[_destChain] != address(0) && s_nativeToken != address(0)) revert TokenAlreadyDeployed();

        bytes32 computedTokenId = keccak256(abi.encode(keccak256('its-interchain-token-id'), address(this), S_SALT_ITS_TOKEN));

        bytes memory gmpPayload = abi.encode(computedTokenId, type(SemiNativeToken).creationCode, SemiNativeToken.initialize.selector);

        s_gasService.payNativeGasForContractCall{ value: msg.value }(
            address(this),
            _destChain,
            address(s_deployer).toString(),
            gmpPayload,
            msg.sender
        );

        s_gateway.callContract(_destChain, address(s_deployer).toString(), gmpPayload);
    }
```

Now that the transaction has been sent from the contract on the source chain. It needs to be received on the destination chain.

### Deployer Contract

Your factory contract is now complete! However, you need to have a factory on all the destination chains that your factory will interact with. To make the factory more modular you can break up the following logic that still needs to be built out into a separate contract that you can call *Deployer.*

This will also be an upgradable contract with a similar `initialize()` function to the factory.

```solidity
contract Deployer {
   IInterchainTokenService public s_its;
   AccessControl public s_accessControl;
   IAxelarGateway public s_gateway;

   bytes32 public S_SALT_ITS_TOKEN;

   /// @custom:oz-upgrades-unsafe-allow constructor
   constructor() {
       _disableInitializers();
   }
  function initialize(IInterchainTokenService _its, AccessControl _accessControl, IAxelarGateway _gateway) external initializer {
        s_its = _its;
        s_accessControl = _accessControl;
        s_gateway = _gateway;

        S_SALT_ITS_TOKEN = 0x0000000000000000000000000000000000000000000000000000000000003039; //12345
    }
}
```

Notice the `S_SALT_ITS_TOKEN` is the exact same value as the `S_SALT_ITS_TOKEN` in the in the `TokenFactory` contract. This is to ensure that the ITS token that will be wired up here on the destination chain will map to the same `interchainTokenId`.

#### Execute

Recall where you left off in the `TokenFactory` contract. You were able to send a cross-chain message from the source chain via the `deployRemoteSemiNativeToken()` function. Now, to handle that message on the destination chain you can use the `execute()` function.

This function takes several parameters including the `_commandId`, `_sourceChain`, `_sourceAddress`, and `_payload`. The `payload` is the GMP message you sent from the source chain

```solidity
function execute(bytes32 _commandId, string calldata _sourceChain, string calldata _sourceAddress, bytes calldata _payload) external {}
```

The first line that must be written here is a call via the `AxelarGateway` to `validateContractCall()`. This function will ensure that the `payload` being passed in is a message that has been validated and confirmed by the Axelar validator set rather than an invalid message from a potentially malicious caller. This can be called as follows

```solidity
if (!s_gateway.validateContractCall(_commandId, _sourceChain, _sourceAddress, keccak256(_payload))) revert NotApprovedByGateway();
```

Now that you know the `payload` is valid you can decode it to get the actual GMP message you passed in from the source chain. Recall, in the `TokenFactory` contract on the src chain encoded a computed `tokenId` value, the creation code of the `SemiNativeToken` contract, and the `selector` for the `SemiNativeToken` contract. These values can be decoded using the `abi.decode()` function

```solidity
(bytes32 computedTokenId, bytes memory semiNativeTokenBytecode, bytes4 semiNativeSelector) = abi.decode(
    _payload,
    (bytes32, bytes, bytes4)
);
```

With your data now available on the destination chain you can deploy the `SemiNativeToken` just like you deployed the origin `NativeToken` on the source chain.

Since the `SemiNativeToken` will also be an upgradable token you must deploy the `implementation` first followed by the `proxy` that will point to the `implementation`. Note the salt value being passed into `_create3()` here is the same value in the `deployHomeNative()` function, meaning that the implementation address of these tokens will be the same address.

```solidity
address newTokenImpl = _create3(semiNativeTokenBytecode, 0x00000000000000000000000000000000000000000000000000000000000004D2);
if (newTokenImpl == address(0)) revert DeploymentFailed();
```

Next, you can call the `getEncodedCreationCodeSemiNative()` code, this will be very similar to the `getEncodedCreationCodeNative()` you wrote in the `TokenFactory` contract.

```solidity
    function _getEncodedCreationCodeSemiNative(
        address _proxyAdmin,
        address _implAddr,
        bytes32 _itsTokenId,
        bytes4 semiNativeSelector
    ) internal view returns (bytes memory proxyCreationCode) {
        //init func args
        bytes memory initData = abi.encodeWithSelector(semiNativeSelector, s_its, _itsTokenId);

        //concat bytecode + init func args
        proxyCreationCode = abi.encodePacked(type(TransparentUpgradeableProxy).creationCode, abi.encode(_implAddr, _proxyAdmin, initData));
    }
```

Now back in your `execute()` function you can call the `_getEncodedCreationCodeSemiNative()`. The output of this will be your `proxy` contract's creation code

```solidity
   bytes memory creationCodeProxy = _getEncodedCreationCodeSemiNative(
            address(this),
            newTokenImpl,
            computedTokenId,
            semiNativeSelector
        );
```

You can now deploy your `proxy` contract and store its address.

```solidity
address newTokenProxy = _create3(creationCodeProxy, 0x000000000000000000000000000000000000000000000000000000000000007B);
if (newTokenProxy == address(0)) revert DeploymentFailed();
s_tokenProxy = ITransparentUpgradeableProxy(newTokenProxy);
```

The final thing you need to do is connect your newly deployed token to ITS. You can do this exactly as you did before, by calling the `deployTokenManager()`.

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

Note, that the parameters passed in are exactly the same as in the `TokenFactory` contract, the only difference is the `TokenManagerType`. Rather than passing in a lock/unlock token manager, you can pass a mint/burn, the reason being that this execution is taking place on a remote chain rather than the source chain where you want to lock up bridged tokens.

Your completed `execute()` function should be as follows

```solidity
  
    function execute(bytes32 _commandId, string calldata _sourceChain, string calldata _sourceAddress, bytes calldata _payload) external {
        if (!s_gateway.validateContractCall(_commandId, _sourceChain, _sourceAddress, keccak256(_payload))) revert NotApprovedByGateway();

        (bytes32 computedTokenId, bytes memory semiNativeTokenBytecode, bytes4 semiNativeSelector) = abi.decode(
            _payload,
            (bytes32, bytes, bytes4)
        );

        address newTokenImpl = _create3(semiNativeTokenBytecode, 0x00000000000000000000000000000000000000000000000000000000000004D2);
        if (newTokenImpl == address(0)) revert DeploymentFailed();

        bytes memory creationCodeProxy = _getEncodedCreationCodeSemiNative(
            address(this),
            newTokenImpl,
            computedTokenId,
            semiNativeSelector
        );

        address newTokenProxy = _create3(creationCodeProxy, 0x000000000000000000000000000000000000000000000000000000000000007B);
        if (newTokenProxy == address(0)) revert DeploymentFailed();

        s_tokenProxy = ITransparentUpgradeableProxy(newTokenProxy);

        // Deploy ITS
        s_its.deployTokenManager(
            S_SALT_ITS_TOKEN,
            '',
            ITokenManagerType.TokenManagerType.MINT_BURN,
            abi.encode(msg.sender.toBytes(), newTokenProxy),
            0
        );
    }
```

#### Optional Callback

Axelar allows for [two-way callback functions](https://www.youtube.com/watch?v=SjVJotyyAPg&t=1s) to have an immediate transaction back from the destination chain to the source chain. In this case you can send the address of your newly deployed `SemiNativeToken` proxy back to your source chain's `TokenFactory` contract.

This can be done very easily in your `execute()` function in the `Deployer` contract. Right underneath where you called `deployTokenManager()` you can call the `callContract()` function.

```solidity
s_gateway.callContract(_sourceChain, _sourceAddress, abi.encode(newTokenProxy));
```

This call will make a cross-chain transaction back to the source chain to the source address that sends the original transaction. You can encode the address of the newTokenProxy as the GMP message to be sent back to the `TokenFactory` contract.

Now back on the `TokenFactory` contract you will need to implement and `execute()` method just like you did on the `Deployer` contract to handle the incoming GMP message.

In this `execute` function you will of course need to implement the `validateContractCall()` function again and then you can decode the GMP message and store in the TokenFactory contract.

```solidity
  function execute(bytes32 _commandId, string calldata _sourceChain, string calldata _sourceAddress, bytes calldata _payload) external {
        if (!s_gateway.validateContractCall(_commandId, _sourceChain, _sourceAddress, keccak256(_payload))) revert NotApprovedByGateway();
        s_semiNativeTokens[_sourceChain] = abi.decode(_payload, (address));
    }
```

### Upgrade `SemiNativeToken`

The final bit of functionality for this demo is the ability to upgrade your token directly from the `Deployer` contract. In the event that you choose to upgrade your `SemiNativeToken`.

To upgrade your token's proxy you will need to trigger the `upgradeAndCall()` function from your `Proxy Admin` contract. In your `Deployer` function create a new function called `upgradeSemiNativeToken()`

```solidity
function upgradeSemiNativeToken(address _proxyAdmin) external onlyAdmin {}
```

First, you need to deploy a new `implementation` contract for your `proxy` to point to. For the purpose of this demo you can simply deploy a new `SemiNativeTokenV2` contract with the exact same code and add a new function to it called `isV2()` that returns a `bool` .

```solidity
   address newTokenImpl = _create3(
            type(SemiNativeTokenV2).creationCode,
            0x0000000000000000000000000000000000000000000000000000000000003439
        );
        if (newTokenImpl == address(0)) revert DeploymentFailed();
```

Now with your `upgradeAndCall()` function deployed, you can upgrade the token `proxy` to the `newTokenImpl` that you just deployed.

```solidity
ProxyAdmin(_proxyAdmin).upgradeAndCall(s_tokenProxy, newTokenImpl, '');
```

At this point, your proxy should successfully be pointing to a new implementation address!

### Deploy

It is critical for the `_create3()` addresses written out in the `Factory` and `Deployer` contracts to work correctly that these contracts are also the same address otherwise the contracts they deploy will be different from one another. To deploy these contracts you can use Axelar's `create3Deployer` script from Axelar's [gmp-sdk](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity) package.

You can deploy the contract via [Hardhat Tasks](https://hardhat.org/hardhat-runner/docs/advanced/create-task). Let's start with Moonbase.

#### Moonbase

Define your task like this

```javascript
task('deployMoonbase', 'deploy deployer on remote chain (Moonbase for testing').setAction(async (taskArgs, hre) => {}
```

Now in the task you can deploy the contract for Moonbase.

First, you need to call the `getWallet()` function, which is written up in the `utils` folder. This will get your live wallet for the Moonbase chain. Make sure to include your private key in the `.env` file for the wallet to work correctly.

```javascript
const wallet = getWallet(chains[1].rpc, hre);
```

Now you can deploy your upgradable `AccessControl` and `Deployer` contract on Moonbase. To do this simply call the `create3DeployContract()` from the `Create3Deployer` script.

```javascript
    const implAccessControl = await create3DeployContract(create3DeployerAddress, wallet, AccessControl, 1720, []);
    const implDeployer = await create3DeployContract(create3DeployerAddress, wallet, Deployer, 1721, []);

    const proxyAccess = await create3DeployContract(create3DeployerAddress, wallet, Proxy, 1722, [
        implAccessControl.address,
        wallet.address,
        '0x',
    ]);
    const proxyDeployer = await create3DeployContract(create3DeployerAddress, wallet, Proxy, 1723, [
        implDeployer.address,
        wallet.address,
        '0x',
    ]);

    console.log(`proxyAccess ${proxyAccess.address}`);
    console.log(`proxyDeployer ${proxyDeployer.address}`);
```

Once deployed you can `initialize` your two newly deployed contracts.

```javascript
const AccessControlFactory = await ethers.getContractFactory('AccessControl');
const DeployerFactory = await ethers.getContractFactory('Deployer');

const proxyAccessControlInstance = await AccessControlFactory.attach(proxyAccess.address);
const proxyDeployerInstance = await DeployerFactory.attach(proxyDeployer.address);

await proxyAccessControlInstance.initialize(wallet.address);
await proxyDeployerInstance.initialize(chains[1].its, proxyAccess.address, chains[1].gateway);
```

To trigger this hardhat script in your CLI type

`hh deployMoonbase --network moonbase`

#### Celo

The same task can be set for the `Celo` deployment. The one difference is that this time you will need to pass in the address of the `deployer` that you `TokenFactory` will be interacting with.

This can be done with the `addParam` call.

```javascript
task('deployHomeCelo', 'deploy factory on home chain, (celo for testing)')
    .addParam('deployer', 'Deployer on dest chain')
    .setAction(async (taskArgs, hre) => {
        const wallet = getWallet(chains[0].rpc, hre);

        const implAccessControl = await create3DeployContract(create3DeployerAddress, wallet, AccessControl, 1720, []);
        const implFactory = await create3DeployContract(create3DeployerAddress, wallet, Factory, 1721, []);

        const proxyAccess = await create3DeployContract(create3DeployerAddress, wallet, Proxy, 1722, [
            implAccessControl.address,
            wallet.address,
            '0x',
        ]);

        const proxyFactory = await create3DeployContract(create3DeployerAddress, wallet, Proxy, 1723, [
            implFactory.address,
            wallet.address,
            '0x',
        ]);

        console.log(`celo contract address: ${proxyFactory.address}`);

        const AccessControlFactory = await ethers.getContractFactory('AccessControl');
        const TokenFactoryFactory = await ethers.getContractFactory('TokenFactory');

        const proxyAccessControlInstance = await AccessControlFactory.attach(proxyAccess.address);
        const proxyFactoryInstance = await TokenFactoryFactory.attach(proxyFactory.address);

        await proxyAccessControlInstance.initialize(wallet.address);

        await proxyFactoryInstance.initialize(
            chains[0].its,
            chains[0].gasService,
            chains[0].gateway,
            proxyAccess.address,
            taskArgs.deployer,
        );
    });
```

Great! At this point once you run `hh deployHomeCelo --deployer "<YOUR_DEPLOYER_ADDRESS>" --network celo` you should get your home chain's `TokenFactory` address in the cli.

### Test

Let's now test this contract using the `hardhat cli`

To wire up your contract to your cli you type in

```bash
hh console --network <YOUR_HOME_CHAIN>

const Contract = await ethers.getContractFactory("TokenFactory")
const contract = await Contract.attach("<YOUR_CONTRACT_ADDRESS>")
```

Now with your contract connected to the cli, let's use your newly deployed `TokenFactory` on the home chain to deploy a `NativeToken` on your home chain. You can do this by calling the `deployHomeNative()` function.

```bash
await contract.deployHomeNative()
```

this should return a transaction receipt in your CLI and store the deployed instance of your contract in the `s_tokenProxy` storage variable. Query the variable to make sure you have the address stored correctly.

```bash
await contract.s_tokenProxy()
--> <"YOUR_TOKEN_ADDRESS"> //should be returned
```

Now with your token deployed on the home chain you can call the `deployRemoteSemiNativeToken()` on the remote chain.

```bash
await contract.deployRemoteSemiNativeToken("moonbeam", {value: "1000000000000000000"})
```

This should return a transaction hash that you can view on the [Axelarscan Block Explorer](https://testnet.axelarscan.io/). While this cross-chain transaction is executing you can interact with your token on the home chain to `mint` some new tokens to your contract.

You can wire it up similarly to how you wired up the `TokenFactory` contract.

```bash
const Contract = await ethers.getContractFactory("NativeToken")
const contract = await Contract.attach("<YOUR_TOKEN_ADDRESS")
```

Now with your token wired up you can mint tokens to your account that you will transfer to the destination chain.

```bash
await contract.mint("YOUR_RECEIVING_ADDRESS", 1000000000000000000)
```

You may need to specify a `gasLimit` when running the previous transaction.

At this point you should have a balance of your new token in your wallet equal to what you just minted. You can now transfer this token between accounts to make sure that the `burn` and `txFee` deductions are working correctly.

#### Cross Chain Transfer

With your token now deployed on multiple chains and a balance minted on the home chain you can now transfer your token from the home chain to the remote chain.

If you set a lock/unlock transaction token manager on the home chain then you will need to `approve` the Interchain Token Service (ITS) contract to handle the tokens on your behalf. This is because [ITS will call](https://github.com/axelarnetwork/interchain-token-service/blob/main/contracts/TokenHandler.sol#L109C13-L109C31) the `transferFrom()` function when sending the cross chain transaction.

```bash
await contract.approve("0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C", 1000000000000000000)
```

You can now interact with ITS programmatically to trigger the cross-chain transfer or you can go through the [explorer](https://alfajores.celoscan.io/address/0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C#writeProxyContract).

To send the transfer from the home chain to the remote chain you will need to call the `interchainTransfer()` function on the ITS contract.

The following params are required

1. `payableAmount`: The gas amount to be paid for the cross-chain tx
    
2. `tokenId`: The interchain token id of your token. You can find your `tokenId` in the logs of `NativeTokenDeployed()` event
    
3. `destinationChain`: The chain where your `Deployer` contract was deployed to
    
4. `destinationAddress`: The address you want to receive the tokens on the destination chain
    
5. `amount`: Amount of tokens to be sent
    
6. `metadata`: Data to be sent with your token. You can leave this as `0x` for this example
    
7. `gasValue`: The gas amount to be paid for the cross-chain tx (in `wei` this time)
    

This should trigger another cross-chain transaction that you can track on Axelarscan.

Once this transaction has executed you should now see your balance increment on the destination chain!

#### Upgrade

To upgrade the token you can call the `upgradeSemiNativeToken()` function your deployer

```bash
await contract.upgradeSemiNativeToken("<YOUR_PROXY_ADMIN_ADDRESS>")
```

Once this is executed. You can query the `isV2()` token function at the same deployed token proxy address at the destination chain, which should now be returning `true`

```bash
const Contract = await ethers.getContractFactory("SemiNativeTokenV2")
const contract = await Contract.attach("<YOUR_PROXY_ADDRESS>")

await contract.isV2()
-> true
```

### Conclusion

Congratulations if you made it until the end of this tutorial! You have now deployed an upgradable custom stablecoin with transaction fee redistribution to token holders. You have used `create3` to deploy all your contracts at static addresses across different chains.

If you were unable to code along you take a look at the completed code [here](https://github.com/benjamin852/Advanced-ITS-Token/tree/complete).

Interchain Token Service is a new initiative with the ability to provide simple cross-chain integration for your ERC20 token, we are excited to see what you will build with ITS.

If you have a new token you wish to deploy or an existing token you are looking to integrate with ITS you are more than welcome to reach out to [Interop Labs](https://www.interoplabs.io/) team to see how we can help.
