Skip to content

Commit

Permalink
feat: another example (#107)
Browse files Browse the repository at this point in the history
* renamed folder and changed version

* npmignore

* npmignore

* change version

* using include pattern instead.

* Fixed most of the things least auhority suggested.

* made lint happy

* Apply suggestions from code review

* fixed some bugs

* added events

* rename set to transfer for distributor and operator

* changed standardized token to always allow token managers to mint/burn it.

* using immutable storage for remoteAddressValidator address to save gas

* Added some recommended changes

* added milap's suggested changes

* Fixed some names and some minor gas optimizations

* prettier and lint

* stash

* import .env in hardhat.config

* trying to fix .env.example

* Added some getters in IRemoteAddressValidator and removed useless check for distributor in the InterchainTokenService.

* removed ternary operators

* made lint happy

* made lint happy

* Added a new token manager to handle fee on transfer and added some tests for it as well

* fixed the liquidity pool check.

* fix a duplication bug

* lint

* added some more tests

* Added more tests

* Added proper re-entrancy protection for fee on transfer token managers.

* change to tx.origin for refunds

* Added support for more kinds of addresses.

* some minor gas opts

* some more gas optimizations.

* Added a getter for chain name to the remote address validator.

* moved the tokenManager getter functionality to a separate contract which saves almost a kilobyte of codesize.

* made lint happy

* Removed tokenManagerGetter and put params into tokenManagers

* Added separate tokenManager interfaces

* addressed ackeeblockchains's 3.0 report

* prettier

* added interchain transfer methods to the service and unified receiving tokens a bit.

* made lint happy

* rename sendToken to interchainTransfer

* changed sendToken everywhere

* changed from uint256.max to a const

* change setting to zero to delete for storage slots.

* rearange storage variables to save a bit of gas.

* Removed unecesairy casts

* made as many event params inexed as possible

* Removed unused imports

* domain separator is calculated each time.

* added some natspec

* added an example for using pre-existing custom tokens.

* added a comment

* feat(TokenManager): added MintBurnFrom and MintBurnFromAddress (#108)

* feat(TokenManager): MintBurnFrom and MintBurnFromAddress

* fix(TokenManager): removed MintBurnFromAddress as deprecated

* Update contracts/interfaces/IERC20BurnableFrom.sol

---------

Co-authored-by: Milap Sheth <[email protected]>

---------

Co-authored-by: Milap Sheth <[email protected]>
Co-authored-by: Kiryl Yermakou <[email protected]>
  • Loading branch information
3 people authored Oct 3, 2023
1 parent 4ca3dd6 commit 7311951
Show file tree
Hide file tree
Showing 18 changed files with 276 additions and 111 deletions.
25 changes: 25 additions & 0 deletions contracts/interfaces/IERC20BurnableFrom.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20BurnableFrom {
/**
* @notice Function to burn tokens from a burn deposit address
* @notice It is needed to support legacy Axelar Gateway tokens
* @dev Can only be called after token is transferred to a deposit address.
* @param salt The address that will have its tokens burnt
*/
function burn(bytes32 salt) external;

/**
* @notice Function to burn tokens
* @notice Requires the caller to have allowance for `amount` on `from`
* @dev Can only be called by the distributor address.
* @param from The address that will have its tokens burnt
* @param amount The amount of tokens to burn
*/
function burnFrom(address from, uint256 amount) external;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20BurnableMintable {
interface IERC20MintableBurnable {
/**
* @notice Function to mint new tokens
* Can only be called by the distributor address.
* @dev Can only be called by the distributor address.
* @param to The address that will receive the minted tokens
* @param amount The amount of tokens to mint
*/
function mint(address to, uint256 amount) external;

/**
* @notice Function to burn tokens
* Can only be called by the distributor address.
* @dev Can only be called by the distributor address.
* @param from The address that will have its tokens burnt
* @param amount The amount of tokens to burn
*/
Expand Down
4 changes: 2 additions & 2 deletions contracts/interfaces/IStandardizedToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.0;

import { IInterchainToken } from './IInterchainToken.sol';
import { IDistributable } from './IDistributable.sol';
import { IERC20BurnableMintable } from './IERC20BurnableMintable.sol';
import { IERC20MintableBurnable } from './IERC20MintableBurnable.sol';
import { ITokenManager } from './ITokenManager.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';

Expand All @@ -13,7 +13,7 @@ import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interf
* @notice This contract implements a standardized token which extends InterchainToken functionality.
* This contract also inherits Distributable and Implementation logic.
*/
interface IStandardizedToken is IInterchainToken, IDistributable, IERC20BurnableMintable, IERC20 {
interface IStandardizedToken is IInterchainToken, IDistributable, IERC20MintableBurnable, IERC20 {
/**
* @notice Returns the contract id, which a proxy can check to ensure no false implementation was used.
*/
Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/ITokenManagerType.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ pragma solidity ^0.8.0;
*/
interface ITokenManagerType {
enum TokenManagerType {
LOCK_UNLOCK,
MINT_BURN,
MINT_BURN_FROM,
LOCK_UNLOCK,
LOCK_UNLOCK_FEE_ON_TRANSFER,
LIQUIDITY_POOL
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/test/FeeOnTransferTokenTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ pragma solidity ^0.8.0;
import { InterchainToken } from '../interchain-token/InterchainToken.sol';
import { Distributable } from '../utils/Distributable.sol';
import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { IERC20BurnableMintable } from '../interfaces/IERC20BurnableMintable.sol';
import { IERC20MintableBurnable } from '../interfaces/IERC20MintableBurnable.sol';

contract FeeOnTransferTokenTest is InterchainToken, Distributable, IERC20BurnableMintable {
contract FeeOnTransferTokenTest is InterchainToken, Distributable, IERC20MintableBurnable {
ITokenManager public tokenManager_;
bool internal tokenManagerRequiresApproval_ = true;

Expand Down
4 changes: 2 additions & 2 deletions contracts/test/InterchainTokenTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ pragma solidity ^0.8.0;
import { InterchainToken } from '../interchain-token/InterchainToken.sol';
import { Distributable } from '../utils/Distributable.sol';
import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { IERC20BurnableMintable } from '../interfaces/IERC20BurnableMintable.sol';
import { IERC20MintableBurnable } from '../interfaces/IERC20MintableBurnable.sol';

contract InterchainTokenTest is InterchainToken, Distributable, IERC20BurnableMintable {
contract InterchainTokenTest is InterchainToken, Distributable, IERC20MintableBurnable {
ITokenManager public tokenManager_;
bool internal tokenManagerRequiresApproval_ = true;
string public name;
Expand Down
4 changes: 2 additions & 2 deletions contracts/token-implementations/StandardizedToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.0;

import { IERC20BurnableMintable } from '../interfaces/IERC20BurnableMintable.sol';
import { IERC20MintableBurnable } from '../interfaces/IERC20MintableBurnable.sol';
import { ITokenManager } from '../interfaces/ITokenManager.sol';

import { InterchainToken } from '../interchain-token/InterchainToken.sol';
Expand All @@ -16,7 +16,7 @@ import { Distributable } from '../utils/Distributable.sol';
* @notice This contract implements a standardized token which extends InterchainToken functionality.
* This contract also inherits Distributable and Implementation logic.
*/
contract StandardizedToken is IERC20BurnableMintable, InterchainToken, ERC20Permit, Implementation, Distributable {
contract StandardizedToken is IERC20MintableBurnable, InterchainToken, ERC20Permit, Implementation, Distributable {
using AddressBytesUtils for bytes;

string public name;
Expand Down
24 changes: 20 additions & 4 deletions contracts/token-manager/TokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen

IInterchainTokenService public immutable interchainTokenService;

// uint256(keccak256('token-address')) - 1
uint256 internal constant TOKEN_ADDRESS_SLOT = 0xc4e632779a6a7838736dd7e5e6a0eadf171dd37dfb6230720e265576dfcf42ba;

/**
* @notice Constructs the TokenManager contract.
* @param interchainTokenService_ The address of the interchain token service
Expand All @@ -46,11 +49,14 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen
}

/**
* @notice A function that should return the address of the token.
* Must be overridden in the inheriting contract.
* @return address address of the token.
* @dev Reads the stored token address from the predetermined storage slot
* @return tokenAddress_ The address of the token
*/
function tokenAddress() public view virtual returns (address);
function tokenAddress() public view virtual returns (address tokenAddress_) {
assembly {
tokenAddress_ := sload(TOKEN_ADDRESS_SLOT)
}
}

/**
* @notice A function that returns the token id.
Expand Down Expand Up @@ -197,6 +203,16 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen
_setFlowLimit(flowLimit);
}

/**
* @dev Stores the token address in the predetermined storage slot
* @param tokenAddress_ The address of the token to store
*/
function _setTokenAddress(address tokenAddress_) internal {
assembly {
sstore(TOKEN_ADDRESS_SLOT, tokenAddress_)
}
}

/**
* @notice Transfers tokens from a specific address to this contract.
* Must be overridden in the inheriting contract.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.0;

import { TokenManagerAddressStorage } from './TokenManagerAddressStorage.sol';
import { TokenManager } from '../TokenManager.sol';
import { NoReEntrancy } from '../../utils/NoReEntrancy.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { ITokenManagerLiquidityPool } from '../../interfaces/ITokenManagerLiquidityPool.sol';
Expand All @@ -16,7 +16,9 @@ import { SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/c
* @dev This contract extends TokenManagerAddressStorage and provides implementation for its abstract methods.
* It uses the Axelar SDK to safely transfer tokens.
*/
contract TokenManagerLiquidityPool is TokenManagerAddressStorage, NoReEntrancy {
contract TokenManagerLiquidityPool is TokenManager, NoReEntrancy {
using SafeTokenTransferFrom for IERC20;

// uint256(keccak256('liquidity-pool-slot')) - 1
uint256 internal constant LIQUIDITY_POOL_SLOT = 0x8e02741a3381812d092c5689c9fc701c5185c1742fdf7954c4c4472be4cc4807;

Expand All @@ -25,7 +27,7 @@ contract TokenManagerLiquidityPool is TokenManagerAddressStorage, NoReEntrancy {
* of TokenManagerAddressStorage which calls the constructor of TokenManager.
* @param interchainTokenService_ The address of the interchain token service contract
*/
constructor(address interchainTokenService_) TokenManagerAddressStorage(interchainTokenService_) {}
constructor(address interchainTokenService_) TokenManager(interchainTokenService_) {}

function implementationType() external pure returns (uint256) {
return uint256(TokenManagerType.LIQUIDITY_POOL);
Expand Down Expand Up @@ -81,7 +83,7 @@ contract TokenManagerLiquidityPool is TokenManagerAddressStorage, NoReEntrancy {
address liquidityPool_ = liquidityPool();
uint256 balance = token.balanceOf(liquidityPool_);

SafeTokenTransferFrom.safeTransferFrom(token, from, liquidityPool_, amount);
token.safeTransferFrom(from, liquidityPool_, amount);

uint256 diff = token.balanceOf(liquidityPool_) - balance;
if (diff < amount) {
Expand All @@ -100,7 +102,7 @@ contract TokenManagerLiquidityPool is TokenManagerAddressStorage, NoReEntrancy {
IERC20 token = IERC20(tokenAddress());
uint256 balance = token.balanceOf(to);

SafeTokenTransferFrom.safeTransferFrom(token, liquidityPool(), to, amount);
token.safeTransferFrom(liquidityPool(), to, amount);

uint256 diff = token.balanceOf(to) - balance;
if (diff < amount) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@

pragma solidity ^0.8.0;

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

import { SafeTokenTransferFrom, SafeTokenTransfer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/SafeTransfer.sol';
import { SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/SafeTransfer.sol';

/**
* @title TokenManagerLockUnlock
* @notice This contract is an implementation of TokenManager that locks and unlocks a specific token on behalf of the interchain token service.
* @dev This contract extends TokenManagerAddressStorage and provides implementation for its abstract methods.
* It uses the Axelar SDK to safely transfer tokens.
*/
contract TokenManagerLockUnlock is TokenManagerAddressStorage, ITokenManagerLockUnlock {
contract TokenManagerLockUnlock is TokenManager, ITokenManagerLockUnlock {
using SafeTokenTransfer for IERC20;
using SafeTokenTransferFrom for IERC20;

/**
* @dev Constructs an instance of TokenManagerLockUnlock. Calls the constructor
* of TokenManagerAddressStorage which calls the constructor of TokenManager.
* @param interchainTokenService_ The address of the interchain token service contract
*/
constructor(address interchainTokenService_) TokenManagerAddressStorage(interchainTokenService_) {}
constructor(address interchainTokenService_) TokenManager(interchainTokenService_) {}

function implementationType() external pure returns (uint256) {
return uint256(TokenManagerType.LOCK_UNLOCK);
Expand All @@ -45,7 +48,7 @@ contract TokenManagerLockUnlock is TokenManagerAddressStorage, ITokenManagerLock
function _takeToken(address from, uint256 amount) internal override returns (uint256) {
IERC20 token = IERC20(tokenAddress());

SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount);
token.safeTransferFrom(from, address(this), amount);

return amount;
}
Expand All @@ -59,7 +62,7 @@ contract TokenManagerLockUnlock is TokenManagerAddressStorage, ITokenManagerLock
function _giveToken(address to, uint256 amount) internal override returns (uint256) {
IERC20 token = IERC20(tokenAddress());

SafeTokenTransfer.safeTransfer(token, to, amount);
token.safeTransfer(to, amount);

return amount;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.0;

import { TokenManagerAddressStorage } from './TokenManagerAddressStorage.sol';
import { TokenManager } from '../TokenManager.sol';
import { NoReEntrancy } from '../../utils/NoReEntrancy.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';

Expand All @@ -14,13 +14,16 @@ import { SafeTokenTransferFrom, SafeTokenTransfer } from '@axelar-network/axelar
* @dev This contract extends TokenManagerAddressStorage and provides implementation for its abstract methods.
* It uses the Axelar SDK to safely transfer tokens.
*/
contract TokenManagerLockUnlockFee is TokenManagerAddressStorage, NoReEntrancy {
contract TokenManagerLockUnlockFee is TokenManager, NoReEntrancy {
using SafeTokenTransfer for IERC20;
using SafeTokenTransferFrom for IERC20;

/**
* @dev Constructs an instance of TokenManagerLockUnlock. Calls the constructor
* of TokenManagerAddressStorage which calls the constructor of TokenManager.
* @param interchainTokenService_ The address of the interchain token service contract
*/
constructor(address interchainTokenService_) TokenManagerAddressStorage(interchainTokenService_) {}
constructor(address interchainTokenService_) TokenManager(interchainTokenService_) {}

function implementationType() external pure returns (uint256) {
return uint256(TokenManagerType.LOCK_UNLOCK_FEE_ON_TRANSFER);
Expand All @@ -46,7 +49,7 @@ contract TokenManagerLockUnlockFee is TokenManagerAddressStorage, NoReEntrancy {
IERC20 token = IERC20(tokenAddress());
uint256 balance = token.balanceOf(address(this));

SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount);
token.safeTransferFrom(from, address(this), amount);

uint256 diff = token.balanceOf(address(this)) - balance;
if (diff < amount) {
Expand All @@ -65,7 +68,7 @@ contract TokenManagerLockUnlockFee is TokenManagerAddressStorage, NoReEntrancy {
IERC20 token = IERC20(tokenAddress());
uint256 balance = token.balanceOf(to);

SafeTokenTransfer.safeTransfer(token, to, amount);
token.safeTransfer(to, amount);

uint256 diff = token.balanceOf(to) - balance;
if (diff < amount) {
Expand Down
Loading

0 comments on commit 7311951

Please sign in to comment.