Solidity utilities

To facilitate interchain development, we have provided some Solidity utilities. These can be found here. Each of these is described below, along with their proposed use. To further assist with the development process the Axelar Chain’s Config package has been made available to provide easy access to all the relevant Axelar configurations on each blockchain Axelar is available on.

đź“ť

Note: When deploying a contract with this tool, the msg.sender in the constructor of the app’s contract will be the deployer contract address and not the wallet address. So msg.sender should not be used for setting the owner etc. when using these. Instead it should be passed in as a constructor arg

Create3 Deployer

Creating an interchain dApp will often require the same contract to be deployed on multiple chains. Furthermore, it is useful to know each address of this contract on each chain, either to know where to send remote contract calls, or where to trust remote contract calls from — often both. If we can guarantee that the contracts in question will be deployed at the same address on each network, then the above is trivial. This can be achieved by deploying each contract from the same address with the same nonce at each network, or by using create3. For this purpose, we launched Create3 Deployer at 0x6513Aedb4D1593BA12e50644401D976aebDc90d8 on every EVM testnet and mainnet that is supported by Axelar. We plan on deploying it on future supported testnets and mainnets, too.

Create3 Deployer exposes the following functions:

  • deployedAddress(bytes bytecode, bytes32 deploySalt): calculates the address of contracts that has been/will be deployed using the contract’s bytecode and salt, by a certain sender.
  • deploy(bytes bytecode, bytes32 deploySalt): deploys a contract with a certain bytecode and salt.

Constructor Arguments

A key benefit of using create3 over create2 is that you get a constant address independent of its bytecode.

In order to pass in data for a contract’s constructor arguments you must encode the values along with the bytecode of the contract.

For example to deploy a contract with a constructor that takes in two arguments, a uint256 and an address, you could call the deploy() function as follows:

function deployContract() external {
Create3Deployer.deploy(
keccak256(bytes("<my salt>")),
abi.encodePacked(
type(NewContract).creationCode,
abi.encode(123, msg.sender)
)
);
}

Create3Deployer JS

You have the option to either interact with the Solidity functionality directly, but we also provide a script to be able to execute this functionality from a Javascript file.

Simply import create3Deployer.js. The following methods are exported from from the create3Deployer.js file

  • async getSaltFromKey(key) calculate the unique salt from a key
  • async estimateGasForCreate3Deploy(deployer, contractJson, args = []): estimates the gas needed to deploy a contract for a specific deployer with a certain contractJson and args
  • async estimateGasForCreate3DeployAndInit(deployer, wallet, contractJson, args = [], initArgs = []): estimates the gas needed to deploy a contract for a specific deployer and wallet, from with a certain contractJson and args, and to have the init(...initArgs) called as part of the deployment.
  • async deployCreate3Contract(deployerAddress, wallet, contractJson, key, args = [], gasLimit = null): uses deployerAddress, an Ethers.js contract pointing to:
    • ConstAddressDeployer, a wallet with native currency.
    • The contractJson to deploy.
    • A string key, which will be hashed to get the salt.
    • The constructor args to make a deployment.
    • The gasLimit allowed for contract deployment
  • async deployCreate3AndInitContract(deployerAddress, wallet, contractJson, key, args = [], initArgs = [], gasLimit = null): same as above, but uses deployAndInit (with initArgs), instead of deploy.
  • async getCreate3Address(deployerAddress, wallet, key): calculate/query the contract address by using the deployerAddress, wallet, and unique key used for a specific deployment

Create2 Deployer

For those who may be using Create2 Deployer (previously called Constant Address Deployer) this option remains available. This contract leverages create2 and is deployed at 0x98b2920d53612483f91f12ed7754e51b4a77919e on every EVM testnet and mainnet that is supported by Axelar.

Create2 Deployer exposes the following functions:

  • deployedAddress(bytes bytecode, address sender, bytes32 salt): calculates the address of contracts that has been/will be deployed with a certain bytecode and salt, by a certain sender.
  • deploy(bytes bytecode, bytes32 salt): deploys a contract with a certain bytecode and salt.
  • deployAndInit(bytes bytecode, bytes32 salt, bytes init): deploys a contract with a certain bytecode and salt, and runs deployedContract.call(init) afterwards. Use in case you need constructor arguments that are not constant across chains, as different constructor arguments result in different bytecodes.

Create2Deployer JS

The above can be used directly, but we also provide a script to execute this functionality from a JavaScript file. Simply import create2Deployer.js. The following methods are exported from from the create2Deployer.js file:

  • async estimateGasForDeploy(contractJson, args = []): estimates the gas needed to deploy a contract with a certain contractJson and args
  • async estimateGasForDeployAndInit(contractJson, args = [], initArgs = []): estimates the gas needed to deploy a contract with a certain contractJson and args, and to have the init(...initArgs) called as part of the deployment.
  • async deployContractConstant(deployer, wallet, contractJson, key, args = []): uses deployer, an Ethers.js contract pointing to:
    • ConstAddressDeployer, a wallet with native currency.
    • The contractJson to deploy.
    • A string key, which will be hashed to get the salt.
    • The constructor args to make a deployment.
  • async deployAndInitContractConstant(deployer, wallet, contractJson, key, args = [], initArgs = []): same as above, but uses deployAndInit (with initArgs), instead of deploy.

String and address utilities

Axelar uses the string representation of addresses (42 characters) for EVM addresses (20 bytes). It is often useful to convert between the two. AddressString.sol is a library that can be used to do so. Below, see an example showing how to use it.

import { StringToAddress, AddressToString } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressString.sol";
contract Test {
using AddressToString for address;
using StringToAddress for string;
function addressToString(address address_) external pure returns (string memory) {
return address_.toString();
}
function stringToAddress(string calldata string_) external pure returns (address) {
return string_.toAddress();
}
}

Edit on GitHub