-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into fix/custom-logic-web-example
- Loading branch information
Showing
15 changed files
with
957 additions
and
301 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; | ||
|
||
import { Minter } from '@axelar-network/interchain-token-service/contracts/utils/Minter.sol'; | ||
/** | ||
* @title InterchainToken | ||
* @notice This contract implements an interchain token which extends InterchainToken functionality. | ||
* @dev This contract also inherits Minter and Implementation logic. | ||
*/ | ||
contract CanonicalToken is ERC20, Minter { | ||
uint8 internal immutable decimals_; | ||
|
||
uint256 internal constant UINT256_MAX = 2 ** 256 - 1; | ||
|
||
/** | ||
* @notice Constructs the InterchainToken contract. | ||
* @dev Makes the implementation act as if it has been setup already to disallow calls to init() (even though that would not achieve anything really). | ||
*/ | ||
constructor(string memory name_, string memory symbol_, uint8 decimalsValue) ERC20(name_, symbol_) { | ||
decimals_ = decimalsValue; | ||
_addMinter(msg.sender); | ||
} | ||
|
||
function decimals() public view override returns (uint8) { | ||
return decimals_; | ||
} | ||
|
||
/** | ||
* @notice Function to mint new tokens. | ||
* @dev Can only be called by the minter address. | ||
* @param account The address that will receive the minted tokens. | ||
* @param amount The amount of tokens to mint. | ||
*/ | ||
function mint(address account, uint256 amount) external onlyRole(uint8(Roles.MINTER)) { | ||
_mint(account, amount); | ||
} | ||
|
||
/** | ||
* @notice Function to burn tokens. | ||
* @dev Can only be called by the minter address. | ||
* @param account The address that will have its tokens burnt. | ||
* @param amount The amount of tokens to burn. | ||
*/ | ||
function burn(address account, uint256 amount) external onlyRole(uint8(Roles.MINTER)) { | ||
_burn(account, amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Call Contract Example | ||
|
||
This example demonstrates how to relay a message from a source-chain to a destination-chain. | ||
|
||
### Prerequisite | ||
|
||
Make sure you've already followed the following steps: | ||
|
||
- [Setup environment variables](/README.md#set-environment-variables) | ||
- [Run the local chains](/README.md#running-the-local-chains) | ||
|
||
### Deployment | ||
|
||
To deploy the contract, use the following command: | ||
|
||
```bash | ||
npm run deploy evm/call-contract [local|testnet] | ||
``` | ||
|
||
The aforementioned command pertains to specifying the intended environment for a project to execute on. It provides the option to choose between local and testnet environments by appending either `local` or `testnet` after the command. | ||
|
||
An example of its usage is demonstrated as follows: `npm run deploy evm/call-contract local` or `npm run deploy evm/call-contract testnet`. | ||
|
||
### Execution | ||
|
||
To execute the example, use the following command: | ||
|
||
```bash | ||
npm run execute evm/call-contract [local|testnet] ${srcChain} ${destChain} ${message} | ||
``` | ||
|
||
### Parameters | ||
|
||
- `srcChain`: The blockchain network from which the message will be relayed. Acceptable values include "Moonbeam", "Avalanche", "Fantom", "Ethereum", and "Polygon". Default value is Avalanche. | ||
- `destChain`: The blockchain network to which the message will be relayed. Acceptable values include "Moonbeam", "Avalanche", "Fantom", "Ethereum", and "Polygon". Default value is Fantom. | ||
- `message`: The message to be relayed between the chains. Default value is "Hello World". | ||
|
||
## Example | ||
|
||
This example deploys the contract on a local network and relays a message "Hello World" from Moonbeam to Avalanche. | ||
|
||
```bash | ||
npm run deploy evm/call-contract local | ||
npm run execute evm/call-contract local "Fantom" "Avalanche" "Hello World" | ||
``` | ||
|
||
The output will be: | ||
|
||
``` | ||
--- Initially --- | ||
value at Avalanche is | ||
--- After --- | ||
value at Avalanche is Hello World | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
'use strict'; | ||
|
||
const { | ||
utils: { deployContract }, | ||
} = require('@axelar-network/axelar-local-dev'); | ||
const { IInterchainTokenService, IInterchainTokenFactory } = require('@axelar-network/axelar-local-dev/dist/contracts'); | ||
const { Contract } = require('ethers'); | ||
const { interchainTransfer } = require('../../../scripts/libs/its-utils'); | ||
|
||
const CanonicalToken = rootRequire('./artifacts/examples/evm/its-canonical-token/CanonicalToken.sol/CanonicalToken.json'); | ||
|
||
async function deploy(chain, wallet) { | ||
console.log(`Deploying CanonicalToken for ${chain.name}.`); | ||
chain.canonicalToken = await deployContract(wallet, CanonicalToken, ['Custon Token', 'CT', 18]); | ||
chain.wallet = wallet; | ||
console.log(`Deployed CanonicalToken for ${chain.name} at ${chain.canonicalToken.address}.`); | ||
} | ||
|
||
async function execute(chains, wallet, options) { | ||
const args = options.args || []; | ||
const { source, destination, calculateBridgeFee } = options; | ||
|
||
const amount = args[2] || 1000; | ||
|
||
const fee = await calculateBridgeFee(source, destination); | ||
|
||
const sourceIts = new Contract(source.interchainTokenService, IInterchainTokenService.abi, wallet.connect(source.provider)); | ||
const destinationIts = new Contract(destination.interchainTokenService, IInterchainTokenService.abi, wallet.connect(destination.provider)); | ||
const sourceFactory = new Contract(source.interchainTokenFactory, IInterchainTokenFactory.abi, wallet.connect(source.provider)); | ||
|
||
console.log(`Registerring canonical token ${source.canonicalToken.address} at ${source.name}`); | ||
await (await sourceFactory.registerCanonicalInterchainToken(source.canonicalToken.address)).wait(); | ||
|
||
console.log(`Deploy remote canonical token from ${source.name} to ${destination.name}`); | ||
await (await sourceFactory.deployRemoteCanonicalInterchainToken('', source.canonicalToken.address, destination.name, fee, {value: fee})).wait(); | ||
|
||
const tokenId = await sourceFactory.canonicalInterchainTokenId(source.canonicalToken.address); | ||
const destinationTokenAddress = await destinationIts.interchainTokenAddress(tokenId); | ||
|
||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); | ||
while (await destination.provider.getCode(destinationTokenAddress) == '0x') { | ||
await sleep(1000); | ||
} | ||
|
||
console.log(`Minting ${amount} canonical tokens to ${wallet.address}`); | ||
await (await source.canonicalToken.mint(wallet.address, amount)).wait(); | ||
|
||
console.log(`Approving ${amount} canonical tokens to the token manager`); | ||
await (await source.canonicalToken.approve(source.interchainTokenService, amount)).wait(); | ||
|
||
await interchainTransfer(source, destination, wallet, tokenId, amount, fee); | ||
} | ||
|
||
module.exports = { | ||
deploy, | ||
execute, | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { InterchainTokenStandard } from '@axelar-network/interchain-token-service/contracts/interchain-token/InterchainTokenStandard.sol'; | ||
// Any ERC20 implementation can be used here, we chose OZ since it is quite well known. | ||
import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; | ||
|
||
import { Minter } from '@axelar-network/interchain-token-service/contracts/utils/Minter.sol'; | ||
/** | ||
* @title InterchainToken | ||
* @notice This contract implements an interchain token which extends InterchainToken functionality. | ||
* @dev This contract also inherits Minter and Implementation logic. | ||
*/ | ||
contract CustomToken is InterchainTokenStandard, ERC20, Minter { | ||
uint8 internal immutable decimals_; | ||
bytes32 internal tokenId; | ||
address internal immutable interchainTokenService_; | ||
|
||
uint256 internal constant UINT256_MAX = 2 ** 256 - 1; | ||
|
||
/** | ||
* @notice Constructs the InterchainToken contract. | ||
* @dev Makes the implementation act as if it has been setup already to disallow calls to init() (even though that would not achieve anything really). | ||
*/ | ||
constructor(string memory name_, string memory symbol_, uint8 decimalsValue, address interchainTokenServiceAddress) ERC20(name_, symbol_) { | ||
decimals_ = decimalsValue; | ||
interchainTokenService_ = interchainTokenServiceAddress; | ||
|
||
_addMinter(interchainTokenService_); | ||
_addMinter(msg.sender); | ||
} | ||
|
||
function decimals() public view override returns (uint8) { | ||
return decimals_; | ||
} | ||
|
||
function setTokenId(bytes32 tokenId_) public { | ||
tokenId = tokenId_; | ||
} | ||
|
||
/** | ||
* @notice Returns the interchain token service | ||
* @return address The interchain token service contract | ||
*/ | ||
function interchainTokenService() public view override returns (address) { | ||
return interchainTokenService_; | ||
} | ||
|
||
/** | ||
* @notice Returns the tokenId for this token. | ||
* @return bytes32 The token manager contract. | ||
*/ | ||
function interchainTokenId() public view override returns (bytes32) { | ||
return tokenId; | ||
} | ||
|
||
/** | ||
* @notice Function to mint new tokens. | ||
* @dev Can only be called by the minter address. | ||
* @param account The address that will receive the minted tokens. | ||
* @param amount The amount of tokens to mint. | ||
*/ | ||
function mint(address account, uint256 amount) external onlyRole(uint8(Roles.MINTER)) { | ||
_mint(account, amount); | ||
} | ||
|
||
/** | ||
* @notice Function to burn tokens. | ||
* @dev Can only be called by the minter address. | ||
* @param account The address that will have its tokens burnt. | ||
* @param amount The amount of tokens to burn. | ||
*/ | ||
function burn(address account, uint256 amount) external onlyRole(uint8(Roles.MINTER)) { | ||
_burn(account, amount); | ||
} | ||
|
||
/** | ||
* @notice A method to be overwritten that will decrease the allowance of the `spender` from `sender` by `amount`. | ||
* @dev Needs to be overwritten. This provides flexibility for the choice of ERC20 implementation used. Must revert if allowance is not sufficient. | ||
*/ | ||
function _spendAllowance(address sender, address spender, uint256 amount) internal override (ERC20, InterchainTokenStandard) { | ||
uint256 _allowance = allowance(sender, spender); | ||
|
||
if (_allowance != UINT256_MAX) { | ||
_approve(sender, spender, _allowance - amount); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# ITS Custom Token Example | ||
|
||
This example demonstrates how to use the ITS with a custom token implementation. | ||
|
||
### Prerequisite | ||
|
||
Make sure you've already followed the following steps: | ||
|
||
- [Setup environment variables](/README.md#set-environment-variables) | ||
- [Run the local chains](/README.md#running-the-local-chains) | ||
|
||
### Deployment | ||
|
||
To deploy the custom token, use the following command: | ||
|
||
```bash | ||
npm run deploy evm/its-custom-token [local|testnet] | ||
``` | ||
|
||
The aforementioned command pertains to specifying the intended environment for a project to execute on. It provides the option to choose between local and testnet environments by appending either `local` or `testnet` after the command. | ||
|
||
An example of its usage is demonstrated as follows: `npm run deploy evm/its-custom-token local` or `npm run deploy evm/its-custom-token testnet`. | ||
|
||
### Execution | ||
|
||
To execute the example, use the following command: | ||
|
||
```bash | ||
npm run execute evm/its-custom-token [local|testnet] ${srcChain} ${destChain} ${amount} ${salt} | ||
``` | ||
|
||
### Parameters | ||
|
||
- `srcChain`: The blockchain network from which the message will be relayed. Acceptable values include "Moonbeam", "Avalanche", "Fantom", "Ethereum", and "Polygon". Default value is Avalanche. | ||
- `destChain`: The blockchain network to which the message will be relayed. Acceptable values include "Moonbeam", "Avalanche", "Fantom", "Ethereum", and "Polygon". Default value is Fantom. | ||
- `amount`: The amount of token to send. The default is 1000. | ||
- `salt`: The 32 byte salt to use for the token. The default is a random salt depending on the proccess that runs the example. | ||
|
||
## Example | ||
|
||
This example deploys the custom token on a local network, registers it with the Interchain Token Service and sends 1234 of said token from Fantom to Avalanche. | ||
|
||
```bash | ||
npm run deploy evm/its-custom-token local | ||
npm run execute evm/its-custom-token local "Fantom" "Avalanche" 1234 0xa457d6C043b7288454773321a440BA8866D47f96D924D4C38a50b2b0698fae46 | ||
``` | ||
|
||
The output will be: | ||
|
||
``` | ||
Registering custom token at for Fantom | ||
Registering custom token at for Avalanche | ||
Minting 1234 of custom tokens to 0xBa86A5719722B02a5D5e388999C25f3333c7A9fb | ||
--- Initially --- | ||
Balance at Avalanche is 0 | ||
Sending 1234 of token 0x8EF758F0D49c53827b47962fA30BDA7e198a4D14 to Avalanche | ||
--- After --- | ||
Balance at Avalanche is 1234 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
'use strict'; | ||
|
||
const { | ||
utils: { deployContract }, | ||
} = require('@axelar-network/axelar-local-dev'); | ||
const { IInterchainTokenService } = require('@axelar-network/axelar-local-dev/dist/contracts'); | ||
const { Contract } = require('ethers'); | ||
const { keccak256, defaultAbiCoder } = require('ethers/lib/utils'); | ||
const { interchainTransfer } = require('../../../scripts/libs/its-utils'); | ||
|
||
const CustomToken = rootRequire('./artifacts/examples/evm/its-custom-token/CustomToken.sol/CustomToken.json'); | ||
const ITokenManager = rootRequire('./artifacts/@axelar-network/interchain-token-service/contracts/interfaces/ITokenManager.sol/ITokenManager.json'); | ||
const MINT_BURN = 0; | ||
|
||
async function deploy(chain, wallet) { | ||
console.log(`Deploying CustomToken for ${chain.name}.`); | ||
chain.customToken = await deployContract(wallet, CustomToken, ['Custon Token', 'CT', 18, chain.interchainTokenService]); | ||
chain.wallet = wallet; | ||
console.log(`Deployed CustomToken for ${chain.name} at ${chain.customToken.address}.`); | ||
} | ||
|
||
|
||
|
||
async function execute(chains, wallet, options) { | ||
const args = options.args || []; | ||
const { source, destination, calculateBridgeFee } = options; | ||
|
||
const amount = args[2] || 1000; | ||
const salt = args[3] || keccak256(defaultAbiCoder.encode(['uint256', 'uint256'], [process.pid, process.ppid])); | ||
|
||
|
||
const fee = await calculateBridgeFee(source, destination); | ||
|
||
async function deployTokenManager(chain, salt) { | ||
console.log(`Registering custom token at for ${chain.name}`); | ||
|
||
const params = defaultAbiCoder.encode(['bytes', 'address'], [wallet.address, chain.customToken.address]); | ||
const its = new Contract(chain.interchainTokenService, IInterchainTokenService.abi, wallet.connect(chain.provider)); | ||
await (await its.deployTokenManager(salt, '', MINT_BURN, params, 0)).wait(); | ||
const tokenId = await its.interchainTokenId(wallet.address, salt); | ||
const tokenManagerAddress = await its.tokenManagerAddress(tokenId); | ||
const tokenManager = new Contract(tokenManagerAddress, ITokenManager.abi, wallet.connect(chain.provider)); | ||
return tokenManager; | ||
} | ||
|
||
const tokenManager = await deployTokenManager(source, salt); | ||
await deployTokenManager(destination, salt); | ||
|
||
const tokenId = await tokenManager.interchainTokenId(); | ||
|
||
console.log(`Minting ${amount} of custom tokens to ${wallet.address}`); | ||
await (await source.customToken.mint(wallet.address, amount)).wait(); | ||
|
||
await interchainTransfer(source, destination, wallet, tokenId, amount, fee); | ||
} | ||
|
||
module.exports = { | ||
deploy, | ||
execute, | ||
}; | ||
|
Oops, something went wrong.