Skip to content

feat: clone instead of fixed proxy #168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions contracts/interchain-token/InterchainToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
pragma solidity ^0.8.0;

import { AddressBytes } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressBytes.sol';
import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
import { Implementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Implementation.sol';

import { IInterchainToken } from '../interfaces/IInterchainToken.sol';

Expand All @@ -17,21 +15,38 @@ import { Distributable } from '../utils/Distributable.sol';
* @notice This contract implements a interchain token which extends InterchainToken functionality.
* This contract also inherits Distributable and Implementation logic.
*/
contract InterchainToken is BaseInterchainToken, ERC20Permit, Implementation, Distributable, IInterchainToken {
contract InterchainToken is BaseInterchainToken, ERC20Permit, Distributable, IInterchainToken {
using AddressBytes for bytes;

string public name;
string public symbol;
uint8 public decimals;
address internal tokenManager_;

bytes32 private constant CONTRACT_ID = keccak256('interchain-token');
// bytes32(uint256(keccak256('interchain-token-is-setup-slot')) - 1);
bytes32 internal constant IS_SETUP_SLOT = 0xb39f35de0a5b2620db9237c1e18c03b5e68a71236c9bdfbcd69f3582bab06df6;

constructor() {
// Make the implementation act as if it has been setup already to disallow calls to init() (even though that wouldn't achieve anything really)
_setSetup();
}

/**
* @notice returns true if the contract has be setup.
*/
function _isSetup() internal view returns (bool isSetup) {
assembly {
isSetup := sload(IS_SETUP_SLOT)
}
}

/**
* @notice Getter for the contract id.
* @notice sets the isSetup to true, to allow only a single setup.
*/
function contractId() external pure override returns (bytes32) {
return CONTRACT_ID;
function _setSetup() internal {
assembly {
sstore(IS_SETUP_SLOT, true)
}
}

/**
Expand All @@ -44,20 +59,29 @@ contract InterchainToken is BaseInterchainToken, ERC20Permit, Implementation, Di

/**
* @notice Setup function to initialize contract parameters
* @param params The setup parameters in bytes
* The setup params include tokenManager, distributor, tokenName, symbol, decimals, mintAmount and mintTo
* @param tokenManagerAddress The address of the token manager of this token
* @param distributor The address of the token distributor
* @param tokenName The name of the token
* @param tokenSymbol The symbopl of the token
* @param tokenDecimals The decimals of the token
*/
function setup(bytes calldata params) external override(Implementation, IImplementation) onlyProxy {
address distributor;
address tokenManagerAddress;
string memory tokenName;
(tokenManagerAddress, distributor, tokenName, symbol, decimals) = abi.decode(params, (address, address, string, string, uint8));
function init(
address tokenManagerAddress,
address distributor,
string calldata tokenName,
string calldata tokenSymbol,
uint8 tokenDecimals
) external {
if (_isSetup()) revert AlreadySetup();
_setSetup();

if (tokenManagerAddress == address(0)) revert TokenManagerAddressZero();
if (bytes(tokenName).length == 0) revert TokenNameEmpty();

tokenManager_ = tokenManagerAddress;
name = tokenName;
symbol = tokenSymbol;
decimals = tokenDecimals;

if (distributor != address(0)) _addDistributor(distributor);
_addDistributor(tokenManagerAddress);
Expand Down
21 changes: 18 additions & 3 deletions contracts/interfaces/IInterchainToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

pragma solidity ^0.8.0;

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

import { IInterchainTokenStandard } from './IInterchainTokenStandard.sol';
import { IDistributable } from './IDistributable.sol';
import { IERC20MintableBurnable } from './IERC20MintableBurnable.sol';
Expand All @@ -12,14 +10,31 @@ import { IERC20Named } from './IERC20Named.sol';
/**
* @title IInterchainToken
*/
interface IInterchainToken is IInterchainTokenStandard, IDistributable, IERC20MintableBurnable, IERC20Named, IImplementation {
interface IInterchainToken is IInterchainTokenStandard, IDistributable, IERC20MintableBurnable, IERC20Named {
error TokenManagerAddressZero();
error TokenNameEmpty();
error AlreadySetup();

/**
* @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 (address tokenManager_);

/**
* @notice Setup function to initialize contract parameters
* @param tokenManagerAddress The address of the token manager of this token
* @param distributor The address of the token distributor
* @param tokenName The name of the token
* @param tokenSymbol The symbopl of the token
* @param tokenDecimals The decimals of the token
*/
function init(
address tokenManagerAddress,
address distributor,
string calldata tokenName,
string calldata tokenSymbol,
uint8 tokenDecimals
) external;
}
33 changes: 0 additions & 33 deletions contracts/proxies/InterchainTokenProxy.sol

This file was deleted.

7 changes: 7 additions & 0 deletions contracts/test/HardCodedConstantsTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TokenManagerLiquidityPool } from '../token-manager/TokenManagerLiquidit
import { Distributable } from '../utils/Distributable.sol';
import { FlowLimit } from '../utils/FlowLimit.sol';
import { Operatable } from '../utils/Operatable.sol';
import { InterchainToken } from '../interchain-token/InterchainToken.sol';

error Invalid();

Expand Down Expand Up @@ -38,3 +39,9 @@ contract TestOperatable is Operatable {

constructor() {}
}

contract TestInterchainToken is InterchainToken {
constructor() {
if (IS_SETUP_SLOT != bytes32(uint256(keccak256('interchain-token-is-setup-slot')) - 1)) revert Invalid();
}
}
15 changes: 10 additions & 5 deletions contracts/utils/InterchainTokenDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ pragma solidity ^0.8.0;
import { Create3 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3.sol';

import { IInterchainTokenDeployer } from '../interfaces/IInterchainTokenDeployer.sol';

import { InterchainTokenProxy } from '../proxies/InterchainTokenProxy.sol';
import { IInterchainToken } from '../interfaces/IInterchainToken.sol';

/**
* @title InterchainTokenDeployer
Expand Down Expand Up @@ -43,12 +42,18 @@ contract InterchainTokenDeployer is IInterchainTokenDeployer, Create3 {
string calldata symbol,
uint8 decimals
) external payable returns (address tokenAddress) {
bytes memory params = abi.encode(tokenManager, distributor, name, symbol, decimals);
// slither-disable-next-line too-many-digits
bytes memory bytecode = bytes.concat(type(InterchainTokenProxy).creationCode, abi.encode(implementationAddress, params));

bytes memory bytecode = new bytes(0x37); //bytes.concat(type(InterchainTokenProxy).creationCode, abi.encode(implementationAddress, params));
address implementation = implementationAddress;
assembly {
mstore(add(bytecode, 0x20), shl(0x60, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73))
mstore(add(bytecode, 0x34), shl(0x60, implementation))
mstore(add(bytecode, 0x48), shl(0x88, 0x5af43d82803e903d91602b57fd5bf3))
}
tokenAddress = _create3(bytecode, salt);
if (tokenAddress.code.length == 0) revert TokenDeploymentFailed();

IInterchainToken(tokenAddress).init(tokenManager, distributor, name, symbol, decimals);
}

function deployedAddress(bytes32 salt) external view returns (address tokenAddress) {
Expand Down
55 changes: 14 additions & 41 deletions test/InterchainToken.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
'use strict';

const chai = require('chai');
const { ethers } = require('hardhat');
const {
getContractAt,
utils: { keccak256, toUtf8Bytes },
} = ethers;
const { expect } = chai;
const { getRandomBytes32, expectRevert, getGasOptions } = require('./utils');
const { getContractAt } = ethers;
const { getRandomBytes32, expectRevert } = require('./utils');
const { deployContract } = require('../scripts/deploy');

describe('InterchainToken', () => {
Expand All @@ -19,7 +14,6 @@ describe('InterchainToken', () => {
const mintAmount = 123;

let token;
let tokenProxy;

let owner;

Expand All @@ -35,7 +29,6 @@ describe('InterchainToken', () => {
const tokenAddress = await interchainTokenDeployer.deployedAddress(salt);

token = await getContractAt('InterchainToken', tokenAddress, owner);
tokenProxy = await getContractAt('InterchainTokenProxy', tokenAddress, owner);

await interchainTokenDeployer
.deployInterchainToken(salt, owner.address, owner.address, name, symbol, decimals)
Expand All @@ -44,41 +37,21 @@ describe('InterchainToken', () => {
await (await token.mint(owner.address, mintAmount)).wait();
});

describe('Interchain Token Proxy', () => {
it('should revert if interchain token implementation is invalid', async () => {
const invalidInterchainToken = await deployContract(owner, 'InvalidInterchainToken');
interchainTokenDeployer = await deployContract(owner, 'InterchainTokenDeployer', [invalidInterchainToken.address]);

const salt = getRandomBytes32();

await expect(
interchainTokenDeployer.deployInterchainToken(salt, owner.address, owner.address, name, symbol, decimals, getGasOptions()),
).to.be.reverted;
});

it('should revert if interchain token setup fails', async () => {
const params = '0x1234';
await expectRevert(
(gasOptions) => deployContract(owner, 'InterchainTokenProxy', [interchainToken.address, params, gasOptions]),
tokenProxy,
'SetupFailed',
);
});

it('should return the correct contract ID', async () => {
const contractID = await token.contractId();
const hash = keccak256(toUtf8Bytes('interchain-token'));
expect(contractID).to.equal(hash);
});
});

describe('Interchain Token', () => {
it('revert on setup if not called by the proxy', async () => {
const implementationAddress = await tokenProxy.implementation();
it('revert on init if not called by the proxy', async () => {
const implementationAddress = await interchainTokenDeployer.implementationAddress();
const implementation = await getContractAt('InterchainToken', implementationAddress, owner);

const params = '0x';
await expectRevert((gasOptions) => implementation.setup(params, gasOptions), token, 'NotProxy');
const tokenManagerAddress = owner.address;
const distributor = owner.address;
const tokenName = 'name';
const tokenSymbol = 'symbol';
const tokenDecimals = 7;
await expectRevert(
(gasOptions) => implementation.init(tokenManagerAddress, distributor, tokenName, tokenSymbol, tokenDecimals, gasOptions),
implementation,
'AlreadySetup',
);
});
});
});
2 changes: 0 additions & 2 deletions test/UtilsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,13 @@ describe('InterchainTokenDeployer', () => {
const tokenAddress = await interchainTokenDeployer.deployedAddress(salt);

const token = await getContractAt('InterchainToken', tokenAddress, ownerWallet);
const tokenProxy = await getContractAt('InterchainTokenProxy', tokenAddress, ownerWallet);

await expect(interchainTokenDeployer.deployInterchainToken(salt, tokenManager, tokenManager, name, symbol, decimals))
.and.to.emit(token, 'RolesAdded')
.withArgs(tokenManager, 1 << DISTRIBUTOR_ROLE)
.to.emit(token, 'RolesAdded')
.withArgs(tokenManager, 1 << DISTRIBUTOR_ROLE);

expect(await tokenProxy.implementation()).to.equal(interchainToken.address);
expect(await token.name()).to.equal(name);
expect(await token.symbol()).to.equal(symbol);
expect(await token.decimals()).to.equal(decimals);
Expand Down