Skip to content

Commit

Permalink
feat: final NatSpec documentation (#173)
Browse files Browse the repository at this point in the history
* feat: final NatSpec documentation

* fix: build

* fix: tests

* feat: small improvements

* fix: remove liquidity pool from docs

* uint -> uint256

* gasvalue comment

---------

Co-authored-by: Dean Amiel <dean@axelar.network>
Co-authored-by: Milap Sheth <milap@axelar.network>
  • Loading branch information
3 people authored Nov 15, 2023
1 parent 001a9f4 commit b348e2a
Show file tree
Hide file tree
Showing 40 changed files with 1,776 additions and 1,580 deletions.
15 changes: 8 additions & 7 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ For the purposes of this document we will use two keywords: deployer, the person
## Architecture

The main workflow of a bridge is the following:
- Obtains `x` token from the user on chain A
- Send a message to chain B indicating that this happened, and where the token should go to
- Receive the above message, and hand `x` token to the appropriate address

- Obtains `x` token from the user on chain A
- Send a message to chain B indicating that this happened, and where the token should go to
- Receive the above message, and hand `x` token to the appropriate address

For this to be a proper bridge it should be possible to perform the above steps for any supported chain being 'chain A' and 'chain B'. There are multiple different possible configurations for any bridge, and we wanted to make it as easy as possible for deployers to get what they want, while making it cheap and easy for users to get their tokens across chains as well.

Expand All @@ -26,14 +27,14 @@ Most current bridge designs aim to get a pre-existing, popular token to differen
### Custom Bridges

Most projects that look to go interchain nowadays have more complex needs that the ones covered by Canonical Bridges: they often need custom `ERC20` designs, and will sometimes want to have additional power over the bridge. This is where the `InterchainTokenService` shines, deployers can claim certain `tokenIds` only based on their `address`, and a `salt` they provide, and specify any kind of `TokenManager` to be deployed and either manage an external `ERC20` or a `InterchainToken`. Users using Custom Bridges need to trust the deployers as they could easily confiscate the funds of users if they wanted to, same as any `ERC20` distributor could confiscate the funds of users. There are currently three kinds of possible `TokenManagers` available, but this number might increase in the future, as we find more potential uses for the `InterchainTokenService`.
- Lock/Unlock: This `TokenManager` will simply transfer tokens from a user to itself or vice versa to initiate/fulfill interchain transfers
- Mint/Burn: This `TokenManager` will burn/mint tokens from/to the user to initiate/fulfill interchain transfers. Tokens used with this kind of `TokenManager` need to be properly permissioned to allow for this behavior.
- Liquidity Pool: This `TokenManager` functions exactly like a Lock/Unlock one, except the balance is kept at a separate, pre-specified account. This allows for deployers to have more control over the bridged funds.

- Lock/Unlock: This `TokenManager` will simply transfer tokens from a user to itself or vice versa to initiate/fulfill interchain transfers
- Mint/Burn: This `TokenManager` will burn/mint tokens from/to the user to initiate/fulfill interchain transfers. Tokens used with this kind of `TokenManager` need to be properly permissioned to allow for this behavior.

## Interchain Address Tracker

`InterchainTokenService` inherits the [`InterchainAddressTracker`](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/contracts/utils/InterchainAddressTracker.sol) to support new chains as they get added to the Axelar Network. It's implemented for obtaining the destination address for outgoing messages, and for validation of incoming messages.

## Interchain Token

We designed an [interface](./contracts/interfaces/IInterchainToken.sol) along a [example implementation](./contracts/interchainToken/InterchainToken.sol) of an ERC20 that can use the `InterchainTokenService` internally. This has the main benefit that for `TokenManagers` that require user approval (Lock/Unlock and Liquidity Pool typically) the token can provide this approval within the same call, providing better UX for users, and saving them some gas.
We designed an [interface](./contracts/interfaces/IInterchainToken.sol) along a [example implementation](./contracts/interchainToken/InterchainToken.sol) of an ERC20 that can use the `InterchainTokenService` internally. This has the main benefit that for `TokenManagers` that require user approval (Lock/Unlock typically) the token can provide this approval within the same call, providing better UX for users, and saving them some gas.
101 changes: 100 additions & 1 deletion contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import { ITokenManagerType } from './interfaces/ITokenManagerType.sol';
import { ITokenManager } from './interfaces/ITokenManager.sol';
import { IInterchainToken } from './interfaces/IInterchainToken.sol';

/**
* @title InterchainTokenFactory
* @notice This contract is responsible for deploying new interchain tokens and managing their token managers.
*/
contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, Multicall, Upgradable {
using AddressBytes for bytes;
using AddressBytes for address;
Expand All @@ -30,6 +34,10 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
bytes32 internal constant PREFIX_INTERCHAIN_TOKEN_SALT = keccak256('interchain-token-salt');
address private constant TOKEN_FACTORY_DEPLOYER = address(0);

/**
* @notice Constructs the InterchainTokenFactory contract.
* @param interchainTokenServiceAddress The address of the interchain token service.
*/
constructor(address interchainTokenServiceAddress) {
if (interchainTokenServiceAddress == address(0)) revert ZeroAddress();
service = IInterchainTokenService(interchainTokenServiceAddress);
Expand All @@ -40,31 +48,72 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M

/**
* @notice Getter for the contract id.
* @return bytes32 The contract id of this contract.
*/
function contractId() external pure returns (bytes32) {
return CONTRACT_ID;
}

/**
* @notice Calculates the salt for an interchain token.
* @param chainNameHash_ The hash of the chain name.
* @param deployer The address of the deployer.
* @param salt A unique identifier to generate the salt.
* @return bytes32 The calculated salt for the interchain token.
*/
function interchainTokenSalt(bytes32 chainNameHash_, address deployer, bytes32 salt) public pure returns (bytes32) {
return keccak256(abi.encode(PREFIX_INTERCHAIN_TOKEN_SALT, chainNameHash_, deployer, salt));
}

/**
* @notice Calculates the salt for a canonical interchain token.
* @param chainNameHash_ The hash of the chain name.
* @param tokenAddress The address of the token.
* @return salt The calculated salt for the interchain token.
*/
function canonicalInterchainTokenSalt(bytes32 chainNameHash_, address tokenAddress) public pure returns (bytes32 salt) {
salt = keccak256(abi.encode(PREFIX_CANONICAL_TOKEN_SALT, chainNameHash_, tokenAddress));
}

/**
* @notice Computes the ID for an interchain token based on the deployer and a salt.
* @param deployer The address that deployed the interchain token.
* @param salt A unique identifier used in the deployment process.
* @return tokenId The ID of the interchain token.
*/
function interchainTokenId(address deployer, bytes32 salt) public view returns (bytes32 tokenId) {
tokenId = service.interchainTokenId(TOKEN_FACTORY_DEPLOYER, interchainTokenSalt(chainNameHash, deployer, salt));
}

/**
* @notice Computes the ID for a canonical interchain token based on its address.
* @param tokenAddress The address of the canonical interchain token.
* @return tokenId The ID of the canonical interchain token.
*/
function canonicalInterchainTokenId(address tokenAddress) public view returns (bytes32 tokenId) {
tokenId = service.interchainTokenId(TOKEN_FACTORY_DEPLOYER, canonicalInterchainTokenSalt(chainNameHash, tokenAddress));
}

/**
* @notice Retrieves the address of an interchain token based on the deployer and a salt.
* @param deployer The address that deployed the interchain token.
* @param salt A unique identifier used in the deployment process.
* @return tokenAddress The address of the interchain token.
*/
function interchainTokenAddress(address deployer, bytes32 salt) public view returns (address tokenAddress) {
tokenAddress = service.interchainTokenAddress(interchainTokenId(deployer, salt));
}

/**
* @notice Deploys a new interchain token with specified parameters.
* @dev Creates a new token and optionally mints an initial amount to a specified distributor.
* @param salt The unique salt for deploying the token.
* @param name The name of the token.
* @param symbol The symbol of the token.
* @param decimals The number of decimals for the token.
* @param mintAmount The amount of tokens to mint initially (can be zero).
* @param distributor The address to receive the initially minted tokens.
*/
function deployInterchainToken(
bytes32 salt,
string calldata name,
Expand Down Expand Up @@ -102,6 +151,14 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
}
}

/**
* @notice Deploys a remote interchain token on a specified destination chain.
* @param originalChainName The name of the chain where the token originally exists.
* @param salt The unique salt for deploying the token.
* @param distributor The address to distribute the token on the destination chain.
* @param destinationChain The name of the destination chain.
* @param gasValue The amount of gas to send for the deployment.
*/
function deployRemoteInterchainToken(
string calldata originalChainName,
bytes32 salt,
Expand Down Expand Up @@ -142,6 +199,16 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
_deployInterchainToken(salt, destinationChain, tokenName, tokenSymbol, tokenDecimals, distributor_, gasValue);
}

/**
* @notice Deploys a new interchain token with specified parameters.
* @param salt The unique salt for deploying the token.
* @param destinationChain The name of the destination chain.
* @param tokenName The name of the token.
* @param tokenSymbol The symbol of the token.
* @param tokenDecimals The number of decimals for the token.
* @param distributor The address to receive the initially minted tokens.
* @param gasValue The amount of gas to send for the transfer.
*/
function _deployInterchainToken(
bytes32 salt,
string memory destinationChain,
Expand All @@ -163,6 +230,11 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
);
}

/**
* @notice Registers a canonical token as an interchain token and deploys its token manager.
* @param tokenAddress The address of the canonical token.
* @return tokenId The unique identifier of the registered interchain token.
*/
function registerCanonicalInterchainToken(address tokenAddress) external payable returns (bytes32 tokenId) {
bytes memory params = abi.encode('', tokenAddress);

Expand All @@ -172,6 +244,13 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
tokenId = service.deployTokenManager(salt, '', TokenManagerType.LOCK_UNLOCK, params, 0);
}

/**
* @notice Deploys a canonical interchain token on a remote chain.
* @param originalChain The name of the chain where the token originally exists.
* @param originalTokenAddress The address of the original token on the original chain.
* @param destinationChain The name of the chain where the token will be deployed.
* @param gasValue The gas amount to be sent for deployment.
*/
function deployRemoteCanonicalInterchainToken(
string calldata originalChain,
address originalTokenAddress,
Expand Down Expand Up @@ -202,6 +281,14 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
_deployInterchainToken(salt, destinationChain, tokenName, tokenSymbol, tokenDecimals, '', gasValue);
}

/**
* @notice Transfers an interchain token to a specified destination chain and address.
* @param tokenId The identifier of the interchain token.
* @param destinationChain The name of the destination chain.
* @param destinationAddress The address on the destination chain to receive the token.
* @param amount The amount of tokens to transfer.
* @param gasValue The amount of gas to send for the transfer.
*/
function interchainTransfer(
bytes32 tokenId,
string calldata destinationChain,
Expand All @@ -219,6 +306,11 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
}
}

/**
* @notice Allows tokens to be transferred from the sender to the contract.
* @param tokenId The identifier of the interchain token.
* @param amount The amount of tokens to transfer.
*/
function tokenTransferFrom(bytes32 tokenId, uint256 amount) external payable {
address tokenAddress = service.validTokenAddress(tokenId);
IInterchainToken token = IInterchainToken(tokenAddress);
Expand All @@ -227,7 +319,9 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
}

/**
* @dev Allow any token to be approved to the token manager.
* @notice Approves a specified amount of tokens to the token manager.
* @param tokenId The identifier of the interchain token.
* @param amount The amount of tokens to approve.
*/
function tokenApprove(bytes32 tokenId, uint256 amount) external payable {
address tokenAddress = service.validTokenAddress(tokenId);
Expand All @@ -237,6 +331,11 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
token.safeCall(abi.encodeWithSelector(token.approve.selector, tokenManager, amount));
}

/**
* @notice Checks if a given token is a gateway token.
* @param token The address of the token to check.
* @return bool True if the token is a gateway token, false otherwise.
*/
function _isGatewayToken(address token) internal view returns (bool) {
string memory symbol = IInterchainToken(token).symbol();
return token == gateway.tokenAddresses(symbol);
Expand Down
Loading

0 comments on commit b348e2a

Please sign in to comment.