Contract Name:
AxelarGateway
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IGovernable } from './IGovernable.sol';
import { IImplementation } from './IImplementation.sol';
interface IAxelarGateway is IImplementation, IGovernable {
/**********\
|* Errors *|
\**********/
error NotSelf();
error InvalidCodeHash();
error SetupFailed();
error InvalidAuthModule();
error InvalidTokenDeployer();
error InvalidAmount();
error InvalidChainId();
error InvalidCommands();
error TokenDoesNotExist(string symbol);
error TokenAlreadyExists(string symbol);
error TokenDeployFailed(string symbol);
error TokenContractDoesNotExist(address token);
error BurnFailed(string symbol);
error MintFailed(string symbol);
error InvalidSetMintLimitsParams();
error ExceedMintLimit(string symbol);
/**********\
|* Events *|
\**********/
event TokenSent(
address indexed sender,
string destinationChain,
string destinationAddress,
string symbol,
uint256 amount
);
event ContractCall(
address indexed sender,
string destinationChain,
string destinationContractAddress,
bytes32 indexed payloadHash,
bytes payload
);
event ContractCallWithToken(
address indexed sender,
string destinationChain,
string destinationContractAddress,
bytes32 indexed payloadHash,
bytes payload,
string symbol,
uint256 amount
);
event Executed(bytes32 indexed commandId);
event TokenDeployed(string symbol, address tokenAddresses);
event ContractCallApproved(
bytes32 indexed commandId,
string sourceChain,
string sourceAddress,
address indexed contractAddress,
bytes32 indexed payloadHash,
bytes32 sourceTxHash,
uint256 sourceEventIndex
);
event ContractCallApprovedWithMint(
bytes32 indexed commandId,
string sourceChain,
string sourceAddress,
address indexed contractAddress,
bytes32 indexed payloadHash,
string symbol,
uint256 amount,
bytes32 sourceTxHash,
uint256 sourceEventIndex
);
event ContractCallExecuted(bytes32 indexed commandId);
event TokenMintLimitUpdated(string symbol, uint256 limit);
event OperatorshipTransferred(bytes newOperatorsData);
event Upgraded(address indexed implementation);
/********************\
|* Public Functions *|
\********************/
function sendToken(
string calldata destinationChain,
string calldata destinationAddress,
string calldata symbol,
uint256 amount
) external;
function callContract(
string calldata destinationChain,
string calldata contractAddress,
bytes calldata payload
) external;
function callContractWithToken(
string calldata destinationChain,
string calldata contractAddress,
bytes calldata payload,
string calldata symbol,
uint256 amount
) external;
function isContractCallApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash
) external view returns (bool);
function isContractCallAndMintApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external view returns (bool);
function validateContractCall(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash
) external returns (bool);
function validateContractCallAndMint(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external returns (bool);
/***********\
|* Getters *|
\***********/
function authModule() external view returns (address);
function tokenDeployer() external view returns (address);
function tokenMintLimit(string memory symbol) external view returns (uint256);
function tokenMintAmount(string memory symbol) external view returns (uint256);
function allTokensFrozen() external view returns (bool);
function implementation() external view returns (address);
function tokenAddresses(string memory symbol) external view returns (address);
function tokenFrozen(string memory symbol) external view returns (bool);
function isCommandExecuted(bytes32 commandId) external view returns (bool);
/************************\
|* Governance Functions *|
\************************/
function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external;
function upgrade(
address newImplementation,
bytes32 newImplementationCodeHash,
bytes calldata setupParams
) external;
/**********************\
|* External Functions *|
\**********************/
function execute(bytes calldata input) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// General interface for upgradable contracts
interface IContractIdentifier {
/**
* @notice Returns the contract ID. It can be used as a check during upgrades.
* @dev Meant to be overridden in derived contracts.
* @return bytes32 The contract ID
*/
function contractId() external pure returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
error InvalidAccount();
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IGovernable Interface
* @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles.
*/
interface IGovernable {
error NotGovernance();
error NotMintLimiter();
error InvalidGovernance();
error InvalidMintLimiter();
event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);
event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance);
/**
* @notice Returns the governance address.
* @return address of the governance
*/
function governance() external view returns (address);
/**
* @notice Returns the mint limiter address.
* @return address of the mint limiter
*/
function mintLimiter() external view returns (address);
/**
* @notice Transfer the governance role to another address.
* @param newGovernance The new governance address
*/
function transferGovernance(address newGovernance) external;
/**
* @notice Transfer the mint limiter role to another address.
* @param newGovernance The new mint limiter address
*/
function transferMintLimiter(address newGovernance) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IContractIdentifier } from './IContractIdentifier.sol';
interface IImplementation is IContractIdentifier {
error NotProxy();
function setup(bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IOwnable Interface
* @notice IOwnable is an interface that abstracts the implementation of a
* contract with ownership control features. It's commonly used in upgradable
* contracts and includes the functionality to get current owner, transfer
* ownership, and propose and accept ownership.
*/
interface IOwnable {
error NotOwner();
error InvalidOwner();
error InvalidOwnerAddress();
event OwnershipTransferStarted(address indexed newOwner);
event OwnershipTransferred(address indexed newOwner);
/**
* @notice Returns the current owner of the contract.
* @return address The address of the current owner
*/
function owner() external view returns (address);
/**
* @notice Returns the address of the pending owner of the contract.
* @return address The address of the pending owner
*/
function pendingOwner() external view returns (address);
/**
* @notice Transfers ownership of the contract to a new address
* @param newOwner The address to transfer ownership to
*/
function transferOwnership(address newOwner) external;
/**
* @notice Proposes to transfer the contract's ownership to a new address.
* The new owner needs to accept the ownership explicitly.
* @param newOwner The address to transfer ownership to
*/
function proposeOwnership(address newOwner) external;
/**
* @notice Transfers ownership to the pending owner.
* @dev Can only be called by the pending owner
*/
function acceptOwnership() external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library ContractAddress {
function isContract(address contractAddress) internal view returns (bool) {
bytes32 existingCodeHash = contractAddress.codehash;
// https://eips.ethereum.org/EIPS/eip-1052
// keccak256('') == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
return
existingCodeHash != bytes32(0) &&
existingCodeHash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '../interfaces/IERC20.sol';
error TokenTransferFailed();
/*
* @title SafeTokenCall
* @dev This library is used for performing safe token transfers.
*/
library SafeTokenCall {
/*
* @notice Make a safe call to a token contract.
* @param token The token contract to interact with.
* @param callData The function call data.
* @throws TokenTransferFailed error if transfer of token is not successful.
*/
function safeCall(IERC20 token, bytes memory callData) internal {
(bool success, bytes memory returnData) = address(token).call(callData);
bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
}
}
/*
* @title SafeTokenTransfer
* @dev This library safely transfers tokens from the contract to a recipient.
*/
library SafeTokenTransfer {
/*
* @notice Transfer tokens to a recipient.
* @param token The token contract.
* @param receiver The recipient of the tokens.
* @param amount The amount of tokens to transfer.
*/
function safeTransfer(
IERC20 token,
address receiver,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
}
}
/*
* @title SafeTokenTransferFrom
* @dev This library helps to safely transfer tokens on behalf of a token holder.
*/
library SafeTokenTransferFrom {
/*
* @notice Transfer tokens on behalf of a token holder.
* @param token The token contract.
* @param from The address of the token holder.
* @param to The address the tokens are to be sent to.
* @param amount The amount of tokens to be transferred.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '../interfaces/IImplementation.sol';
/**
* @title Implementation
* @notice This contract serves as a base for other contracts and enforces a proxy-first access restriction.
* @dev Derived contracts must implement the setup function.
*/
abstract contract Implementation is IImplementation {
address private immutable implementationAddress;
/**
* @dev Contract constructor that sets the implementation address to the address of this contract.
*/
constructor() {
implementationAddress = address(this);
}
/**
* @dev Modifier to require the caller to be the proxy contract.
* Reverts if the caller is the current contract (i.e., the implementation contract itself).
*/
modifier onlyProxy() {
if (implementationAddress == address(this)) revert NotProxy();
_;
}
/**
* @notice Initializes contract parameters.
* This function is intended to be overridden by derived contracts.
* The overriding function must have the onlyProxy modifier.
* @param params The parameters to be used for initialization
*/
function setup(bytes calldata params) external virtual;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
import { IContractIdentifier } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IContractIdentifier.sol';
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import { SafeTokenCall, SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { ContractAddress } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/ContractAddress.sol';
import { Implementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Implementation.sol';
import { IAxelarAuth } from './interfaces/IAxelarAuth.sol';
import { IBurnableMintableCappedERC20 } from './interfaces/IBurnableMintableCappedERC20.sol';
import { ITokenDeployer } from './interfaces/ITokenDeployer.sol';
import { ECDSA } from './ECDSA.sol';
import { DepositHandler } from './DepositHandler.sol';
import { EternalStorage } from './EternalStorage.sol';
/**
* @title AxelarGateway Contract
* @notice This contract serves as the gateway for cross-chain contract calls,
* and token transfers within the Axelar network.
* It includes functions for sending tokens, calling contracts, and validating contract calls.
* The contract is managed via the decentralized governance mechanism on the Axelar network.
* @dev EternalStorage is used to simplify storage for upgradability, and InterchainGovernance module is used for governance.
*/
contract AxelarGateway is IAxelarGateway, Implementation, EternalStorage {
using SafeTokenCall for IERC20;
using SafeTokenTransfer for IERC20;
using SafeTokenTransferFrom for IERC20;
using ContractAddress for address;
error InvalidImplementation();
enum TokenType {
InternalBurnable,
InternalBurnableFrom,
External
}
/**
* @dev Deprecated slots. Should not be reused.
*/
// bytes32 internal constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen');
// bytes32 internal constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen');
/**
* @dev Storage slot with the address of the current implementation. `keccak256('eip1967.proxy.implementation') - 1`.
*/
bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
/**
* @dev Storage slot with the address of the current governance. keccak256('governance')) - 1
*/
bytes32 internal constant KEY_GOVERNANCE = bytes32(0xabea6fd3db56a6e6d0242111b43ebb13d1c42709651c032c7894962023a1f909);
/**
* @dev Storage slot with the address of the current mint limiter. keccak256('mint-limiter')) - 1
*/
bytes32 internal constant KEY_MINT_LIMITER = bytes32(0x627f0c11732837b3240a2de89c0b6343512886dd50978b99c76a68c6416a4d92);
bytes32 internal constant PREFIX_COMMAND_EXECUTED = keccak256('command-executed');
bytes32 internal constant PREFIX_TOKEN_ADDRESS = keccak256('token-address');
bytes32 internal constant PREFIX_TOKEN_TYPE = keccak256('token-type');
bytes32 internal constant PREFIX_CONTRACT_CALL_APPROVED = keccak256('contract-call-approved');
bytes32 internal constant PREFIX_CONTRACT_CALL_APPROVED_WITH_MINT = keccak256('contract-call-approved-with-mint');
bytes32 internal constant PREFIX_TOKEN_MINT_LIMIT = keccak256('token-mint-limit');
bytes32 internal constant PREFIX_TOKEN_MINT_AMOUNT = keccak256('token-mint-amount');
bytes32 internal constant SELECTOR_BURN_TOKEN = keccak256('burnToken');
bytes32 internal constant SELECTOR_DEPLOY_TOKEN = keccak256('deployToken');
bytes32 internal constant SELECTOR_MINT_TOKEN = keccak256('mintToken');
bytes32 internal constant SELECTOR_APPROVE_CONTRACT_CALL = keccak256('approveContractCall');
bytes32 internal constant SELECTOR_APPROVE_CONTRACT_CALL_WITH_MINT = keccak256('approveContractCallWithMint');
bytes32 internal constant SELECTOR_TRANSFER_OPERATORSHIP = keccak256('transferOperatorship');
address public immutable authModule;
address public immutable tokenDeployer;
/**
* @notice Constructs the AxelarGateway contract.
* @param authModule_ The address of the authentication module
* @param tokenDeployer_ The address of the token deployer
*/
constructor(address authModule_, address tokenDeployer_) {
if (authModule_.code.length == 0) revert InvalidAuthModule();
if (tokenDeployer_.code.length == 0) revert InvalidTokenDeployer();
authModule = authModule_;
tokenDeployer = tokenDeployer_;
}
/**
* @notice Ensures that the caller of the function is the gateway contract itself.
*/
modifier onlySelf() {
if (msg.sender != address(this)) revert NotSelf();
_;
}
/**
* @notice Ensures that the caller of the function is the governance address.
*/
modifier onlyGovernance() {
if (msg.sender != getAddress(KEY_GOVERNANCE)) revert NotGovernance();
_;
}
/**
* @notice Ensures that the caller of the function is either the mint limiter or governance.
*/
modifier onlyMintLimiter() {
if (msg.sender != getAddress(KEY_MINT_LIMITER) && msg.sender != getAddress(KEY_GOVERNANCE)) revert NotMintLimiter();
_;
}
/******************\
|* Public Methods *|
\******************/
/**
* @notice Send the specified token to the destination chain and address.
* @param destinationChain The chain to send tokens to. A registered chain name on Axelar must be used here
* @param destinationAddress The address on the destination chain to send tokens to
* @param symbol The symbol of the token to send
* @param amount The amount of tokens to send
*/
function sendToken(
string calldata destinationChain,
string calldata destinationAddress,
string calldata symbol,
uint256 amount
) external {
_burnTokenFrom(msg.sender, symbol, amount);
emit TokenSent(msg.sender, destinationChain, destinationAddress, symbol, amount);
}
/**
* @notice Calls a contract on the specified destination chain with a given payload.
* This function is the entry point for general message passing between chains.
* @param destinationChain The chain where the destination contract exists. A registered chain name on Axelar must be used here
* @param destinationContractAddress The address of the contract to call on the destination chain
* @param payload The payload to be sent to the destination contract, usually representing an encoded function call with arguments
*/
function callContract(
string calldata destinationChain,
string calldata destinationContractAddress,
bytes calldata payload
) external {
emit ContractCall(msg.sender, destinationChain, destinationContractAddress, keccak256(payload), payload);
}
/**
* @notice Calls a contract on the specified destination chain with a given payload and token amount.
* This function is the entry point for general message passing with token transfer between chains.
* @param destinationChain The chain where the destination contract exists. A registered chain name on Axelar must be used here
* @param destinationContractAddress The address of the contract to call with tokens on the destination chain
* @param payload The payload to be sent to the destination contract, usually representing an encoded function call with arguments
* @param symbol The symbol of the token to be sent with the call
* @param amount The amount of tokens to be sent with the call
*/
function callContractWithToken(
string calldata destinationChain,
string calldata destinationContractAddress,
bytes calldata payload,
string calldata symbol,
uint256 amount
) external {
_burnTokenFrom(msg.sender, symbol, amount);
emit ContractCallWithToken(msg.sender, destinationChain, destinationContractAddress, keccak256(payload), payload, symbol, amount);
}
/**
* @notice Checks whether a contract call has been approved by the gateway.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param contractAddress The contract address that will be called
* @param payloadHash The hash of the payload for that will be sent with the call
* @return bool A boolean value indicating whether the contract call has been approved by the gateway.
*/
function isContractCallApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash
) external view override returns (bool) {
return getBool(_getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash));
}
/**
* @notice Checks whether a contract call with token has been approved by the gateway.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param contractAddress The contract address that will be called, and where tokens will be sent
* @param payloadHash The hash of the payload for that will be sent with the call
* @param symbol The symbol of the token to be sent with the call
* @param amount The amount of tokens to be sent with the call
* @return bool A boolean value indicating whether the contract call with token has been approved by the gateway.
*/
function isContractCallAndMintApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external view override returns (bool) {
return
getBool(
_getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount)
);
}
/**
* @notice Called on the destination chain gateway by the recipient of the cross-chain contract call to validate it and only allow execution
* if this function returns true.
* @dev Once validated, the gateway marks the message as executed so the contract call is not executed twice.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param payloadHash The hash of the payload for that will be sent with the call
* @return valid True if the contract call is approved, false otherwise
*/
function validateContractCall(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash
) external override returns (bool valid) {
bytes32 key = _getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, msg.sender, payloadHash);
valid = getBool(key);
if (valid) {
_setBool(key, false);
emit ContractCallExecuted(commandId);
}
}
/**
* @notice Called on the destination chain gateway to validate the approval of a contract call with token transfer and only
* allow execution if this function returns true.
* @dev Once validated, the gateway marks the message as executed so the contract call with token is not executed twice.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param payloadHash The hash of the payload for that will be sent with the call
* @param symbol The symbol of the token to be sent with the call
* @param amount The amount of tokens to be sent with the call
* @return valid True if the contract call with token is approved, false otherwise
*/
function validateContractCallAndMint(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external override returns (bool valid) {
bytes32 key = _getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, msg.sender, payloadHash, symbol, amount);
valid = getBool(key);
if (valid) {
// Prevent re-entrancy
_setBool(key, false);
emit ContractCallExecuted(commandId);
_mintToken(symbol, msg.sender, amount);
}
}
/***********\
|* Getters *|
\***********/
/**
* @notice Gets the address of governance, should be the address of InterchainGovernance.
* @return address The address of governance.
*/
function governance() public view override returns (address) {
return getAddress(KEY_GOVERNANCE);
}
/**
* @notice Gets the address of the mint limiter, should be the address of Multisig.
* @return address The address of the mint limiter.
*/
function mintLimiter() public view override returns (address) {
return getAddress(KEY_MINT_LIMITER);
}
/**
* @notice Gets the transfer limit for a specific token symbol within the configured epoch.
* @param symbol The symbol of the token
* @return uint The transfer limit for the given token.
*/
function tokenMintLimit(string memory symbol) public view override returns (uint256) {
return getUint(_getTokenMintLimitKey(symbol));
}
/**
* @notice Gets the transfer amount for a specific token symbol within the configured epoch.
* @param symbol The symbol of the token
* @return uint The transfer amount for the given token.
*/
function tokenMintAmount(string memory symbol) public view override returns (uint256) {
return getUint(_getTokenMintAmountKey(symbol, block.timestamp / 6 hours));
}
/**
* @dev This function is kept around to keep things working for internal
* tokens that were deployed before the token freeze functionality was removed
*/
function allTokensFrozen() external pure override returns (bool) {
return false;
}
/**
* @notice Gets the address of the gateway implementation contract.
* @return address The address of the gateway implementation.
*/
function implementation() public view override returns (address) {
return getAddress(KEY_IMPLEMENTATION);
}
/**
* @notice Gets the address of a specific token using its symbol.
* @param symbol The symbol of the token
* @return address The address of the token associated with the given symbol.
*/
function tokenAddresses(string memory symbol) public view override returns (address) {
return getAddress(_getTokenAddressKey(symbol));
}
/**
* @dev Deprecated. This function is kept around to keep things working for internal tokens that were deployed before the token freeze functionality was removed
*/
function tokenFrozen(string memory) external pure override returns (bool) {
return false;
}
/**
* @notice Checks whether a command with a given command ID has been executed.
* @param commandId The command ID to check
* @return bool True if the command has been executed, false otherwise
*/
function isCommandExecuted(bytes32 commandId) public view override returns (bool) {
return getBool(_getIsCommandExecutedKey(commandId));
}
/**
* @notice Gets the contract ID of the Axelar Gateway.
* @return bytes32 The keccak256 hash of the string 'axelar-gateway'
*/
function contractId() public pure returns (bytes32) {
return keccak256('axelar-gateway');
}
/************************\
|* Governance Functions *|
\************************/
/**
* @notice Transfers the governance role to a new address.
* @param newGovernance The address to transfer the governance role to.
* @dev Only the current governance entity can call this function.
*/
function transferGovernance(address newGovernance) external override onlyGovernance {
if (newGovernance == address(0)) revert InvalidGovernance();
_transferGovernance(newGovernance);
}
/**
* @notice Transfers the mint limiter role to a new address.
* @param newMintLimiter The address to transfer the mint limiter role to.
* @dev Only the current mint limiter or the governance address can call this function.
*/
function transferMintLimiter(address newMintLimiter) external override onlyMintLimiter {
if (newMintLimiter == address(0)) revert InvalidMintLimiter();
_transferMintLimiter(newMintLimiter);
}
/**
* @notice Sets the transfer limits for an array of tokens.
* @param symbols The array of token symbols to set the transfer limits for
* @param limits The array of transfer limits corresponding to the symbols
* @dev Only the mint limiter or the governance address can call this function.
*/
function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external override onlyMintLimiter {
uint256 length = symbols.length;
if (length != limits.length) revert InvalidSetMintLimitsParams();
for (uint256 i; i < length; ++i) {
string memory symbol = symbols[i];
uint256 limit = limits[i];
if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);
_setTokenMintLimit(symbol, limit);
}
}
/**
* @notice Upgrades the contract to a new implementation.
* @param newImplementation The address of the new implementation
* @param newImplementationCodeHash The code hash of the new implementation
* @param setupParams Optional setup params for the new implementation
* @dev Only the governance address can call this function.
*/
function upgrade(
address newImplementation,
bytes32 newImplementationCodeHash,
bytes calldata setupParams
) external override onlyGovernance {
if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();
if (contractId() != IContractIdentifier(newImplementation).contractId()) revert InvalidImplementation();
emit Upgraded(newImplementation);
_setImplementation(newImplementation);
if (setupParams.length != 0) {
// slither-disable-next-line controlled-delegatecall
(bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(IImplementation.setup.selector, setupParams));
if (!success) revert SetupFailed();
}
}
/**********************\
|* External Functions *|
\**********************/
/**
* @notice Sets up the governance and mint limiter roles, and transfers operatorship if necessary.
* This function is called by the proxy during initial deployment, and optionally called during gateway upgrades.
* @param params The encoded parameters containing the governance and mint limiter addresses, as well as the new operator data.
* @dev Not publicly accessible as it's overshadowed in the proxy.
*/
function setup(bytes calldata params) external override(IImplementation, Implementation) onlyProxy {
(address governance_, address mintLimiter_, bytes memory newOperatorsData) = abi.decode(params, (address, address, bytes));
if (governance_ != address(0)) _transferGovernance(governance_);
if (mintLimiter_ != address(0)) _transferMintLimiter(mintLimiter_);
if (newOperatorsData.length != 0) {
emit OperatorshipTransferred(newOperatorsData);
IAxelarAuth(authModule).transferOperatorship(newOperatorsData);
}
}
/**
* @notice Executes a batch of commands signed by the Axelar network. There are a finite set of command types that can be executed.
* @param input The encoded input containing the data for the batch of commands, as well as the proof that verifies the integrity of the data.
* @dev Each command has a corresponding commandID that is guaranteed to be unique from the Axelar network.
* @dev This function allows retrying a commandID if the command initially failed to be processed.
* @dev Ignores unknown commands or duplicate commandIDs.
* @dev Emits an Executed event for successfully executed commands.
*/
// slither-disable-next-line cyclomatic-complexity
function execute(bytes calldata input) external override {
(bytes memory data, bytes memory proof) = abi.decode(input, (bytes, bytes));
bytes32 messageHash = ECDSA.toEthSignedMessageHash(keccak256(data));
// returns true for current operators
// slither-disable-next-line reentrancy-no-eth
bool allowOperatorshipTransfer = IAxelarAuth(authModule).validateProof(messageHash, proof);
uint256 chainId;
bytes32[] memory commandIds;
string[] memory commands;
bytes[] memory params;
(chainId, commandIds, commands, params) = abi.decode(data, (uint256, bytes32[], string[], bytes[]));
if (chainId != block.chainid) revert InvalidChainId();
uint256 commandsLength = commandIds.length;
if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();
for (uint256 i; i < commandsLength; ++i) {
bytes32 commandId = commandIds[i];
// Ignore if duplicate commandId received
if (isCommandExecuted(commandId)) continue;
bytes4 commandSelector;
bytes32 commandHash = keccak256(abi.encodePacked(commands[i]));
if (commandHash == SELECTOR_DEPLOY_TOKEN) {
commandSelector = AxelarGateway.deployToken.selector;
} else if (commandHash == SELECTOR_MINT_TOKEN) {
commandSelector = AxelarGateway.mintToken.selector;
} else if (commandHash == SELECTOR_APPROVE_CONTRACT_CALL) {
commandSelector = AxelarGateway.approveContractCall.selector;
} else if (commandHash == SELECTOR_APPROVE_CONTRACT_CALL_WITH_MINT) {
commandSelector = AxelarGateway.approveContractCallWithMint.selector;
} else if (commandHash == SELECTOR_BURN_TOKEN) {
commandSelector = AxelarGateway.burnToken.selector;
} else if (commandHash == SELECTOR_TRANSFER_OPERATORSHIP) {
if (!allowOperatorshipTransfer) continue;
allowOperatorshipTransfer = false;
commandSelector = AxelarGateway.transferOperatorship.selector;
} else {
// Ignore unknown commands
continue;
}
// Prevent a re-entrancy from executing this command before it can be marked as successful.
_setCommandExecuted(commandId, true);
// slither-disable-next-line calls-loop,reentrancy-no-eth
(bool success, ) = address(this).call(abi.encodeWithSelector(commandSelector, params[i], commandId));
// slither-disable-next-line reentrancy-events
if (success) emit Executed(commandId);
else _setCommandExecuted(commandId, false);
}
}
/******************\
|* Self Functions *|
\******************/
/**
* @notice Deploys a new token or registers an existing token in the gateway contract itself.
* @param params Encoded parameters including the token name, symbol, decimals, cap, token address, and mint limit
* @dev If the token address is not specified, a new token is deployed and registed as InternalBurnableFrom
* @dev If the token address is specified, the token is marked as External.
* @dev Emits a TokenDeployed event with the symbol and token address.
*/
function deployToken(bytes calldata params, bytes32) external onlySelf {
(string memory name, string memory symbol, uint8 decimals, uint256 cap, address tokenAddress, uint256 mintLimit) = abi.decode(
params,
(string, string, uint8, uint256, address, uint256)
);
// Ensure that this symbol has not been taken.
if (tokenAddresses(symbol) != address(0)) revert TokenAlreadyExists(symbol);
_setTokenMintLimit(symbol, mintLimit);
if (tokenAddress == address(0)) {
// If token address is not specified, it indicates a request to deploy one.
bytes32 salt = keccak256(abi.encodePacked(symbol));
_setTokenType(symbol, TokenType.InternalBurnableFrom);
// slither-disable-next-line reentrancy-no-eth,controlled-delegatecall
(bool success, bytes memory data) = tokenDeployer.delegatecall(
abi.encodeWithSelector(ITokenDeployer.deployToken.selector, name, symbol, decimals, cap, salt)
);
if (!success) revert TokenDeployFailed(symbol);
tokenAddress = abi.decode(data, (address));
} else {
// If token address is specified, ensure that there is a contact at the specified address.
if (tokenAddress.code.length == uint256(0)) revert TokenContractDoesNotExist(tokenAddress);
// Mark that this symbol is an external token, which is needed to differentiate between operations on mint and burn.
_setTokenType(symbol, TokenType.External);
}
// slither-disable-next-line reentrancy-events
emit TokenDeployed(symbol, tokenAddress);
_setTokenAddress(symbol, tokenAddress);
}
/**
* @notice Transfers a specific amount of tokens to an account, based on the provided symbol.
* @param params Encoded parameters including the token symbol, recipient address, and amount to mint.
* @dev This function will revert if the token is not registered with the gatewaty.
* @dev If the token type is External, a safe transfer is performed to the recipient account.
* @dev If the token type is Internal (InternalBurnable or InternalBurnableFrom), the mint function is called on the token address.
*/
function mintToken(bytes calldata params, bytes32) external onlySelf {
(string memory symbol, address account, uint256 amount) = abi.decode(params, (string, address, uint256));
_mintToken(symbol, account, amount);
}
/**
* @notice Burns tokens of a given symbol, either through an external deposit handler or a token defined burn method.
* @param params Encoded parameters including the token symbol and a salt value for the deposit handler
*/
function burnToken(bytes calldata params, bytes32) external onlySelf {
(string memory symbol, bytes32 salt) = abi.decode(params, (string, bytes32));
address tokenAddress = tokenAddresses(symbol);
if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
if (_getTokenType(symbol) == TokenType.External) {
address depositHandlerAddress = _getCreate2Address(salt, keccak256(abi.encodePacked(type(DepositHandler).creationCode)));
if (depositHandlerAddress.isContract()) return;
DepositHandler depositHandler = new DepositHandler{ salt: salt }();
(bool success, bytes memory returnData) = depositHandler.execute(
tokenAddress,
abi.encodeWithSelector(IERC20.transfer.selector, address(this), IERC20(tokenAddress).balanceOf(address(depositHandler)))
);
if (!success || (returnData.length != uint256(0) && !abi.decode(returnData, (bool)))) revert BurnFailed(symbol);
// NOTE: `depositHandler` must always be destroyed in the same runtime context that it is deployed.
depositHandler.destroy(address(this));
} else {
IBurnableMintableCappedERC20(tokenAddress).burn(salt);
}
}
/**
* @notice Approves a contract call.
* @param params Encoded parameters including the source chain, source address, contract address, payload hash, transaction hash, and event index
* @param commandId to associate with the approval
*/
function approveContractCall(bytes calldata params, bytes32 commandId) external onlySelf {
(
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
bytes32 sourceTxHash,
uint256 sourceEventIndex
) = abi.decode(params, (string, string, address, bytes32, bytes32, uint256));
_setContractCallApproved(commandId, sourceChain, sourceAddress, contractAddress, payloadHash);
emit ContractCallApproved(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, sourceTxHash, sourceEventIndex);
}
/**
* @notice Approves a contract call with token transfer.
* @param params Encoded parameters including the source chain, source address, contract address, payload hash, token symbol,
* token amount, transaction hash, and event index.
* @param commandId to associate with the approval
*/
function approveContractCallWithMint(bytes calldata params, bytes32 commandId) external onlySelf {
(
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
string memory symbol,
uint256 amount,
bytes32 sourceTxHash,
uint256 sourceEventIndex
) = abi.decode(params, (string, string, address, bytes32, string, uint256, bytes32, uint256));
_setContractCallApprovedWithMint(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount);
emit ContractCallApprovedWithMint(
commandId,
sourceChain,
sourceAddress,
contractAddress,
payloadHash,
symbol,
amount,
sourceTxHash,
sourceEventIndex
);
}
/**
* @notice Transfers operatorship with the provided data by calling the transferOperatorship function on the auth module.
* @param newOperatorsData Encoded data for the new operators
*/
function transferOperatorship(bytes calldata newOperatorsData, bytes32) external onlySelf {
emit OperatorshipTransferred(newOperatorsData);
IAxelarAuth(authModule).transferOperatorship(newOperatorsData);
}
/********************\
|* Internal Methods *|
\********************/
function _mintToken(
string memory symbol,
address account,
uint256 amount
) internal {
address tokenAddress = tokenAddresses(symbol);
if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
_setTokenMintAmount(symbol, tokenMintAmount(symbol) + amount);
if (_getTokenType(symbol) == TokenType.External) {
IERC20(tokenAddress).safeTransfer(account, amount);
} else {
IBurnableMintableCappedERC20(tokenAddress).mint(account, amount);
}
}
/**
* @notice Burns or locks a specific amount of tokens from a sender's account based on the provided symbol.
* @param sender Address of the account from which to burn the tokens
* @param symbol Symbol of the token to burn
* @param amount Amount of tokens to burn
* @dev Depending on the token type (External, InternalBurnableFrom, or InternalBurnable), the function either
* transfers the tokens to gateway contract itself or calls a burn function on the token contract.
*/
function _burnTokenFrom(
address sender,
string memory symbol,
uint256 amount
) internal {
address tokenAddress = tokenAddresses(symbol);
if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
if (amount == 0) revert InvalidAmount();
TokenType tokenType = _getTokenType(symbol);
if (tokenType == TokenType.External) {
IERC20(tokenAddress).safeTransferFrom(sender, address(this), amount);
} else if (tokenType == TokenType.InternalBurnableFrom) {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IBurnableMintableCappedERC20.burnFrom.selector, sender, amount));
} else {
IERC20(tokenAddress).safeTransferFrom(sender, IBurnableMintableCappedERC20(tokenAddress).depositAddress(bytes32(0)), amount);
IBurnableMintableCappedERC20(tokenAddress).burn(bytes32(0));
}
}
/********************\
|* Pure Key Getters *|
\********************/
function _getTokenMintLimitKey(string memory symbol) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_TOKEN_MINT_LIMIT, symbol));
}
function _getTokenMintAmountKey(string memory symbol, uint256 day) internal pure returns (bytes32) {
return keccak256(abi.encode(PREFIX_TOKEN_MINT_AMOUNT, symbol, day));
}
function _getTokenTypeKey(string memory symbol) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_TOKEN_TYPE, symbol));
}
function _getTokenAddressKey(string memory symbol) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_TOKEN_ADDRESS, symbol));
}
function _getIsCommandExecutedKey(bytes32 commandId) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_COMMAND_EXECUTED, commandId));
}
function _getIsContractCallApprovedKey(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash
) internal pure returns (bytes32) {
return keccak256(abi.encode(PREFIX_CONTRACT_CALL_APPROVED, commandId, sourceChain, sourceAddress, contractAddress, payloadHash));
}
function _getIsContractCallApprovedWithMintKey(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
string memory symbol,
uint256 amount
) internal pure returns (bytes32) {
return
keccak256(
abi.encode(
PREFIX_CONTRACT_CALL_APPROVED_WITH_MINT,
commandId,
sourceChain,
sourceAddress,
contractAddress,
payloadHash,
symbol,
amount
)
);
}
/********************\
|* Internal Getters *|
\********************/
function _getCreate2Address(bytes32 salt, bytes32 codeHash) internal view returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, codeHash)))));
}
function _getTokenType(string memory symbol) internal view returns (TokenType) {
return TokenType(getUint(_getTokenTypeKey(symbol)));
}
/********************\
|* Internal Setters *|
\********************/
function _setTokenMintLimit(string memory symbol, uint256 limit) internal {
emit TokenMintLimitUpdated(symbol, limit);
_setUint(_getTokenMintLimitKey(symbol), limit);
}
function _setTokenMintAmount(string memory symbol, uint256 amount) internal {
uint256 limit = tokenMintLimit(symbol);
if (limit > 0 && amount > limit) revert ExceedMintLimit(symbol);
_setUint(_getTokenMintAmountKey(symbol, block.timestamp / 6 hours), amount);
}
function _setTokenType(string memory symbol, TokenType tokenType) internal {
_setUint(_getTokenTypeKey(symbol), uint256(tokenType));
}
function _setTokenAddress(string memory symbol, address tokenAddress) internal {
_setAddress(_getTokenAddressKey(symbol), tokenAddress);
}
function _setCommandExecuted(bytes32 commandId, bool executed) internal {
_setBool(_getIsCommandExecutedKey(commandId), executed);
}
function _setContractCallApproved(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash
) internal {
_setBool(_getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash), true);
}
function _setContractCallApprovedWithMint(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
string memory symbol,
uint256 amount
) internal {
_setBool(
_getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount),
true
);
}
function _setImplementation(address newImplementation) internal {
_setAddress(KEY_IMPLEMENTATION, newImplementation);
}
function _transferGovernance(address newGovernance) internal {
emit GovernanceTransferred(getAddress(KEY_GOVERNANCE), newGovernance);
_setAddress(KEY_GOVERNANCE, newGovernance);
}
function _transferMintLimiter(address newMintLimiter) internal {
emit MintLimiterTransferred(getAddress(KEY_MINT_LIMITER), newMintLimiter);
_setAddress(KEY_MINT_LIMITER, newMintLimiter);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract DepositHandler {
error IsLocked();
error NotContract();
uint256 internal constant IS_NOT_LOCKED = uint256(1);
uint256 internal constant IS_LOCKED = uint256(2);
uint256 internal _lockedStatus = IS_NOT_LOCKED;
modifier noReenter() {
if (_lockedStatus == IS_LOCKED) revert IsLocked();
_lockedStatus = IS_LOCKED;
_;
_lockedStatus = IS_NOT_LOCKED;
}
function execute(address callee, bytes calldata data) external noReenter returns (bool success, bytes memory returnData) {
if (callee.code.length == 0) revert NotContract();
(success, returnData) = callee.call(data);
}
// NOTE: The gateway should always destroy the `DepositHandler` in the same runtime context that deploys it.
function destroy(address etherDestination) external noReenter {
selfdestruct(payable(etherDestination));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
error InvalidSignatureLength();
error InvalidS();
error InvalidV();
error InvalidSignature();
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address signer) {
// Check the signature length
if (signature.length != 65) revert InvalidSignatureLength();
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) revert InvalidS();
if (v != 27 && v != 28) revert InvalidV();
// If the signature is valid (and not malleable), return the signer address
if ((signer = ecrecover(hash, v, r, s)) == address(0)) revert InvalidSignature();
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', hash));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) private _uintStorage;
mapping(bytes32 => string) private _stringStorage;
mapping(bytes32 => address) private _addressStorage;
mapping(bytes32 => bytes) private _bytesStorage;
mapping(bytes32 => bool) private _boolStorage;
mapping(bytes32 => int256) private _intStorage;
// *** Getter Methods ***
function getUint(bytes32 key) public view returns (uint256) {
return _uintStorage[key];
}
function getString(bytes32 key) public view returns (string memory) {
return _stringStorage[key];
}
function getAddress(bytes32 key) public view returns (address) {
return _addressStorage[key];
}
function getBytes(bytes32 key) public view returns (bytes memory) {
return _bytesStorage[key];
}
function getBool(bytes32 key) public view returns (bool) {
return _boolStorage[key];
}
function getInt(bytes32 key) public view returns (int256) {
return _intStorage[key];
}
// *** Setter Methods ***
function _setUint(bytes32 key, uint256 value) internal {
_uintStorage[key] = value;
}
function _setString(bytes32 key, string memory value) internal {
_stringStorage[key] = value;
}
function _setAddress(bytes32 key, address value) internal {
_addressStorage[key] = value;
}
function _setBytes(bytes32 key, bytes memory value) internal {
_bytesStorage[key] = value;
}
function _setBool(bytes32 key, bool value) internal {
_boolStorage[key] = value;
}
function _setInt(bytes32 key, int256 value) internal {
_intStorage[key] = value;
}
// *** Delete Methods ***
function _deleteUint(bytes32 key) internal {
delete _uintStorage[key];
}
function _deleteString(bytes32 key) internal {
delete _stringStorage[key];
}
function _deleteAddress(bytes32 key) internal {
delete _addressStorage[key];
}
function _deleteBytes(bytes32 key) internal {
delete _bytesStorage[key];
}
function _deleteBool(bytes32 key) internal {
delete _boolStorage[key];
}
function _deleteInt(bytes32 key) internal {
delete _intStorage[key];
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IOwnable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IOwnable.sol';
interface IAxelarAuth is IOwnable {
function validateProof(bytes32 messageHash, bytes calldata proof) external returns (bool currentOperators);
function transferOperatorship(bytes calldata params) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { IERC20Burn } from './IERC20Burn.sol';
import { IERC20BurnFrom } from './IERC20BurnFrom.sol';
import { IMintableCappedERC20 } from './IMintableCappedERC20.sol';
interface IBurnableMintableCappedERC20 is IERC20Burn, IERC20BurnFrom, IMintableCappedERC20 {
function depositAddress(bytes32 salt) external view returns (address);
function burn(bytes32 salt) external;
function burnFrom(address account, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
error InvalidAccount();
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IERC20Burn {
function burn(bytes32 salt) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IERC20BurnFrom {
function burnFrom(address account, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IERC20Permit {
function DOMAIN_SEPARATOR() external view returns (bytes32);
function nonces(address account) external view returns (uint256);
function permit(
address issuer,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { IERC20 } from './IERC20.sol';
import { IERC20Permit } from './IERC20Permit.sol';
import { IOwnable } from './IOwnable.sol';
interface IMintableCappedERC20 is IERC20, IERC20Permit, IOwnable {
error CapExceeded();
function cap() external view returns (uint256);
function mint(address account, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IOwnable {
error NotOwner();
error InvalidOwner();
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITokenDeployer {
function deployToken(
string calldata name,
string calldata symbol,
uint8 decimals,
uint256 cap,
bytes32 salt
) external returns (address tokenAddress);
}