Deploy consistent Create2Factory, TestRecipient and InterchainAccountRouters (#1123)

* Deploy consistent Create2Factory, TestRecipient and InterchainAccountRouters

* .gitignore

* PR Review
pull/1135/head
Nam Chu Hoai 2 years ago committed by GitHub
parent 65e0bfb2e6
commit 68c394a2a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitignore
  2. 114
      solidity/core/contracts/Create2Factory.sol
  3. 8
      typescript/ica/contracts/InterchainAccountRouter.sol
  4. 15
      typescript/ica/src/deploy.ts
  5. 23
      typescript/infra/config/environments/mainnet/create2/addresses.json
  6. 58
      typescript/infra/config/environments/mainnet/create2/verification.json
  7. 23
      typescript/infra/config/environments/mainnet/interchain/addresses.json
  8. 58
      typescript/infra/config/environments/mainnet/interchain/verification.json
  9. 23
      typescript/infra/config/environments/mainnet/testrecipient/addresses.json
  10. 58
      typescript/infra/config/environments/mainnet/testrecipient/verification.json
  11. 12
      typescript/infra/config/environments/testnet2/core/verification/verification.json
  12. 29
      typescript/infra/config/environments/testnet2/create2/addresses.json
  13. 74
      typescript/infra/config/environments/testnet2/create2/verification.json
  14. 29
      typescript/infra/config/environments/testnet2/interchain/addresses.json
  15. 74
      typescript/infra/config/environments/testnet2/interchain/verification.json
  16. 29
      typescript/infra/config/environments/testnet2/testrecipient/addresses.json
  17. 74
      typescript/infra/config/environments/testnet2/testrecipient/verification.json
  18. 2
      typescript/infra/scripts/check-deploy.ts
  19. 29
      typescript/infra/scripts/create2/deploy.ts
  20. 40
      typescript/infra/scripts/funding/fund-deterministic-key-from-deployer.ts
  21. 71
      typescript/infra/scripts/interchain/deploy.ts
  22. 24
      typescript/infra/scripts/testrecipient/deploy.ts
  23. 6
      typescript/infra/src/agents/key-utils.ts
  24. 1
      typescript/infra/src/agents/roles.ts
  25. 36
      typescript/infra/src/create2/index.ts
  26. 33
      typescript/infra/src/deploy.ts
  27. 13
      typescript/infra/src/funding/deterministic-keys.ts
  28. 37
      typescript/infra/src/testrecipient/index.ts
  29. 11
      typescript/sdk/src/consts/chainConnectionConfigs.ts
  30. 4
      typescript/sdk/src/consts/environments/testnet2.json
  31. 92
      typescript/sdk/src/deploy/HyperlaneDeployer.ts
  32. 1
      typescript/sdk/src/deploy/core/HyperlaneCoreDeployer.ts

2
.gitignore vendored

@ -15,6 +15,8 @@ tmp.env
.DS_STORE
typescript/*/node_modules
typescript/infra/config/environments/test/create2/*json
typescript/infra/config/environments/test/testrecipient/*json
solidity/*/artifacts
.yarn/install-state.gz

@ -0,0 +1,114 @@
// SPDX-License-Identifier: MIT
// Copied from https://github.com/axelarnetwork/axelar-utils-solidity/commits/main/contracts/ConstAddressDeployer.sol
pragma solidity ^0.8.0;
contract Create2Factory {
error EmptyBytecode();
error FailedDeploy();
error FailedInit();
event Deployed(
bytes32 indexed bytecodeHash,
bytes32 indexed salt,
address indexed deployedAddress
);
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {deployedAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already by the same `msg.sender`.
*/
function deploy(bytes memory bytecode, bytes32 salt)
external
returns (address deployedAddress_)
{
deployedAddress_ = _deploy(
bytecode,
keccak256(abi.encode(msg.sender, salt))
);
}
/**
* @dev Deploys a contract using `CREATE2` and initialize it. The address where the contract
* will be deployed can be known in advance via {deployedAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already by the same `msg.sender`.
* - `init` is used to initialize the deployed contract
* as an option to not have the constructor args affect the address derived by `CREATE2`.
*/
function deployAndInit(
bytes memory bytecode,
bytes32 salt,
bytes calldata init
) external returns (address deployedAddress_) {
deployedAddress_ = _deploy(
bytecode,
keccak256(abi.encode(msg.sender, salt))
);
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = deployedAddress_.call(init);
if (!success) revert FailedInit();
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} or {deployAndInit} by `sender`.
* Any change in the `bytecode`, `sender`, or `salt` will result in a new destination address.
*/
function deployedAddress(
bytes calldata bytecode,
address sender,
bytes32 salt
) external view returns (address deployedAddress_) {
bytes32 newSalt = keccak256(abi.encode(sender, salt));
deployedAddress_ = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
address(this),
newSalt,
keccak256(bytecode) // init code hash
)
)
)
)
);
}
function _deploy(bytes memory bytecode, bytes32 salt)
internal
returns (address deployedAddress_)
{
if (bytecode.length == 0) revert EmptyBytecode();
// solhint-disable-next-line no-inline-assembly
assembly {
deployedAddress_ := create2(
0,
add(bytecode, 32),
mload(bytecode),
salt
)
}
if (deployedAddress_ == address(0)) revert FailedDeploy();
emit Deployed(keccak256(bytecode), salt, deployedAddress_);
}
}

@ -8,6 +8,7 @@ import {Router} from "@hyperlane-xyz/app/contracts/Router.sol";
import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/*
* @title The Hello World App
@ -17,12 +18,13 @@ contract InterchainAccountRouter is Router {
bytes constant bytecode = type(OwnableMulticall).creationCode;
bytes32 constant bytecodeHash = bytes32(keccak256(bytecode));
constructor(
function initialize(
address _owner,
address _abacusConnectionManager,
address _interchainGasPaymaster
) {
) public initializer {
// Transfer ownership of the contract to deployer
_transferOwnership(msg.sender);
_transferOwnership(_owner);
// Set the addresses for the ACM and IGP
// Alternatively, this could be done later in an initialize method
_setAbacusConnectionManager(_abacusConnectionManager);

@ -7,6 +7,8 @@ import {
RouterConfig,
} from '@hyperlane-xyz/sdk';
import { InterchainAccountRouter__factory } from '../types';
import {
InterchainAccountContracts,
InterchainAccountFactories,
@ -34,10 +36,15 @@ export class InterchainAccountDeployer<
// Custom contract deployment logic can go here
// If no custom logic is needed, call deployContract for the router
async deployContracts(chain: Chain, config: InterchainAccountConfig) {
const router = await this.deployContract(chain, 'router', [
config.connectionManager,
config.interchainGasPaymaster,
]);
const initCalldata =
InterchainAccountRouter__factory.createInterface().encodeFunctionData(
'initialize',
[config.owner, config.connectionManager, config.interchainGasPaymaster],
);
const router = await this.deployContract(chain, 'router', [], {
create2Salt: 'asdasdsd',
initCalldata,
});
return {
router,
};

@ -0,0 +1,23 @@
{
"bsc": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"avalanche": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"polygon": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"celo": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"arbitrum": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"optimism": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"ethereum": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
}
}

@ -0,0 +1,58 @@
{
"bsc": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"avalanche": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"polygon": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"celo": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"arbitrum": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"optimism": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"ethereum": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
]
}

@ -0,0 +1,23 @@
{
"bsc": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"avalanche": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"polygon": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"celo": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"arbitrum": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"optimism": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"ethereum": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
}
}

@ -0,0 +1,58 @@
{
"bsc": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"avalanche": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"polygon": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"celo": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"arbitrum": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"optimism": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"ethereum": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
]
}

@ -0,0 +1,23 @@
{
"bsc": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"avalanche": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"polygon": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"celo": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"arbitrum": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"optimism": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"ethereum": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
}
}

@ -0,0 +1,58 @@
{
"bsc": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"avalanche": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"polygon": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"celo": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"arbitrum": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"optimism": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"ethereum": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
]
}

@ -1365,6 +1365,12 @@
"address": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE",
"constructorArguments": "000000000000000000000000833dad7ff66884389d5f0cecba446ffaa7d2837e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000448624c35c000000000000000000000000000000000000000000000000000000006d6f2d610000000000000000000000004926a10788306d84202a2adbd290b7743146cc1700000000000000000000000000000000000000000000000000000000",
"isProxy": true
},
{
"name": "connectionManager",
"address": "0x01812D60958798695391dacF092BAc4a715B1718",
"constructorArguments": "",
"isProxy": false
}
],
"moonbasealpha": [
@ -1529,6 +1535,12 @@
"address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7",
"constructorArguments": "000000000000000000000000b08d78f439e55d02c398519eef61606a5926245f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000448624c35c000000000000000000000000000000000000000000000000000000000000000500000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a00000000000000000000000000000000000000000000000000000000",
"isProxy": true
},
{
"name": "connectionManager",
"address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f",
"constructorArguments": "",
"isProxy": false
}
]
}

@ -0,0 +1,29 @@
{
"alfajores": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"kovan": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"fuji": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"mumbai": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"bsctestnet": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"arbitrumrinkeby": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"optimismkovan": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"goerli": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
},
"moonbasealpha": {
"Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a"
}
}

@ -0,0 +1,74 @@
{
"alfajores": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"kovan": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"fuji": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"mumbai": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"bsctestnet": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"arbitrumrinkeby": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"optimismkovan": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"goerli": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
],
"moonbasealpha": [
{
"name": "Create2Factory",
"address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a",
"constructorArguments": "",
"isProxy": false
}
]
}

@ -0,0 +1,29 @@
{
"alfajores": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"kovan": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"fuji": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"mumbai": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"bsctestnet": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"optimismkovan": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"goerli": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"moonbasealpha": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
},
"arbitrumrinkeby": {
"router": "0x28DB114018576cF6c9A523C17903455A161d18C4"
}
}

@ -0,0 +1,74 @@
{
"alfajores": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"kovan": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"fuji": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"mumbai": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"bsctestnet": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"arbitrumrinkeby": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"optimismkovan": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"goerli": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
],
"moonbasealpha": [
{
"name": "InterchainAccountRouter",
"address": "0x28DB114018576cF6c9A523C17903455A161d18C4",
"isProxy": false,
"constructorArguments": ""
}
]
}

@ -0,0 +1,29 @@
{
"alfajores": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"kovan": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"fuji": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"mumbai": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"bsctestnet": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"arbitrumrinkeby": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"optimismkovan": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"goerli": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
},
"moonbasealpha": {
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE"
}
}

@ -0,0 +1,74 @@
{
"alfajores": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"kovan": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"fuji": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"mumbai": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"bsctestnet": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"arbitrumrinkeby": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"optimismkovan": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"goerli": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
],
"moonbasealpha": [
{
"name": "TestRecipient",
"address": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE",
"isProxy": false,
"constructorArguments": ""
}
]
}

@ -21,6 +21,8 @@ async function check() {
throw new Error(
`Checking core deploy yielded ${coreChecker.violations.length} violations`,
);
} else {
console.info('CoreChecker found no violations');
}
}

@ -0,0 +1,29 @@
import path from 'path';
import { Contexts } from '../../config/contexts';
import { KEY_ROLE_ENUM } from '../../src/agents/roles';
import { Create2FactoryDeployer, factories } from '../../src/create2';
import { deployWithArtifacts } from '../../src/deploy';
import {
getCoreEnvironmentConfig,
getEnvironment,
getEnvironmentDirectory,
} from '../utils';
async function main() {
const environment = await getEnvironment();
const coreConfig = getCoreEnvironmentConfig(environment);
const multiProvider = await coreConfig.getMultiProvider(
Contexts.Abacus,
KEY_ROLE_ENUM.Create2Deployer,
);
const deployer = new Create2FactoryDeployer(multiProvider);
const dir = path.join(getEnvironmentDirectory(environment), 'create2');
await deployWithArtifacts(dir, factories, deployer);
}
main()
.then(() => console.info('Deployment complete'))
.catch(console.error);

@ -1,3 +1,4 @@
import { BigNumber } from 'ethers';
import { format } from 'util';
import { objMap, promiseObjAll } from '@hyperlane-xyz/sdk';
@ -19,7 +20,8 @@ async function main() {
'role',
Object.keys(DeterministicKeyRoles).filter((x) => !(parseInt(x) >= 0)),
)
.demandOption('role')
.string('address')
.describe('address', 'The address to fund')
.number('gas-amount')
.alias('g', 'gas-amount')
.describe(
@ -35,6 +37,10 @@ async function main() {
chainStrs.map((chainStr: string) => assertChain(chainStr)),
).argv;
if (argv.address === undefined && argv.role === undefined) {
throw new Error('Have to specify either --role or --address');
}
const environment = await getEnvironment();
const coreConfig = getCoreEnvironmentConfig(environment);
const multiProvider = await coreConfig.getMultiProvider(
@ -42,22 +48,36 @@ async function main() {
KEY_ROLE_ENUM.Deployer,
);
const key = await getDeterministicKey(
environment,
// @ts-ignore
DeterministicKeyRoles[argv.role],
);
const address =
argv.address ||
(
await getDeterministicKey(
environment,
// @ts-ignore
DeterministicKeyRoles[argv.role],
)
).address;
await promiseObjAll(
objMap(multiProvider.chainMap, async (_, dc) => {
objMap(multiProvider.chainMap, async (chain, dc) => {
if (argv.chainsToSkip?.includes(chain)) {
return;
}
// fund signer on each network with gas * gasPrice
const actual = await dc.provider.getBalance(key.address);
const gasPrice = await dc.provider.getGasPrice();
const actual = await dc.provider.getBalance(address);
const gasPrice = BigNumber.from(
await (dc.overrides.gasPrice ||
dc.overrides.maxFeePerGas ||
dc.provider.getGasPrice()),
);
const desired = gasPrice.mul(argv.gasAmount!);
const value = desired.sub(actual);
if (value.gt(0)) {
console.log(
`Funding ${address} on chain '${chain}' with ${value} native tokens`,
);
await dc.sendTransaction({
to: key.address,
to: address,
value,
});
}

@ -1,26 +1,14 @@
import { Wallet } from 'ethers';
import path from 'path';
import {
InterchainAccountContracts,
InterchainAccountDeployer,
interchainAccountFactories,
} from '@hyperlane-xyz/interchain-accounts';
import {
ChainMap,
HyperlaneCore,
buildContracts,
objMap,
promiseObjAll,
serializeContracts,
} from '@hyperlane-xyz/sdk';
import { HyperlaneCore } from '@hyperlane-xyz/sdk';
import { Contexts } from '../../config/contexts';
import { KEY_ROLE_ENUM } from '../../src/agents/roles';
import { readJSON, writeJSON } from '../../src/utils/utils';
import { deployWithArtifacts } from '../../src/deploy';
import { getConfiguration } from '../helloworld/utils';
import {
getContext,
getCoreEnvironmentConfig,
getEnvironment,
getEnvironmentDirectory,
@ -30,71 +18,22 @@ import {
// should eventually be deduped
async function main() {
const environment = await getEnvironment();
const context = await getContext();
const coreConfig = getCoreEnvironmentConfig(environment);
// Always deploy from the abacus deployer
const multiProvider = await coreConfig.getMultiProvider(
Contexts.Abacus,
KEY_ROLE_ENUM.Deployer,
);
const multiProvider = await coreConfig.getMultiProvider();
const core = HyperlaneCore.fromEnvironment(environment, multiProvider as any);
const dir = path.join(
getEnvironmentDirectory(environment),
'interchain',
context,
);
let contracts: ChainMap<any, InterchainAccountContracts> = {};
try {
const addresses = readJSON(dir, 'addresses.json');
contracts = buildContracts(addresses, interchainAccountFactories) as any;
} catch (e) {
console.error(e);
}
const dir = path.join(getEnvironmentDirectory(environment), 'interchain');
// config gcp deployer key as owner
const configMap = await getConfiguration(environment, multiProvider);
// create fresh signer
const signer = Wallet.createRandom();
// per-network deployment cost
const deploymentGas = 3_000_000; // larger than should be necessary
await promiseObjAll(
objMap(multiProvider.chainMap, async (_, dc) => {
// fund signer on each network with gas * gasPrice
const actual = await dc.provider.getBalance(signer.address);
const gasPrice = await dc.provider.getGasPrice();
const desired = gasPrice.mul(deploymentGas);
const value = desired.sub(actual);
if (value.gt(0)) {
await dc.sendTransaction({
to: signer.address,
value,
});
}
}),
);
// rotate signer to fresh key on all chains
multiProvider.rotateSigner(signer);
const deployer = new InterchainAccountDeployer(
multiProvider,
configMap,
core,
);
try {
contracts = await deployer.deploy(contracts);
} catch (e) {
console.error(e);
contracts = deployer.deployedContracts as any;
}
writeJSON(dir, 'verification.json', deployer.verificationInputs);
writeJSON(dir, 'addresses.json', serializeContracts(contracts));
await deployWithArtifacts(dir, interchainAccountFactories, deployer);
}
main()

@ -0,0 +1,24 @@
import path from 'path';
import { deployWithArtifacts } from '../../src/deploy';
import { TestRecipientDeployer, factories } from '../../src/testrecipient';
import {
getCoreEnvironmentConfig,
getEnvironment,
getEnvironmentDirectory,
} from '../utils';
async function main() {
const environment = await getEnvironment();
const coreConfig = getCoreEnvironmentConfig(environment);
const multiProvider = await coreConfig.getMultiProvider();
const deployer = new TestRecipientDeployer(multiProvider);
const dir = path.join(getEnvironmentDirectory(environment), 'testrecipient');
await deployWithArtifacts(dir, factories, deployer);
}
main()
.then(() => console.info('Deployment complete'))
.catch(console.error);

@ -21,7 +21,11 @@ export function getCloudAgentKey<Chain extends ChainName>(
chainName?: Chain,
index?: number,
): CloudAgentKey {
if (agentConfig.aws && role !== KEY_ROLE_ENUM.Deployer) {
if (
agentConfig.aws &&
role !== KEY_ROLE_ENUM.Deployer &&
role !== KEY_ROLE_ENUM.Create2Deployer
) {
// The deployer is always GCP-based
return new AgentAwsKey(agentConfig, role, chainName, index);
} else {

@ -4,6 +4,7 @@ export enum KEY_ROLE_ENUM {
Deployer = 'deployer',
Bank = 'bank',
Kathy = 'kathy',
Create2Deployer = 'create2deployer',
}
export const ALL_KEY_ROLES = [

@ -0,0 +1,36 @@
import { Create2Factory, Create2Factory__factory } from '@hyperlane-xyz/core';
import {
ChainName,
HyperlaneDeployer,
MultiProvider,
} from '@hyperlane-xyz/sdk';
export const factories = {
Create2Factory: new Create2Factory__factory(),
};
type Contracts = {
Create2Factory: Create2Factory;
};
export class Create2FactoryDeployer<
Chain extends ChainName,
> extends HyperlaneDeployer<Chain, any, Contracts, typeof factories> {
constructor(multiProvider: MultiProvider<Chain>) {
super(
multiProvider,
multiProvider.map(() => ({})),
factories,
);
}
async deployContracts(chain: Chain) {
const Create2Factory = await this.deployContract(
chain,
'Create2Factory',
[],
);
return {
Create2Factory,
};
}
}

@ -0,0 +1,33 @@
import {
ChainMap,
HyperlaneContracts,
HyperlaneDeployer,
HyperlaneFactories,
buildContracts,
serializeContracts,
} from '@hyperlane-xyz/sdk';
import { readJSON, writeJSON } from './utils/utils';
export async function deployWithArtifacts<T extends HyperlaneFactories>(
dir: string,
factories: T,
deployer: HyperlaneDeployer<any, any, any, T>,
) {
let contracts: ChainMap<any, HyperlaneContracts> = {};
try {
const addresses = readJSON(dir, 'addresses.json');
contracts = buildContracts(addresses, factories) as any;
} catch (e) {
console.error(e);
}
try {
contracts = await deployer.deploy(contracts);
} catch (e) {
console.error(e);
contracts = deployer.deployedContracts as any;
}
writeJSON(dir, 'verification.json', deployer.verificationInputs);
writeJSON(dir, 'addresses.json', serializeContracts(contracts));
}

@ -8,10 +8,17 @@ import { KEY_ROLE_ENUM } from '../agents/roles';
// Keys that are derived from the deployer key, mainly to have deterministic addresses on every chain
// The order here matters so don't mix it up
export enum DeterministicKeyRoles {
InterchainAccountV1,
InterchainAccount,
TestRecipient,
Create2Factory,
}
const DeterministicKeyRoleNonces = {
[DeterministicKeyRoles.InterchainAccount]: 0,
[DeterministicKeyRoles.TestRecipient]: 0,
[DeterministicKeyRoles.Create2Factory]: 0,
};
export const getDeterministicKey = async (
environment: string,
deterministicKeyRole: DeterministicKeyRoles,
@ -23,6 +30,8 @@ export const getDeterministicKey = async (
);
await deployerKey.fetch();
const seed = HDNode.fromSeed(deployerKey.privateKey);
const derivedKey = seed.derivePath(`m/44'/60'/0'/0/${deterministicKeyRole}`);
const derivedKey = seed.derivePath(
`m/44'/60'/0'/${deterministicKeyRole}/${DeterministicKeyRoleNonces[deterministicKeyRole]}`,
);
return new Wallet(derivedKey.privateKey);
};

@ -0,0 +1,37 @@
import { TestRecipient, TestRecipient__factory } from '@hyperlane-xyz/core';
import {
ChainName,
HyperlaneDeployer,
MultiProvider,
} from '@hyperlane-xyz/sdk';
export const factories = {
TestRecipient: new TestRecipient__factory(),
};
type Contracts = {
TestRecipient: TestRecipient;
};
export class TestRecipientDeployer<
Chain extends ChainName,
> extends HyperlaneDeployer<Chain, any, Contracts, typeof factories> {
constructor(multiProvider: MultiProvider<Chain>) {
super(
multiProvider,
multiProvider.map(() => ({})),
factories,
);
}
async deployContracts(chain: Chain) {
const TestRecipient = await this.deployContract(
chain,
'TestRecipient',
[],
{ create2Salt: 'testtest32' },
);
return {
TestRecipient,
};
}
}

@ -71,7 +71,8 @@ export const alfajores: IChainConnection = {
44787,
),
confirmations: 1,
blockExplorerUrl: 'https://alfajores-blockscout.celo-testnet.org',
blockExplorerUrl: 'https://alfajores.celoscan.io',
apiPrefix: 'api-',
};
export const auroratestnet: IChainConnection = {
@ -89,6 +90,7 @@ export const fuji: IChainConnection = {
),
confirmations: 3,
blockExplorerUrl: 'https://testnet.snowtrace.io',
apiPrefix: 'api-',
};
export const goerli: IChainConnection = {
@ -98,6 +100,7 @@ export const goerli: IChainConnection = {
),
confirmations: 1,
blockExplorerUrl: 'https://goerli.etherscan.io/',
apiPrefix: 'api-',
};
export const kovan: IChainConnection = {
@ -107,6 +110,7 @@ export const kovan: IChainConnection = {
),
confirmations: 1,
blockExplorerUrl: 'https://kovan.etherscan.io',
apiPrefix: 'api-',
};
export const mumbai: IChainConnection = {
@ -116,6 +120,7 @@ export const mumbai: IChainConnection = {
),
confirmations: 30,
blockExplorerUrl: 'https://mumbai.polygonscan.com',
apiPrefix: 'api-',
};
export const bsctestnet: IChainConnection = {
@ -125,6 +130,7 @@ export const bsctestnet: IChainConnection = {
),
confirmations: 1,
blockExplorerUrl: 'https://testnet.bscscan.com',
apiPrefix: 'api-',
};
export const arbitrumrinkeby: IChainConnection = {
@ -134,6 +140,7 @@ export const arbitrumrinkeby: IChainConnection = {
),
confirmations: 1,
blockExplorerUrl: 'https://testnet.arbiscan.io',
apiPrefix: 'api-',
};
export const optimismkovan: IChainConnection = {
@ -143,6 +150,7 @@ export const optimismkovan: IChainConnection = {
),
confirmations: 1,
blockExplorerUrl: 'https://kovan-optimistic.etherscan.io',
apiPrefix: 'api-',
};
export const moonbasealpha: IChainConnection = {
@ -152,6 +160,7 @@ export const moonbasealpha: IChainConnection = {
),
confirmations: 1,
blockExplorerUrl: 'https://moonbase.moonscan.io/',
apiPrefix: 'api-',
};
export const test1: IChainConnection = {

@ -638,7 +638,7 @@
},
"goerli": {
"upgradeBeaconController": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7",
"connectionManager": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2",
"connectionManager": "0x01812D60958798695391dacF092BAc4a715B1718",
"interchainGasPaymaster": {
"kind": "UpgradeBeacon",
"proxy": "0x44b764045BfDC68517e10e783E69B376cef196B2",
@ -729,7 +729,7 @@
},
"moonbasealpha": {
"upgradeBeaconController": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8",
"connectionManager": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37",
"connectionManager": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f",
"interchainGasPaymaster": {
"kind": "UpgradeBeacon",
"proxy": "0xeb6f11189197223c656807a83B0DD374f9A6dF44",

@ -2,6 +2,7 @@ import { Debugger, debug } from 'debug';
import { ethers } from 'ethers';
import {
Create2Factory__factory,
Ownable,
UpgradeBeaconProxy__factory,
UpgradeBeacon__factory,
@ -26,6 +27,13 @@ export interface DeployerOptions {
logger?: Debugger;
}
export interface DeployOptions {
create2Salt?: string;
initCalldata?: string;
}
export const CREATE2FACTORY_ADDRESS =
'0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a';
export abstract class HyperlaneDeployer<
Chain extends ChainName,
Config,
@ -113,6 +121,7 @@ export abstract class HyperlaneDeployer<
factory: F,
contractName: string,
args: Parameters<F['deploy']>,
deployOpts?: DeployOptions,
): Promise<ReturnType<F['deploy']>> {
const cachedContract = this.deployedContracts[chain]?.[contractName];
if (cachedContract) {
@ -128,31 +137,92 @@ export abstract class HyperlaneDeployer<
this.logger(`Deploy ${contractName} on ${chain}`);
const contract = await factory
.connect(signer)
.deploy(...args, chainConnection.overrides);
if (
deployOpts &&
deployOpts.create2Salt &&
(await chainConnection.provider.getCode(CREATE2FACTORY_ADDRESS)) != '0x'
) {
if (args.length > 0) {
throw new Error("Can't use CREATE2 with deployment args");
}
this.logger(`Deploying with CREATE2 factory`);
const create2Factory = Create2Factory__factory.connect(
CREATE2FACTORY_ADDRESS,
signer,
);
const salt = ethers.utils.keccak256(
ethers.utils.hexlify(ethers.utils.toUtf8Bytes(deployOpts.create2Salt)),
);
const contractAddr = await create2Factory.deployedAddress(
factory.bytecode,
await signer.getAddress(),
salt,
);
await chainConnection.handleTx(contract.deployTransaction);
const deployTx = deployOpts.initCalldata
? await create2Factory.deployAndInit(
factory.bytecode,
salt,
deployOpts.initCalldata,
chainConnection.overrides,
)
: await create2Factory.deploy(
factory.bytecode,
salt,
chainConnection.overrides,
);
await chainConnection.handleTx(deployTx);
const verificationInput = getContractVerificationInput(
contractName,
contract,
factory.bytecode,
);
this.verificationInputs[chain].push(verificationInput);
return contract as ReturnType<F['deploy']>;
this.verificationInputs[chain].push({
name: contractName,
address: contractAddr,
isProxy: false,
constructorArguments: '',
});
return factory.attach(contractAddr).connect(signer) as ReturnType<
F['deploy']
>;
} else {
const contract = await factory
.connect(signer)
.deploy(...args, chainConnection.overrides);
await chainConnection.handleTx(contract.deployTransaction);
if (deployOpts?.initCalldata) {
this.logger(`Initialize ${contractName} on ${chain}`);
const initTx = await signer.sendTransaction({
to: contract.address,
data: deployOpts.initCalldata,
});
await chainConnection.handleTx(initTx);
}
const verificationInput = getContractVerificationInput(
contractName,
contract,
factory.bytecode,
);
this.verificationInputs[chain].push(verificationInput);
return contract as ReturnType<F['deploy']>;
}
}
async deployContract<K extends keyof Factories>(
chain: Chain,
contractName: K,
args: Parameters<Factories[K]['deploy']>,
deployOpts?: DeployOptions,
): Promise<ReturnType<Factories[K]['deploy']>> {
return this.deployContractFromFactory(
chain,
this.factories[contractName],
contractName.toString(),
args,
deployOpts,
);
}

@ -185,6 +185,7 @@ export class HyperlaneCoreDeployer<
inboxes[remote]!.inbox.address,
);
if (!isEnrolled) {
this.logger(`Enrolling inbox for remote '${remote}'`);
const enrollTx = await connectionManager.enrollInbox(
chainMetadata[remote].id,
inboxes[remote]!.inbox.address,

Loading…
Cancel
Save