Skip to content

Commit

Permalink
feat: custom token registrars (#116)
Browse files Browse the repository at this point in the history
* Added a canonical token registrar.

* wrote some canonical token registrar tests

* made the standardized token registrar

* Wrote some basic tests

* lint and fix a merge bug

* feat: bump to latest gmp-sdk and cgp

* rebase over dep bump

* clean lock file

* slither checks

* Added roles, tests pending

* fix: tests

* Added some tests and fixed constants

* made lint happy

* Chnaged roles constants to enum to make slither happy

* Fixed tests

* querrying chain name from the remoteAddressValidator for the registrars

* stash to add more utils to roles.

* Added getter for distributor and opearator.

* made lint happy

* fixed a spelling error

* Fixed tests and interfaces

* Removed a lot of unused imports and made routers upgradable.

* Addressed changes

* update for the new roles functions

* allow for mintAmount for standardized tokens

* update sdk

* updage to newest sdk

* made lint happy

* added some changes

* Added some utilities

* made lint happy

* fix test

* build warnings

* revert fix

* fix create3 deployer reference

* fix test

* slither warnings

* switch proxies

* fix import

---------

Co-authored-by: Milap Sheth <[email protected]>
Co-authored-by: Dean Amiel <[email protected]>
  • Loading branch information
3 people authored Nov 1, 2023
1 parent cd663a4 commit b7bc804
Show file tree
Hide file tree
Showing 24 changed files with 1,556 additions and 416 deletions.
3 changes: 2 additions & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"no-inline-assembly": "off",
"no-empty-blocks": "off",
"avoid-low-level-calls": "off",
"not-rely-on-time": "off"
"not-rely-on-time": "off",
"immutable-vars-naming": "off"
}
}
13 changes: 10 additions & 3 deletions contracts/interchain-token-service/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { IRemoteAddressValidator } from '../interfaces/IRemoteAddressValidator.s
import { IInterchainTokenExecutable } from '../interfaces/IInterchainTokenExecutable.sol';
import { IInterchainTokenExpressExecutable } from '../interfaces/IInterchainTokenExpressExecutable.sol';
import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { ITokenManagerProxy } from '../interfaces/ITokenManagerProxy.sol';
import { IERC20Named } from '../interfaces/IERC20Named.sol';

import { AddressBytesUtils } from '../libraries/AddressBytesUtils.sol';
Expand Down Expand Up @@ -475,13 +474,21 @@ contract InterchainTokenService is
}
}

/**
* @notice Transfer a token interchain.
* @param tokenId the tokenId for the token link.
* @param destinationChain the name of the chain to send the token to.
* @param destinationAddress the recipient of the interchain transfer.
* @param amount the amount of token to give.
* @param metadata the data to be passed to the destination. If provided with a bytes4(0) prefix, it'll execute the destination contract.
*/
function interchainTransfer(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
bytes calldata metadata
) external notPaused {
) external payable notPaused {
ITokenManager tokenManager = ITokenManager(getTokenManagerAddress(tokenId));
amount = tokenManager.takeToken(msg.sender, amount);
_transmitSendToken(tokenId, msg.sender, destinationChain, destinationAddress, amount, metadata);
Expand All @@ -493,7 +500,7 @@ contract InterchainTokenService is
bytes calldata destinationAddress,
uint256 amount,
bytes calldata data
) external notPaused {
) external payable notPaused {
ITokenManager tokenManager = ITokenManager(getTokenManagerAddress(tokenId));
amount = tokenManager.takeToken(msg.sender, amount);
uint32 prefix = 0;
Expand Down
18 changes: 18 additions & 0 deletions contracts/interfaces/ICanonicalTokenRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface ICanonicalTokenRegistrar {
error ZeroAddress();
error ApproveFailed();

function chainNameHash() external view returns (bytes32);

function getCanonicalTokenSalt(address tokenAddress) external view returns (bytes32 salt);

function getCanonicalTokenId(address tokenAddress) external view returns (bytes32 tokenId);

function registerCanonicalToken(address tokenAddress) external payable returns (bytes32 tokenId);

function deployAndRegisterRemoteCanonicalToken(bytes32 salt, string calldata destinationChain, uint256 gasValue) external payable;
}
4 changes: 3 additions & 1 deletion contracts/interfaces/IDistributable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

pragma solidity ^0.8.0;

interface IDistributable {
import { IRolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.sol';

interface IDistributable is IRolesBase {
/**
* @notice Change the distributor of the contract
* @dev Can only be called by the current distributor
Expand Down
11 changes: 9 additions & 2 deletions contracts/interfaces/IInterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IContractIdentifier } from '@axelar-network/axelar-gmp-sdk-solidity/con
import { ITokenManagerType } from './ITokenManagerType.sol';
import { IPausable } from './IPausable.sol';
import { IMulticall } from './IMulticall.sol';
import { IRemoteAddressValidator } from './IRemoteAddressValidator.sol';

interface IInterchainTokenService is ITokenManagerType, IAxelarValuedExpressExecutable, IPausable, IMulticall, IContractIdentifier {
error ZeroAddress();
Expand Down Expand Up @@ -82,6 +83,12 @@ interface IInterchainTokenService is ITokenManagerType, IAxelarValuedExpressExec
);
event CustomTokenIdClaimed(bytes32 indexed tokenId, address indexed deployer, bytes32 indexed salt);

/**
* @notice Returns the address of the token manager deployer contract.
* @return remoteAddressValidator_ The remoteAddressValidator.
*/
function remoteAddressValidator() external view returns (IRemoteAddressValidator remoteAddressValidator_);

/**
* @notice Returns the address of the token manager deployer contract.
* @return tokenManagerDeployerAddress The address of the token manager deployer contract.
Expand Down Expand Up @@ -238,15 +245,15 @@ interface IInterchainTokenService is ITokenManagerType, IAxelarValuedExpressExec
bytes calldata destinationAddress,
uint256 amount,
bytes calldata metadata
) external;
) external payable;

function sendTokenWithData(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
bytes calldata data
) external;
) external payable;

/**
* @notice Initiates an interchain token transfer. Only callable by TokenManagers
Expand Down
4 changes: 3 additions & 1 deletion contracts/interfaces/IOperatable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

pragma solidity ^0.8.0;

interface IOperatable {
import { IRolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.sol';

interface IOperatable is IRolesBase {
/**
* @notice Change the operator of the contract
* @dev Can only be called by the current operator
Expand Down
20 changes: 17 additions & 3 deletions contracts/interfaces/IStandardizedToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,34 @@

pragma solidity ^0.8.0;

import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { IContractIdentifier } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IContractIdentifier.sol';

import { IImplementation } from './IImplementation.sol';
import { IInterchainToken } from './IInterchainToken.sol';
import { IDistributable } from './IDistributable.sol';
import { IERC20MintableBurnable } from './IERC20MintableBurnable.sol';
import { ITokenManager } from './ITokenManager.sol';
import { IERC20Named } from './IERC20Named.sol';

/**
* @title StandardizedToken
* @notice This contract implements a standardized token which extends InterchainToken functionality.
* This contract also inherits Distributable and Implementation logic.
*/
interface IStandardizedToken is IImplementation, IInterchainToken, IDistributable, IERC20MintableBurnable, IERC20, IContractIdentifier {
interface IStandardizedToken is IInterchainToken, IDistributable, IERC20MintableBurnable, IERC20Named, IContractIdentifier {
error TokenManagerAddressZero();
error TokenNameEmpty();

/**
* @notice Called by the proxy to setup itself.
* @dev This should be hidden by the proxy.
* @param params the data to be used for the initialization.
*/
function setup(bytes calldata params) external;

/**
* @notice Getter for the tokenManager used for this token.
* @dev Needs to be overwitten.
* @return tokenManager_ the TokenManager called to facilitate cross chain transfers.
*/
function tokenManager() external view returns (ITokenManager tokenManager_);
}
2 changes: 0 additions & 2 deletions contracts/interfaces/IStandardizedTokenDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

pragma solidity ^0.8.0;

import { Create3Deployer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3Deployer.sol';

/**
* @title IStandardizedTokenDeployer
* @notice This contract is used to deploy new instances of the StandardizedTokenProxy contract.
Expand Down
36 changes: 36 additions & 0 deletions contracts/interfaces/IStandardizedTokenRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IStandardizedTokenRegistrar {
error ZeroAddress();
error NotDistributor(address distributor);
error NotOperator(address operator);
error NonZeroMintAmount();

function chainNameHash() external view returns (bytes32);

function getStandardizedTokenSalt(address deployer, bytes32 salt) external view returns (bytes32);

function getStandardizedTokenId(address deployer, bytes32 salt) external view returns (bytes32 tokenId);

function getStandardizedTokenAddress(address deployer, bytes32 salt) external view returns (address tokenAddress);

function deployStandardizedToken(
bytes32 salt,
string calldata name,
string calldata symbol,
uint8 decimals,
uint256 mintAmount,
address distributor
) external payable;

function deployRemoteStandarizedToken(
bytes32 salt,
address additionalDistributor,
address optionalOperator,
uint256 mintAmount,
string memory destinationChain,
uint256 gasValue
) external payable;
}
2 changes: 0 additions & 2 deletions contracts/interfaces/ITokenManagerDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

pragma solidity ^0.8.0;

import { Create3Deployer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3Deployer.sol';

/**
* @title ITokenManagerDeployer
* @notice This contract is used to deploy new instances of the TokenManagerProxy contract.
Expand Down
28 changes: 28 additions & 0 deletions contracts/proxies/CanonicalTokenRegistrarProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Proxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Proxy.sol';

/**
* @title CanonicalTokenRegistrarProxy
* @dev Proxy contract for interchain token service contracts. Inherits from the Proxy contract.
*/
contract CanonicalTokenRegistrarProxy is Proxy {
bytes32 private constant CONTRACT_ID = keccak256('canonical-token-registrar');

/**
* @dev Constructs the InterchainTokenServiceProxy contract.
* @param implementationAddress Address of the interchain token service implementation
* @param owner Address of the owner of the proxy
*/
constructor(address implementationAddress, address owner) Proxy(implementationAddress, owner, '') {}

/**
* @dev Override for the 'contractId' function in FinalProxy. Returns a unique identifier for this contract.
* @return bytes32 identifier for this contract
*/
function contractId() internal pure override returns (bytes32) {
return CONTRACT_ID;
}
}
28 changes: 28 additions & 0 deletions contracts/proxies/StandardizedTokenRegistrarProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Proxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Proxy.sol';

/**
* @title StandardizedTokenRegistrarProxy
* @dev Proxy contract for interchain token service contracts. Inherits from the Proxy contract.
*/
contract StandardizedTokenRegistrarProxy is Proxy {
bytes32 private constant CONTRACT_ID = keccak256('standardized-token-registrar');

/**
* @dev Constructs the InterchainTokenServiceProxy contract.
* @param implementationAddress Address of the interchain token service implementation
* @param owner Address of the owner of the proxy
*/
constructor(address implementationAddress, address owner) Proxy(implementationAddress, owner, '') {}

/**
* @dev Override for the 'contractId' function in FinalProxy. Returns a unique identifier for this contract.
* @return bytes32 identifier for this contract
*/
function contractId() internal pure override returns (bytes32) {
return CONTRACT_ID;
}
}
13 changes: 8 additions & 5 deletions contracts/test/HardCodedConstantsTest.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT

// solhint-disable-next-line one-contract-per-file
pragma solidity ^0.8.0;

import { TokenManagerLiquidityPool } from '../token-manager/implementations/TokenManagerLiquidityPool.sol';
Expand All @@ -9,12 +10,14 @@ import { NoReEntrancy } from '../utils/NoReEntrancy.sol';
import { Operatable } from '../utils/Operatable.sol';
import { Pausable } from '../utils/Pausable.sol';

error Invalid();

contract TestTokenManager is TokenManagerLiquidityPool {
string public constant NAME = 'TestTokenManager';

constructor(address interchainTokenService_) TokenManagerLiquidityPool(interchainTokenService_) {
require(TOKEN_ADDRESS_SLOT == uint256(keccak256('token-address')) - 1, 'invalid constant');
require(LIQUIDITY_POOL_SLOT == uint256(keccak256('liquidity-pool-slot')) - 1, 'invalid constant');
if (TOKEN_ADDRESS_SLOT != uint256(keccak256('token-address')) - 1) revert Invalid();
if (LIQUIDITY_POOL_SLOT != uint256(keccak256('liquidity-pool-slot')) - 1) revert Invalid();
}
}

Expand All @@ -28,15 +31,15 @@ contract TestFlowLimit is FlowLimit {
string public constant NAME = 'TestFlowLimit';

constructor() {
require(FLOW_LIMIT_SLOT == uint256(keccak256('flow-limit')) - 1, 'invalid constant');
if (FLOW_LIMIT_SLOT != uint256(keccak256('flow-limit')) - 1) revert Invalid();
}
}

contract TestNoReEntrancy is NoReEntrancy {
string public constant NAME = 'TestNoReEntrancy';

constructor() {
require(ENTERED_SLOT == uint256(keccak256('entered')) - 1, 'invalid constant');
if (ENTERED_SLOT != uint256(keccak256('entered')) - 1) revert Invalid();
}
}

Expand All @@ -50,6 +53,6 @@ contract TestPausable is Pausable {
string public constant NAME = 'TestPausable';

constructor() {
require(PAUSE_SLOT == uint256(keccak256('paused')) - 1, 'invalid constant');
if (PAUSE_SLOT != uint256(keccak256('paused')) - 1) revert Invalid();
}
}
1 change: 0 additions & 1 deletion contracts/test/InterchainExecutableTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
pragma solidity ^0.8.0;

import { InterchainTokenExpressExecutable } from '../executable/InterchainTokenExpressExecutable.sol';
import { IInterchainTokenService } from '../interfaces/IInterchainTokenService.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';

contract InterchainExecutableTest is InterchainTokenExpressExecutable {
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/utils/MulticallTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract MulticallTest is Multicall {
}

function function3() external pure {
// solhint-disable-next-line reason-string
// solhint-disable-next-line reason-string,custom-errors
revert();
}

Expand Down
6 changes: 3 additions & 3 deletions contracts/token-implementations/StandardizedToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

pragma solidity ^0.8.0;

import { IImplementation } from '../interfaces/IImplementation.sol';
import { IStandardizedToken } from '../interfaces/IStandardizedToken.sol';
import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { IInterchainToken } from '../interfaces/IInterchainToken.sol';

import { InterchainToken } from '../interchain-token/InterchainToken.sol';
import { ERC20Permit } from '../token-implementations/ERC20Permit.sol';
Expand Down Expand Up @@ -38,7 +38,7 @@ contract StandardizedToken is InterchainToken, ERC20Permit, Implementation, Dist
* @notice Returns the token manager for this token
* @return ITokenManager The token manager contract
*/
function tokenManager() public view override(InterchainToken, IInterchainToken) returns (ITokenManager) {
function tokenManager() public view override(InterchainToken, IStandardizedToken) returns (ITokenManager) {
return ITokenManager(tokenManager_);
}

Expand All @@ -47,7 +47,7 @@ contract StandardizedToken is InterchainToken, ERC20Permit, Implementation, Dist
* @param params The setup parameters in bytes
* The setup params include tokenManager, distributor, tokenName, symbol, decimals, mintAmount and mintTo
*/
function setup(bytes calldata params) external override onlyProxy {
function setup(bytes calldata params) external override(IImplementation, IStandardizedToken) onlyProxy {
{
address distributor_;
address tokenManagerAddress;
Expand Down
1 change: 0 additions & 1 deletion contracts/token-manager/TokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pragma solidity ^0.8.0;

import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { IInterchainTokenService } from '../interfaces/IInterchainTokenService.sol';
import { ITokenManagerProxy } from '../interfaces/ITokenManagerProxy.sol';

import { Operatable } from '../utils/Operatable.sol';
import { FlowLimit } from '../utils/FlowLimit.sol';
Expand Down
Loading

0 comments on commit b7bc804

Please sign in to comment.