├── .solhintignore ├── .github ├── CODEOWNERS ├── workflows │ ├── aderyn.yaml │ ├── slither.yaml │ ├── release-snapshot.yaml │ ├── lint.yaml │ ├── test.yaml │ ├── upload-contract-docs.yaml │ ├── conventional-commits.yaml │ └── codecov.yaml └── actions │ ├── nodejs-ci │ └── action.yaml │ ├── setup-tools │ └── action.yaml │ ├── aderyn │ └── action.yaml │ └── upload-docs │ └── action.yaml ├── contracts ├── test │ ├── libs │ │ ├── NoFallback.sol │ │ ├── TestAddressBytes.sol │ │ ├── TestStringStorage.sol │ │ └── LibsTest.sol │ ├── upgradable │ │ ├── FixedImplementation.sol │ │ ├── CallInitProxy.sol │ │ ├── TestInitProxy.sol │ │ ├── InvalidUpgradableTest.sol │ │ ├── TestFixedProxy.sol │ │ ├── TestImplementation.sol │ │ ├── TestFinalProxy.sol │ │ ├── TestUpgradable.sol │ │ ├── TestProxy.sol │ │ ├── InvalidProxyImplementation.sol │ │ ├── InvalidSetupProxyImplementation.sol │ │ ├── ProxyImplementation.sol │ │ └── DifferentProxyImplementation.sol │ ├── governance │ │ ├── Target.sol │ │ ├── TestBaseMultisig.sol │ │ ├── TestInterchainMultisig.sol │ │ ├── TestInterchainGovernance.sol │ │ └── TestBaseWeightedMultisig.sol │ ├── utils │ │ ├── TestOperators.sol │ │ ├── TestPausable.sol │ │ ├── TestReentrancyGuard.sol │ │ ├── TestOwnable.sol │ │ ├── TestMulticall.sol │ │ ├── TestInterchainAddressTracker.sol │ │ ├── TimeLockTest.sol │ │ └── TestRoles.sol │ ├── token │ │ ├── ERC20MintableBurnableInit.sol │ │ └── ERC20MintableBurnable.sol │ ├── gateway │ │ ├── TestBaseAmplifierGateway.sol │ │ └── TestAxelarAmplifierGateway.sol │ ├── deploy │ │ ├── TestCreate3.sol │ │ └── TestCreate2.sol │ ├── mocks │ │ └── MockGatewayValidation.sol │ ├── executable │ │ ├── AxelarExecutableTest.sol │ │ └── AxelarExecutableWithTokenTest.sol │ ├── gas-estimation │ │ └── TestInterchainGasEstimation.sol │ ├── gmp │ │ ├── DestinationChainReceiver.sol │ │ ├── SourceChainSender.sol │ │ ├── DestinationChainTokenSwapper.sol │ │ ├── SourceChainSwapCaller.sol │ │ ├── ExpressExecutableTest.sol │ │ ├── DestinationChainSwapExecutable.sol │ │ ├── DestinationChainSwapExpressDisabled.sol │ │ ├── ExecutableSample.sol │ │ ├── DestinationChainSwapExpress.sol │ │ └── ValuedExpressExecutableTest.sol │ └── express │ │ ├── TestAxelarExpressExecutable.sol │ │ └── TestAxelarValuedExpressExecutable.sol ├── interfaces │ ├── ICaller.sol │ ├── IImplementation.sol │ ├── IERC20MintableBurnable.sol │ ├── IReentrancyGuard.sol │ ├── IDeploy.sol │ ├── IInitProxy.sol │ ├── IFinalProxy.sol │ ├── IContractIdentifier.sol │ ├── IProxy.sol │ ├── IAxelarAmplifierAuth.sol │ ├── IPausable.sol │ ├── IUpgradable.sol │ ├── IMultisig.sol │ ├── IMulticall.sol │ ├── IContractExecutor.sol │ ├── IAxelarAmplifierGatewayAuth.sol │ ├── ITimeLock.sol │ ├── IAxelarValuedExpressExecutable.sol │ ├── IRolesBase.sol │ ├── IAxelarValuedExpressExecutableWithToken.sol │ ├── IGovernable.sol │ ├── IAxelarExecutableWithToken.sol │ ├── IInterchainAddressTracker.sol │ ├── IAxelarExecutable.sol │ ├── IOwnable.sol │ ├── IOperators.sol │ ├── IInterchainGasEstimation.sol │ ├── IDeployer.sol │ ├── IAxelarAmplifierGateway.sol │ ├── IBaseWeightedMultisig.sol │ ├── IInterchainTransfer.sol │ ├── IBaseMultisig.sol │ ├── IAxelarExpressExecutable.sol │ ├── IAxelarServiceGovernance.sol │ ├── IERC20.sol │ ├── IRoles.sol │ └── IAxelarExpressExecutableWithToken.sol ├── libs │ ├── ContractAddress.sol │ ├── StringStorage.sol │ ├── SafeNativeTransfer.sol │ ├── AddressBytes.sol │ ├── Bytes32String.sol │ ├── AddressString.sol │ ├── SafeTransfer.sol │ └── ECDSA.sol ├── gateway │ ├── AxelarAmplifierGatewayProxy.sol │ └── README.md ├── deploy │ ├── ConstAddressDeployer.sol │ ├── CreateDeploy.sol │ ├── Create2Deployer.sol │ ├── Create3Deployer.sol │ ├── Create3Address.sol │ ├── Create3.sol │ └── Create2.sol ├── utils │ ├── Caller.sol │ ├── Multicall.sol │ ├── ReentrancyGuard.sol │ ├── Pausable.sol │ └── Operators.sol ├── types │ ├── AmplifierGatewayTypes.sol │ ├── WeightedMultisigTypes.sol │ └── GasEstimationTypes.sol ├── upgradable │ ├── Implementation.sol │ ├── Proxy.sol │ ├── BaseProxy.sol │ ├── InitProxy.sol │ └── FixedProxy.sol ├── governance │ ├── Multisig.sol │ └── DESIGN.md └── executable │ └── AxelarExecutable.sol ├── .eslintrc ├── .solcover.js ├── slither.config.json ├── .changeset ├── config.json └── README.md ├── .solhint.json ├── scripts ├── constAddressDeployer.js ├── flatten-contracts.sh └── generateOverviewDocs.js ├── .prettierrc ├── test ├── utils │ ├── ReentrancyGuard.js │ ├── Implementation.js │ ├── AddressBytes.js │ ├── StringStorage.js │ ├── Pausable.js │ └── Multicall.js └── deploy │ ├── Create3.js │ └── Create2.js ├── CHANGELOG.md ├── LICENSE ├── index.js ├── .gitignore ├── package.json └── README.md /.solhintignore: -------------------------------------------------------------------------------- 1 | contracts/upgradable/Proxy.sol 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | * @axelarnetwork/protocol 5 | -------------------------------------------------------------------------------- /contracts/test/libs/NoFallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract NoFallback { 6 | // No fallback function 7 | } 8 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "richardpringle", 3 | "env": { 4 | "es2020": true, 5 | "mocha": true 6 | }, 7 | "rules": { 8 | "no-unused-expressions": "off" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: ['test'], 3 | mocha: { 4 | grep: '@skip-on-coverage', // Add to test description to skip coverage from being run some tests, such as bytecode checks 5 | invert: true, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /contracts/interfaces/ICaller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface ICaller { 6 | error InvalidContract(address target); 7 | error InsufficientBalance(); 8 | error ExecutionFailed(); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/test/upgradable/FixedImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract FixedImplementation { 6 | uint256 public value; 7 | 8 | function set(uint256 _val) external { 9 | value = _val; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "detectors_to_exclude": "assembly,low-level-calls,solc-version,missing-zero-check,timestamp,calls-loop", 3 | "filter_paths": "(contracts/test/|contracts/gateway/AxelarAmplifierGatewayProxy.sol|contracts/libs/SafeNativeTransfer.sol|node_modules/)" 4 | } 5 | -------------------------------------------------------------------------------- /contracts/test/governance/Target.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract Target { 6 | event TargetCalled(); 7 | 8 | function callTarget() external payable { 9 | emit TargetCalled(); 10 | } 11 | 12 | receive() external payable {} 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interfaces/IImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IContractIdentifier } from './IContractIdentifier.sol'; 6 | 7 | interface IImplementation is IContractIdentifier { 8 | error NotProxy(); 9 | 10 | function setup(bytes calldata data) external; 11 | } 12 | -------------------------------------------------------------------------------- /contracts/test/utils/TestOperators.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract TestOperators { 6 | event NumAdded(uint256 num); 7 | 8 | function setNum(uint256 num) external payable returns (bool) { 9 | emit NumAdded(num); 10 | 11 | return true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.3/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /contracts/test/upgradable/CallInitProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { InitProxy } from '../../upgradable/InitProxy.sol'; 6 | 7 | contract CallInitProxy is InitProxy { 8 | function getContractId() external pure returns (bytes32) { 9 | return contractId(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20MintableBurnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IERC20 } from './IERC20.sol'; 6 | 7 | interface IERC20MintableBurnable is IERC20 { 8 | function mint(address to, uint256 amount) external; 9 | 10 | function burn(address from, uint256 amount) external; 11 | } 12 | -------------------------------------------------------------------------------- /contracts/interfaces/IReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title ReentrancyGuard 7 | * @notice This contract provides a mechanism to halt the execution of specific functions 8 | * if a pause condition is activated. 9 | */ 10 | interface IReentrancyGuard { 11 | error ReentrantCall(); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/test/upgradable/TestInitProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { InitProxy } from '../../upgradable/InitProxy.sol'; 6 | 7 | contract TestInitProxy is InitProxy { 8 | function contractId() internal pure override returns (bytes32) { 9 | return keccak256('proxy-implementation'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/interfaces/IDeploy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IDeploy Interface 7 | * @notice This interface defines the errors for a contract that is responsible for deploying new contracts. 8 | */ 9 | interface IDeploy { 10 | error EmptyBytecode(); 11 | error AlreadyDeployed(); 12 | error DeployFailed(); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interfaces/IInitProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IProxy } from './IProxy.sol'; 6 | 7 | // General interface for upgradable contracts 8 | interface IInitProxy is IProxy { 9 | function init( 10 | address implementationAddress, 11 | address newOwner, 12 | bytes memory params 13 | ) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interfaces/IFinalProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IProxy } from './IProxy.sol'; 6 | 7 | // General interface for upgradable contracts 8 | interface IFinalProxy is IProxy { 9 | function isFinal() external view returns (bool); 10 | 11 | function finalUpgrade(bytes memory bytecode, bytes calldata setupParams) external returns (address); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/test/upgradable/InvalidUpgradableTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Upgradable } from '../../upgradable/Upgradable.sol'; 6 | 7 | contract InvalidUpgradableTest is Upgradable { 8 | function contractId() external pure override returns (bytes32) { 9 | return keccak256('invalid'); 10 | } 11 | 12 | function _setup(bytes calldata data) internal override {} 13 | } 14 | -------------------------------------------------------------------------------- /contracts/test/governance/TestBaseMultisig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { BaseMultisig } from '../../governance/BaseMultisig.sol'; 6 | 7 | contract TestBaseMultisig is BaseMultisig { 8 | constructor(address[] memory accounts, uint256 threshold) BaseMultisig(accounts, threshold) {} 9 | 10 | function resetVotes(bytes32 topic) external { 11 | _resetSignerVotes(topic); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/test/upgradable/TestFixedProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { FixedProxy } from '../../upgradable/FixedProxy.sol'; 6 | 7 | contract TestFixedProxy is FixedProxy { 8 | constructor(address implementationAddress) FixedProxy(implementationAddress) {} 9 | 10 | function contractId() internal pure override returns (bytes32) { 11 | return keccak256('proxy-implementation'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interfaces/IContractIdentifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | // General interface for upgradable contracts 6 | interface IContractIdentifier { 7 | /** 8 | * @notice Returns the contract ID. It can be used as a check during upgrades. 9 | * @dev Meant to be overridden in derived contracts. 10 | * @return bytes32 The contract ID 11 | */ 12 | function contractId() external pure returns (bytes32); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interfaces/IProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | // General interface for upgradable contracts 6 | interface IProxy { 7 | error InvalidOwner(); 8 | error InvalidImplementation(); 9 | error SetupFailed(); 10 | error NotOwner(); 11 | error AlreadyInitialized(); 12 | 13 | function implementation() external view returns (address); 14 | 15 | function setup(bytes calldata setupParams) external; 16 | } 17 | -------------------------------------------------------------------------------- /contracts/test/token/ERC20MintableBurnableInit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { ERC20MintableBurnable } from './ERC20MintableBurnable.sol'; 6 | 7 | contract ERC20MintableBurnableInit is ERC20MintableBurnable { 8 | constructor(uint8 decimals) ERC20MintableBurnable('', '', decimals) {} 9 | 10 | function init(string memory name_, string memory symbol_) external { 11 | name = name_; 12 | symbol = symbol_; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/test/gateway/TestBaseAmplifierGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { BaseAmplifierGateway } from '../../gateway/BaseAmplifierGateway.sol'; 6 | 7 | contract TestBaseAmplifierGateway is BaseAmplifierGateway { 8 | constructor() BaseAmplifierGateway() { 9 | if (BASE_AMPLIFIER_GATEWAY_SLOT != bytes32(uint256(keccak256('BaseAmplifierGateway.Slot')) - 1)) { 10 | revert('BaseAmplifierGateway.Slot'); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarAmplifierAuth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IBaseWeightedMultisig } from './IBaseWeightedMultisig.sol'; 6 | import { IAxelarAmplifierGatewayAuth } from './IAxelarAmplifierGatewayAuth.sol'; 7 | 8 | /** 9 | * @title IAxelarAmplifierAuth Interface 10 | * @notice This interface defines the functions that the Axelar Amplifier Auth contract supports 11 | */ 12 | interface IAxelarAmplifierAuth is IBaseWeightedMultisig, IAxelarAmplifierGatewayAuth { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/aderyn.yaml: -------------------------------------------------------------------------------- 1 | name: Aderyn Static Analysis 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | aderyn: 14 | runs-on: blacksmith-2vcpu-ubuntu-2204 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Run Aderyn Static Analysis 21 | uses: ./.github/actions/aderyn 22 | with: 23 | rust_toolchain: stable 24 | -------------------------------------------------------------------------------- /contracts/test/upgradable/TestImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Implementation } from '../../upgradable/Implementation.sol'; 6 | 7 | contract TestImplementation is Implementation { 8 | uint256 public val; 9 | 10 | function setup(bytes calldata params) external override onlyProxy { 11 | val = abi.decode(params, (uint256)); 12 | } 13 | 14 | function contractId() external pure override returns (bytes32) { 15 | return keccak256('TestImplementation'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/test/deploy/TestCreate3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Create3 } from '../../deploy/Create3.sol'; 6 | 7 | contract TestCreate3 is Create3 { 8 | event Deployed(address addr); 9 | 10 | function deploy(bytes memory code, bytes32 salt) public payable returns (address addr) { 11 | addr = _create3(code, salt); 12 | 13 | emit Deployed(addr); 14 | } 15 | 16 | function deployedAddress(bytes32 salt) public view returns (address addr) { 17 | addr = _create3Address(salt); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test/libs/TestAddressBytes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AddressBytes } from '../../libs/AddressBytes.sol'; 6 | 7 | contract TestAddressBytes { 8 | using AddressBytes for address; 9 | using AddressBytes for bytes; 10 | 11 | function toAddress(bytes memory bytesAddress) external pure returns (address addr) { 12 | return bytesAddress.toAddress(); 13 | } 14 | 15 | function toBytes(address addr) external pure returns (bytes memory bytesAddress) { 16 | return addr.toBytes(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/test/libs/TestStringStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { StringStorage } from '../../libs/StringStorage.sol'; 6 | 7 | contract TestStringStorage { 8 | function set(bytes32 slot, string calldata value) external { 9 | StringStorage.set(slot, value); 10 | } 11 | 12 | function get(bytes32 slot) external view returns (string memory value) { 13 | value = StringStorage.get(slot); 14 | } 15 | 16 | function clear(bytes32 slot) external { 17 | StringStorage.clear(slot); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test/utils/TestPausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Pausable } from '../../utils/Pausable.sol'; 6 | 7 | contract TestPausable is Pausable { 8 | event TestEvent(); 9 | 10 | function pause() external { 11 | _pause(); 12 | } 13 | 14 | function unpause() external { 15 | _unpause(); 16 | } 17 | 18 | function testPaused() external whenNotPaused { 19 | emit TestEvent(); 20 | } 21 | 22 | function testNotPaused() external whenPaused { 23 | emit TestEvent(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/test/deploy/TestCreate2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Create2 } from '../../deploy/Create2.sol'; 6 | 7 | contract TestCreate2 is Create2 { 8 | event Deployed(address addr); 9 | 10 | function deploy(bytes memory code, bytes32 salt) public payable returns (address addr) { 11 | addr = _create2(code, salt); 12 | 13 | emit Deployed(addr); 14 | } 15 | 16 | function deployedAddress(bytes memory code, bytes32 salt) public view returns (address addr) { 17 | addr = _create2Address(code, salt); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/libs/ContractAddress.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | library ContractAddress { 6 | function isContract(address contractAddress) internal view returns (bool) { 7 | bytes32 existingCodeHash = contractAddress.codehash; 8 | 9 | // https://eips.ethereum.org/EIPS/eip-1052 10 | // keccak256('') == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 11 | return 12 | existingCodeHash != bytes32(0) && 13 | existingCodeHash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/gateway/AxelarAmplifierGatewayProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Proxy } from '../upgradable/Proxy.sol'; 6 | 7 | contract AxelarAmplifierGatewayProxy is Proxy { 8 | bytes32 public constant CONTRACT_ID = keccak256('axelar-amplifier-gateway'); 9 | 10 | constructor( 11 | address implementationAddress, 12 | address owner, 13 | bytes memory setupParams 14 | ) Proxy(implementationAddress, owner, setupParams) {} 15 | 16 | function contractId() internal pure override returns (bytes32) { 17 | return CONTRACT_ID; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/deploy/ConstAddressDeployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Create2Deployer } from './Create2Deployer.sol'; 6 | 7 | /** 8 | * @title ConstAddressDeployer Contract 9 | * @dev This contract is deprecated in favour of Create2Deployer and exported for backwards compatibility. 10 | * @notice This contract is responsible for deploying and initializing new contracts using the `CREATE2` method 11 | * which computes the deployed contract address based on the bytecode, deployer address, and deployment salt. 12 | */ 13 | contract ConstAddressDeployer is Create2Deployer { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": [], 4 | "rules": { 5 | "quotes": ["error", "single"], 6 | "compiler-version": ["off"], 7 | "func-visibility": [ 8 | "warn", 9 | { 10 | "ignoreConstructors": true 11 | } 12 | ], 13 | "no-inline-assembly": "off", 14 | "no-empty-blocks": "off", 15 | "avoid-low-level-calls": "off", 16 | "not-rely-on-time": "off", 17 | "no-complex-fallback": "off", 18 | "immutable-vars-naming": ["warn", 19 | { 20 | "immutablesAsConstants": false 21 | } 22 | ], 23 | "one-contract-per-file": "off" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/test/mocks/MockGatewayValidation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract MockGatewayValidation { 6 | function validateContractCall( 7 | bytes32, 8 | string calldata, 9 | string calldata, 10 | bytes32 11 | ) external pure returns (bool) { 12 | return true; 13 | } 14 | 15 | function validateContractCallAndMint( 16 | bytes32, 17 | string calldata, 18 | string calldata, 19 | bytes32, 20 | string calldata, 21 | uint256 22 | ) external pure returns (bool) { 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/constAddressDeployer.js: -------------------------------------------------------------------------------- 1 | const { 2 | estimateGasForCreate2Deploy, 3 | estimateGasForCreate2DeployAndInit, 4 | create2DeployContract, 5 | create2DeployAndInitContract, 6 | getCreate2Address, 7 | } = require('./create2Deployer'); 8 | 9 | /** 10 | * @dev These methods are deprecated and exported for backwards compatibility. 11 | */ 12 | module.exports = { 13 | estimateGasForDeploy: estimateGasForCreate2Deploy, 14 | estimateGasForDeployAndInit: estimateGasForCreate2DeployAndInit, 15 | deployContractConstant: create2DeployContract, 16 | deployAndInitContractConstant: create2DeployAndInitContract, 17 | predictContractConstant: getCreate2Address, 18 | }; 19 | -------------------------------------------------------------------------------- /.github/workflows/slither.yaml: -------------------------------------------------------------------------------- 1 | name: Slither Static Analysis 2 | 3 | on: 4 | - pull_request 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | slither: 12 | runs-on: blacksmith-2vcpu-ubuntu-2204 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup tools 18 | uses: ./.github/actions/setup-tools 19 | with: 20 | install-nodejs: 'true' 21 | 22 | - name: Run Slither 23 | uses: crytic/slither-action@v0.3.2 24 | env: 25 | NO_OVERRIDES: true 26 | with: 27 | node-version: 18 28 | slither-version: 0.10.1 29 | -------------------------------------------------------------------------------- /contracts/interfaces/IPausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title Pausable 7 | * @notice This contract provides a mechanism to halt the execution of specific functions 8 | * if a pause condition is activated. 9 | */ 10 | interface IPausable { 11 | event Paused(address indexed account); 12 | event Unpaused(address indexed account); 13 | 14 | error Pause(); 15 | error NotPaused(); 16 | 17 | /** 18 | * @notice Check if the contract is paused 19 | * @return paused A boolean representing the pause status. True if paused, false otherwise. 20 | */ 21 | function paused() external view returns (bool); 22 | } 23 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "tabWidth": 4, 5 | "useTabs": false, 6 | "bracketSpacing": true, 7 | "plugins": ["prettier-plugin-solidity"], 8 | "overrides": [ 9 | { 10 | "files": "*.sol", 11 | "options": { 12 | "explicitTypes": "always" 13 | } 14 | }, 15 | { 16 | "files": "*.js", 17 | "options": { 18 | "trailingComma": "all" 19 | } 20 | }, 21 | { 22 | "files": "*.json", 23 | "options": { 24 | "tabWidth": 2 25 | } 26 | }, 27 | { 28 | "files": "*.yaml", 29 | "options": { 30 | "tabWidth": 2 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /contracts/test/executable/AxelarExecutableTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExecutable } from '../../executable/AxelarExecutable.sol'; 6 | 7 | contract AxelarExecutableTest is AxelarExecutable { 8 | event Received(uint256 num); 9 | 10 | constructor(address gatewayAddress) AxelarExecutable(gatewayAddress) {} 11 | 12 | function _execute( 13 | bytes32, /*commandId*/ 14 | string calldata, /*sourceChain*/ 15 | string calldata, /*sourceAddress*/ 16 | bytes calldata payload 17 | ) internal override { 18 | uint256 num = abi.decode(payload, (uint256)); 19 | emit Received(num); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/test/gas-estimation/TestInterchainGasEstimation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { GasInfo } from '../../types/GasEstimationTypes.sol'; 6 | import { InterchainGasEstimation } from '../../gas-estimation/InterchainGasEstimation.sol'; 7 | 8 | contract TestInterchainGasEstimation is InterchainGasEstimation { 9 | constructor() { 10 | if (GAS_SERVICE_SLOT != bytes32(uint256(keccak256('GasEstimate.Slot')) - 1)) { 11 | revert('TestGasEstimate: invalid slot'); 12 | } 13 | } 14 | 15 | function updateGasInfo(string calldata chain, GasInfo calldata info) external { 16 | _setGasInfo(chain, info); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/test/gmp/DestinationChainReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExecutable } from '../../executable/AxelarExecutable.sol'; 6 | 7 | contract DestinationChainReceiver is AxelarExecutable { 8 | event Received(uint256 num); 9 | 10 | constructor(address gatewayAddress) AxelarExecutable(gatewayAddress) {} 11 | 12 | function _execute( 13 | bytes32, /*commandId*/ 14 | string calldata, /*sourceChain*/ 15 | string calldata, /*sourceAddress*/ 16 | bytes calldata payload 17 | ) internal override { 18 | uint256 num = abi.decode(payload, (uint256)); 19 | emit Received(num); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/test/gateway/TestAxelarAmplifierGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarAmplifierGateway } from '../../gateway/AxelarAmplifierGateway.sol'; 6 | 7 | contract TestAxelarAmplifierGateway is AxelarAmplifierGateway { 8 | constructor( 9 | uint256 previousSignersRetention_, 10 | bytes32 domainSeparator_, 11 | uint256 minimumRotationDelay_ 12 | ) AxelarAmplifierGateway(previousSignersRetention_, domainSeparator_, minimumRotationDelay_) { 13 | if (AXELAR_AMPLIFIER_GATEWAY_SLOT != bytes32(uint256(keccak256('AxelarAmplifierGateway.Slot')) - 1)) { 14 | revert('AxelarAmplifierGateway.Slot'); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/test/upgradable/TestFinalProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { FinalProxy } from '../../upgradable/FinalProxy.sol'; 6 | 7 | contract TestFinalProxy is FinalProxy { 8 | constructor( 9 | address implementationAddress, 10 | address owner, 11 | bytes memory setupParams 12 | ) FinalProxy(implementationAddress, owner, setupParams) { 13 | if (FINAL_IMPLEMENTATION_SALT != bytes32(uint256(keccak256('final-implementation')) - 1)) 14 | revert('invalid final salt'); 15 | } 16 | 17 | function contractId() internal pure override returns (bytes32) { 18 | return keccak256('proxy-implementation'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/test/utils/TestReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { ReentrancyGuard } from '../../utils/ReentrancyGuard.sol'; 6 | 7 | contract TestReentrancyGuard is ReentrancyGuard { 8 | uint256 public value; 9 | 10 | constructor() { 11 | require(ENTERED_SLOT == uint256(keccak256('ReentrancyGuard:entered')) - 1, 'invalid constant'); 12 | } 13 | 14 | function testFunction() external noReEntrancy { 15 | value = 1; 16 | this.callback(); 17 | value = 2; 18 | } 19 | 20 | function testFunction2() external noReEntrancy { 21 | value = 2; 22 | } 23 | 24 | function callback() external noReEntrancy {} 25 | } 26 | -------------------------------------------------------------------------------- /contracts/interfaces/IUpgradable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IOwnable } from './IOwnable.sol'; 6 | import { IImplementation } from './IImplementation.sol'; 7 | 8 | // General interface for upgradable contracts 9 | interface IUpgradable is IOwnable, IImplementation { 10 | error InvalidCodeHash(); 11 | error InvalidImplementation(); 12 | error SetupFailed(); 13 | 14 | event Upgraded(address indexed newImplementation); 15 | 16 | function implementation() external view returns (address); 17 | 18 | function upgrade( 19 | address newImplementation, 20 | bytes32 newImplementationCodeHash, 21 | bytes calldata params 22 | ) external; 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/utils/TestOwnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Ownable } from '../../utils/Ownable.sol'; 6 | 7 | contract TestOwnable is Ownable { 8 | uint256 public num; 9 | 10 | event NumAdded(uint256 num); 11 | 12 | constructor(address _owner) Ownable(_owner) { 13 | if (_OWNER_SLOT != keccak256('owner')) revert('invalid owner slot'); 14 | if (_OWNERSHIP_TRANSFER_SLOT != keccak256('ownership-transfer')) revert('invalid ownership transfer slot'); 15 | } 16 | 17 | function setNum(uint256 _num) external payable onlyOwner returns (bool) { 18 | num = _num; 19 | 20 | emit NumAdded(_num); 21 | 22 | return true; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/actions/nodejs-ci/action.yaml: -------------------------------------------------------------------------------- 1 | name: Install node.js dependencues 2 | description: 'Setup node.js and install dependencies' 3 | 4 | inputs: 5 | node-version: 6 | description: 'The version of node.js CLI to install' 7 | required: true 8 | default: '18' 9 | 10 | runs: 11 | using: 'composite' 12 | 13 | steps: 14 | - name: Setup Node 15 | # TODO: consider while migrating the `setup-node` action later the usage of `blacksmith` actions 16 | uses: useblacksmith/setup-node@v5 17 | with: 18 | node-version: '${{ inputs.node-version }}' 19 | cache: 'npm' 20 | registry-url: 'https://registry.npmjs.org' 21 | 22 | - name: Install dependencies 23 | shell: bash 24 | run: npm ci 25 | -------------------------------------------------------------------------------- /contracts/test/governance/TestInterchainMultisig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { InterchainMultisig } from '../../governance/InterchainMultisig.sol'; 6 | import { WeightedSigners } from '../../types/WeightedMultisigTypes.sol'; 7 | 8 | contract TestInterchainMultisig is InterchainMultisig { 9 | constructor( 10 | string memory chainName, 11 | bytes32 domainSeparator_, 12 | WeightedSigners memory weightedSigners 13 | ) InterchainMultisig(chainName, domainSeparator_, weightedSigners) { 14 | if (INTERCHAIN_MULTISIG_SLOT != bytes32(uint256(keccak256('InterchainMultisig.Slot')) - 1)) { 15 | revert('InterchainMultisig.Slot'); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/actions/setup-tools/action.yaml: -------------------------------------------------------------------------------- 1 | name: Setup tools 2 | description: 'Setup tools for repo workflows' 3 | 4 | inputs: 5 | install-nodejs: 6 | description: "Install nodejs and setup repo's npm package" 7 | required: false 8 | default: 'false' 9 | 10 | runs: 11 | using: 'composite' 12 | steps: 13 | # This Action bundles other reusable actions in case needed. 14 | 15 | # Install Node.js and its dependencies 16 | - name: Setup node.js and install dependencies 17 | if: inputs.install-nodejs == 'true' 18 | uses: ./.github/actions/nodejs-ci 19 | with: 20 | node-version: '18' # Version is hardcoded across all other steps to ensure stability of the code, although the `package.json` has `>=18` engine version 21 | -------------------------------------------------------------------------------- /contracts/test/upgradable/TestUpgradable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Upgradable } from '../../upgradable/Upgradable.sol'; 6 | 7 | contract TestUpgradable is Upgradable { 8 | uint256 public num; 9 | 10 | constructor() Upgradable() { 11 | if (_IMPLEMENTATION_SLOT != bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)) { 12 | revert('invalid implementation slot'); 13 | } 14 | } 15 | 16 | function _setup(bytes calldata data) internal override { 17 | num = abi.decode(data, (uint256)); 18 | } 19 | 20 | function contractId() external pure override returns (bytes32) { 21 | return keccak256('proxy-implementation'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/libs/StringStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | library StringStorage { 6 | struct Wrapper { 7 | string value; 8 | } 9 | 10 | function set(bytes32 slot, string memory value) internal { 11 | _getStorageStruct(slot).value = value; 12 | } 13 | 14 | function get(bytes32 slot) internal view returns (string memory value) { 15 | value = _getStorageStruct(slot).value; 16 | } 17 | 18 | function clear(bytes32 slot) internal { 19 | delete _getStorageStruct(slot).value; 20 | } 21 | 22 | function _getStorageStruct(bytes32 slot) internal pure returns (Wrapper storage wrapper) { 23 | assembly { 24 | wrapper.slot := slot 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/test/upgradable/TestProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Proxy } from '../../upgradable/Proxy.sol'; 6 | 7 | contract TestProxy is Proxy { 8 | constructor( 9 | address implementationAddress, 10 | address owner, 11 | bytes memory setupParams 12 | ) Proxy(implementationAddress, owner, setupParams) { 13 | if (_IMPLEMENTATION_SLOT != bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)) 14 | revert('invalid implementation slot'); 15 | 16 | if (_OWNER_SLOT != keccak256('owner')) revert('invalid owner slot'); 17 | } 18 | 19 | function contractId() internal pure override returns (bytes32) { 20 | return keccak256('proxy-implementation'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/deploy/CreateDeploy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title CreateDeploy Contract 7 | * @notice This contract deploys new contracts using the `CREATE` opcode and is used as part of 8 | * the `CREATE3` deployment method. 9 | */ 10 | contract CreateDeploy { 11 | /** 12 | * @dev Deploys a new contract with the specified bytecode using the `CREATE` opcode. 13 | * @param bytecode The bytecode of the contract to be deployed 14 | */ 15 | // slither-disable-next-line locked-ether 16 | function deploy(bytes memory bytecode) external payable { 17 | assembly { 18 | if iszero(create(0, add(bytecode, 32), mload(bytecode))) { 19 | revert(0, 0) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/token/ERC20MintableBurnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IERC20MintableBurnable } from '../../interfaces/IERC20MintableBurnable.sol'; 6 | import { ERC20 } from './ERC20.sol'; 7 | 8 | contract ERC20MintableBurnable is ERC20, IERC20MintableBurnable { 9 | constructor( 10 | string memory name_, 11 | string memory symbol_, 12 | uint8 decimals_ 13 | ) ERC20(name_, symbol_, decimals_) {} 14 | 15 | function burn(address account, uint256 amount) external { 16 | _approve(account, msg.sender, allowance[account][msg.sender] - amount); 17 | _burn(account, amount); 18 | } 19 | 20 | function mint(address account, uint256 amount) external { 21 | _mint(account, amount); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/gateway/README.md: -------------------------------------------------------------------------------- 1 | # Axelar Amplifier Gateway 2 | 3 | Axelar Amplifier Gateway is a smart contract that lives on the external chain that is connecting to the Axelar Network. It faciliates sending and receiving of cross-chain messages to other chains via the Axelar Network. `AxelarAmplifierGateway` is the EVM reference implementation of the external gateway. 4 | 5 | Please see the [INTEGRATION.md](INTEGRATION.md) for more details on how to implement and integrate the external gateway contract for a given chain. 6 | 7 | ## Documentation & Build 8 | 9 | Please refer to [README.md](../../README.md) for documentation and build instructions. 10 | 11 | ## Deployment 12 | 13 | The official `AxelarAmplifierGateway` contract deployment scripts are located in this [repo](https://github.com/axelarnetwork/axelar-contract-deployments/tree/main/evm#evm-deployment-scripts). 14 | -------------------------------------------------------------------------------- /contracts/deploy/Create2Deployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Deployer } from './Deployer.sol'; 6 | import { Create2 } from './Create2.sol'; 7 | 8 | /** 9 | * @title Create2Deployer Contract 10 | * @notice This contract is responsible for deploying and initializing new contracts using the `CREATE2` method 11 | * which computes the deployed contract address based on the bytecode, deployer address, and deployment salt. 12 | */ 13 | contract Create2Deployer is Create2, Deployer { 14 | function _deploy(bytes memory bytecode, bytes32 deploySalt) internal override returns (address) { 15 | return _create2(bytecode, deploySalt); 16 | } 17 | 18 | function _deployedAddress(bytes memory bytecode, bytes32 deploySalt) internal view override returns (address) { 19 | return _create2Address(bytecode, deploySalt); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/interfaces/IMultisig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IBaseMultisig } from './IBaseMultisig.sol'; 6 | import { ICaller } from './ICaller.sol'; 7 | import { IContractExecutor } from './IContractExecutor.sol'; 8 | 9 | /** 10 | * @title IMultisig Interface 11 | * @notice This interface extends IMultisigBase by adding an execute function for multisignature transactions. 12 | */ 13 | interface IMultisig is ICaller, IContractExecutor, IBaseMultisig { 14 | /** 15 | * @notice Withdraws native token from the contract 16 | * @param recipient The address to send the native token to 17 | * @param amount The amount of native token to send 18 | * @dev This function is only callable by the contract itself after passing according proposal 19 | */ 20 | function withdraw(address recipient, uint256 amount) external; 21 | } 22 | -------------------------------------------------------------------------------- /contracts/interfaces/IMulticall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IMulticall 7 | * @notice This contract is a multi-functional smart contract which allows for multiple 8 | * contract calls in a single transaction. 9 | */ 10 | interface IMulticall { 11 | error MulticallFailed(); 12 | 13 | /** 14 | * @notice Performs multiple delegate calls and returns the results of all calls as an array 15 | * @dev This function requires that the contract has sufficient balance for the delegate calls. 16 | * If any of the calls fail, the function will revert with the failure message. 17 | * @param data An array of encoded function calls 18 | * @return results An bytes array with the return data of each function call 19 | */ 20 | function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); 21 | } 22 | -------------------------------------------------------------------------------- /test/utils/ReentrancyGuard.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const { ethers } = require('hardhat'); 5 | const { deployContract } = require('../utils'); 6 | const { expect } = chai; 7 | 8 | describe('ReentrancyGuard', () => { 9 | let guard; 10 | let ownerWallet; 11 | 12 | before(async () => { 13 | const wallets = await ethers.getSigners(); 14 | ownerWallet = wallets[0]; 15 | 16 | guard = await deployContract(ownerWallet, 'TestReentrancyGuard'); 17 | }); 18 | 19 | it('Should revert on reentrancy', async function () { 20 | await expect(guard.testFunction()).to.be.revertedWithCustomError(guard, 'ReentrantCall'); 21 | }); 22 | 23 | it('Should set internal state back to NOT_ENTERED after noReEntrancy modified contract call', async function () { 24 | await expect(guard.testFunction2()).to.not.be.reverted; 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /contracts/deploy/Create3Deployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Deployer } from './Deployer.sol'; 6 | import { Create3 } from './Create3.sol'; 7 | 8 | /** 9 | * @title Create3Deployer Contract 10 | * @notice This contract is responsible for deploying and initializing new contracts using the `CREATE3` method 11 | * which computes the deployed contract address based on the deployer address and deployment salt. 12 | */ 13 | contract Create3Deployer is Create3, Deployer { 14 | function _deploy(bytes memory bytecode, bytes32 deploySalt) internal override returns (address) { 15 | return _create3(bytecode, deploySalt); 16 | } 17 | 18 | function _deployedAddress( 19 | bytes memory, /* bytecode */ 20 | bytes32 deploySalt 21 | ) internal view override returns (address) { 22 | return _create3Address(deploySalt); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/interfaces/IContractExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IContractExecutor Interface 7 | * @notice This interface defines the execute function used to interact with external contracts. 8 | */ 9 | interface IContractExecutor { 10 | /** 11 | * @notice Executes a call to an external contract. 12 | * @dev Execution logic is left up to the implementation. 13 | * @param target The address of the contract to be called 14 | * @param callData The calldata to be sent 15 | * @param nativeValue The amount of native token (e.g., Ether) to be sent along with the call 16 | * @return bytes The data returned from the executed call 17 | */ 18 | function executeContract( 19 | address target, 20 | bytes calldata callData, 21 | uint256 nativeValue 22 | ) external payable returns (bytes memory); 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/release-snapshot.yaml: -------------------------------------------------------------------------------- 1 | # This workflow publishes snapshot releases as an npm package. These releases aren't supposed to be reusable in `production` environments. 2 | # It's triggered manually in case a snapshot release would be needed for testing purposes. 3 | name: Release Snapshot 4 | 5 | on: 6 | workflow_dispatch: 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | release-snapshot: 12 | name: Release Snapshot 13 | runs-on: blacksmith-2vcpu-ubuntu-2204 14 | steps: 15 | - name: Checkout Repo 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup tools 19 | uses: ./.github/actions/setup-tools 20 | with: 21 | install-nodejs: 'true' 22 | 23 | - name: Build and Publish a Snapshot to NPM 24 | run: | 25 | npm run release-snapshot 26 | env: 27 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 28 | -------------------------------------------------------------------------------- /contracts/test/gmp/SourceChainSender.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarGateway } from '../../interfaces/IAxelarGateway.sol'; 6 | 7 | contract SourceChainSender { 8 | IAxelarGateway public gateway; 9 | string public destinationChain; 10 | string public executableAddress; 11 | 12 | event Sent(uint256 num); 13 | 14 | constructor( 15 | address gateway_, 16 | string memory destinationChain_, 17 | string memory executableAddress_ 18 | ) { 19 | gateway = IAxelarGateway(gateway_); 20 | destinationChain = destinationChain_; 21 | executableAddress = executableAddress_; 22 | } 23 | 24 | function send(uint256 num) external { 25 | bytes memory payload = abi.encode(num); 26 | gateway.callContract(destinationChain, executableAddress, payload); 27 | emit Sent(num); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/libs/SafeNativeTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | error NativeTransferFailed(); 6 | 7 | /* 8 | * @title SafeNativeTransfer 9 | * @dev This library is used for performing safe native value transfers in Solidity by utilizing inline assembly. 10 | */ 11 | library SafeNativeTransfer { 12 | /* 13 | * @notice Perform a native transfer to a given address. 14 | * @param receiver The recipient address to which the amount will be sent. 15 | * @param amount The amount of native value to send. 16 | * @throws NativeTransferFailed error if transfer is not successful. 17 | */ 18 | function safeNativeTransfer(address receiver, uint256 amount) internal { 19 | bool success; 20 | 21 | assembly { 22 | success := call(gas(), receiver, amount, 0, 0, 0, 0) 23 | } 24 | 25 | if (!success) revert NativeTransferFailed(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | lint: 12 | runs-on: blacksmith-2vcpu-ubuntu-2204 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup tools 19 | uses: ./.github/actions/setup-tools 20 | with: 21 | install-nodejs: 'true' 22 | 23 | - name: Lint 24 | run: npm run lint 25 | 26 | - name: Prettier 27 | run: npm run prettier 28 | 29 | - name: Check for changes 30 | run: | 31 | if [ -n "$(git status --porcelain)" ]; then 32 | echo Following files are changed... 33 | git status 34 | 35 | echo Changes: 36 | git diff 37 | 38 | exit 1; 39 | else 40 | exit 0; 41 | fi 42 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @axelar-network/axelar-gmp-sdk-solidity 2 | 3 | ## 6.0.6 4 | 5 | ### Patch Changes 6 | 7 | - 07330bc: fix gh release action 8 | 9 | ## 6.0.5 10 | 11 | ### Patch Changes 12 | 13 | - 6093489: Added GitHub actions for automatic docs generation and Aderyn static analysis for contracts 14 | 15 | ## 6.0.4 16 | 17 | ### Patch Changes 18 | 19 | - f25a2d4: Allowed chainName() to be accessible internally in InterchainAddressTracker 20 | 21 | ## 6.0.3 22 | 23 | ### Patch Changes 24 | 25 | - cb4385d: add public access for the publish config 26 | 27 | ## 6.0.2 28 | 29 | ### Patch Changes 30 | 31 | - 7ecf8e3: split express executable gateway token contracts. Use `*WithToken` variants of express executables if you need gateway token support 32 | - 53c9a1e: update signerHash to signersHash 33 | 34 | ## 6.0.1 35 | 36 | ### Patch Changes 37 | 38 | - 819f361: reverted use of named return value in RolesBase to not change bytecode of InterchainToken 39 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarAmplifierGatewayAuth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Proof, WeightedSigners } from '../types/WeightedMultisigTypes.sol'; 6 | 7 | interface IAxelarAmplifierGatewayAuth { 8 | /** 9 | * @notice This function takes messageHash and proof data and reverts if proof is invalid 10 | * @param dataHash The hash of the message that was signed 11 | * @param proof The data containing signers with signatures 12 | * @return isLatestSigners True if provided signers are the current ones 13 | */ 14 | function validateProof(bytes32 dataHash, Proof calldata proof) external view returns (bool isLatestSigners); 15 | 16 | /** 17 | * @notice This function rotates the current signers with a new set 18 | * @param newSigners The data containing the new signers and their weights 19 | */ 20 | function rotateSigners(WeightedSigners calldata newSigners) external; 21 | } 22 | -------------------------------------------------------------------------------- /contracts/test/governance/TestInterchainGovernance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { InterchainGovernance } from '../../governance/InterchainGovernance.sol'; 6 | 7 | /** 8 | * @dev This exists so Slither knows InterchainGovernance methods are being used. 9 | */ 10 | contract TestInterchainGovernance is InterchainGovernance { 11 | constructor( 12 | address gatewayAddress, 13 | string memory governanceChain_, 14 | string memory governanceAddress_, 15 | uint256 minimumTimeDelay 16 | ) InterchainGovernance(gatewayAddress, governanceChain_, governanceAddress_, minimumTimeDelay) {} 17 | 18 | function executeProposalAction( 19 | bytes32 commandId, 20 | string calldata sourceChain, 21 | string calldata sourceAddress, 22 | bytes calldata payload 23 | ) external { 24 | _execute(commandId, sourceChain, sourceAddress, payload); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/utils/Caller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { ContractAddress } from '../libs/ContractAddress.sol'; 6 | import { ICaller } from '../interfaces/ICaller.sol'; 7 | 8 | contract Caller is ICaller { 9 | using ContractAddress for address; 10 | 11 | /** 12 | * @dev Calls a target address with specified calldata and optionally sends value. 13 | */ 14 | function _call( 15 | address target, 16 | bytes calldata callData, 17 | uint256 nativeValue 18 | ) internal returns (bytes memory) { 19 | if (!target.isContract()) revert InvalidContract(target); 20 | 21 | if (nativeValue > address(this).balance) revert InsufficientBalance(); 22 | 23 | (bool success, bytes memory data) = target.call{ value: nativeValue }(callData); 24 | if (!success) { 25 | revert ExecutionFailed(); 26 | } 27 | 28 | return data; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/types/AmplifierGatewayTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @notice This enum represents the different types of commands that can be processed by the Axelar Amplifier Gateway 7 | */ 8 | enum CommandType { 9 | ApproveMessages, 10 | RotateSigners 11 | } 12 | 13 | /** 14 | * @notice This struct represents a message that is to be processed by the Amplifier Gateway 15 | * @param sourceChain The chain from which the message originated 16 | * @param messageId The unique identifier for the message 17 | * @param sourceAddress The address from which the message originated 18 | * @param contractAddress The address of the contract that the message is intended for 19 | * @param payloadHash The hash of the payload that is to be processed 20 | */ 21 | struct Message { 22 | string sourceChain; 23 | string messageId; 24 | string sourceAddress; 25 | address contractAddress; 26 | bytes32 payloadHash; 27 | } 28 | -------------------------------------------------------------------------------- /contracts/interfaces/ITimeLock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title ITimeLock 7 | * @dev Interface for a TimeLock that enables function execution after a certain time has passed. 8 | */ 9 | interface ITimeLock { 10 | error InvalidTimeLockHash(); 11 | error TimeLockAlreadyScheduled(); 12 | error TimeLockNotReady(); 13 | 14 | /** 15 | * @notice Returns a minimum time delay at which the TimeLock may be scheduled. 16 | * @return uint Minimum scheduling delay time (in secs) from the current block timestamp 17 | */ 18 | function minimumTimeLockDelay() external view returns (uint256); 19 | 20 | /** 21 | * @notice Returns the timestamp after which the TimeLock may be executed. 22 | * @param hash The hash of the timelock 23 | * @return uint The timestamp after which the timelock with the given hash can be executed 24 | */ 25 | function getTimeLock(bytes32 hash) external view returns (uint256); 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Testing 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | test: 12 | runs-on: blacksmith-2vcpu-ubuntu-2204 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup tools 18 | uses: ./.github/actions/setup-tools 19 | with: 20 | install-nodejs: 'true' 21 | 22 | - name: Build project and capture output 23 | run: npm run build > build.log 2>&1 24 | 25 | - name: Check for build errors 26 | run: | 27 | if grep -q -i "error" build.log || grep -q -i "warning" build.log; then 28 | echo "Build contains following errors or warnings..." 29 | 30 | cat build.log 31 | 32 | exit 1; 33 | else 34 | exit 0; 35 | fi 36 | 37 | - name: Test 38 | env: 39 | CHECK_CONTRACT_SIZE: true 40 | run: npm run test -- --parallel 41 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarValuedExpressExecutable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarExpressExecutable } from './IAxelarExpressExecutable.sol'; 6 | 7 | /** 8 | * @title IAxelarValuedExpressExecutable 9 | * @dev Interface for the Axelar Valued Express Executable contract. 10 | */ 11 | interface IAxelarValuedExpressExecutable is IAxelarExpressExecutable { 12 | /** 13 | * @dev Returns the value (token address and amount) associated with a contract call 14 | * @param sourceChain The source chain. 15 | * @param sourceAddress The source address. 16 | * @param payload The payload data. 17 | * @return tokenAddress The address of the token used. 18 | * @return value The value associated with the contract call. 19 | */ 20 | function contractCallValue( 21 | string calldata sourceChain, 22 | string calldata sourceAddress, 23 | bytes calldata payload 24 | ) external view returns (address tokenAddress, uint256 value); 25 | } 26 | -------------------------------------------------------------------------------- /contracts/types/WeightedMultisigTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @notice This struct represents the weighted signer 7 | * @param signer The address of the weighted signer 8 | * @param weight The weight of the weighted singer 9 | */ 10 | struct WeightedSigner { 11 | address signer; 12 | uint128 weight; 13 | } 14 | 15 | /** 16 | * @notice This struct represents the weighted signers payload 17 | * @param signers The list of weighted signers 18 | * @param threshold The threshold for the weighted signers 19 | * @param nonce The nonce to distinguish different weighted signer sets 20 | */ 21 | struct WeightedSigners { 22 | WeightedSigner[] signers; 23 | uint128 threshold; 24 | bytes32 nonce; 25 | } 26 | 27 | /** 28 | * @notice This struct represents a proof for a message from the weighted signers 29 | * @param signers The weighted signers 30 | * @param signatures The list of signatures 31 | */ 32 | struct Proof { 33 | WeightedSigners signers; 34 | bytes[] signatures; 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/upload-contract-docs.yaml: -------------------------------------------------------------------------------- 1 | name: Upload Contract Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | jobs: 14 | upload-docs: 15 | runs-on: blacksmith-2vcpu-ubuntu-2204 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup tools 22 | uses: ./.github/actions/setup-tools 23 | with: 24 | install-nodejs: 'true' 25 | 26 | - name: Run Upload Docs Action 27 | uses: axelarnetwork/axelar-gmp-sdk-solidity/.github/actions/upload-docs@main 28 | with: 29 | branches: main 30 | environment_name: github-pages 31 | script: scripts/generateOverviewDocs.js 32 | 33 | deploy: 34 | needs: upload-docs 35 | environment: 36 | name: github-pages 37 | url: ${{ steps.deployment.outputs.page_url }} 38 | runs-on: blacksmith-2vcpu-ubuntu-2204 39 | steps: 40 | - name: Deploy to GitHub Pages 41 | id: deployment 42 | uses: actions/deploy-pages@v4 43 | -------------------------------------------------------------------------------- /contracts/test/upgradable/InvalidProxyImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IUpgradable } from '../../interfaces/IUpgradable.sol'; 6 | 7 | contract InvalidProxyImplementation is IUpgradable { 8 | function owner() external pure override returns (address) { 9 | return address(0); 10 | } 11 | 12 | function transferOwnership(address) external override {} 13 | 14 | function pendingOwner() external pure returns (address) { 15 | return address(0); 16 | } 17 | 18 | function proposeOwnership(address newOwner) external {} 19 | 20 | function acceptOwnership() external {} 21 | 22 | function implementation() external pure override returns (address) { 23 | return address(0); 24 | } 25 | 26 | function upgrade( 27 | address, 28 | bytes32, 29 | bytes calldata 30 | ) external override {} 31 | 32 | function setup(bytes calldata _data) external {} 33 | 34 | function contractId() external pure returns (bytes32) { 35 | return keccak256('invalid-proxy-implementation'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Axelar Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contracts/test/express/TestAxelarExpressExecutable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; 6 | 7 | contract TestAxelarExpressExecutable is AxelarExpressExecutableWithToken { 8 | constructor(address gateway_) AxelarExpressExecutableWithToken(gateway_) { 9 | if ( 10 | PREFIX_EXPRESS_EXECUTE != keccak256('express-execute') || 11 | PREFIX_EXPRESS_EXECUTE_WITH_TOKEN != keccak256('express-execute-with-token') 12 | ) revert('invalid express execute prefix'); 13 | } 14 | 15 | function _execute( 16 | bytes32 commandId, 17 | string calldata sourceChain, 18 | string calldata sourceAddress, 19 | bytes calldata payload 20 | ) internal virtual override {} 21 | 22 | function _executeWithToken( 23 | bytes32 commandId, 24 | string calldata sourceChain, 25 | string calldata sourceAddress, 26 | bytes calldata payload, 27 | string calldata tokenSymbol, 28 | uint256 amount 29 | ) internal virtual override {} 30 | } 31 | -------------------------------------------------------------------------------- /contracts/interfaces/IRolesBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IRolesBase Interface 7 | * @notice IRolesBase is an interface that abstracts the implementation of a 8 | * contract with role control internal functions. 9 | */ 10 | interface IRolesBase { 11 | error MissingRole(address account, uint8 role); 12 | error MissingAllRoles(address account, uint256 accountRoles); 13 | error MissingAnyOfRoles(address account, uint256 accountRoles); 14 | 15 | error InvalidProposedRoles(address fromAccount, address toAccount, uint256 accountRoles); 16 | 17 | event RolesProposed(address indexed fromAccount, address indexed toAccount, uint256 accountRoles); 18 | event RolesAdded(address indexed account, uint256 accountRoles); 19 | event RolesRemoved(address indexed account, uint256 accountRoles); 20 | 21 | /** 22 | * @notice Checks if an account has a role. 23 | * @param account The address to check 24 | * @param role The role to check 25 | * @return True if the account has the role, false otherwise 26 | */ 27 | function hasRole(address account, uint8 role) external view returns (bool); 28 | } 29 | -------------------------------------------------------------------------------- /contracts/test/gmp/DestinationChainTokenSwapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IERC20 } from '../../interfaces/IERC20.sol'; 6 | 7 | contract DestinationChainTokenSwapper { 8 | error WrongTokenPair(); 9 | 10 | address public tokenA; 11 | address public tokenB; 12 | 13 | constructor(address tokenA_, address tokenB_) { 14 | tokenA = tokenA_; 15 | tokenB = tokenB_; 16 | } 17 | 18 | function swap( 19 | address tokenAddress, 20 | address toTokenAddress, 21 | uint256 amount, 22 | address recipient 23 | ) external returns (uint256 convertedAmount) { 24 | IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount); 25 | 26 | if (tokenAddress == tokenA) { 27 | if (toTokenAddress != tokenB) revert WrongTokenPair(); 28 | 29 | convertedAmount = amount * 2; 30 | } else { 31 | if (tokenAddress != tokenB || toTokenAddress != tokenA) revert WrongTokenPair(); 32 | 33 | convertedAmount = amount / 2; 34 | } 35 | 36 | IERC20(toTokenAddress).transfer(recipient, convertedAmount); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/test/upgradable/InvalidSetupProxyImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IUpgradable } from '../../interfaces/IUpgradable.sol'; 6 | 7 | contract InvalidSetupProxyImplementation is IUpgradable { 8 | function owner() external pure override returns (address) { 9 | return address(0); 10 | } 11 | 12 | function transferOwnership(address) external override {} 13 | 14 | function pendingOwner() external pure returns (address) { 15 | return address(0); 16 | } 17 | 18 | function proposeOwnership(address newOwner) external {} 19 | 20 | function acceptOwnership() external {} 21 | 22 | function implementation() external pure override returns (address) { 23 | return address(0); 24 | } 25 | 26 | function upgrade( 27 | address, 28 | bytes32, 29 | bytes calldata 30 | ) external override {} 31 | 32 | function setup( 33 | bytes calldata /* data */ 34 | ) external pure { 35 | revert('Always reverts'); 36 | } 37 | 38 | function contractId() external pure returns (bytes32) { 39 | return keccak256('proxy-implementation'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/conventional-commits.yaml: -------------------------------------------------------------------------------- 1 | name: Ensure Conventional Commit message 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | ensure-CC: 12 | runs-on: blacksmith-2vcpu-ubuntu-2204 13 | 14 | steps: 15 | - name: semantic-pull-request 16 | uses: amannn/action-semantic-pull-request@v5 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | with: 20 | # Configure additional validation for the subject based on a regex. 21 | # This example ensures the subject doesn't start with an uppercase character. 22 | subjectPattern: ^(?![A-Z]).+$ 23 | # If `subjectPattern` is configured, you can use this property to override 24 | # the default error message that is shown when the pattern doesn't match. 25 | # The variables `subject` and `title` can be used within the message. 26 | subjectPatternError: | 27 | The subject "{subject}" found in the pull request title "{title}" 28 | didn't match the configured pattern. Please ensure that the subject 29 | doesn't start with an uppercase character. 30 | -------------------------------------------------------------------------------- /contracts/test/utils/TestMulticall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Multicall } from '../../utils/Multicall.sol'; 6 | 7 | contract TestMulticall is Multicall { 8 | uint256 public nonce; 9 | bytes[] public lastMulticallReturns; 10 | event Function1Called(uint256 nonce_); 11 | event Function2Called(uint256 nonce_); 12 | 13 | function function1() external returns (uint256) { 14 | uint256 nonce_ = nonce++; 15 | emit Function1Called(nonce_); 16 | return nonce_; 17 | } 18 | 19 | function function2() external returns (uint256) { 20 | uint256 nonce_ = nonce++; 21 | emit Function2Called(nonce_); 22 | return nonce_; 23 | } 24 | 25 | function function3() external pure { 26 | revert('function3 failed'); 27 | } 28 | 29 | function function4() external pure { 30 | // solhint-disable-next-line reason-string 31 | revert(); 32 | } 33 | 34 | function multicallTest(bytes[] calldata data) external { 35 | lastMulticallReturns = multicall(data); 36 | } 37 | 38 | function getLastMulticallReturns() external view returns (bytes[] memory r) { 39 | return lastMulticallReturns; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/deploy/Create3Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | import { CreateDeploy } from './CreateDeploy.sol'; 4 | 5 | pragma solidity ^0.8.0; 6 | 7 | /** 8 | * @title Create3Address contract 9 | * @notice This contract can be used to predict the deterministic deployment address of a contract deployed with the `CREATE3` technique. 10 | */ 11 | contract Create3Address { 12 | /// @dev bytecode hash of the CreateDeploy helper contract 13 | bytes32 internal immutable createDeployBytecodeHash; 14 | 15 | constructor() { 16 | createDeployBytecodeHash = keccak256(type(CreateDeploy).creationCode); 17 | } 18 | 19 | /** 20 | * @notice Compute the deployed address that will result from the `CREATE3` method. 21 | * @param deploySalt A salt to influence the contract address 22 | * @return deployed The deterministic contract address if it was deployed 23 | */ 24 | function _create3Address(bytes32 deploySalt) internal view returns (address deployed) { 25 | address deployer = address( 26 | uint160(uint256(keccak256(abi.encodePacked(hex'ff', address(this), deploySalt, createDeployBytecodeHash)))) 27 | ); 28 | 29 | deployed = address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', deployer, hex'01'))))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/codecov.yaml: -------------------------------------------------------------------------------- 1 | name: Code Coverage 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - releases/** 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | coverage: 16 | runs-on: blacksmith-2vcpu-ubuntu-2204 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | 22 | - name: Setup tools 23 | uses: ./.github/actions/setup-tools 24 | with: 25 | install-nodejs: 'true' 26 | 27 | # Step to do builds and check for warnings 28 | - name: Build 29 | run: npm run build > build.log 2>&1 30 | 31 | - name: Check for build warnings 32 | run: | 33 | if grep -q -i "error" build.log || grep -q -i "warning" build.log; then 34 | echo "Build contains following errors or warnings..." 35 | cat build.log 36 | exit 1; 37 | else 38 | exit 0; 39 | fi 40 | 41 | - name: Generate code coverage 42 | run: npm run coverage 43 | 44 | - name: Upload coverage to Codecov 45 | uses: codecov/codecov-action@v3 46 | with: 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | files: lcov.info 49 | fail_ci_if_error: true 50 | -------------------------------------------------------------------------------- /contracts/test/gmp/SourceChainSwapCaller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarGatewayWithToken } from '../../interfaces/IAxelarGatewayWithToken.sol'; 6 | import { IERC20 } from '../../interfaces/IERC20.sol'; 7 | 8 | contract SourceChainSwapCaller { 9 | IAxelarGatewayWithToken public gateway; 10 | string public destinationChain; 11 | string public executableAddress; 12 | 13 | constructor( 14 | address gateway_, 15 | string memory destinationChain_, 16 | string memory executableAddress_ 17 | ) { 18 | gateway = IAxelarGatewayWithToken(gateway_); 19 | destinationChain = destinationChain_; 20 | executableAddress = executableAddress_; 21 | } 22 | 23 | function swapToken( 24 | string memory symbolA, 25 | string memory symbolB, 26 | uint256 amount, 27 | string memory recipient 28 | ) external payable { 29 | address tokenX = gateway.tokenAddresses(symbolA); 30 | bytes memory payload = abi.encode(symbolB, recipient); 31 | 32 | IERC20(tokenX).transferFrom(msg.sender, address(this), amount); 33 | 34 | IERC20(tokenX).approve(address(gateway), amount); 35 | gateway.callContractWithToken(destinationChain, executableAddress, payload, symbolA, amount); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/test/libs/LibsTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { StringToAddress, AddressToString } from '../../libs/AddressString.sol'; 6 | import { Bytes32ToString, StringToBytes32 } from '../../libs/Bytes32String.sol'; 7 | import { SafeNativeTransfer } from '../../libs/SafeNativeTransfer.sol'; 8 | 9 | contract LibsTest { 10 | using AddressToString for address; 11 | using StringToAddress for string; 12 | using Bytes32ToString for bytes32; 13 | using StringToBytes32 for string; 14 | using SafeNativeTransfer for address; 15 | 16 | function addressToString(address address_) external pure returns (string memory) { 17 | return address_.toString(); 18 | } 19 | 20 | function stringToAddress(string calldata string_) external pure returns (address) { 21 | return string_.toAddress(); 22 | } 23 | 24 | function bytes32ToString(bytes32 bytes_) external pure returns (string memory) { 25 | return bytes_.toTrimmedString(); 26 | } 27 | 28 | function stringToBytes32(string calldata string_) external pure returns (bytes32) { 29 | return string_.toBytes32(); 30 | } 31 | 32 | function nativeTransfer(address receiver, uint256 amount) external { 33 | receiver.safeNativeTransfer(amount); 34 | } 35 | 36 | receive() external payable {} 37 | } 38 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarExpressExecutableWithToken } from './IAxelarExpressExecutableWithToken.sol'; 6 | import { IAxelarValuedExpressExecutable } from './IAxelarValuedExpressExecutable.sol'; 7 | 8 | /** 9 | * @title IAxelarValuedExpressExecutableWithToken 10 | * @dev Interface for the Axelar Valued Express Executable With Token contract. 11 | */ 12 | interface IAxelarValuedExpressExecutableWithToken is IAxelarExpressExecutableWithToken, IAxelarValuedExpressExecutable { 13 | /** 14 | * @dev Returns the value (token address and amount) associated with a contract call with token. 15 | * @param sourceChain The source chain. 16 | * @param sourceAddress The source address. 17 | * @param payload The payload data. 18 | * @param symbol The token symbol. 19 | * @param amount The amount of tokens. 20 | * @return tokenAddress The address of the token used. 21 | * @return value The value associated with the contract call. 22 | */ 23 | function contractCallWithTokenValue( 24 | string calldata sourceChain, 25 | string calldata sourceAddress, 26 | bytes calldata payload, 27 | string calldata symbol, 28 | uint256 amount 29 | ) external view returns (address tokenAddress, uint256 value); 30 | } 31 | -------------------------------------------------------------------------------- /contracts/test/gmp/ExpressExecutableTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; 6 | 7 | contract AxelarExpressExecutableTest is AxelarExpressExecutableWithToken { 8 | event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); 9 | event ExecutedWithToken( 10 | bytes32 commandId, 11 | string sourceChain, 12 | string sourceAddress, 13 | bytes payload, 14 | string symbol, 15 | uint256 amount 16 | ); 17 | 18 | constructor(address gateway_) AxelarExpressExecutableWithToken(gateway_) {} 19 | 20 | function _execute( 21 | bytes32 commandId, 22 | string calldata sourceChain, 23 | string calldata sourceAddress, 24 | bytes calldata payload 25 | ) internal override { 26 | emit Executed(commandId, sourceChain, sourceAddress, payload); 27 | } 28 | 29 | function _executeWithToken( 30 | bytes32 commandId, 31 | string calldata sourceChain, 32 | string calldata sourceAddress, 33 | bytes calldata payload, 34 | string calldata symbol, 35 | uint256 amount 36 | ) internal override { 37 | emit ExecutedWithToken(commandId, sourceChain, sourceAddress, payload, symbol, amount); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/test/upgradable/ProxyImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IUpgradable } from '../../interfaces/IUpgradable.sol'; 6 | 7 | contract ProxyImplementation is IUpgradable { 8 | uint256 public value; 9 | string public name; 10 | 11 | function owner() external pure override returns (address) { 12 | return address(0); 13 | } 14 | 15 | function transferOwnership(address) external override {} 16 | 17 | function pendingOwner() external pure returns (address) { 18 | return address(0); 19 | } 20 | 21 | function proposeOwnership(address newOwner) external {} 22 | 23 | function acceptOwnership() external {} 24 | 25 | function implementation() external pure override returns (address) { 26 | return address(0); 27 | } 28 | 29 | function upgrade( 30 | address, 31 | bytes32, 32 | bytes calldata 33 | ) external override {} 34 | 35 | function set(uint256 _val) external { 36 | value = _val; 37 | } 38 | 39 | function setup(bytes calldata _data) external { 40 | (uint256 initVal, string memory _name) = abi.decode(_data, (uint256, string)); 41 | value = initVal; 42 | name = _name; 43 | } 44 | 45 | function contractId() external pure returns (bytes32) { 46 | return keccak256('proxy-implementation'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/utils/Implementation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const { ethers, network } = require('hardhat'); 5 | const { defaultAbiCoder } = ethers.utils; 6 | const { expect } = chai; 7 | const { deployContract } = require('../utils.js'); 8 | 9 | describe('Implementation', () => { 10 | let implementation, proxy; 11 | let ownerWallet; 12 | 13 | const val = 123; 14 | 15 | before(async () => { 16 | const wallets = await ethers.getSigners(); 17 | ownerWallet = wallets[0]; 18 | 19 | implementation = await deployContract(ownerWallet, 'TestImplementation'); 20 | proxy = await deployContract(ownerWallet, 'InitProxy', []); 21 | 22 | const params = defaultAbiCoder.encode(['uint256'], [val]); 23 | await proxy 24 | .init(implementation.address, ownerWallet.address, params) 25 | .then((d) => d.wait(network.config.confirmations)); 26 | 27 | const factory = await ethers.getContractFactory('TestImplementation', ownerWallet); 28 | 29 | proxy = factory.attach(proxy.address); 30 | }); 31 | 32 | it('Should test the implementation contract', async () => { 33 | expect(await proxy.val()).to.equal(val); 34 | 35 | const params = defaultAbiCoder.encode(['uint256'], [val]); 36 | await expect(implementation.setup(params)).to.be.revertedWithCustomError(implementation, 'NotProxy'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /contracts/test/utils/TestInterchainAddressTracker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Ownable } from '../../utils/Ownable.sol'; 6 | import { InterchainAddressTracker } from '../../utils/InterchainAddressTracker.sol'; 7 | 8 | contract TestInterchainAddressTracker is InterchainAddressTracker, Ownable { 9 | string public name = 'Test'; // Dummy var for a different bytecode 10 | 11 | error Invalid(); 12 | 13 | constructor( 14 | string memory chainName_, 15 | string[] memory trustedChainNames, 16 | string[] memory trustedAddresses 17 | ) Ownable(msg.sender) { 18 | _setChainName(chainName_); 19 | 20 | if (_CHAIN_NAME_SLOT != bytes32(uint256(keccak256('interchain-address-tracker-chain-name')) - 1)) 21 | revert Invalid(); 22 | 23 | uint256 length = trustedChainNames.length; 24 | 25 | if (length != trustedAddresses.length) revert LengthMismatch(); 26 | 27 | for (uint256 i; i < length; ++i) { 28 | _setTrustedAddress(trustedChainNames[i], trustedAddresses[i]); 29 | } 30 | } 31 | 32 | function setTrustedAddress(string memory chain, string memory address_) external onlyOwner { 33 | _setTrustedAddress(chain, address_); 34 | } 35 | 36 | function removeTrustedAddress(string memory chain) external onlyOwner { 37 | _removeTrustedAddress(chain); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/libs/AddressBytes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title AddressBytesUtils 7 | * @dev This library provides utility functions to convert between `address` and `bytes`. 8 | */ 9 | library AddressBytes { 10 | error InvalidBytesLength(bytes bytesAddress); 11 | 12 | /** 13 | * @dev Converts a bytes address to an address type. 14 | * @param bytesAddress The bytes representation of an address 15 | * @return addr The converted address 16 | */ 17 | function toAddress(bytes memory bytesAddress) internal pure returns (address addr) { 18 | if (bytesAddress.length != 20) revert InvalidBytesLength(bytesAddress); 19 | 20 | assembly { 21 | addr := mload(add(bytesAddress, 20)) 22 | } 23 | } 24 | 25 | /** 26 | * @dev Converts an address to bytes. 27 | * @param addr The address to be converted 28 | * @return bytesAddress The bytes representation of the address 29 | */ 30 | function toBytes(address addr) internal pure returns (bytes memory bytesAddress) { 31 | bytesAddress = new bytes(20); 32 | // we can test if using a single 32 byte variable that is the address with the length together and using one mstore would be slightly cheaper. 33 | assembly { 34 | mstore(add(bytesAddress, 20), addr) 35 | mstore(bytesAddress, 20) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/upgradable/Implementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IImplementation } from '../interfaces/IImplementation.sol'; 6 | 7 | /** 8 | * @title Implementation 9 | * @notice This contract serves as a base for other contracts and enforces a proxy-first access restriction. 10 | * @dev Derived contracts must implement the setup function. 11 | */ 12 | abstract contract Implementation is IImplementation { 13 | address private immutable implementationAddress; 14 | 15 | /** 16 | * @dev Contract constructor that sets the implementation address to the address of this contract. 17 | */ 18 | constructor() { 19 | implementationAddress = address(this); 20 | } 21 | 22 | /** 23 | * @dev Modifier to require the caller to be the proxy contract. 24 | * Reverts if the caller is the current contract (i.e., the implementation contract itself). 25 | */ 26 | modifier onlyProxy() { 27 | if (implementationAddress == address(this)) revert NotProxy(); 28 | _; 29 | } 30 | 31 | /** 32 | * @notice Initializes contract parameters. 33 | * This function is intended to be overridden by derived contracts. 34 | * The overriding function must have the onlyProxy modifier. 35 | * @param params The parameters to be used for initialization 36 | */ 37 | function setup(bytes calldata params) external virtual; 38 | } 39 | -------------------------------------------------------------------------------- /scripts/flatten-contracts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | echo "Flattening contracts..." 6 | 7 | OUTPUT="./artifacts/flattened" 8 | SOURCE="contracts" 9 | EXCLUDE="contracts/test" 10 | 11 | if [ ! -d "$SOURCE" ]; then 12 | echo "$SOURCE is not a valid folder" 13 | exit 1 14 | fi 15 | 16 | rm -rf "$OUTPUT" 17 | mkdir -p "$OUTPUT" 18 | 19 | # Flatten files 20 | find "$SOURCE" -name '*.sol' -print | while read -r file; do 21 | if echo "$file" | grep -q "$EXCLUDE"; then 22 | continue 23 | fi 24 | 25 | path="${file#${SOURCE}/}" 26 | mkdir -p "$OUTPUT"/"$(dirname "${path}")" 27 | 28 | # Flatten contract 29 | hardhat flatten "$file" >flat.sol 30 | 31 | # Remove hardhat comment at the top 32 | tail -n+3 flat.sol >"$OUTPUT/$path" 33 | 34 | # Remove duplicate License identifiers and pragmas that the explorers don't like 35 | text=$(grep -vE "pragma solidity .*" "$OUTPUT/$path" | grep -vE "// Original license:.*") 36 | 37 | echo "// Source: $SOURCE/$path\n\n" >"$OUTPUT/$path" 38 | echo "pragma solidity ^0.8.0;\n\n" >>"$OUTPUT/$path" 39 | printf "%s" "$text" >>"$OUTPUT/$path" 40 | 41 | # Prettify source (in particular, remove extra newlines) 42 | prettier --write "$OUTPUT/$path" 43 | done 44 | 45 | # Delete temp file 46 | rm -f flat.sol 47 | 48 | if [ -z "$(ls -A $OUTPUT)" ]; then 49 | echo "No contracts from source $SOURCE/ were found at $OUTPUT" 50 | exit 1 51 | fi 52 | 53 | echo "Flattened" 54 | -------------------------------------------------------------------------------- /contracts/interfaces/IGovernable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IGovernable Interface 7 | * @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles. 8 | */ 9 | interface IGovernable { 10 | error NotGovernance(); 11 | error NotMintLimiter(); 12 | error InvalidGovernance(); 13 | error InvalidMintLimiter(); 14 | 15 | event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance); 16 | event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance); 17 | 18 | /** 19 | * @notice Returns the governance address. 20 | * @return address of the governance 21 | */ 22 | function governance() external view returns (address); 23 | 24 | /** 25 | * @notice Returns the mint limiter address. 26 | * @return address of the mint limiter 27 | */ 28 | function mintLimiter() external view returns (address); 29 | 30 | /** 31 | * @notice Transfer the governance role to another address. 32 | * @param newGovernance The new governance address 33 | */ 34 | function transferGovernance(address newGovernance) external; 35 | 36 | /** 37 | * @notice Transfer the mint limiter role to another address. 38 | * @param newGovernance The new mint limiter address 39 | */ 40 | function transferMintLimiter(address newGovernance) external; 41 | } 42 | -------------------------------------------------------------------------------- /test/utils/AddressBytes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { ethers } = require('hardhat'); 4 | const chai = require('chai'); 5 | const { deployContract } = require('../utils'); 6 | const { defaultAbiCoder, arrayify, toUtf8Bytes, hexlify } = ethers.utils; 7 | const { expect } = chai; 8 | 9 | describe('AddressBytes', () => { 10 | let addressBytes; 11 | let ownerWallet; 12 | 13 | before(async () => { 14 | const wallets = await ethers.getSigners(); 15 | ownerWallet = wallets[0]; 16 | 17 | addressBytes = await deployContract(ownerWallet, 'TestAddressBytes'); 18 | }); 19 | 20 | it('Should convert bytes address to address', async () => { 21 | const bytesAddress = arrayify(ownerWallet.address); 22 | const convertedAddress = await addressBytes.toAddress(bytesAddress); 23 | expect(convertedAddress).to.eq(ownerWallet.address); 24 | }); 25 | 26 | it('Should revert on invalid bytes length', async () => { 27 | const bytesAddress = defaultAbiCoder.encode(['bytes'], [toUtf8Bytes(ownerWallet.address)]); 28 | await expect(addressBytes.toAddress(bytesAddress)).to.be.revertedWithCustomError( 29 | addressBytes, 30 | 'InvalidBytesLength', 31 | ); 32 | }); 33 | 34 | it('Should convert address to bytes address', async () => { 35 | const convertedAddress = await addressBytes.toBytes(ownerWallet.address); 36 | expect(convertedAddress).to.eq(hexlify(ownerWallet.address)); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /contracts/test/upgradable/DifferentProxyImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IUpgradable } from '../../interfaces/IUpgradable.sol'; 6 | 7 | contract DifferentProxyImplementation is IUpgradable { 8 | uint256 public value; 9 | string public name; 10 | 11 | function owner() external pure override returns (address) { 12 | return address(0); 13 | } 14 | 15 | function transferOwnership(address) external override {} 16 | 17 | function pendingOwner() external pure returns (address) { 18 | return address(0); 19 | } 20 | 21 | function proposeOwnership(address newOwner) external {} 22 | 23 | function acceptOwnership() external {} 24 | 25 | function implementation() external pure override returns (address) { 26 | return address(0); 27 | } 28 | 29 | function upgrade( 30 | address, 31 | bytes32, 32 | bytes calldata 33 | ) external override {} 34 | 35 | function set(uint256 _val) external { 36 | value = _val; 37 | } 38 | 39 | function setup(bytes calldata _data) external { 40 | (uint256 initVal, string memory _name) = abi.decode(_data, (uint256, string)); 41 | value = initVal; 42 | name = _name; 43 | } 44 | 45 | function contractId() external pure returns (bytes32) { 46 | return keccak256('proxy-implementation'); 47 | } 48 | 49 | function different() external pure returns (bool) { 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/types/GasEstimationTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title GasEstimationType 7 | * @notice This enum represents the gas estimation types for different chains. 8 | */ 9 | enum GasEstimationType { 10 | Default, 11 | OptimismEcotone, 12 | OptimismBedrock, 13 | Arbitrum, 14 | Scroll 15 | } 16 | 17 | /** 18 | * @title GasInfo 19 | * @notice This struct represents the gas pricing information for a specific chain. 20 | * @dev Smaller uint types are used for efficient struct packing to save storage costs. 21 | */ 22 | struct GasInfo { 23 | /// @dev Custom gas pricing rule, such as L1 data fee on L2s 24 | uint64 gasEstimationType; 25 | /// @dev Scalar value needed for specific gas estimation types, expected to be less than 1e10 26 | uint64 l1FeeScalar; 27 | /// @dev Axelar base fee for cross-chain message approval on destination, in terms of source native gas token 28 | uint128 axelarBaseFee; 29 | /// @dev Gas price of destination chain, in terms of the source chain token, i.e dest_gas_price * dest_token_market_price / src_token_market_price 30 | uint128 relativeGasPrice; 31 | /// @dev Needed for specific gas estimation types. Blob base fee of destination chain, in terms of the source chain token, i.e dest_blob_base_fee * dest_token_market_price / src_token_market_price 32 | uint128 relativeBlobBaseFee; 33 | /// @dev Axelar express fee for express execution, in terms of source chain token 34 | uint128 expressFee; 35 | } 36 | -------------------------------------------------------------------------------- /test/utils/StringStorage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { ethers } = require('hardhat'); 4 | const chai = require('chai'); 5 | const { deployContract } = require('../utils'); 6 | const { keccak256 } = ethers.utils; 7 | const { expect } = chai; 8 | 9 | describe('StringStorage', () => { 10 | let stringStorage; 11 | let ownerWallet; 12 | 13 | before(async () => { 14 | const wallets = await ethers.getSigners(); 15 | ownerWallet = wallets[0]; 16 | 17 | stringStorage = await deployContract(ownerWallet, 'TestStringStorage'); 18 | }); 19 | 20 | it('Should store, load and delete a short string properly', async () => { 21 | const str = 'hello'; 22 | const slot = keccak256('0x1234'); 23 | 24 | await stringStorage.set(slot, str).then((tx) => tx.wait()); 25 | 26 | expect(await stringStorage.get(slot)).to.equal(str); 27 | 28 | await stringStorage.clear(slot).then((tx) => tx.wait()); 29 | 30 | expect(await stringStorage.get(slot)).to.equal(''); 31 | }); 32 | 33 | it('Should store, load and delete a long string properly', async () => { 34 | const str = keccak256('0x1234') + keccak256('0x5678'); 35 | const slot = keccak256('0x1234'); 36 | 37 | await stringStorage.set(slot, str).then((tx) => tx.wait()); 38 | 39 | expect(await stringStorage.get(slot)).to.equal(str); 40 | 41 | await stringStorage.clear(slot).then((tx) => tx.wait()); 42 | 43 | expect(await stringStorage.get(slot)).to.equal(''); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /contracts/test/executable/AxelarExecutableWithTokenTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExecutableWithToken } from '../../executable/AxelarExecutableWithToken.sol'; 6 | import { IInterchainTransferReceived } from '../../interfaces/IInterchainTransfer.sol'; 7 | 8 | contract AxelarExecutableWithTokenTest is AxelarExecutableWithToken, IInterchainTransferReceived { 9 | event Received(uint256 num); 10 | event ReceivedWithToken(uint256 num, address tokenAddress, uint256 amount); 11 | 12 | constructor(address gatewayAddress) AxelarExecutableWithToken(gatewayAddress) {} 13 | 14 | function _execute( 15 | bytes32, /*commandId*/ 16 | string calldata, /*sourceChain*/ 17 | string calldata, /*sourceAddress*/ 18 | bytes calldata payload 19 | ) internal override { 20 | uint256 num = abi.decode(payload, (uint256)); 21 | emit Received(num); 22 | } 23 | 24 | function _executeWithToken( 25 | bytes32, /*commandId*/ 26 | string calldata sourceChain, 27 | string calldata sourceAddress, 28 | bytes calldata, /*payload*/ 29 | string calldata tokenSymbol, 30 | uint256 amount 31 | ) internal override { 32 | emit InterchainTransferReceived( 33 | sourceChain, 34 | sourceAddress, 35 | bytes(sourceAddress), 36 | address(this), 37 | gatewayWithToken().tokenAddresses(tokenSymbol), 38 | amount 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/libs/Bytes32String.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | library StringToBytes32 { 6 | error InvalidStringLength(); 7 | 8 | function toBytes32(string memory str) internal pure returns (bytes32) { 9 | // Converting a string to bytes32 for immutable storage 10 | bytes memory stringBytes = bytes(str); 11 | 12 | // We can store up to 31 bytes of data as 1 byte is for encoding length 13 | if (stringBytes.length == 0 || stringBytes.length > 31) revert InvalidStringLength(); 14 | 15 | uint256 stringNumber = uint256(bytes32(stringBytes)); 16 | 17 | // Storing string length as the last byte of the data 18 | stringNumber |= 0xff & stringBytes.length; 19 | return bytes32(stringNumber); 20 | } 21 | } 22 | 23 | library Bytes32ToString { 24 | function toTrimmedString(bytes32 stringData) internal pure returns (string memory converted) { 25 | // recovering string length as the last byte of the data 26 | uint256 length = 0xff & uint256(stringData); 27 | 28 | // restoring the string with the correct length 29 | assembly { 30 | converted := mload(0x40) 31 | // new "memory end" including padding (the string isn't larger than 32 bytes) 32 | mstore(0x40, add(converted, 0x40)) 33 | // store length in memory 34 | mstore(converted, length) 35 | // write actual data 36 | mstore(add(converted, 0x20), stringData) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/utils/Multicall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IMulticall } from '../interfaces/IMulticall.sol'; 6 | 7 | /** 8 | * @title Multicall 9 | * @notice This contract is a multi-functional smart contract which allows for multiple 10 | * contract calls in a single transaction. 11 | */ 12 | contract Multicall is IMulticall { 13 | /** 14 | * @notice Performs multiple delegate calls and returns the results of all calls as an array 15 | * @dev This function requires that the contract has sufficient balance for the delegate calls. 16 | * If any of the calls fail, the function will revert with the failure message. 17 | * @param data An array of encoded function calls 18 | * @return results An bytes array with the return data of each function call 19 | */ 20 | function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) { 21 | results = new bytes[](data.length); 22 | bool success; 23 | bytes memory result; 24 | for (uint256 i = 0; i < data.length; ++i) { 25 | // slither-disable-next-line calls-loop,delegatecall-loop 26 | (success, result) = address(this).delegatecall(data[i]); 27 | 28 | if (!success) { 29 | if (result.length == 0) revert MulticallFailed(); 30 | assembly { 31 | revert(add(32, result), mload(result)) 32 | } 33 | } 34 | 35 | results[i] = result; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/test/governance/TestBaseWeightedMultisig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { BaseWeightedMultisig } from '../../governance/BaseWeightedMultisig.sol'; 6 | import { Proof, WeightedSigners } from '../../types/WeightedMultisigTypes.sol'; 7 | 8 | contract TestBaseWeightedMultisig is BaseWeightedMultisig { 9 | event DummyEvent(); 10 | 11 | constructor( 12 | uint256 previousSignersRetention_, 13 | bytes32 domainSeparator_, 14 | WeightedSigners memory initialSigners 15 | ) BaseWeightedMultisig(previousSignersRetention_, domainSeparator_, 0) { 16 | if (BASE_WEIGHTED_MULTISIG_SLOT != bytes32(uint256(keccak256('BaseWeightedMultisig.Slot')) - 1)) { 17 | revert('BaseWeightedMultisig.Slot'); 18 | } 19 | 20 | _rotateSigners(initialSigners, false); 21 | } 22 | 23 | function rotateSigners(WeightedSigners calldata newSigners) external { 24 | _rotateSigners(newSigners, false); 25 | } 26 | 27 | function validateProof(bytes32 dataHash, Proof calldata proof) external view returns (bool isLatestSigners) { 28 | return _validateProof(dataHash, proof); 29 | } 30 | 31 | // use a non-view method to allow gas reporting in tests 32 | function validateProofCall(bytes32 dataHash, Proof calldata proof) external returns (bool isLatestSigners) { 33 | // emit an event to avoid compiler warning about making this into a view 34 | emit DummyEvent(); 35 | 36 | return _validateProof(dataHash, proof); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarExecutableWithToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarExecutable } from './IAxelarExecutable.sol'; 6 | 7 | /** 8 | * @title IAxelarExecutableWithToken 9 | * @dev Interface for a contract that can execute commands from Axelar Gateway involving token transfers. 10 | * It extends IAxelarExecutable to include token-related functionality. 11 | */ 12 | interface IAxelarExecutableWithToken is IAxelarExecutable { 13 | /** 14 | * @notice Executes the specified command sent from another chain and includes a token transfer. 15 | * @dev This function should be implemented to handle incoming commands that include token transfers. 16 | * It will be called by an implementation of `IAxelarGatewayWithToken`. 17 | * @param commandId The identifier of the command to execute. 18 | * @param sourceChain The name of the source chain from where the command originated. 19 | * @param sourceAddress The address on the source chain that sent the command. 20 | * @param payload The payload of the command to be executed. 21 | * @param tokenSymbol The symbol of the token to be transferred with this command. 22 | * @param amount The amount of tokens to be transferred with this command. 23 | */ 24 | function executeWithToken( 25 | bytes32 commandId, 26 | string calldata sourceChain, 27 | string calldata sourceAddress, 28 | bytes calldata payload, 29 | string calldata tokenSymbol, 30 | uint256 amount 31 | ) external; 32 | } 33 | -------------------------------------------------------------------------------- /.github/actions/aderyn/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Aderyn Static Analysis' 2 | description: 'Run Aderyn static analysis and upload the report as an artifact' 3 | 4 | inputs: 5 | rust_toolchain: 6 | description: 'Rust toolchain version' 7 | required: true 8 | default: 'stable' 9 | 10 | runs: 11 | using: 'composite' 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Setup Rust 17 | uses: dtolnay/rust-toolchain@stable 18 | with: 19 | toolchain: ${{ inputs.rust_toolchain }} 20 | 21 | - name: Install Aderyn 22 | run: | 23 | curl --proto '=https' --tlsv1.2 -LsSf https://github.com/cyfrin/aderyn/releases/latest/download/aderyn-installer.sh | bash 24 | echo "$HOME/.cyfrin/bin" >> $GITHUB_PATH 25 | shell: bash 26 | 27 | - name: Generate Aderyn Report 28 | id: run_aderyn 29 | run: | 30 | aderyn --output report.md . 2>&1 | tee aderyn_output.log 31 | shell: bash 32 | 33 | - name: Print Aderyn Warnings to Console 34 | run: cat report.md 35 | shell: bash 36 | 37 | - name: Upload Aderyn Report as Artifact 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: aderyn-report 41 | path: report.md 42 | 43 | - name: Check Aderyn Report for Warnings 44 | run: | 45 | if grep -E "warning:|Found issues" aderyn_output.log; then 46 | echo "Aderyn analysis found warnings." 47 | exit 1 48 | else 49 | echo "No warnings or issues found by Aderyn." 50 | fi 51 | shell: bash 52 | -------------------------------------------------------------------------------- /contracts/test/utils/TimeLockTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { TimeLock } from '../../utils/TimeLock.sol'; 6 | 7 | contract TimeLockTest is TimeLock { 8 | uint256 public num; 9 | 10 | event NumUpdated(uint256 newNum); 11 | 12 | constructor(uint256 _minimumTimeDelay) TimeLock(_minimumTimeDelay) { 13 | if (PREFIX_TIME_LOCK != keccak256('time-lock')) revert('invalid time-lock slot'); 14 | } 15 | 16 | function getNum() external view returns (uint256) { 17 | return num; 18 | } 19 | 20 | function scheduleSetNum(uint256 _num, uint256 _eta) external { 21 | bytes32 hash = keccak256(abi.encodePacked(_num)); 22 | 23 | _scheduleTimeLock(hash, _eta); 24 | } 25 | 26 | function scheduleSetNumByHash(bytes32 _hash, uint256 _eta) external { 27 | _scheduleTimeLock(_hash, _eta); 28 | } 29 | 30 | function cancelSetNum(uint256 _num) external { 31 | bytes32 hash = keccak256(abi.encodePacked(_num)); 32 | 33 | _cancelTimeLock(hash); 34 | } 35 | 36 | function cancelSetNumByHash(bytes32 _hash) external { 37 | _cancelTimeLock(_hash); 38 | } 39 | 40 | function setNum(uint256 _num) external { 41 | bytes32 hash = keccak256(abi.encodePacked(_num)); 42 | 43 | _finalizeTimeLock(hash); 44 | 45 | num = _num; 46 | 47 | emit NumUpdated(_num); 48 | } 49 | 50 | function setNumByHash(bytes32 _hash, uint256 _num) external { 51 | _finalizeTimeLock(_hash); 52 | 53 | num = _num; 54 | 55 | emit NumUpdated(_num); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.github/actions/upload-docs/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Upload Docs" 2 | 3 | description: "Build and upload docs to GitHub Pages" 4 | inputs: 5 | branches: 6 | description: "Branches to trigger the workflow on" 7 | required: false 8 | default: "main" 9 | environment_name: 10 | description: "Name of the deployment environment" 11 | required: false 12 | default: "github-pages" 13 | script: 14 | description: 'The script to run for generating documentation' 15 | required: true 16 | 17 | runs: 18 | using: "composite" 19 | steps: 20 | - name: Create docs directory 21 | run: mkdir -p docs 22 | shell: bash 23 | 24 | - name: Build Solidity documentation 25 | run: npx hardhat docgen 26 | shell: bash 27 | 28 | - name: Generate Overview Documentation 29 | run: node ${{ inputs.script }} 30 | shell: bash 31 | 32 | - name: Verify docs directory is not empty 33 | run: | 34 | if [ ! -d "docs" ] || [ ! -s "docs/index.md" ]; then 35 | echo "Docs directory is empty or does not exist" 36 | exit 1 37 | fi 38 | shell: bash 39 | 40 | - name: Add YAML front matter for Jekyll 41 | run: | 42 | find docs -name '*.md' -exec sh -c 'sed -i.bak "1s/^/---\nlayout: default\n---\n/" "$0" && rm "$0.bak"' {} \; 43 | shell: bash 44 | 45 | - name: Setup Github Pages 46 | uses: actions/configure-pages@v5 47 | 48 | - name: Build with Jekyll 49 | uses: actions/jekyll-build-pages@v1 50 | with: 51 | source: ./docs 52 | 53 | - name: Upload artifact 54 | uses: actions/upload-pages-artifact@v3 55 | -------------------------------------------------------------------------------- /contracts/test/express/TestAxelarValuedExpressExecutable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarValuedExpressExecutableWithToken } from '../../express/AxelarValuedExpressExecutableWithToken.sol'; 6 | 7 | contract TestAxelarValuedExpressExecutable is AxelarValuedExpressExecutableWithToken { 8 | constructor(address gateway_) AxelarValuedExpressExecutableWithToken(gateway_) {} 9 | 10 | function contractCallValue( 11 | string calldata, /* sourceChain */ 12 | string calldata, /* sourceAddress */ 13 | bytes calldata /* payload */ 14 | ) public pure override returns (address tokenAddress, uint256 value) { 15 | return (address(0), 0); 16 | } 17 | 18 | function contractCallWithTokenValue( 19 | string calldata, /* sourceChain */ 20 | string calldata, /* sourceAddress */ 21 | bytes calldata, /* payload */ 22 | string calldata, /* symbol */ 23 | uint256 /* amount */ 24 | ) public pure override returns (address tokenAddress, uint256 value) { 25 | return (address(0), 0); 26 | } 27 | 28 | function _execute( 29 | bytes32 commandId, 30 | string calldata sourceChain, 31 | string calldata sourceAddress, 32 | bytes calldata payload 33 | ) internal virtual override {} 34 | 35 | function _executeWithToken( 36 | bytes32 commandId, 37 | string calldata sourceChain, 38 | string calldata sourceAddress, 39 | bytes calldata payload, 40 | string calldata tokenSymbol, 41 | uint256 amount 42 | ) internal virtual override {} 43 | } 44 | -------------------------------------------------------------------------------- /contracts/libs/AddressString.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | library StringToAddress { 6 | error InvalidAddressString(); 7 | 8 | function toAddress(string memory addressString) internal pure returns (address) { 9 | bytes memory stringBytes = bytes(addressString); 10 | uint160 addressNumber = 0; 11 | uint8 stringByte; 12 | 13 | if (stringBytes.length != 42 || stringBytes[0] != '0' || stringBytes[1] != 'x') revert InvalidAddressString(); 14 | 15 | for (uint256 i = 2; i < 42; ++i) { 16 | stringByte = uint8(stringBytes[i]); 17 | 18 | if ((stringByte >= 97) && (stringByte <= 102)) stringByte -= 87; 19 | else if ((stringByte >= 65) && (stringByte <= 70)) stringByte -= 55; 20 | else if ((stringByte >= 48) && (stringByte <= 57)) stringByte -= 48; 21 | else revert InvalidAddressString(); 22 | 23 | addressNumber |= uint160(uint256(stringByte) << ((41 - i) << 2)); 24 | } 25 | 26 | return address(addressNumber); 27 | } 28 | } 29 | 30 | library AddressToString { 31 | function toString(address address_) internal pure returns (string memory) { 32 | bytes memory addressBytes = abi.encodePacked(address_); 33 | bytes memory characters = '0123456789abcdef'; 34 | bytes memory stringBytes = new bytes(42); 35 | 36 | stringBytes[0] = '0'; 37 | stringBytes[1] = 'x'; 38 | 39 | for (uint256 i; i < 20; ++i) { 40 | stringBytes[2 + i * 2] = characters[uint8(addressBytes[i] >> 4)]; 41 | stringBytes[3 + i * 2] = characters[uint8(addressBytes[i] & 0x0f)]; 42 | } 43 | 44 | return string(stringBytes); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/deploy/Create3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IDeploy } from '../interfaces/IDeploy.sol'; 6 | import { ContractAddress } from '../libs/ContractAddress.sol'; 7 | import { CreateDeploy } from './CreateDeploy.sol'; 8 | import { Create3Address } from './Create3Address.sol'; 9 | 10 | /** 11 | * @title Create3 contract 12 | * @notice This contract can be used to deploy a contract with a deterministic address that depends only on 13 | * the deployer address and deployment salt, not the contract bytecode and constructor parameters. 14 | */ 15 | contract Create3 is Create3Address, IDeploy { 16 | using ContractAddress for address; 17 | 18 | /** 19 | * @notice Deploys a new contract using the `CREATE3` method. 20 | * @dev This function first deploys the CreateDeploy contract using 21 | * the `CREATE2` opcode and then utilizes the CreateDeploy to deploy the 22 | * new contract with the `CREATE` opcode. 23 | * @param bytecode The bytecode of the contract to be deployed 24 | * @param deploySalt A salt to influence the contract address 25 | * @return deployed The address of the deployed contract 26 | */ 27 | function _create3(bytes memory bytecode, bytes32 deploySalt) internal returns (address deployed) { 28 | deployed = _create3Address(deploySalt); 29 | 30 | if (bytecode.length == 0) revert EmptyBytecode(); 31 | if (deployed.isContract()) revert AlreadyDeployed(); 32 | 33 | // Deploy using create2 34 | CreateDeploy create = new CreateDeploy{ salt: deploySalt }(); 35 | 36 | if (address(create) == address(0)) revert DeployFailed(); 37 | 38 | // Deploy using create 39 | create.deploy(bytecode); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/interfaces/IInterchainAddressTracker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IInterchainAddressTracker 7 | * @dev Manages trusted addresses by chain, keeps track of addresses supported by the Axelar gateway contract 8 | */ 9 | interface IInterchainAddressTracker { 10 | error ZeroAddress(); 11 | error LengthMismatch(); 12 | error ZeroStringLength(); 13 | error UntrustedChain(); 14 | 15 | event TrustedAddressSet(string chain, string address_); 16 | event TrustedAddressRemoved(string chain); 17 | 18 | /** 19 | * @dev Gets the name of the chain this is deployed at 20 | */ 21 | function chainName() external view returns (string memory); 22 | 23 | /** 24 | * @dev Gets the trusted address at a remote chain 25 | * @param chain Chain name of the remote chain 26 | * @return trustedAddress_ The trusted address for the chain. Returns '' if the chain is untrusted 27 | */ 28 | function trustedAddress(string memory chain) external view returns (string memory trustedAddress_); 29 | 30 | /** 31 | * @dev Gets the trusted address hash for a chain 32 | * @param chain Chain name 33 | * @return trustedAddressHash_ the hash of the trusted address for that chain 34 | */ 35 | function trustedAddressHash(string memory chain) external view returns (bytes32 trustedAddressHash_); 36 | 37 | /** 38 | * @dev Checks whether the interchain sender is a trusted address 39 | * @param chain Chain name of the sender 40 | * @param address_ Address of the sender 41 | * @return bool true if the sender chain/address are trusted, false otherwise 42 | */ 43 | function isTrustedAddress(string calldata chain, string calldata address_) external view returns (bool); 44 | } 45 | -------------------------------------------------------------------------------- /contracts/test/gmp/DestinationChainSwapExecutable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExecutableWithToken } from '../../executable/AxelarExecutableWithToken.sol'; 6 | import { IERC20 } from '../../interfaces/IERC20.sol'; 7 | import { DestinationChainTokenSwapper } from './DestinationChainTokenSwapper.sol'; 8 | 9 | contract DestinationChainSwapExecutable is AxelarExecutableWithToken { 10 | DestinationChainTokenSwapper public immutable swapper; 11 | 12 | constructor(address gatewayAddress, address swapperAddress) AxelarExecutableWithToken(gatewayAddress) { 13 | swapper = DestinationChainTokenSwapper(swapperAddress); 14 | } 15 | 16 | function _executeWithToken( 17 | bytes32, /*commandId*/ 18 | string calldata sourceChain, 19 | string calldata, /*sourceAddress*/ 20 | bytes calldata payload, 21 | string calldata tokenSymbolA, 22 | uint256 amount 23 | ) internal override { 24 | (string memory tokenSymbolB, string memory recipient) = abi.decode(payload, (string, string)); 25 | 26 | address tokenA = gatewayWithToken().tokenAddresses(tokenSymbolA); 27 | address tokenB = gatewayWithToken().tokenAddresses(tokenSymbolB); 28 | 29 | IERC20(tokenA).approve(address(swapper), amount); 30 | uint256 convertedAmount = swapper.swap(tokenA, tokenB, amount, address(this)); 31 | 32 | IERC20(tokenB).approve(address(gateway()), convertedAmount); 33 | gatewayWithToken().sendToken(sourceChain, recipient, tokenSymbolB, convertedAmount); 34 | } 35 | 36 | function _execute( 37 | bytes32 commandId, 38 | string calldata sourceChain, 39 | string calldata sourceAddress, 40 | bytes calldata payload 41 | ) internal override {} 42 | } 43 | -------------------------------------------------------------------------------- /contracts/utils/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IReentrancyGuard } from '../interfaces/IReentrancyGuard.sol'; 6 | 7 | /** 8 | * @title ReentrancyGuard 9 | * @notice This contract provides a mechanism to halt the execution of specific functions 10 | * if a pause condition is activated. 11 | */ 12 | contract ReentrancyGuard is IReentrancyGuard { 13 | // uint256(keccak256('ReentrancyGuard:entered')) - 1 14 | uint256 internal constant ENTERED_SLOT = 0x1a771c70cada93a906f955a7dec24a83d7954ba2f75256be4febcf62b395d532; 15 | uint256 internal constant NOT_ENTERED = 1; 16 | uint256 internal constant ENTERED = 2; 17 | 18 | /** 19 | * @notice A modifier that throws a ReEntrancy custom error if the contract is entered 20 | * @dev This modifier should be used with functions that can be entered twice 21 | */ 22 | modifier noReEntrancy() { 23 | if (_hasEntered()) revert ReentrantCall(); 24 | _setEntered(ENTERED); 25 | _; 26 | _setEntered(NOT_ENTERED); 27 | } 28 | 29 | /** 30 | * @notice Check if the contract is already executing. 31 | * @return entered A boolean representing the entered status. True if already executing, false otherwise. 32 | */ 33 | function _hasEntered() internal view returns (bool entered) { 34 | assembly { 35 | entered := eq(sload(ENTERED_SLOT), ENTERED) 36 | } 37 | } 38 | 39 | /** 40 | * @notice Sets the entered status of the contract 41 | * @param entered A boolean representing the entered status. True if already executing, false otherwise. 42 | */ 43 | function _setEntered(uint256 entered) internal { 44 | assembly { 45 | sstore(ENTERED_SLOT, entered) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/utils/Pausable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const { ethers } = require('hardhat'); 5 | const { expect } = chai; 6 | const { deployContract } = require('../utils.js'); 7 | 8 | describe('Pausable', () => { 9 | let test; 10 | let ownerWallet; 11 | 12 | before(async () => { 13 | const wallets = await ethers.getSigners(); 14 | ownerWallet = wallets[0]; 15 | 16 | test = await deployContract(ownerWallet, 'TestPausable'); 17 | }); 18 | 19 | it('Should be able to set paused to true or false', async () => { 20 | await expect(test.pause()).to.emit(test, 'Paused').withArgs(ownerWallet.address); 21 | expect(await test.paused()).to.equal(true); 22 | await expect(test.unpause()).to.emit(test, 'Unpaused').withArgs(ownerWallet.address); 23 | expect(await test.paused()).to.equal(false); 24 | }); 25 | 26 | it('Should be able to execute notPaused functions only when not paused', async () => { 27 | await expect(test.pause()).to.emit(test, 'Paused').withArgs(ownerWallet.address); 28 | await expect(test.testPaused()).to.be.revertedWithCustomError(test, 'Pause'); 29 | 30 | await expect(test.unpause()).to.emit(test, 'Unpaused').withArgs(ownerWallet.address); 31 | await expect(test.testPaused()).to.emit(test, 'TestEvent'); 32 | }); 33 | 34 | it('Should be able to execute paused functions only when paused', async () => { 35 | await expect(test.pause()).to.emit(test, 'Paused').withArgs(ownerWallet.address); 36 | await expect(test.testNotPaused()).to.emit(test, 'TestEvent'); 37 | 38 | await expect(test.unpause()).to.emit(test, 'Unpaused').withArgs(ownerWallet.address); 39 | await expect(test.testNotPaused()).to.be.revertedWithCustomError(test, 'NotPaused'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarExecutable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarGateway } from './IAxelarGateway.sol'; 6 | 7 | /** 8 | * @title IAxelarExecutable 9 | * @dev Interface for a contract that is executable by Axelar Gateway's cross-chain message passing. 10 | * It defines a standard interface to execute commands sent from another chain. 11 | */ 12 | interface IAxelarExecutable { 13 | /** 14 | * @dev Thrown when a function is called with an invalid address. 15 | */ 16 | error InvalidAddress(); 17 | 18 | /** 19 | * @dev Thrown when the call is not approved by the Axelar Gateway. 20 | */ 21 | error NotApprovedByGateway(); 22 | 23 | /** 24 | * @notice Returns the address of the AxelarGateway contract. 25 | * @return The Axelar Gateway contract associated with this executable contract. 26 | */ 27 | function gateway() external view returns (IAxelarGateway); 28 | 29 | /** 30 | * @notice Executes the specified command sent from another chain. 31 | * @dev This function is called by the Axelar Gateway to carry out cross-chain commands. 32 | * Reverts if the call is not approved by the gateway or other checks fail. 33 | * @param commandId The identifier of the command to execute. 34 | * @param sourceChain The name of the source chain from where the command originated. 35 | * @param sourceAddress The address on the source chain that sent the command. 36 | * @param payload The payload of the command to be executed. This typically includes the function selector and encoded arguments. 37 | */ 38 | function execute( 39 | bytes32 commandId, 40 | string calldata sourceChain, 41 | string calldata sourceAddress, 42 | bytes calldata payload 43 | ) external; 44 | } 45 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | estimateGasForCreate2Deploy, 4 | estimateGasForCreate2DeployAndInit, 5 | create2DeployContract, 6 | create2DeployAndInitContract, 7 | getCreate2Address, 8 | } = require('./scripts/create2Deployer'); 9 | const { 10 | estimateGasForCreate3Deploy, 11 | estimateGasForCreate3DeployAndInit, 12 | create3DeployContract, 13 | create3DeployAndInitContract, 14 | getCreate3Address, 15 | } = require('./scripts/create3Deployer'); 16 | const { 17 | estimateGasForDeploy, 18 | estimateGasForDeployAndInit, 19 | deployContractConstant, 20 | deployAndInitContractConstant, 21 | predictContractConstant, 22 | } = require('./scripts/constAddressDeployer'); 23 | const { 24 | deployUpgradable, 25 | deployCreate2InitUpgradable, 26 | deployCreate3Upgradable, 27 | deployCreate3InitUpgradable, 28 | upgradeUpgradable, 29 | } = require('./scripts/upgradable'); 30 | const { printObj } = require('./scripts/utils'); 31 | const { generateDocsIndex } = require('./scripts/generateOverviewDocs'); 32 | 33 | module.exports = { 34 | estimateGasForDeploy, 35 | estimateGasForDeployAndInit, 36 | deployContractConstant, 37 | deployAndInitContractConstant, 38 | predictContractConstant, 39 | 40 | estimateGasForCreate2Deploy, 41 | estimateGasForCreate2DeployAndInit, 42 | create2DeployContract, 43 | create2DeployAndInitContract, 44 | getCreate2Address, 45 | 46 | estimateGasForCreate3Deploy, 47 | estimateGasForCreate3DeployAndInit, 48 | create3DeployContract, 49 | create3DeployAndInitContract, 50 | getCreate3Address, 51 | 52 | deployUpgradable, 53 | deployCreate2InitUpgradable, 54 | deployCreate3Upgradable, 55 | deployCreate3InitUpgradable, 56 | upgradeUpgradable, 57 | printObj, 58 | generateDocsIndex, 59 | }; 60 | -------------------------------------------------------------------------------- /contracts/interfaces/IOwnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IOwnable Interface 7 | * @notice IOwnable is an interface that abstracts the implementation of a 8 | * contract with ownership control features. It's commonly used in upgradable 9 | * contracts and includes the functionality to get current owner, transfer 10 | * ownership, and propose and accept ownership. 11 | */ 12 | interface IOwnable { 13 | error NotOwner(); 14 | error InvalidOwner(); 15 | error InvalidOwnerAddress(); 16 | 17 | event OwnershipTransferStarted(address indexed newOwner); 18 | event OwnershipTransferred(address indexed newOwner); 19 | 20 | /** 21 | * @notice Returns the current owner of the contract. 22 | * @return address The address of the current owner 23 | */ 24 | function owner() external view returns (address); 25 | 26 | /** 27 | * @notice Returns the address of the pending owner of the contract. 28 | * @return address The address of the pending owner 29 | */ 30 | function pendingOwner() external view returns (address); 31 | 32 | /** 33 | * @notice Transfers ownership of the contract to a new address 34 | * @param newOwner The address to transfer ownership to 35 | */ 36 | function transferOwnership(address newOwner) external; 37 | 38 | /** 39 | * @notice Proposes to transfer the contract's ownership to a new address. 40 | * The new owner needs to accept the ownership explicitly. 41 | * @param newOwner The address to transfer ownership to 42 | */ 43 | function proposeOwnership(address newOwner) external; 44 | 45 | /** 46 | * @notice Transfers ownership to the pending owner. 47 | * @dev Can only be called by the pending owner 48 | */ 49 | function acceptOwnership() external; 50 | } 51 | -------------------------------------------------------------------------------- /scripts/generateOverviewDocs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | function generateDocsIndex(dir, outputFile, topLevelDir = 'docs') { 7 | const content = ['# Docs\n']; 8 | 9 | // List all Markdown files in the directory under "Contracts" 10 | const files = fs.readdirSync(dir).filter((file) => file.endsWith('.md') && file !== 'index.md'); 11 | 12 | if (files.length > 0) { 13 | content.push('## Contracts'); 14 | files.forEach((file) => { 15 | const title = path.parse(file).name; 16 | content.push(`- [${title}](${file})`); 17 | }); 18 | } 19 | 20 | // List all subdirectories under "Directories" 21 | const subdirs = fs.readdirSync(dir).filter((subdir) => fs.statSync(path.join(dir, subdir)).isDirectory()); 22 | 23 | if (subdirs.length > 0) { 24 | content.push('\n## Directories'); 25 | subdirs.forEach((subdir) => { 26 | const childDir = path.join(dir, subdir); 27 | const childOutput = path.join(childDir, 'index.md'); 28 | generateDocsIndex(childDir, childOutput, topLevelDir); 29 | content.push(`- [${subdir}](${subdir}/index.md)`); 30 | }); 31 | } 32 | 33 | // Check if README.md exists at the top level and append its contents 34 | const readmePath = path.join('README.md'); 35 | 36 | if (fs.existsSync(readmePath) && dir === topLevelDir) { 37 | const readmeContent = fs.readFileSync(readmePath, 'utf-8'); 38 | content.push(`\n${readmeContent}`); 39 | } 40 | 41 | fs.writeFileSync(outputFile, content.join('\n')); 42 | console.log(`Generated ${outputFile}`); 43 | } 44 | 45 | if (require.main === module) { 46 | const parentDir = 'docs'; 47 | const parentOutput = path.join(parentDir, 'index.md'); 48 | generateDocsIndex(parentDir, parentOutput, parentDir); 49 | } 50 | 51 | module.exports = { 52 | generateDocsIndex, 53 | }; 54 | -------------------------------------------------------------------------------- /contracts/interfaces/IOperators.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IOwnable } from './IOwnable.sol'; 6 | import { IContractExecutor } from './IContractExecutor.sol'; 7 | 8 | /** 9 | * @title IOperators Interface 10 | * @notice Interface for an access control mechanism where operators have exclusive 11 | * permissions to execute functions. 12 | */ 13 | interface IOperators is IOwnable, IContractExecutor { 14 | error NotOperator(); 15 | error InvalidOperator(); 16 | error OperatorAlreadyAdded(); 17 | error NotAnOperator(); 18 | error ExecutionFailed(); 19 | 20 | event OperatorAdded(address indexed operator); 21 | event OperatorRemoved(address indexed operator); 22 | 23 | /** 24 | * @notice Check if an account is an operator. 25 | * @param account Address of the account to check 26 | * @return bool True if the account is an operator, false otherwise 27 | */ 28 | function isOperator(address account) external view returns (bool); 29 | 30 | /** 31 | * @notice Adds an operator. 32 | * @param operator The address to add as an operator 33 | */ 34 | function addOperator(address operator) external; 35 | 36 | /** 37 | * @notice Removes an operator. 38 | * @param operator The address of the operator to remove 39 | */ 40 | function removeOperator(address operator) external; 41 | 42 | /** 43 | * @notice Executes an external contract call. 44 | * @dev Execution logic is left up to the implementation. 45 | * @param target The contract to call 46 | * @param callData The data to call the target contract with 47 | * @param nativeValue The amount of native asset to send with the call 48 | * @return bytes The data returned from the contract call 49 | */ 50 | function executeContract( 51 | address target, 52 | bytes calldata callData, 53 | uint256 nativeValue 54 | ) external payable returns (bytes memory); 55 | } 56 | -------------------------------------------------------------------------------- /contracts/interfaces/IInterchainGasEstimation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol'; 6 | 7 | /** 8 | * @title IInterchainGasEstimation Interface 9 | * @notice This is an interface for the InterchainGasEstimation contract 10 | * which allows for estimating gas fees for cross-chain communication on the Axelar network. 11 | */ 12 | interface IInterchainGasEstimation { 13 | error UnsupportedEstimationType(GasEstimationType gasEstimationType); 14 | 15 | /** 16 | * @notice Event emitted when the gas price for a specific chain is updated. 17 | * @param chain The name of the chain 18 | * @param info The gas info for the chain 19 | */ 20 | event GasInfoUpdated(string chain, GasInfo info); 21 | 22 | /** 23 | * @notice Returns the gas price for a specific chain. 24 | * @param chain The name of the chain 25 | * @return gasInfo The gas info for the chain 26 | */ 27 | function getGasInfo(string calldata chain) external view returns (GasInfo memory); 28 | 29 | /** 30 | * @notice Estimates the gas fee for a cross-chain contract call. 31 | * @param destinationChain Axelar registered name of the destination chain 32 | * @param destinationAddress Destination contract address being called 33 | * @param executionGasLimit The gas limit to be used for the destination contract execution, 34 | * e.g. pass in 200k if your app consumes needs upto 200k for this contract call 35 | * @param params Additional parameters for the gas estimation 36 | * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be forwarded to the gas service. 37 | */ 38 | function estimateGasFee( 39 | string calldata destinationChain, 40 | string calldata destinationAddress, 41 | bytes calldata payload, 42 | uint256 executionGasLimit, 43 | bytes calldata params 44 | ) external view returns (uint256 gasEstimate); 45 | } 46 | -------------------------------------------------------------------------------- /contracts/test/utils/TestRoles.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { Roles } from '../../utils/Roles.sol'; 6 | 7 | contract TestRoles is Roles { 8 | error InvalidRolesLength(); 9 | 10 | event NumSet(uint256 _num); 11 | 12 | uint256 public num; 13 | 14 | constructor(address[] memory accounts, uint8[][] memory roleSets) { 15 | uint256 length = accounts.length; 16 | if (length != roleSets.length) revert InvalidRolesLength(); 17 | 18 | for (uint256 i = 0; i < length; ++i) { 19 | _addRoles(accounts[i], roleSets[i]); 20 | } 21 | } 22 | 23 | function setNum(uint256 _num, uint8 role) external onlyRole(role) { 24 | num = _num; 25 | emit NumSet(_num); 26 | } 27 | 28 | function setNumWithAllRoles(uint256 _num, uint8[] calldata roles) external withEveryRole(roles) { 29 | num = _num; 30 | emit NumSet(_num); 31 | } 32 | 33 | function setNumWithAnyRoles(uint256 _num, uint8[] calldata roles) external withAnyRole(roles) { 34 | num = _num; 35 | emit NumSet(_num); 36 | } 37 | 38 | function addRole(address account, uint8 role) external { 39 | _addRole(account, role); 40 | } 41 | 42 | function addRoles(address account, uint8[] calldata roles) external { 43 | _addRoles(account, roles); 44 | } 45 | 46 | function removeRole(address account, uint8 role) external { 47 | _removeRole(account, role); 48 | } 49 | 50 | function removeRoles(address account, uint8[] calldata roles) external { 51 | _removeRoles(account, roles); 52 | } 53 | 54 | function transferRole( 55 | address from, 56 | address to, 57 | uint8 role 58 | ) external { 59 | _transferRole(from, to, role); 60 | } 61 | 62 | function proposeRole(address account, uint8 role) external { 63 | _proposeRole(msg.sender, account, role); 64 | } 65 | 66 | function acceptRole(address account, uint8 role) external { 67 | _acceptRole(account, msg.sender, role); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/test/gmp/DestinationChainSwapExpressDisabled.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExecutableWithToken } from '../../executable/AxelarExecutableWithToken.sol'; 6 | import { IERC20 } from '../../interfaces/IERC20.sol'; 7 | import { DestinationChainTokenSwapper } from './DestinationChainTokenSwapper.sol'; 8 | 9 | contract DestinationChainSwapExpressDisabled is AxelarExecutableWithToken { 10 | DestinationChainTokenSwapper public immutable swapper; 11 | 12 | event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); 13 | 14 | constructor(address gatewayAddress, address swapperAddress) AxelarExecutableWithToken(gatewayAddress) { 15 | swapper = DestinationChainTokenSwapper(swapperAddress); 16 | } 17 | 18 | function _execute( 19 | bytes32 commandId, 20 | string calldata sourceChain, 21 | string calldata sourceAddress, 22 | bytes calldata payload 23 | ) internal override { 24 | emit Executed(commandId, sourceChain, sourceAddress, payload); 25 | } 26 | 27 | function _executeWithToken( 28 | bytes32, /*commandId*/ 29 | string calldata sourceChain, 30 | string calldata, /*sourceAddress*/ 31 | bytes calldata payload, 32 | string calldata tokenSymbolA, 33 | uint256 amount 34 | ) internal override { 35 | (string memory tokenSymbolB, string memory recipient) = abi.decode(payload, (string, string)); 36 | 37 | address tokenA = gatewayWithToken().tokenAddresses(tokenSymbolA); 38 | address tokenB = gatewayWithToken().tokenAddresses(tokenSymbolB); 39 | 40 | IERC20(tokenA).approve(address(swapper), amount); 41 | uint256 convertedAmount = swapper.swap(tokenA, tokenB, amount, address(this)); 42 | 43 | IERC20(tokenB).approve(address(gateway()), convertedAmount); 44 | gatewayWithToken().sendToken(sourceChain, recipient, tokenSymbolB, convertedAmount); 45 | } 46 | 47 | function contractId() external pure returns (bytes32) { 48 | return keccak256('destination-chain-swap-express'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test/gmp/ExecutableSample.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; 6 | 7 | contract ExecutableSample is AxelarExpressExecutableWithToken { 8 | string public value; 9 | string public sourceChain; 10 | string public sourceAddress; 11 | 12 | event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); 13 | event ExecutedWithToken( 14 | bytes32 commandId, 15 | string sourceChain, 16 | string sourceAddress, 17 | bytes payload, 18 | string symbol, 19 | uint256 amount 20 | ); 21 | 22 | constructor(address gateway_) AxelarExpressExecutableWithToken(gateway_) {} 23 | 24 | // Call this function to update the value of this contract along with all its siblings'. 25 | function setRemoteValue( 26 | string calldata destinationChain, 27 | string calldata destinationAddress, 28 | string calldata value_ 29 | ) external payable { 30 | bytes memory payload = abi.encode(value_); 31 | 32 | gatewayWithToken().callContract(destinationChain, destinationAddress, payload); 33 | } 34 | 35 | // Handles calls created by setAndSend. Updates this contract's value 36 | function _execute( 37 | bytes32 commandId_, 38 | string calldata sourceChain_, 39 | string calldata sourceAddress_, 40 | bytes calldata payload_ 41 | ) internal override { 42 | (value) = abi.decode(payload_, (string)); 43 | sourceChain = sourceChain_; 44 | sourceAddress = sourceAddress_; 45 | emit Executed(commandId_, sourceChain, sourceAddress, payload_); 46 | } 47 | 48 | function _executeWithToken( 49 | bytes32 commandId_, 50 | string calldata sourceChain_, 51 | string calldata sourceAddress_, 52 | bytes calldata payload_, 53 | string calldata tokenSymbol_, 54 | uint256 amount_ 55 | ) internal override { 56 | emit ExecutedWithToken(commandId_, sourceChain_, sourceAddress_, payload_, tokenSymbol_, amount_); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/utils/Pausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IPausable } from '../interfaces/IPausable.sol'; 6 | 7 | /** 8 | * @title Pausable 9 | * @notice This contract provides a mechanism to halt the execution of specific functions 10 | * if a pause condition is activated. 11 | */ 12 | contract Pausable is IPausable { 13 | // uint256(keccak256('paused')) - 1 14 | uint256 internal constant PAUSE_SLOT = 0xee35723ac350a69d2a92d3703f17439cbaadf2f093a21ba5bf5f1a53eb2a14d8; 15 | 16 | /** 17 | * @notice A modifier that throws a Paused custom error if the contract is paused 18 | * @dev This modifier should be used with functions that can be paused 19 | */ 20 | modifier whenNotPaused() { 21 | if (paused()) revert Pause(); 22 | _; 23 | } 24 | 25 | modifier whenPaused() { 26 | if (!paused()) revert NotPaused(); 27 | _; 28 | } 29 | 30 | /** 31 | * @notice Check if the contract is paused 32 | * @return paused_ A boolean representing the pause status. True if paused, false otherwise. 33 | */ 34 | function paused() public view returns (bool paused_) { 35 | assembly { 36 | paused_ := sload(PAUSE_SLOT) 37 | } 38 | } 39 | 40 | /** 41 | * @notice Pauses the contract 42 | * @dev This function should be callable by the owner/governance. 43 | */ 44 | function _pause() internal { 45 | _setPaused(true); 46 | emit Paused(msg.sender); 47 | } 48 | 49 | /** 50 | * @notice Unpauses the contract 51 | * @dev This function should be callable by the owner/governance. 52 | */ 53 | function _unpause() internal { 54 | _setPaused(false); 55 | emit Unpaused(msg.sender); 56 | } 57 | 58 | /** 59 | * @notice Sets the pause status of the contract 60 | * @dev This is an internal function, meaning it can only be called from within the contract itself 61 | * or from derived contracts. 62 | * @param paused_ The new pause status 63 | */ 64 | function _setPaused(bool paused_) internal { 65 | assembly { 66 | sstore(PAUSE_SLOT, paused_) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/interfaces/IDeployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IDeploy } from './IDeploy.sol'; 6 | 7 | /** 8 | * @title IDeployer Interface 9 | * @notice This interface defines the contract responsible for deploying and optionally initializing new contracts 10 | * via a specified deployment method. 11 | */ 12 | interface IDeployer is IDeploy { 13 | error DeployInitFailed(); 14 | 15 | event Deployed(address indexed deployedAddress, address indexed sender, bytes32 indexed salt, bytes32 bytecodeHash); 16 | 17 | /** 18 | * @notice Deploys a contract using a deployment method defined by derived contracts. 19 | * @param bytecode The bytecode of the contract to be deployed 20 | * @param salt A salt to influence the contract address 21 | * @return deployedAddress_ The address of the deployed contract 22 | */ 23 | function deploy(bytes memory bytecode, bytes32 salt) external payable returns (address deployedAddress_); 24 | 25 | /** 26 | * @notice Deploys a contract using a deployment method defined by derived contracts and initializes it. 27 | * @param bytecode The bytecode of the contract to be deployed 28 | * @param salt A salt to influence the contract address 29 | * @param init Init data used to initialize the deployed contract 30 | * @return deployedAddress_ The address of the deployed contract 31 | */ 32 | function deployAndInit( 33 | bytes memory bytecode, 34 | bytes32 salt, 35 | bytes calldata init 36 | ) external payable returns (address deployedAddress_); 37 | 38 | /** 39 | * @notice Returns the address where a contract will be stored if deployed via {deploy} or {deployAndInit} by `sender`. 40 | * @param bytecode The bytecode of the contract 41 | * @param sender The address that will deploy the contract 42 | * @param salt The salt that will be used to influence the contract address 43 | * @return deployedAddress_ The address that the contract will be deployed to 44 | */ 45 | function deployedAddress( 46 | bytes calldata bytecode, 47 | address sender, 48 | bytes32 salt 49 | ) external view returns (address deployedAddress_); 50 | } 51 | -------------------------------------------------------------------------------- /contracts/upgradable/Proxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IProxy } from '../interfaces/IProxy.sol'; 6 | import { IContractIdentifier } from '../interfaces/IContractIdentifier.sol'; 7 | import { BaseProxy } from './BaseProxy.sol'; 8 | 9 | /** 10 | * @title Proxy Contract 11 | * @notice A proxy contract that delegates calls to a designated implementation contract. Inherits from BaseProxy. 12 | * @dev The constructor takes in the address of the implementation contract, the owner address, and any optional setup 13 | * parameters for the implementation contract. 14 | */ 15 | contract Proxy is BaseProxy { 16 | /** 17 | * @notice Constructs the proxy contract with the implementation address, owner address, and optional setup parameters. 18 | * @param implementationAddress The address of the implementation contract 19 | * @param owner The owner address 20 | * @param setupParams Optional parameters to setup the implementation contract 21 | * @dev The constructor verifies that the owner address is not the zero address and that the contract ID of the implementation is valid. 22 | * It then stores the implementation address and owner address in their designated storage slots and calls the setup function on the 23 | * implementation (if setup params exist). 24 | */ 25 | constructor( 26 | address implementationAddress, 27 | address owner, 28 | bytes memory setupParams 29 | ) { 30 | if (owner == address(0)) revert InvalidOwner(); 31 | 32 | bytes32 id = contractId(); 33 | // Skipping the check if contractId() is not set by an inheriting proxy contract 34 | if (id != bytes32(0) && IContractIdentifier(implementationAddress).contractId() != id) 35 | revert InvalidImplementation(); 36 | 37 | assembly { 38 | sstore(_IMPLEMENTATION_SLOT, implementationAddress) 39 | sstore(_OWNER_SLOT, owner) 40 | } 41 | 42 | if (setupParams.length != 0) { 43 | (bool success, ) = implementationAddress.delegatecall( 44 | abi.encodeWithSelector(BaseProxy.setup.selector, setupParams) 45 | ); 46 | if (!success) revert SetupFailed(); 47 | } 48 | } 49 | 50 | function contractId() internal pure virtual override returns (bytes32) { 51 | return bytes32(0); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/deploy/Create2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IDeploy } from '../interfaces/IDeploy.sol'; 6 | import { ContractAddress } from '../libs/ContractAddress.sol'; 7 | 8 | /** 9 | * @title Create2 contract 10 | * @notice This contract can be used to deploy a contract with a deterministic address that depends on 11 | * the contract bytecode, deployer address, and deployment salt. 12 | */ 13 | contract Create2 is IDeploy { 14 | using ContractAddress for address; 15 | 16 | /** 17 | * @notice Deploys a new contract using the `CREATE2` method. 18 | * @dev This function deploys the contract using `CREATE2` opcode. 19 | * @param bytecode The bytecode of the contract to be deployed 20 | * @param deploySalt A salt to influence the contract address 21 | * @return deployed The address of the deployed contract 22 | */ 23 | function _create2(bytes memory bytecode, bytes32 deploySalt) internal returns (address deployed) { 24 | deployed = _create2Address(bytecode, deploySalt); 25 | 26 | if (bytecode.length == 0) revert EmptyBytecode(); 27 | if (deployed.isContract()) revert AlreadyDeployed(); 28 | 29 | assembly { 30 | deployed := create2(0, add(bytecode, 32), mload(bytecode), deploySalt) 31 | } 32 | 33 | if (deployed == address(0)) revert DeployFailed(); 34 | } 35 | 36 | /** 37 | * @notice Computes the deployed address that will result from the `CREATE2` method. 38 | * @param bytecode The bytecode of the contract to be deployed 39 | * @param deploySalt A salt to influence the contract address 40 | * @return address The deterministic contract address if it was deployed 41 | */ 42 | function _create2Address(bytes memory bytecode, bytes32 deploySalt) internal view returns (address) { 43 | return 44 | address( 45 | uint160( 46 | uint256( 47 | keccak256( 48 | abi.encodePacked( 49 | hex'ff', 50 | address(this), 51 | deploySalt, 52 | keccak256(bytecode) // init code hash 53 | ) 54 | ) 55 | ) 56 | ) 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarAmplifierGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IBaseAmplifierGateway } from './IBaseAmplifierGateway.sol'; 6 | import { IBaseWeightedMultisig } from './IBaseWeightedMultisig.sol'; 7 | import { IUpgradable } from './IUpgradable.sol'; 8 | 9 | import { WeightedSigners, Proof } from '../types/WeightedMultisigTypes.sol'; 10 | import { Message } from '../types/AmplifierGatewayTypes.sol'; 11 | 12 | /** 13 | * @title IAxelarAmplifierGateway 14 | * @dev Interface for the Axelar Gateway that supports general message passing. 15 | */ 16 | interface IAxelarAmplifierGateway is IBaseAmplifierGateway, IBaseWeightedMultisig, IUpgradable { 17 | error NotLatestSigners(); 18 | error InvalidSender(address sender); 19 | 20 | event OperatorshipTransferred(address newOperator); 21 | 22 | /** 23 | * @notice Approves an array of messages, signed by the Axelar signers. 24 | * @param messages The array of messages to verify. 25 | * @param proof The proof signed by the Axelar signers for this command. 26 | */ 27 | function approveMessages(Message[] calldata messages, Proof calldata proof) external; 28 | 29 | /** 30 | * @notice Update the signer data for the auth module, signed by the Axelar signers. 31 | * @param newSigners The data for the new signers. 32 | * @param proof The proof signed by the Axelar signers for this command. 33 | */ 34 | function rotateSigners(WeightedSigners memory newSigners, Proof calldata proof) external; 35 | 36 | /** 37 | * @notice This function takes dataHash and proof and reverts if proof is invalid 38 | * @param dataHash The hash of the data being signed 39 | * @param proof The proof from Axelar signers 40 | * @return isLatestSigners True if provided signers are the current ones 41 | */ 42 | function validateProof(bytes32 dataHash, Proof calldata proof) external view returns (bool isLatestSigners); 43 | 44 | /** 45 | * @notice Returns the address of the gateway operator. 46 | * @return The address of the operator. 47 | */ 48 | function operator() external view returns (address); 49 | 50 | /** 51 | * @notice Transfer the operatorship to a new address. 52 | * @param newOperator The address of the new operator. 53 | */ 54 | function transferOperatorship(address newOperator) external; 55 | } 56 | -------------------------------------------------------------------------------- /contracts/libs/SafeTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IERC20 } from '../interfaces/IERC20.sol'; 6 | 7 | error TokenTransferFailed(); 8 | 9 | /* 10 | * @title SafeTokenCall 11 | * @dev This library is used for performing safe token transfers. 12 | */ 13 | library SafeTokenCall { 14 | /* 15 | * @notice Make a safe call to a token contract. 16 | * @param token The token contract to interact with. 17 | * @param callData The function call data. 18 | * @throws TokenTransferFailed error if transfer of token is not successful. 19 | */ 20 | function safeCall(IERC20 token, bytes memory callData) internal { 21 | (bool success, bytes memory returnData) = address(token).call(callData); 22 | bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool))); 23 | 24 | if (!transferred || address(token).code.length == 0) revert TokenTransferFailed(); 25 | } 26 | } 27 | 28 | /* 29 | * @title SafeTokenTransfer 30 | * @dev This library safely transfers tokens from the contract to a recipient. 31 | */ 32 | library SafeTokenTransfer { 33 | /* 34 | * @notice Transfer tokens to a recipient. 35 | * @param token The token contract. 36 | * @param receiver The recipient of the tokens. 37 | * @param amount The amount of tokens to transfer. 38 | */ 39 | function safeTransfer( 40 | IERC20 token, 41 | address receiver, 42 | uint256 amount 43 | ) internal { 44 | SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount)); 45 | } 46 | } 47 | 48 | /* 49 | * @title SafeTokenTransferFrom 50 | * @dev This library helps to safely transfer tokens on behalf of a token holder. 51 | */ 52 | library SafeTokenTransferFrom { 53 | /* 54 | * @notice Transfer tokens on behalf of a token holder. 55 | * @param token The token contract. 56 | * @param from The address of the token holder. 57 | * @param to The address the tokens are to be sent to. 58 | * @param amount The amount of tokens to be transferred. 59 | */ 60 | function safeTransferFrom( 61 | IERC20 token, 62 | address from, 63 | address to, 64 | uint256 amount 65 | ) internal { 66 | SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/test/gmp/DestinationChainSwapExpress.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; 6 | import { IERC20 } from '../../interfaces/IERC20.sol'; 7 | import { DestinationChainTokenSwapper } from './DestinationChainTokenSwapper.sol'; 8 | 9 | contract DestinationChainSwapExpress is AxelarExpressExecutableWithToken { 10 | DestinationChainTokenSwapper public immutable swapper; 11 | 12 | event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); 13 | event ExecutedWithToken( 14 | bytes32 commandId, 15 | string sourceChain, 16 | string sourceAddress, 17 | bytes payload, 18 | string symbol, 19 | uint256 amount 20 | ); 21 | 22 | constructor(address gatewayAddress, address swapperAddress) AxelarExpressExecutableWithToken(gatewayAddress) { 23 | swapper = DestinationChainTokenSwapper(swapperAddress); 24 | } 25 | 26 | function _execute( 27 | bytes32 commandId, 28 | string calldata sourceChain, 29 | string calldata sourceAddress, 30 | bytes calldata payload 31 | ) internal override { 32 | emit Executed(commandId, sourceChain, sourceAddress, payload); 33 | } 34 | 35 | function _executeWithToken( 36 | bytes32 commandId, 37 | string calldata sourceChain, 38 | string calldata sourceAddress, 39 | bytes calldata payload, 40 | string calldata tokenSymbolA, 41 | uint256 amount 42 | ) internal override { 43 | (string memory tokenSymbolB, string memory recipient) = abi.decode(payload, (string, string)); 44 | 45 | address tokenA = gatewayWithToken().tokenAddresses(tokenSymbolA); 46 | address tokenB = gatewayWithToken().tokenAddresses(tokenSymbolB); 47 | 48 | IERC20(tokenA).approve(address(swapper), amount); 49 | uint256 convertedAmount = swapper.swap(tokenA, tokenB, amount, address(this)); 50 | 51 | IERC20(tokenB).approve(address(gatewayWithToken()), convertedAmount); 52 | gatewayWithToken().sendToken(sourceChain, recipient, tokenSymbolB, convertedAmount); 53 | emit ExecutedWithToken(commandId, sourceChain, sourceAddress, payload, tokenSymbolA, amount); 54 | } 55 | 56 | function contractId() external pure returns (bytes32) { 57 | return keccak256('destination-chain-swap-express'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/governance/Multisig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IMultisig } from '../interfaces/IMultisig.sol'; 6 | import { BaseMultisig } from './BaseMultisig.sol'; 7 | import { SafeNativeTransfer } from '../libs/SafeNativeTransfer.sol'; 8 | import { Caller } from '../utils/Caller.sol'; 9 | 10 | /** 11 | * @title Multisig Contract 12 | * @notice An extension of MultisigBase that can call functions on any contract. 13 | */ 14 | contract Multisig is Caller, BaseMultisig, IMultisig { 15 | using SafeNativeTransfer for address; 16 | 17 | /** 18 | * @notice Contract constructor 19 | * @dev Sets the initial list of signers and corresponding threshold. 20 | * @param accounts Address array of the signers 21 | * @param threshold Signature threshold required to validate a transaction 22 | */ 23 | constructor(address[] memory accounts, uint256 threshold) BaseMultisig(accounts, threshold) {} 24 | 25 | /** 26 | * @notice Executes an external contract call. 27 | * @notice This function is protected by the onlySigners requirement. 28 | * @dev Calls a target address with specified calldata and passing provided native value. 29 | * @param target The address of the contract to call 30 | * @param callData The data encoding the function and arguments to call 31 | * @param nativeValue The amount of native currency (e.g., ETH) to send along with the call 32 | * @return data return data from executed function call 33 | */ 34 | function executeContract( 35 | address target, 36 | bytes calldata callData, 37 | uint256 nativeValue 38 | ) external payable returns (bytes memory) { 39 | if (!_isFinalSignerVote()) return bytes(''); 40 | 41 | return _call(target, callData, nativeValue); 42 | } 43 | 44 | /** 45 | * @notice Withdraws native token from the contract. 46 | * @notice This function is protected by the onlySigners modifier. 47 | * @param recipient The address to send the native token to 48 | * @param amount The amount of native token to send 49 | * @dev This function is only callable by the contract itself after passing according proposal 50 | */ 51 | function withdraw(address recipient, uint256 amount) external onlySigners { 52 | recipient.safeNativeTransfer(amount); 53 | } 54 | 55 | /** 56 | * @notice Making contact able to receive native value 57 | */ 58 | receive() external payable {} 59 | } 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | typechain 3 | examples/metamask/chain*.json 4 | examples/metamask/abi/**.json 5 | **/.DS_Store 6 | ./test.js 7 | ./quick.json 8 | local.json 9 | temp.js 10 | temp2.js 11 | keys.json 12 | 13 | #Hardhat files 14 | cache 15 | 16 | # Logs 17 | logs 18 | *.log 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | lerna-debug.log* 23 | 24 | # Diagnostic reports (https://nodejs.org/api/report.html) 25 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 26 | 27 | # Runtime data 28 | pids 29 | *.pid 30 | *.seed 31 | *.pid.lock 32 | 33 | # Directory for instrumented libs generated by jscoverage/JSCover 34 | lib-cov 35 | 36 | # Coverage directory used by tools like istanbul 37 | coverage 38 | coverage.json 39 | *.lcov 40 | 41 | # nyc test coverage 42 | .nyc_output 43 | 44 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 45 | .grunt 46 | 47 | # Bower dependency directory (https://bower.io/) 48 | bower_components 49 | 50 | # node-waf configuration 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | build/Release 55 | 56 | # Dependency directories 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # TypeScript cache 63 | *.tsbuildinfo 64 | 65 | # Optional npm cache directory 66 | .npm 67 | 68 | # Optional eslint cache 69 | .eslintcache 70 | 71 | # Microbundle cache 72 | .rpt2_cache/ 73 | .rts2_cache_cjs/ 74 | .rts2_cache_es/ 75 | .rts2_cache_umd/ 76 | 77 | # Optional REPL history 78 | .node_repl_history 79 | 80 | # Output of 'npm pack' 81 | *.tgz 82 | 83 | # Yarn Integrity file 84 | .yarn-integrity 85 | 86 | # dotenv environment variables file 87 | .env 88 | .env.test 89 | 90 | # parcel-bundler cache (https://parceljs.org/) 91 | .cache 92 | 93 | # Next.js build output 94 | .next 95 | dist 96 | 97 | # Nuxt.js build / generate output 98 | .nuxt 99 | 100 | # Gatsby files 101 | .cache/ 102 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 103 | # https://nextjs.org/blog/next-9-1#public-directory-support 104 | # public 105 | 106 | # vuepress build output 107 | .vuepress/dist 108 | 109 | # Serverless directories 110 | .serverless/ 111 | 112 | # FuseBox cache 113 | .fusebox/ 114 | 115 | # DynamoDB Local files 116 | .dynamodb/ 117 | 118 | # TernJS port file 119 | .tern-port 120 | 121 | # IDE 122 | .idea 123 | 124 | # Build 125 | artifacts 126 | /interfaces 127 | temp-arguments.js 128 | -------------------------------------------------------------------------------- /contracts/interfaces/IBaseWeightedMultisig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IBaseWeightedMultisig { 6 | error InvalidSigners(); 7 | error InvalidThreshold(); 8 | error MalformedSignatures(); 9 | error LowSignaturesWeight(); 10 | error InvalidWeights(); 11 | error DuplicateSigners(bytes32 signersHash); 12 | error RedundantSignaturesProvided(uint256 required, uint256 provided); 13 | error InsufficientRotationDelay(uint256 minimumRotationDelay, uint256 lastRotationTimestamp, uint256 timeElapsed); 14 | 15 | event SignersRotated(uint256 indexed epoch, bytes32 indexed signersHash, bytes signers); 16 | 17 | /** 18 | * @dev This function returns the old signers retention period 19 | * @return uint256 The old signers retention period 20 | */ 21 | function previousSignersRetention() external view returns (uint256); 22 | 23 | /** 24 | * @dev This function returns the current signers epoch 25 | * @return uint256 The current signers epoch 26 | */ 27 | function epoch() external view returns (uint256); 28 | 29 | /** 30 | * @dev Returns the hash for a given signers epoch 31 | * @param signerEpoch The epoch to get the hash for 32 | * @return The hash for the given epoch 33 | */ 34 | function signersHashByEpoch(uint256 signerEpoch) external view returns (bytes32); 35 | 36 | /** 37 | * @dev Returns the epoch for a given hash 38 | * @param signersHash The hash to get the epoch for 39 | * @return The epoch for the given hash 40 | */ 41 | function epochBySignersHash(bytes32 signersHash) external view returns (uint256); 42 | 43 | /** 44 | * @notice This function returns the timestamp for the last signer rotation 45 | * @return uint256 The last rotation timestamp 46 | */ 47 | function lastRotationTimestamp() external view returns (uint256); 48 | 49 | /** 50 | * @notice This function returns the time elapsed (in secs) since the last rotation 51 | * @return uint256 The time since the last rotation 52 | */ 53 | function timeSinceRotation() external view returns (uint256); 54 | 55 | /** 56 | * @notice Compute the message hash that is signed by the weighted signers 57 | * @param signersHash The hash of the weighted signers that sign off on the data 58 | * @param dataHash The hash of the data 59 | * @return The message hash to be signed 60 | */ 61 | function messageHashToSign(bytes32 signersHash, bytes32 dataHash) external view returns (bytes32); 62 | } 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@axelar-network/axelar-gmp-sdk-solidity", 3 | "version": "6.0.6", 4 | "description": "Solidity GMP SDK and utilities provided by Axelar for cross-chain development", 5 | "main": "index.js", 6 | "overrides": { 7 | "elliptic": "^6.6.1" 8 | }, 9 | "scripts": { 10 | "build": "npx hardhat clean && npx hardhat compile && npm run copy:interfaces", 11 | "test": "npx hardhat test", 12 | "check": "npx hardhat clean && npx hardhat compile && npx hardhat check", 13 | "test-evm-versions": "bash scripts/test-evm-versions.sh", 14 | "copy:interfaces": "rm -rf interfaces && mkdir interfaces && cp artifacts/contracts/interfaces/*/*.json interfaces/ && rm interfaces/*.dbg.json", 15 | "clean:artifacts": "rm -rf artifacts/build-info artifacts/*/test artifacts/contracts/*/*/*.dbg.json", 16 | "lint": "solhint 'contracts/**/*.sol' && eslint 'scripts/**/*.js' 'test/**/*.js'", 17 | "prettier": "prettier --write 'contracts/**/*.sol' 'test/**/*.js' 'scripts/**/*.js' '*.js'", 18 | "release": "npm run build && npm run flatten && npm run clean:artifacts && changeset publish", 19 | "release-snapshot": "npm run build && npm run flatten && npm run clean:artifacts && npm version 0.0.0-snapshot.$(git rev-parse --short HEAD) --git-tag-version=false && npm publish --no-git-checks --tag snapshot --access public", 20 | "cs": "changeset", 21 | "flatten": "sh scripts/flatten-contracts.sh", 22 | "coverage": "cross-env COVERAGE=true hardhat coverage", 23 | "generate-overview-docs": "node scripts/generateOverviewDocs.js" 24 | }, 25 | "author": "axelar-network", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "@axelar-network/axelar-chains-config": "^1.2.0", 29 | "@changesets/cli": "^2.27.9", 30 | "@nomicfoundation/hardhat-toolbox": "^2.0.2", 31 | "elliptic": "^6.6.1", 32 | "cross-env": "^7.0.3", 33 | "eslint": "^8.57.0", 34 | "eslint-config-richardpringle": "^2.0.0", 35 | "fs-extra": "^11.1.1", 36 | "hardhat": "~2.22.3", 37 | "hardhat-contract-sizer": "^2.10.0", 38 | "hardhat-storage-layout": "^0.1.7", 39 | "lodash": "^4.17.21", 40 | "mocha": "^10.2.0", 41 | "prettier": "^2.8.8", 42 | "prettier-plugin-solidity": "1.0.0-beta.19", 43 | "solhint": "^4.5.2", 44 | "solidity-docgen": "^0.6.0-beta.36" 45 | }, 46 | "engines": { 47 | "node": ">=18" 48 | }, 49 | "publishConfig": { 50 | "access": "public" 51 | }, 52 | "files": [ 53 | "artifacts", 54 | "contracts", 55 | "interfaces", 56 | "scripts", 57 | "README.md", 58 | "hardhat.config.js" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /contracts/upgradable/BaseProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IProxy } from '../interfaces/IProxy.sol'; 6 | 7 | /** 8 | * @title BaseProxy Contract 9 | * @dev This abstract contract implements a basic proxy that stores an implementation address. Fallback function 10 | * calls are delegated to the implementation. This contract is meant to be inherited by other proxy contracts. 11 | */ 12 | abstract contract BaseProxy is IProxy { 13 | // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) 14 | bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 15 | // keccak256('owner') 16 | bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; 17 | 18 | /** 19 | * @dev Returns the current implementation address. 20 | * @return implementation_ The address of the current implementation contract 21 | */ 22 | function implementation() public view virtual returns (address implementation_) { 23 | assembly { 24 | implementation_ := sload(_IMPLEMENTATION_SLOT) 25 | } 26 | } 27 | 28 | /** 29 | * @dev Shadows the setup function of the implementation contract so it can't be called directly via the proxy. 30 | * @param params The setup parameters for the implementation contract. 31 | */ 32 | function setup(bytes calldata params) external {} 33 | 34 | /** 35 | * @dev Returns the contract ID. It can be used as a check during upgrades. Meant to be implemented in derived contracts. 36 | * @return bytes32 The contract ID 37 | */ 38 | function contractId() internal pure virtual returns (bytes32); 39 | 40 | /** 41 | * @dev Fallback function. Delegates the call to the current implementation contract. 42 | */ 43 | fallback() external payable virtual { 44 | address implementation_ = implementation(); 45 | assembly { 46 | calldatacopy(0, 0, calldatasize()) 47 | 48 | let result := delegatecall(gas(), implementation_, 0, calldatasize(), 0, 0) 49 | returndatacopy(0, 0, returndatasize()) 50 | 51 | switch result 52 | case 0 { 53 | revert(0, returndatasize()) 54 | } 55 | default { 56 | return(0, returndatasize()) 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * @dev Payable fallback function. Can be overridden in derived contracts. 63 | */ 64 | receive() external payable virtual {} 65 | } 66 | -------------------------------------------------------------------------------- /contracts/interfaces/IInterchainTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IInterchainTransferSent 7 | * @dev Interface for tracking asset value transfers using General Message Passing (GMP) calls in the Axelar Network. 8 | * This interface defines an event that should be emitted when a GMP transfer is sent, 9 | * allowing for standardized volume tracking across different implementations. 10 | */ 11 | interface IInterchainTransferSent { 12 | /** 13 | * @dev Emitted when a GMP transfer is sent, providing details for volume tracking. 14 | * @param destinationChain The Axelar chain identifier of the destination chain. 15 | * @param destinationContractAddress The address of the contract on the destination chain that receives the transfer. 16 | * @param recipient The address of the final recipient of the transferred assets on the destination chain. 17 | * @param token The address of the token contract on the source chain. 18 | * @param amount The amount (in atomic units) of tokens transferred. 19 | */ 20 | event InterchainTransferSent( 21 | string destinationChain, 22 | string destinationContractAddress, 23 | address indexed sender, 24 | bytes recipient, 25 | address indexed token, 26 | uint256 amount 27 | ); 28 | } 29 | 30 | /** 31 | * @title IInterchainTransferReceived 32 | * @dev Interface for tracking asset value transfers using General Message Passing (GMP) calls in the Axelar Network. 33 | * This interface defines an event that should be emitted when a GMP transfer is received, 34 | * allowing for standardized volume tracking across different implementations. 35 | */ 36 | interface IInterchainTransferReceived { 37 | /** 38 | * @dev Emitted when an interchain transfer is received, providing details for volume tracking. 39 | * @param sourceChain The Axelar chain identifier of the source chain. 40 | * @param sourceAddress The address of the contract that initiated the transfer on the source chain. 41 | * @param sender The address of the sender in case it is different from the source contract address 42 | * @param recipient The address of the final recipient of the transferred assets on the destination chain. 43 | * @param token The address of the token contract on the destination chain. 44 | * @param amount The amount (in atomic units) of tokens received. 45 | */ 46 | event InterchainTransferReceived( 47 | string sourceChain, 48 | string sourceAddress, 49 | bytes sender, 50 | address indexed recipient, 51 | address indexed token, 52 | uint256 amount 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /contracts/interfaces/IBaseMultisig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IMultisigBase Interface 7 | * @notice An interface defining the base operations for a multi-signature contract. 8 | */ 9 | interface IBaseMultisig { 10 | error NotSigner(); 11 | error AlreadyVoted(); 12 | error InvalidSigners(); 13 | error InvalidSignerThreshold(); 14 | error DuplicateSigner(address account); 15 | 16 | /**********\ 17 | |* Events *| 18 | \**********/ 19 | 20 | event MultisigVoted( 21 | bytes32 indexed topic, 22 | uint256 indexed signerEpoch, 23 | address indexed voter, 24 | uint256 voteCount, 25 | uint256 threshold 26 | ); 27 | 28 | event MultisigOperationExecuted( 29 | bytes32 indexed topic, 30 | uint256 indexed signerEpoch, 31 | address indexed voter, 32 | uint256 threshold 33 | ); 34 | 35 | event SignersRotated(address[] newAccounts, uint256 newThreshold); 36 | 37 | /***********\ 38 | |* Getters *| 39 | \***********/ 40 | 41 | /** 42 | * @notice Gets the current epoch. 43 | * @return uint The current epoch 44 | */ 45 | function signerEpoch() external view returns (uint256); 46 | 47 | /** 48 | * @notice Gets the threshold of current signers. 49 | * @return uint The threshold number 50 | */ 51 | function signerThreshold() external view returns (uint256); 52 | 53 | /** 54 | * @notice Gets the array of current signers. 55 | * @return array of signer addresses 56 | */ 57 | function signerAccounts() external view returns (address[] memory); 58 | 59 | /** 60 | * @notice Getter to determine if an account is a signer 61 | * @return boolean indicating if the account is a signer 62 | */ 63 | function isSigner(address account) external view returns (bool); 64 | 65 | /** 66 | * @notice Getter to determine if an account has voted on a topic 67 | * @return boolean indicating if the account has voted 68 | */ 69 | function hasSignerVoted(address account, bytes32 topic) external view returns (bool); 70 | 71 | /** 72 | * @notice Get the number of votes for a topic 73 | * @return uint256 indicating the number of votes for a topic 74 | */ 75 | function getSignerVotesCount(bytes32 topic) external view returns (uint256); 76 | 77 | /***********\ 78 | |* Setters *| 79 | \***********/ 80 | 81 | /** 82 | * @notice Update the signers and threshold for the multisig contract. 83 | * @param newAccounts The array of new signers 84 | * @param newThreshold The new threshold of signers required 85 | */ 86 | function rotateSigners(address[] memory newAccounts, uint256 newThreshold) external; 87 | } 88 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarExpressExecutable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarExecutable } from './IAxelarExecutable.sol'; 6 | 7 | /** 8 | * @title IAxelarExpressExecutable 9 | * @notice Interface for the Axelar Express Executable contract. 10 | */ 11 | interface IAxelarExpressExecutable is IAxelarExecutable { 12 | // Custom errors 13 | error AlreadyExecuted(); 14 | error InsufficientValue(); 15 | 16 | /** 17 | * @notice Emitted when an express execution is successfully performed. 18 | * @param commandId The unique identifier for the command. 19 | * @param sourceChain The source chain. 20 | * @param sourceAddress The source address. 21 | * @param payloadHash The hash of the payload. 22 | * @param expressExecutor The address of the express executor. 23 | */ 24 | event ExpressExecuted( 25 | bytes32 indexed commandId, 26 | string sourceChain, 27 | string sourceAddress, 28 | bytes32 payloadHash, 29 | address indexed expressExecutor 30 | ); 31 | 32 | /** 33 | * @notice Emitted when an express execution is fulfilled. 34 | * @param commandId The commandId for the contractCall. 35 | * @param sourceChain The source chain. 36 | * @param sourceAddress The source address. 37 | * @param payloadHash The hash of the payload. 38 | * @param expressExecutor The address of the express executor. 39 | */ 40 | event ExpressExecutionFulfilled( 41 | bytes32 indexed commandId, 42 | string sourceChain, 43 | string sourceAddress, 44 | bytes32 payloadHash, 45 | address indexed expressExecutor 46 | ); 47 | 48 | /** 49 | * @notice Returns the express executor for a given command. 50 | * @param commandId The commandId for the contractCall. 51 | * @param sourceChain The source chain. 52 | * @param sourceAddress The source address. 53 | * @param payloadHash The hash of the payload. 54 | * @return expressExecutor The address of the express executor. 55 | */ 56 | function getExpressExecutor( 57 | bytes32 commandId, 58 | string calldata sourceChain, 59 | string calldata sourceAddress, 60 | bytes32 payloadHash 61 | ) external view returns (address expressExecutor); 62 | 63 | /** 64 | * @notice Express executes a contract call. 65 | * @param commandId The commandId for the contractCall. 66 | * @param sourceChain The source chain. 67 | * @param sourceAddress The source address. 68 | * @param payload The payload data. 69 | */ 70 | function expressExecute( 71 | bytes32 commandId, 72 | string calldata sourceChain, 73 | string calldata sourceAddress, 74 | bytes calldata payload 75 | ) external payable; 76 | } 77 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarServiceGovernance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IInterchainGovernance } from './IInterchainGovernance.sol'; 6 | 7 | /** 8 | * @title IAxelarServiceGovernance Interface 9 | * @dev This interface extends IInterchainGovernance for operator proposal actions 10 | */ 11 | interface IAxelarServiceGovernance is IInterchainGovernance { 12 | error InvalidOperator(); 13 | error NotApproved(); 14 | error NotAuthorized(); 15 | 16 | event OperatorProposalApproved( 17 | bytes32 indexed proposalHash, 18 | address indexed targetContract, 19 | bytes callData, 20 | uint256 nativeValue 21 | ); 22 | 23 | event OperatorProposalCancelled( 24 | bytes32 indexed proposalHash, 25 | address indexed targetContract, 26 | bytes callData, 27 | uint256 nativeValue 28 | ); 29 | 30 | event OperatorProposalExecuted( 31 | bytes32 indexed proposalHash, 32 | address indexed targetContract, 33 | bytes callData, 34 | uint256 nativeValue 35 | ); 36 | 37 | event OperatorshipTransferred(address indexed oldOperator, address indexed newOperator); 38 | 39 | /** 40 | * @notice Returns whether an operator proposal has been approved 41 | * @param proposalHash The hash of the proposal 42 | * @return bool True if the proposal has been approved, False otherwise 43 | */ 44 | function operatorApprovals(bytes32 proposalHash) external view returns (bool); 45 | 46 | /** 47 | * @notice Returns whether an operator proposal has been approved 48 | * @param target The address of the contract targeted by the proposal 49 | * @param callData The call data to be sent to the target contract 50 | * @param nativeValue The amount of native tokens to be sent to the target contract 51 | * @return bool True if the proposal has been approved, False otherwise 52 | */ 53 | function isOperatorProposalApproved( 54 | address target, 55 | bytes calldata callData, 56 | uint256 nativeValue 57 | ) external view returns (bool); 58 | 59 | /** 60 | * @notice Executes an operator proposal 61 | * @param targetContract The target address the proposal will call 62 | * @param callData The data that encodes the function and arguments to call on the target contract 63 | */ 64 | function executeOperatorProposal( 65 | address targetContract, 66 | bytes calldata callData, 67 | uint256 value 68 | ) external payable; 69 | 70 | /** 71 | * @notice Transfers the operator address to a new address 72 | * @dev Only the current operator or the governance can call this function 73 | * @param newOperator The new operator address 74 | */ 75 | function transferOperatorship(address newOperator) external; 76 | } 77 | -------------------------------------------------------------------------------- /test/deploy/Create3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const { expect } = chai; 5 | const { ethers } = require('hardhat'); 6 | 7 | const { getSaltFromKey } = require('../../scripts/utils'); 8 | const BurnableMintableCappedERC20 = require('../../artifacts/contracts/test/token/ERC20MintableBurnable.sol/ERC20MintableBurnable.json'); 9 | const { ContractFactory } = require('ethers'); 10 | 11 | describe('Create3Deployer', () => { 12 | let deployerWallet; 13 | let userWallet; 14 | 15 | let deployerFactory; 16 | let deployer; 17 | const name = 'test'; 18 | const symbol = 'test'; 19 | const decimals = 16; 20 | 21 | before(async () => { 22 | [deployerWallet, userWallet] = await ethers.getSigners(); 23 | 24 | deployerFactory = await ethers.getContractFactory('TestCreate3', deployerWallet); 25 | }); 26 | 27 | beforeEach(async () => { 28 | deployer = await deployerFactory.deploy().then((d) => d.deployed()); 29 | }); 30 | 31 | describe('deploy', () => { 32 | it('should revert on deploy with empty bytecode', async () => { 33 | const key = 'a test key'; 34 | const salt = getSaltFromKey(key); 35 | const bytecode = '0x'; 36 | 37 | await expect(deployer.connect(userWallet).deploy(bytecode, salt)).to.be.revertedWithCustomError( 38 | deployer, 39 | 'EmptyBytecode', 40 | ); 41 | }); 42 | 43 | it('should deploy to the predicted address', async () => { 44 | const key = 'a test key'; 45 | const salt = getSaltFromKey(key); 46 | 47 | const address = await deployer.deployedAddress(salt); 48 | 49 | const factory = new ContractFactory(BurnableMintableCappedERC20.abi, BurnableMintableCappedERC20.bytecode); 50 | const bytecode = factory.getDeployTransaction(name, symbol, decimals).data; 51 | 52 | await expect(deployer.deploy(bytecode, salt)).to.emit(deployer, 'Deployed').withArgs(address); 53 | }); 54 | 55 | it('should not forward native value', async () => { 56 | const key = 'a test key'; 57 | const salt = getSaltFromKey(key); 58 | 59 | const address = await deployer.deployedAddress(salt); 60 | 61 | const factory = new ContractFactory(BurnableMintableCappedERC20.abi, BurnableMintableCappedERC20.bytecode); 62 | const bytecode = factory.getDeployTransaction(name, symbol, decimals).data; 63 | 64 | await expect(deployer.deploy(bytecode, salt, { value: 10 })) 65 | .to.emit(deployer, 'Deployed') 66 | .withArgs(address); 67 | 68 | expect(await ethers.provider.getBalance(address)).to.equal(0); 69 | expect(await ethers.provider.getBalance(deployer.address)).to.equal(10); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /contracts/test/gmp/ValuedExpressExecutableTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { AxelarValuedExpressExecutableWithToken } from '../../express/AxelarValuedExpressExecutableWithToken.sol'; 6 | 7 | contract AxelarValuedExpressExecutableTest is AxelarValuedExpressExecutableWithToken { 8 | event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); 9 | event ExecutedWithToken( 10 | bytes32 commandId, 11 | string sourceChain, 12 | string sourceAddress, 13 | bytes payload, 14 | string symbol, 15 | uint256 amount 16 | ); 17 | 18 | uint256 public callValue; 19 | uint256 public callWithTokenValue; 20 | address public expressToken; 21 | 22 | constructor(address gateway_) AxelarValuedExpressExecutableWithToken(gateway_) {} 23 | 24 | function setExpressToken(address expressToken_) external { 25 | expressToken = expressToken_; 26 | } 27 | 28 | function setCallValue(uint256 callValue_) external { 29 | callValue = callValue_; 30 | } 31 | 32 | // Returns the amount of token (corresponding to `tokenAddress`) that this call is worth. If `tokenAddress` is address(0), then amount is in terms of the native token. 33 | function contractCallValue( 34 | string calldata, /*sourceChain*/ 35 | string calldata, /*sourceAddress*/ 36 | bytes calldata /*payload*/ 37 | ) public view override returns (address tokenAddress, uint256 value) { 38 | value = callValue; 39 | tokenAddress = expressToken; 40 | } 41 | 42 | // Returns the amount of token (corresponding to `tokenAddress`) that this call is worth. If `tokenAddress` is address(0), then amount is in terms of the native token. 43 | function contractCallWithTokenValue( 44 | string calldata, /*sourceChain*/ 45 | string calldata, /*sourceAddress*/ 46 | bytes calldata, /*payload*/ 47 | string calldata, /*symbol*/ 48 | uint256 /*amount*/ 49 | ) public view override returns (address tokenAddress, uint256 value) { 50 | value = callValue; 51 | tokenAddress = expressToken; 52 | } 53 | 54 | function _execute( 55 | bytes32 commandId, 56 | string calldata sourceChain, 57 | string calldata sourceAddress, 58 | bytes calldata payload 59 | ) internal override { 60 | emit Executed(commandId, sourceChain, sourceAddress, payload); 61 | } 62 | 63 | function _executeWithToken( 64 | bytes32 commandId, 65 | string calldata sourceChain, 66 | string calldata sourceAddress, 67 | bytes calldata payload, 68 | string calldata symbol, 69 | uint256 amount 70 | ) internal override { 71 | emit ExecutedWithToken(commandId, sourceChain, sourceAddress, payload, symbol, amount); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/deploy/Create2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const { expect } = chai; 5 | const { ethers } = require('hardhat'); 6 | 7 | const { getSaltFromKey } = require('../../scripts/utils'); 8 | const BurnableMintableCappedERC20 = require('../../artifacts/contracts/test/token/ERC20MintableBurnable.sol/ERC20MintableBurnable.json'); 9 | const { ContractFactory } = require('ethers'); 10 | 11 | describe('Create2Deployer', () => { 12 | let deployerWallet; 13 | let userWallet; 14 | 15 | let deployerFactory; 16 | let deployer; 17 | const name = 'test'; 18 | const symbol = 'test'; 19 | const decimals = 16; 20 | 21 | before(async () => { 22 | [deployerWallet, userWallet] = await ethers.getSigners(); 23 | 24 | deployerFactory = await ethers.getContractFactory('TestCreate2', deployerWallet); 25 | }); 26 | 27 | beforeEach(async () => { 28 | deployer = await deployerFactory.deploy().then((d) => d.deployed()); 29 | }); 30 | 31 | describe('deploy', () => { 32 | it('should revert on deploy with empty bytecode', async () => { 33 | const key = 'a test key'; 34 | const salt = getSaltFromKey(key); 35 | const bytecode = '0x'; 36 | 37 | await expect(deployer.connect(userWallet).deploy(bytecode, salt)).to.be.revertedWithCustomError( 38 | deployer, 39 | 'EmptyBytecode', 40 | ); 41 | }); 42 | 43 | it('should deploy to the predicted address', async () => { 44 | const key = 'a test key'; 45 | const salt = getSaltFromKey(key); 46 | 47 | const factory = new ContractFactory(BurnableMintableCappedERC20.abi, BurnableMintableCappedERC20.bytecode); 48 | const bytecode = factory.getDeployTransaction(name, symbol, decimals).data; 49 | 50 | const address = await deployer.deployedAddress(bytecode, salt); 51 | 52 | await expect(deployer.deploy(bytecode, salt)).to.emit(deployer, 'Deployed').withArgs(address); 53 | }); 54 | 55 | it('should not forward native value', async () => { 56 | const key = 'a test key'; 57 | const salt = getSaltFromKey(key); 58 | 59 | const factory = new ContractFactory(BurnableMintableCappedERC20.abi, BurnableMintableCappedERC20.bytecode); 60 | const bytecode = factory.getDeployTransaction(name, symbol, decimals).data; 61 | 62 | const address = await deployer.deployedAddress(bytecode, salt); 63 | 64 | await expect(deployer.deploy(bytecode, salt, { value: 10 })) 65 | .to.emit(deployer, 'Deployed') 66 | .withArgs(address); 67 | 68 | expect(await ethers.provider.getBalance(address)).to.equal(0); 69 | expect(await ethers.provider.getBalance(deployer.address)).to.equal(10); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/utils/Multicall.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const { ethers } = require('hardhat'); 5 | const { defaultAbiCoder } = ethers.utils; 6 | const { expect } = chai; 7 | const { deployContract } = require('../utils.js'); 8 | 9 | describe('Mutlicall', () => { 10 | let test; 11 | let function1Data; 12 | let function2Data; 13 | let function3Data; 14 | let function4Data; 15 | let ownerWallet; 16 | 17 | before(async () => { 18 | const wallets = await ethers.getSigners(); 19 | ownerWallet = wallets[0]; 20 | 21 | test = await deployContract(ownerWallet, 'TestMulticall'); 22 | function1Data = (await test.populateTransaction.function1()).data; 23 | function2Data = (await test.populateTransaction.function2()).data; 24 | function3Data = (await test.populateTransaction.function3()).data; 25 | function4Data = (await test.populateTransaction.function4()).data; 26 | }); 27 | 28 | it('should test the multicall', async () => { 29 | const nonce = Number(await test.nonce()); 30 | await expect(test.multicall([function1Data, function2Data, function2Data, function1Data])) 31 | .to.emit(test, 'Function1Called') 32 | .withArgs(nonce) 33 | .and.to.emit(test, 'Function2Called') 34 | .withArgs(nonce + 1) 35 | .and.to.emit(test, 'Function2Called') 36 | .withArgs(nonce + 2) 37 | .and.to.emit(test, 'Function1Called') 38 | .withArgs(nonce + 3); 39 | }); 40 | 41 | it('should test the multicall returns', async () => { 42 | const nonce = Number(await test.nonce()); 43 | await expect(test.multicallTest([function2Data, function1Data, function2Data, function2Data])) 44 | .to.emit(test, 'Function2Called') 45 | .withArgs(nonce) 46 | .and.to.emit(test, 'Function1Called') 47 | .withArgs(nonce + 1) 48 | .and.to.emit(test, 'Function2Called') 49 | .withArgs(nonce + 2) 50 | .and.to.emit(test, 'Function2Called') 51 | .withArgs(nonce + 3); 52 | const lastReturns = await test.getLastMulticallReturns(); 53 | 54 | for (let i = 0; i < lastReturns.length; i++) { 55 | const val = Number(defaultAbiCoder.decode(['uint256'], lastReturns[i])); 56 | expect(val).to.equal(nonce + i); 57 | } 58 | }); 59 | 60 | it('should revert if any of the calls fail', async () => { 61 | await expect(test.multicall([function1Data, function2Data, function3Data, function1Data])).to.be.revertedWith( 62 | 'function3 failed', 63 | ); 64 | }); 65 | 66 | it('should revert with error if a call fails without revert data', async () => { 67 | await expect(test.multicall([function1Data, function4Data])).to.be.revertedWithCustomError( 68 | test, 69 | 'MulticallFailed', 70 | ); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /contracts/upgradable/InitProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IInitProxy } from '../interfaces/IInitProxy.sol'; 6 | import { IContractIdentifier } from '../interfaces/IContractIdentifier.sol'; 7 | import { BaseProxy } from './BaseProxy.sol'; 8 | 9 | /** 10 | * @title InitProxy Contract 11 | * @notice A proxy contract that can be initialized to use a specified implementation and owner. Inherits from BaseProxy 12 | * and implements the IInitProxy interface. 13 | * @dev This proxy is constructed empty and then later initialized with the implementation contract address, new owner address, 14 | * and any optional setup parameters. 15 | */ 16 | contract InitProxy is BaseProxy, IInitProxy { 17 | /** 18 | * @dev Initializes the contract and sets the caller as the owner of the contract. 19 | */ 20 | constructor() { 21 | assembly { 22 | sstore(_OWNER_SLOT, caller()) 23 | } 24 | } 25 | 26 | function contractId() internal pure virtual override returns (bytes32) { 27 | return bytes32(0); 28 | } 29 | 30 | /** 31 | * @notice Initializes the proxy contract with the specified implementation, new owner, and any optional setup parameters. 32 | * @param implementationAddress The address of the implementation contract 33 | * @param newOwner The address of the new proxy owner 34 | * @param params Optional parameters to be passed to the setup function of the implementation contract 35 | * @dev This function is only callable by the owner of the proxy. If the proxy has already been initialized, it will revert. 36 | * If the contract ID of the implementation is incorrect, it will also revert. It then stores the implementation address and 37 | * new owner address in the designated storage slots and calls the setup function on the implementation (if setup params exist). 38 | */ 39 | function init( 40 | address implementationAddress, 41 | address newOwner, 42 | bytes memory params 43 | ) external { 44 | address owner; 45 | 46 | assembly { 47 | owner := sload(_OWNER_SLOT) 48 | } 49 | 50 | if (msg.sender != owner) revert NotOwner(); 51 | if (implementation() != address(0)) revert AlreadyInitialized(); 52 | 53 | bytes32 id = contractId(); 54 | // Skipping the check if contractId() is not set by an inheriting proxy contract 55 | if (id != bytes32(0) && IContractIdentifier(implementationAddress).contractId() != id) 56 | revert InvalidImplementation(); 57 | 58 | assembly { 59 | sstore(_IMPLEMENTATION_SLOT, implementationAddress) 60 | sstore(_OWNER_SLOT, newOwner) 61 | } 62 | 63 | if (params.length != 0) { 64 | (bool success, ) = implementationAddress.delegatecall( 65 | abi.encodeWithSelector(BaseProxy.setup.selector, params) 66 | ); 67 | if (!success) revert SetupFailed(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Axelar GMP SDK Solidity 2 | 3 | This repository contains all the necessary ingredients for successful cross-chain development 4 | utilizing the Axelar General Message Passing protocol. 5 | 6 | ## Documentation 7 | 8 | * [Introduction](https://docs.axelar.dev/dev/intro) 9 | * [General Message Passing](https://docs.axelar.dev/dev/gmp-overview) 10 | * [Build](https://docs.axelar.dev/dev/build/getting-started) 11 | * [Video](https://docs.axelar.dev/dev/guides/video-guides) 12 | 13 | ## Build 14 | 15 | We recommend using the latest Node.js [LTS version](https://nodejs.org/en/about/releases/). 16 | 17 | ```bash 18 | npm ci 19 | 20 | npm run build 21 | 22 | npm run test 23 | ``` 24 | 25 | Pre-compiled bytecodes can be found under [Releases](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/releases). 26 | Furthermore, pre-compiled bytecodes and ABI are shipped in the [npm package](https://www.npmjs.com/package/@axelar-network/axelar-gmp-sdk-solidity) and can be imported via: 27 | 28 | ```bash 29 | npm install @axelar-network/axelar-gmp-sdk-solidity 30 | ``` 31 | 32 | ```javascript 33 | const IAxelarExecutable = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IAxelarExecutable.json'); 34 | 35 | const Upgradable = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/upgradable/Upgradable.sol/Upgradable.json'); 36 | ``` 37 | 38 | Unit tests can also be run against live networks for integration testing, see [here](https://github.com/axelarnetwork/axelar-cgp-solidity#live-network-testing). 39 | 40 | ### Development 41 | 42 | Check gas usage 43 | ```bash 44 | REPORT_GAS=true npm run test 45 | ``` 46 | 47 | Check storage layout of contracts. 48 | ```bash 49 | STORAGE_LAYOUT=true npm run check 50 | ``` 51 | 52 | Check contract bytecode size 53 | ```bash 54 | CHECK_CONTRACT_SIZE=true npm run build 55 | ``` 56 | 57 | ## Available contracts 58 | 59 | ### AxelarExecutable 60 | 61 | Base interface for validating and executing GMP contract calls. 62 | 63 | ### AxelarExpressExecutable 64 | 65 | Interface that allows expediting GMP calls by lending assets and performing execution 66 | before it fully propagates through the Axelar network. 67 | 68 | ### Create2Deployer and Create3Deployer 69 | 70 | These contracts are used to deploy your Executable to have the same address on different EVM chains. 71 | This simplifies message validation from peer Executables. You can learn more in the 72 | [documentation](https://docs.axelar.dev/dev/general-message-passing/solidity-utilities). 73 | 74 | ### Proxy and Upgradable 75 | 76 | Base implementation of upgradable contracts designed to be deployed with `Create3Deployer` 77 | and to have the same Proxy address on different EVM chains. 78 | 79 | ### AddressString 80 | 81 | Allows conversion between `string` and `address` data types 82 | 83 | ### AddressBytes 84 | 85 | Allows conversion between `bytes` and `address` data types 86 | 87 | ### Bytes32String 88 | 89 | Allows conversion between `string` and `bytes32` data types 90 | for storing strings under 31 bytes into a single storage slot 91 | -------------------------------------------------------------------------------- /contracts/upgradable/FixedProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IProxy } from '../interfaces/IProxy.sol'; 6 | import { IContractIdentifier } from '../interfaces/IContractIdentifier.sol'; 7 | 8 | /** 9 | * @title FixedProxy Contract 10 | * @notice The FixedProxy is a type of Proxy contract with a fixed implementation that cannot be updated. 11 | * It implements the IProxy interface. Any function calls to this contract will be forwarded to the implementation contract. 12 | */ 13 | contract FixedProxy is IProxy { 14 | /** 15 | * @dev The immutable address of the implementation contract. 16 | * This address is set in the constructor and cannot be updated after. 17 | */ 18 | address public immutable implementation; 19 | 20 | /** 21 | * @dev Constructs a FixedProxy contract with the given implementation address. 22 | * @param implementationAddress The address of the implementation contract 23 | */ 24 | constructor(address implementationAddress) { 25 | bytes32 id = contractId(); 26 | // Skipping the check if contractId() is not set by an inheriting proxy contract 27 | if (id != bytes32(0) && IContractIdentifier(implementationAddress).contractId() != id) 28 | revert InvalidImplementation(); 29 | 30 | implementation = implementationAddress; 31 | } 32 | 33 | /** 34 | * @dev Shadows the setup function of the implementation contract so it can't be called directly via the proxy. 35 | * @param setupParams The setup parameters for the implementation contract. 36 | */ 37 | function setup(bytes calldata setupParams) external {} 38 | 39 | /** 40 | * @notice Returns the contract ID. It can be used as a check during upgrades. 41 | * @dev Meant to be overridden in derived contracts. 42 | * @return bytes32 The contract ID 43 | */ 44 | function contractId() internal pure virtual returns (bytes32) { 45 | return bytes32(0); 46 | } 47 | 48 | /** 49 | * @dev Fallback function that delegates all calls to the implementation contract. 50 | * If the call fails, it reverts with the returned error data. If it succeeds, it returns the data from the call. 51 | */ 52 | // slither-disable-next-line locked-ether 53 | fallback() external payable virtual { 54 | address implementation_ = implementation; 55 | 56 | assembly { 57 | calldatacopy(0, 0, calldatasize()) 58 | 59 | let result := delegatecall(gas(), implementation_, 0, calldatasize(), 0, 0) 60 | returndatacopy(0, 0, returndatasize()) 61 | 62 | switch result 63 | case 0 { 64 | revert(0, returndatasize()) 65 | } 66 | default { 67 | return(0, returndatasize()) 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * @dev Payable fallback function. Can be overridden in derived contracts. 74 | */ 75 | // slither-disable-next-line locked-ether 76 | receive() external payable virtual {} 77 | } 78 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | error InvalidAccount(); 10 | 11 | /** 12 | * @dev Returns the amount of tokens in existence. 13 | */ 14 | function totalSupply() external view returns (uint256); 15 | 16 | /** 17 | * @dev Returns the amount of tokens owned by `account`. 18 | */ 19 | function balanceOf(address account) external view returns (uint256); 20 | 21 | /** 22 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 23 | * 24 | * Returns a boolean value indicating whether the operation succeeded. 25 | * 26 | * Emits a {Transfer} event. 27 | */ 28 | function transfer(address recipient, uint256 amount) external returns (bool); 29 | 30 | /** 31 | * @dev Returns the remaining number of tokens that `spender` will be 32 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 33 | * zero by default. 34 | * 35 | * This value changes when {approve} or {transferFrom} are called. 36 | */ 37 | function allowance(address owner, address spender) external view returns (uint256); 38 | 39 | /** 40 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 41 | * 42 | * Returns a boolean value indicating whether the operation succeeded. 43 | * 44 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 45 | * that someone may use both the old and the new allowance by unfortunate 46 | * transaction ordering. One possible solution to mitigate this race 47 | * condition is to first reduce the spender's allowance to 0 and set the 48 | * desired value afterwards: 49 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 50 | * 51 | * Emits an {Approval} event. 52 | */ 53 | function approve(address spender, uint256 amount) external returns (bool); 54 | 55 | /** 56 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 57 | * allowance mechanism. `amount` is then deducted from the caller's 58 | * allowance. 59 | * 60 | * Returns a boolean value indicating whether the operation succeeded. 61 | * 62 | * Emits a {Transfer} event. 63 | */ 64 | function transferFrom( 65 | address sender, 66 | address recipient, 67 | uint256 amount 68 | ) external returns (bool); 69 | 70 | /** 71 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 72 | * another (`to`). 73 | * 74 | * Note that `value` may be zero. 75 | */ 76 | event Transfer(address indexed from, address indexed to, uint256 value); 77 | 78 | /** 79 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 80 | * a call to {approve}. `value` is the new allowance. 81 | */ 82 | event Approval(address indexed owner, address indexed spender, uint256 value); 83 | } 84 | -------------------------------------------------------------------------------- /contracts/executable/AxelarExecutable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarGateway } from '../interfaces/IAxelarGateway.sol'; 6 | import { IAxelarExecutable } from '../interfaces/IAxelarExecutable.sol'; 7 | 8 | /** 9 | * @title AxelarExecutable 10 | * @dev Abstract contract to be inherited by contracts that need to execute cross-chain commands via Axelar's Gateway. 11 | * It implements the IAxelarExecutable interface. 12 | */ 13 | abstract contract AxelarExecutable is IAxelarExecutable { 14 | /// @dev Reference to the Axelar Gateway contract. 15 | address internal immutable gatewayAddress; 16 | 17 | /** 18 | * @dev Contract constructor that sets the Axelar Gateway address. 19 | * Reverts if the provided address is the zero address. 20 | * @param gateway_ The address of the Axelar Gateway contract. 21 | */ 22 | constructor(address gateway_) { 23 | if (gateway_ == address(0)) revert InvalidAddress(); 24 | 25 | gatewayAddress = gateway_; 26 | } 27 | 28 | /** 29 | * @notice Executes the cross-chain command after validating it with the Axelar Gateway. 30 | * @dev This function ensures the call is approved by Axelar Gateway before execution. 31 | * It uses a hash of the payload for validation and internally calls _execute for the actual command execution. 32 | * Reverts if the validation fails. 33 | * @param commandId The unique identifier of the cross-chain message being executed. 34 | * @param sourceChain The name of the source chain from which the message originated. 35 | * @param sourceAddress The address on the source chain that sent the message. 36 | * @param payload The payload of the message payload. 37 | */ 38 | function execute( 39 | bytes32 commandId, 40 | string calldata sourceChain, 41 | string calldata sourceAddress, 42 | bytes calldata payload 43 | ) external virtual { 44 | bytes32 payloadHash = keccak256(payload); 45 | 46 | if (!gateway().validateContractCall(commandId, sourceChain, sourceAddress, payloadHash)) 47 | revert NotApprovedByGateway(); 48 | 49 | _execute(commandId, sourceChain, sourceAddress, payload); 50 | } 51 | 52 | /** 53 | * @dev Internal virtual function to be overridden by child contracts to execute the command. 54 | * It allows child contracts to define their custom command execution logic. 55 | * @param commandId The identifier of the command to execute. 56 | * @param sourceChain The name of the source chain from which the command originated. 57 | * @param sourceAddress The address on the source chain that sent the command. 58 | * @param payload The payload of the command to be executed. 59 | */ 60 | function _execute( 61 | bytes32 commandId, 62 | string calldata sourceChain, 63 | string calldata sourceAddress, 64 | bytes calldata payload 65 | ) internal virtual; 66 | 67 | /** 68 | * @notice Returns the address of the AxelarGateway contract. 69 | * @return The Axelar Gateway instance. 70 | */ 71 | function gateway() public view returns (IAxelarGateway) { 72 | return IAxelarGateway(gatewayAddress); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /contracts/interfaces/IRoles.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IRolesBase } from './IRolesBase.sol'; 6 | 7 | /** 8 | * @title IRoles Interface 9 | * @notice IRoles is an interface that abstracts the implementation of a 10 | * contract with role control features. It's commonly included for the functionality to 11 | * get current role, transfer role, and propose and accept role. 12 | */ 13 | interface IRoles is IRolesBase { 14 | error InvalidProposedAccount(address account); 15 | 16 | /** 17 | * @notice Checks if an account has all the roles. 18 | * @param account The address to check 19 | * @param roles The roles to check 20 | * @return True if the account has all the roles, false otherwise 21 | */ 22 | function hasAllTheRoles(address account, uint8[] memory roles) external view returns (bool); 23 | 24 | /** 25 | * @notice Checks if an account has any of the roles. 26 | * @param account The address to check 27 | * @param roles The roles to check 28 | * @return True if the account has any of the roles, false otherwise 29 | */ 30 | function hasAnyOfRoles(address account, uint8[] memory roles) external view returns (bool); 31 | 32 | /** 33 | * @notice Returns the roles of an account. 34 | * @param account The address to get the roles for 35 | * @return accountRoles The roles of the account in uint256 format 36 | */ 37 | function getAccountRoles(address account) external view returns (uint256 accountRoles); 38 | 39 | /** 40 | * @notice Returns the pending role of the contract. 41 | * @param fromAccount The address with the current roles 42 | * @param toAccount The address with the pending roles 43 | * @return proposedRoles_ The pending role of the contract in uint256 format 44 | */ 45 | function getProposedRoles(address fromAccount, address toAccount) external view returns (uint256 proposedRoles_); 46 | 47 | /** 48 | * @notice Transfers roles of the contract to a new account. 49 | * @dev Can only be called by the account with all the roles. 50 | * @dev Emits RolesRemoved and RolesAdded events. 51 | * @param toAccount The address to transfer role to 52 | * @param roles The roles to transfer 53 | */ 54 | function transferRoles(address toAccount, uint8[] memory roles) external; 55 | 56 | /** 57 | * @notice Propose to transfer roles of message sender to a new account. 58 | * @dev Can only be called by the account with all the proposed roles. 59 | * @dev emits a RolesProposed event. 60 | * @dev Roles are not transferred until the new role accepts the role transfer. 61 | * @param toAccount The address to transfer role to 62 | * @param roles The roles to transfer 63 | */ 64 | function proposeRoles(address toAccount, uint8[] memory roles) external; 65 | 66 | /** 67 | * @notice Accepts roles transferred from another account. 68 | * @dev Can only be called by the pending account with all the proposed roles. 69 | * @dev Emits RolesRemoved and RolesAdded events. 70 | * @param fromAccount The address of the current role 71 | * @param roles The roles to accept 72 | */ 73 | function acceptRoles(address fromAccount, uint8[] memory roles) external; 74 | } 75 | -------------------------------------------------------------------------------- /contracts/libs/ECDSA.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. 7 | * 8 | * These functions can be used to verify that a message was signed by the holder 9 | * of the private keys of a given address. 10 | */ 11 | library ECDSA { 12 | error InvalidSignatureLength(); 13 | error InvalidS(); 14 | error InvalidV(); 15 | error InvalidSignature(); 16 | 17 | /** 18 | * @dev Returns the address that signed a hashed message (`hash`) with 19 | * `signature`. This address can then be used for verification purposes. 20 | * 21 | * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: 22 | * this function rejects them by requiring the `s` value to be in the lower 23 | * half order, and the `v` value to be either 27 or 28. 24 | * 25 | * IMPORTANT: `hash` _must_ be the result of a hash operation for the 26 | * verification to be secure: it is possible to craft signatures that 27 | * recover to arbitrary addresses for non-hashed data. A safe way to ensure 28 | * this is by receiving a hash of the original message (which may otherwise 29 | * be too long), and then calling {toEthSignedMessageHash} on it. 30 | */ 31 | function recover(bytes32 hash, bytes memory signature) internal pure returns (address signer) { 32 | // Check the signature length 33 | if (signature.length != 65) revert InvalidSignatureLength(); 34 | 35 | // Divide the signature in r, s and v variables 36 | bytes32 r; 37 | bytes32 s; 38 | uint8 v; 39 | 40 | // ecrecover takes the signature parameters, and the only way to get them 41 | // currently is to use assembly. 42 | // solhint-disable-next-line no-inline-assembly 43 | assembly { 44 | r := mload(add(signature, 0x20)) 45 | s := mload(add(signature, 0x40)) 46 | v := byte(0, mload(add(signature, 0x60))) 47 | } 48 | 49 | // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature 50 | // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines 51 | // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most 52 | // signatures from current libraries generate a unique signature with an s-value in the lower half order. 53 | // 54 | // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value 55 | // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or 56 | // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept 57 | // these malleable signatures as well. 58 | if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) revert InvalidS(); 59 | 60 | if (v != 27 && v != 28) revert InvalidV(); 61 | 62 | signer = ecrecover(hash, v, r, s); 63 | 64 | // If the signature is valid (and not malleable), return the signer address 65 | if (signer == address(0)) revert InvalidSignature(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /contracts/governance/DESIGN.md: -------------------------------------------------------------------------------- 1 | # Axelar Governance Protocol 2 | 3 | The Axelar Governance Protocol is a cross-chain governance protocol that allows for the creation, cancellation, and execution of governance proposals. The protocol is implemented in the `InterchainGovernance` contract. 4 | 5 | ## Design Principles 6 | 7 | The protocol is designed with security and decentralization in mind. Proposals are initiated and voted on the Axelar Network. If proposals are successfully voted on, the governance address can send execution commands to governance modules on different chains via General Message Passing (GMP). These commands are scheduled via a timelock mechanism to ensure that actions cannot be executed immediately after being proposed, giving participants time to react. 8 | 9 | ## Security Properties 10 | 11 | The `InterchainGovernance` contract uses several security measures: 12 | 13 | - **Timelock Mechanism**: This mechanism ensures that there's a delay between when a proposal is made and when it can be executed. This gives participants time to react. 14 | - **Only Governance Modifier**: Only the governance contract can initiate proposals. 15 | - **Only Self Modifier**: Some functions can only be called by the contract itself. 16 | - **Safe Native Transfer**: The contract uses SafeNativeTransfer for transferring native tokens to prevent reentrancy attacks. 17 | 18 | ## Commands 19 | 20 | There are two types of commands that can be sent to a governance module: 21 | 22 | 1. `ScheduleTimeLockProposal`: This command schedules a new proposal for future execution. 23 | 2. `CancelTimeLockProposal`: This command cancels an existing proposal. 24 | 25 | These commands are processed by `_processCommand` function which takes as parameters: command type, target address, call data (function signature and parameters), native token value (if any), and ETA (the timestamp after which this proposal could be executed). 26 | 27 | ## Upgrading Gateway Contract 28 | 29 | To upgrade gateway contracts on different chains, an upgrade calldata needs to be sent as part of a ScheduleTimeLockProposal command. The calldata should trigger an upgrade function in the target gateway contract. 30 | 31 | ## Withdrawing Funds 32 | 33 | Funds can be withdrawn from this contract when it's targeted by its own withdraw function through ScheduleTimeLockProposal command with onlySelf modifier ensuring only this contract itself could call it. 34 | 35 | ## Implementation Details 36 | 37 | When implementing this protocol in another smart-contract language or non-EVM platform: 38 | 39 | 1. Implement similar functionality as provided by Solidity such as hashing (`keccak256`), ABI encoding (`abi.encode`, `abi.encodePacked`) etc. 40 | 2. Implement equivalent modifiers like `onlyGovernance`, `onlySelf`. 41 | 3. Implement safe transfer methods for native tokens or assets. 42 | 4. Ensure proper handling of timestamps for scheduling timelocks. 43 | 5. Ensure proper access control mechanisms so only authorized entities could propose or cancel proposals. 44 | 45 | 46 | In conclusion, while implementing Axelar Governance Protocol on non-EVM platforms might require some adjustments due to differences in languages or environments; core principles such as decentralized voting process, timelock mechanism for executing proposals should remain intact ensuring secure cross-chain governance operations across multiple blockchains within Axelar Network ecosystem. 47 | -------------------------------------------------------------------------------- /contracts/utils/Operators.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IOperators } from '../interfaces/IOperators.sol'; 6 | import { Ownable } from './Ownable.sol'; 7 | 8 | /** 9 | * @title Operators 10 | * @notice This contract provides an access control mechanism, where an owner can register 11 | * operator accounts that can call arbitrary contracts on behalf of this contract. 12 | * @dev The owner account is initially set as the deployer address. 13 | */ 14 | contract Operators is Ownable, IOperators { 15 | mapping(address => bool) public operators; 16 | 17 | /** 18 | * @notice Sets the initial owner of the contract. 19 | */ 20 | constructor(address initialOwner) Ownable(initialOwner) {} 21 | 22 | /** 23 | * @notice Modifier that requires the `msg.sender` to be an operator. 24 | * @dev Reverts with a NotOperator error if the condition is not met. 25 | */ 26 | modifier onlyOperator() { 27 | if (!operators[msg.sender]) revert NotOperator(); 28 | _; 29 | } 30 | 31 | /** 32 | * @notice Returns whether an address is an operator. 33 | * @param account Address to check 34 | * @return boolean whether the address is an operator 35 | */ 36 | function isOperator(address account) external view returns (bool) { 37 | return operators[account]; 38 | } 39 | 40 | /** 41 | * @notice Adds an address as an operator. 42 | * @dev Can only be called by the current owner. 43 | * @param operator address to be added as operator 44 | */ 45 | function addOperator(address operator) external onlyOwner { 46 | if (operator == address(0)) revert InvalidOperator(); 47 | if (operators[operator]) revert OperatorAlreadyAdded(); 48 | 49 | operators[operator] = true; 50 | 51 | emit OperatorAdded(operator); 52 | } 53 | 54 | /** 55 | * @notice Removes an address as an operator. 56 | * @dev Can only be called by the current owner. 57 | * @param operator address to be removed as operator 58 | */ 59 | function removeOperator(address operator) external onlyOwner { 60 | if (operator == address(0)) revert InvalidOperator(); 61 | if (!operators[operator]) revert NotAnOperator(); 62 | 63 | operators[operator] = false; 64 | 65 | emit OperatorRemoved(operator); 66 | } 67 | 68 | /** 69 | * @notice Allows an operator to execute arbitrary functions on any smart contract. 70 | * @dev Can only be called by an operator. 71 | * @param target address of the contract to execute the function on. Existence is not checked. 72 | * @param callData ABI encoded function call to execute on target 73 | * @param nativeValue The amount of native asset to send with the call. If `nativeValue` is set to `0`, then `msg.value` is forwarded instead. 74 | * @return data return data from executed function call 75 | */ 76 | function executeContract( 77 | address target, 78 | bytes calldata callData, 79 | uint256 nativeValue 80 | ) external payable onlyOperator returns (bytes memory) { 81 | if (nativeValue == 0) { 82 | nativeValue = msg.value; 83 | } 84 | 85 | (bool success, bytes memory data) = target.call{ value: nativeValue }(callData); 86 | if (!success) { 87 | revert ExecutionFailed(); 88 | } 89 | 90 | return data; 91 | } 92 | 93 | /** 94 | * @notice This function enables the contract to accept native value transfers. 95 | */ 96 | receive() external payable {} 97 | } 98 | -------------------------------------------------------------------------------- /contracts/interfaces/IAxelarExpressExecutableWithToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import { IAxelarExpressExecutable } from './IAxelarExpressExecutable.sol'; 6 | import { IAxelarExecutableWithToken } from './IAxelarExecutableWithToken.sol'; 7 | 8 | /** 9 | * @title IAxelarExpressExecutableWithToken 10 | * @notice Interface for the Axelar Express Executable contract with token. 11 | */ 12 | interface IAxelarExpressExecutableWithToken is IAxelarExpressExecutable, IAxelarExecutableWithToken { 13 | /** 14 | * @notice Emitted when an express execution with a token is successfully performed. 15 | * @param commandId The unique identifier for the command. 16 | * @param sourceChain The source chain. 17 | * @param sourceAddress The source address. 18 | * @param payloadHash The hash of the payload. 19 | * @param symbol The token symbol. 20 | * @param amount The amount of tokens. 21 | * @param expressExecutor The address of the express executor. 22 | */ 23 | event ExpressExecutedWithToken( 24 | bytes32 indexed commandId, 25 | string sourceChain, 26 | string sourceAddress, 27 | bytes32 payloadHash, 28 | string symbol, 29 | uint256 indexed amount, 30 | address indexed expressExecutor 31 | ); 32 | 33 | /** 34 | * @notice Emitted when an express execution with a token is fulfilled. 35 | * @param commandId The commandId for the contractCallWithToken. 36 | * @param sourceChain The source chain. 37 | * @param sourceAddress The source address. 38 | * @param payloadHash The hash of the payload. 39 | * @param symbol The token symbol. 40 | * @param amount The amount of tokens. 41 | * @param expressExecutor The address of the express executor. 42 | */ 43 | event ExpressExecutionWithTokenFulfilled( 44 | bytes32 indexed commandId, 45 | string sourceChain, 46 | string sourceAddress, 47 | bytes32 payloadHash, 48 | string symbol, 49 | uint256 indexed amount, 50 | address indexed expressExecutor 51 | ); 52 | 53 | /** 54 | * @notice Returns the express executor with token for a given command. 55 | * @param commandId The commandId for the contractCallWithToken. 56 | * @param sourceChain The source chain. 57 | * @param sourceAddress The source address. 58 | * @param payloadHash The hash of the payload. 59 | * @param symbol The token symbol. 60 | * @param amount The amount of tokens. 61 | * @return expressExecutor The address of the express executor. 62 | */ 63 | function getExpressExecutorWithToken( 64 | bytes32 commandId, 65 | string calldata sourceChain, 66 | string calldata sourceAddress, 67 | bytes32 payloadHash, 68 | string calldata symbol, 69 | uint256 amount 70 | ) external view returns (address expressExecutor); 71 | 72 | /** 73 | * @notice Express executes a contract call with token. 74 | * @param commandId The commandId for the contractCallWithToken. 75 | * @param sourceChain The source chain. 76 | * @param sourceAddress The source address. 77 | * @param payload The payload data. 78 | * @param symbol The token symbol. 79 | * @param amount The amount of token. 80 | */ 81 | function expressExecuteWithToken( 82 | bytes32 commandId, 83 | string calldata sourceChain, 84 | string calldata sourceAddress, 85 | bytes calldata payload, 86 | string calldata symbol, 87 | uint256 amount 88 | ) external payable; 89 | } 90 | --------------------------------------------------------------------------------