Replace foundry with HyperlaneCoreDeployer (#28)
This PR replaces foundry with the SDK deployment tooling. To deploy hyperlane to a new chain, users add their `ChainMetadata` to `config/chains.ts`, and their `MultisigIsmConfig(s)` to `config/multisig_ism.ts`, and run: ``` yarn ts-node scripts/deploy.ts --local foochain --remotes goerli mumbai --key 0x1234... ``` This script deploys core, IGP, ISM, and TestRecipient contracts to the local chain, and IGP, ISM, and TestRecipient contracts to the remote chains, writing contract addresses to `artifacts/addresses.json`, and agent config to `artifacts/agent_config.json` Users can test their deployment by running: ``` yarn ts-node scripts/test-messages.ts --chains foochain goerli mumbai --key 0x1234... ``` This script dispatches messages between each pair of provided chains, regularly polling the mailboxes to check to see if they were delivered.asaj/ci-2
parent
b839529296
commit
1fdf00846f
@ -1,6 +1,4 @@ |
||||
node_modules/ |
||||
broadcast/ |
||||
out/ |
||||
forge-cache/ |
||||
cache/ |
||||
config/*_agent_config.json |
||||
dist/ |
||||
artifacts/ |
||||
.yarn* |
||||
|
@ -1,4 +0,0 @@ |
||||
[submodule "lib/forge-std"] |
||||
path = lib/forge-std |
||||
url = https://github.com/foundry-rs/forge-std |
||||
tag = v1.1.1 |
@ -0,0 +1,22 @@ |
||||
{ |
||||
"anvil1": { |
||||
"validatorAnnounce": "0x38a024C0b412B9d1db8BC398140D00F5Af3093D4", |
||||
"proxyAdmin": "0xD42912755319665397FF090fBB63B1a31aE87Cee", |
||||
"mailbox": "0x525C7063E7C20997BaaE9bDa922159152D0e8417", |
||||
"multisigIsm": "0xCA8c8688914e0F7096c920146cd0Ad85cD7Ae8b9", |
||||
"testRecipient": "0x976fcd02f7C4773dd89C309fBF55D5923B4c98a1", |
||||
"storageGasOracle": "0xfcDB4564c18A9134002b9771816092C9693622e3", |
||||
"interchainGasPaymaster": "0x32EEce76C2C2e8758584A83Ee2F522D4788feA0f", |
||||
"defaultIsmInterchainGasPaymaster": "0x02b0B4EFd909240FCB2Eb5FAe060dC60D112E3a4" |
||||
}, |
||||
"anvil2": { |
||||
"multisigIsm": "0xF32D39ff9f6Aa7a7A64d7a4F00a54826Ef791a55", |
||||
"testRecipient": "0x976fcd02f7C4773dd89C309fBF55D5923B4c98a1", |
||||
"proxyAdmin": "0xD42912755319665397FF090fBB63B1a31aE87Cee", |
||||
"storageGasOracle": "0xfcDB4564c18A9134002b9771816092C9693622e3", |
||||
"interchainGasPaymaster": "0x32EEce76C2C2e8758584A83Ee2F522D4788feA0f", |
||||
"defaultIsmInterchainGasPaymaster": "0x02b0B4EFd909240FCB2Eb5FAe060dC60D112E3a4", |
||||
"validatorAnnounce": "0x5FeaeBfB4439F3516c74939A9D04e95AFE82C4ae", |
||||
"mailbox": "0xB0f05d25e41FbC2b52013099ED9616f1206Ae21B" |
||||
} |
||||
} |
@ -0,0 +1,84 @@ |
||||
for CHAIN in anvil1 anvil2 |
||||
do |
||||
mkdir ./artifacts/$CHAIN ./artifacts/$CHAIN/state \ |
||||
./artifacts/$CHAIN/validator ./artifacts/$CHAIN/relayer |
||||
chmod 777 ./artifacts/$CHAIN -R |
||||
done |
||||
|
||||
anvil --chain-id 31337 -p 8545 --state ./artifacts/anvil1/state > /dev/null & |
||||
ANVIL_1_PID=$! |
||||
|
||||
anvil --chain-id 31338 -p 8555 --state ./artifacts/anvil2/state > /dev/null & |
||||
ANVIL_2_PID=$! |
||||
|
||||
sleep 1 |
||||
|
||||
set -e |
||||
|
||||
for i in "anvil1 anvil2 --no-write-agent-config" "anvil2 anvil1 --write-agent-config" |
||||
do |
||||
set -- $i |
||||
echo "Deploying contracts to $1" |
||||
DEBUG=hyperlane* yarn ts-node scripts/deploy.ts --local $1 --remotes $2 \ |
||||
--key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 $3 |
||||
done |
||||
|
||||
|
||||
for i in "anvil1 8545 ANVIL1" "anvil2 8555 ANVIL2" |
||||
do |
||||
set -- $i |
||||
echo "Running validator on $1" |
||||
# Won't work on anything but linux due to -net=host |
||||
docker run --mount type=bind,source="$(pwd)/artifacts",target=/config --net=host \ |
||||
-e CONFIG_FILES=/config/agent_config.json -e HYP_VALIDATOR_ORIGINCHAINNAME=$1 \ |
||||
-e HYP_VALIDATOR_REORGPERIOD=1 -e HYP_VALIDATOR_INTERVAL=1 \ |
||||
-e HYP_BASE_CHAINS_${3}_CONNECTION_URL=http://127.0.0.1:${2} \ |
||||
-e HYP_VALIDATOR_VALIDATOR_TYPE=hexKey \ |
||||
-e HYP_VALIDATOR_VALIDATOR_KEY=0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 \ |
||||
-e HYP_VALIDATOR_CHECKPOINTSYNCER_TYPE=localStorage \ |
||||
-e HYP_VALIDATOR_CHECKPOINTSYNCER_PATH=/config/${1}/validator \ |
||||
-e HYP_BASE_TRACING_LEVEL=info -e HYP_BASE_TRACING_FMT=pretty \ |
||||
gcr.io/abacus-labs-dev/hyperlane-agent:5bf8aed-20230323-140136 ./validator & |
||||
done |
||||
|
||||
sleep 10 |
||||
|
||||
for i in "anvil1 8545" "anvil2 8555" |
||||
do |
||||
set -- $i |
||||
echo "Announcing validator on $1" |
||||
VALIDATOR_ANNOUNCE_ADDRESS=$(cat ./artifacts/addresses.json | jq -r ".$1.validatorAnnounce") |
||||
VALIDATOR=$(cat ./artifacts/$1/validator/announcement.json | jq -r '.value.validator') |
||||
STORAGE_LOCATION=$(cat ./artifacts/$1/validator/announcement.json | jq -r '.value.storage_location') |
||||
SIGNATURE=$(cat ./artifacts/$1/validator/announcement.json | jq -r '.serialized_signature') |
||||
cast send $VALIDATOR_ANNOUNCE_ADDRESS \ |
||||
"announce(address, string calldata, bytes calldata)(bool)" \ |
||||
$VALIDATOR $STORAGE_LOCATION $SIGNATURE --rpc-url http://127.0.0.1:$2 \ |
||||
--private-key 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba |
||||
done |
||||
|
||||
|
||||
for i in "anvil1 anvil2 ANVIL2" "anvil2 anvil1 ANVIL1" |
||||
do |
||||
set -- $i |
||||
echo "Running relayer on $1" |
||||
docker run --mount type=bind,source="$(pwd)/artifacts",target=/config --net=host \ |
||||
-e CONFIG_FILES=/config/agent_config.json \ |
||||
-e HYP_BASE_CHAINS_ANVIL1_CONNECTION_URL=http://127.0.0.1:8545 \ |
||||
-e HYP_BASE_CHAINS_ANVIL2_CONNECTION_URL=http://127.0.0.1:8555 \ |
||||
-e HYP_BASE_TRACING_LEVEL=info -e HYP_BASE_TRACING_FMT=pretty \ |
||||
-e HYP_RELAYER_ORIGINCHAINNAME=$1 -e HYP_RELAYER_DESTINATIONCHAINNAMES=$2 \ |
||||
-e HYP_RELAYER_ALLOWLOCALCHECKPOINTSYNCERS=true -e HYP_RELAYER_DB=/config/$1/relayer \ |
||||
-e HYP_RELAYER_GASPAYMENTENFORCEMENT='[{"type":"none"}]' \ |
||||
-e HYP_BASE_CHAINS_${3}_SIGNER_TYPE=hexKey \ |
||||
-e HYP_BASE_CHAINS_${3}_SIGNER_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 \ |
||||
gcr.io/abacus-labs-dev/hyperlane-agent:5bf8aed-20230323-140136 ./relayer & |
||||
done |
||||
|
||||
|
||||
|
||||
DEBUG=hyperlane* yarn ts-node scripts/test-messages.ts --chains anvil1 anvil2 --key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --timeout 60 |
||||
|
||||
docker ps -aq | xargs docker stop | xargs docker rm |
||||
kill $ANVIL_1_PID |
||||
kill $ANVIL_2_PID |
@ -0,0 +1,24 @@ |
||||
import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; |
||||
|
||||
export const chains: ChainMap<ChainMetadata> = { |
||||
// ----------- Your chains here -----------------
|
||||
anvil: { |
||||
name: 'anvil1', |
||||
// anvil default chain id
|
||||
chainId: 31337, |
||||
publicRpcUrls: [ |
||||
{ |
||||
http: 'http://127.0.0.1:8545', |
||||
}, |
||||
], |
||||
}, |
||||
anvil2: { |
||||
name: 'anvil2', |
||||
chainId: 31338, |
||||
publicRpcUrls: [ |
||||
{ |
||||
http: 'http://127.0.0.1:8555', |
||||
}, |
||||
], |
||||
}, |
||||
}; |
@ -1,96 +0,0 @@ |
||||
{ |
||||
"foochain": { |
||||
"threshold": 1, |
||||
"validators": ["0xb17861defd784d13c257f2430c4724ffa7ffc554"] |
||||
}, |
||||
"alfajores": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0xe6072396568e73ce6803b12b7e04164e839f1e54", |
||||
"0x9f177f51289b22515f41f95872e1511391b8e105", |
||||
"0x15f77400845eb1c971ad08de050861d5508cad6c" |
||||
] |
||||
}, |
||||
"fuji": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0x9fa19ead5ec76e437948b35e227511b106293c40", |
||||
"0x227e7d6507762ece0c94678f8c103eff9d682476", |
||||
"0x2379e43740e4aa4fde48cf4f00a3106df1d8420d" |
||||
] |
||||
}, |
||||
"mumbai": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0x0a664ea799447da6b15645cf8b9e82072a68343f", |
||||
"0x6ae6f12929a960aba24ba74ea310e3d37d0ac045", |
||||
"0x51f70c047cd73bc7873273707501568857a619c4" |
||||
] |
||||
}, |
||||
"bsctestnet": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0x23338c8714976dd4a57eaeff17cbd26d7e275c08", |
||||
"0x85a618d7450ebc37e0d682371f08dac94eec7a76", |
||||
"0x95b76562e4ba1791a27ba4236801271c9115b141" |
||||
] |
||||
}, |
||||
"goerli": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0xf43fbd072fd38e1121d4b3b0b8a35116bbb01ea9", |
||||
|
||||
"0xa33020552a21f35e75bd385c6ab95c3dfa82d930", |
||||
|
||||
"0x0bba4043ff242f8bf3f39bafa8930a84d644d947" |
||||
] |
||||
}, |
||||
"moonbasealpha": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0x890c2aeac157c3f067f3e42b8afc797939c59a32", |
||||
"0x1b06d6fe69b972ed7420c83599d5a5c0fc185904", |
||||
"0xe70b85206a968a99a597581f0fa09c99e7681093" |
||||
] |
||||
}, |
||||
"optimismgoerli": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0xbb8d77eefbecc55db6e5a19b0fc3dc290776f189", |
||||
"0x69792508b4ddaa3ca52241ccfcd1e0b119a1ee65", |
||||
"0x11ddb46c6b653e0cdd7ad5bee32ae316e18f8453" |
||||
] |
||||
}, |
||||
"arbitrumgoerli": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0xce798fa21e323f6b24d9838a10ffecdefdfc4f30", |
||||
"0xa792d39dca4426927e0f00c1618d61c9cb41779d", |
||||
"0xdf181fcc11dfac5d01467e4547101a856dd5aa04" |
||||
] |
||||
}, |
||||
"test1": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0xf2561178e8ad8cadd161a855adc6eb02282b426d", |
||||
"0x28d638618aca345450604a4bb17e50ce6fe6ee0e", |
||||
"0x8dc5bd07ffd0a0053bbf4499747ba6da8442ab36" |
||||
] |
||||
}, |
||||
"test2": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0xf2561178e8ad8cadd161a855adc6eb02282b426d", |
||||
"0x28d638618aca345450604a4bb17e50ce6fe6ee0e", |
||||
"0x8dc5bd07ffd0a0053bbf4499747ba6da8442ab36" |
||||
] |
||||
}, |
||||
"test3": { |
||||
"threshold": 2, |
||||
"validators": [ |
||||
"0xf2561178e8ad8cadd161a855adc6eb02282b426d", |
||||
"0x28d638618aca345450604a4bb17e50ce6fe6ee0e", |
||||
"0x8dc5bd07ffd0a0053bbf4499747ba6da8442ab36" |
||||
] |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
import { ChainMap, MultisigIsmConfig } from '@hyperlane-xyz/sdk'; |
||||
|
||||
export const multisigIsmConfig: ChainMap<MultisigIsmConfig> = { |
||||
// ----------- Your chains here -----------------
|
||||
anvil1: { |
||||
threshold: 1, |
||||
validators: [ |
||||
// Last anvil address
|
||||
'0xa0ee7a142d267c1f36714e4a8f75612f20a79720', |
||||
], |
||||
}, |
||||
anvil2: { |
||||
threshold: 1, |
||||
validators: [ |
||||
// Last anvil address
|
||||
'0xa0ee7a142d267c1f36714e4a8f75612f20a79720', |
||||
], |
||||
}, |
||||
}; |
@ -1,119 +0,0 @@ |
||||
{ |
||||
"foochain": { |
||||
"id": 1174786466, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"testRecipient": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", |
||||
"create2Factory": "0x0000000000000000000000000000000000000000", |
||||
"mailbox": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788", |
||||
"validatorAnnounce": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", |
||||
"interchainGasPaymaster": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", |
||||
"proxyAdmin": "0x5FbDB2315678afecb367f032d93F642f64180aa3" |
||||
} |
||||
}, |
||||
"alfajores": { |
||||
"id": 44787, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"fuji": { |
||||
"id": 43113, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"mumbai": { |
||||
"id": 80001, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"bsctestnet": { |
||||
"id": 97, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"goerli": { |
||||
"id": 5, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"moonbasealpha": { |
||||
"id": 1287, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"optimismgoerli": { |
||||
"id": 420, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"arbitrumgoerli": { |
||||
"id": 421613, |
||||
"owner": "0xfaD1C94469700833717Fa8a3017278BC1cA8031C", |
||||
"contracts": { |
||||
"proxyAdmin": "0x4e4D563e2cBFC35c4BC16003685443Fae2FA702f", |
||||
"mailbox": "0xCC737a94FecaeC165AbCf12dED095BB13F037685", |
||||
"interchainGasPaymaster": "0x8f9C3888bFC8a5B25AED115A82eCbb788b196d2a", |
||||
"create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", |
||||
"validatorAnnounce": "0x3Fc742696D5dc9846e04f7A1823D92cb51695f9a", |
||||
"testRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
||||
} |
||||
}, |
||||
"test1": { |
||||
"id": 13371 |
||||
}, |
||||
"test2": { |
||||
"id": 13372 |
||||
}, |
||||
"test3": { |
||||
"id": 13373 |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
import { ChainMap } from '@hyperlane-xyz/sdk'; |
||||
|
||||
export const startBlocks: ChainMap<number> = { |
||||
// --------------- Mainnets ---------------------
|
||||
celo: 16884144, |
||||
ethereum: 16271503, |
||||
avalanche: 24145479, |
||||
polygon: 37313389, |
||||
bsc: 25063295, |
||||
arbitrum: 49073182, |
||||
optimism: 55698988, |
||||
moonbeam: 2595747, |
||||
gnosis: 25900000, |
||||
// --------------- Testnets ---------------------
|
||||
alfajores: 14863532, |
||||
fuji: 16330615, |
||||
mumbai: 29390033, |
||||
bsctestnet: 25001629, |
||||
goerli: 8039005, |
||||
sepolia: 3082913, |
||||
moonbasealpha: 3310405, |
||||
optimismgoerli: 3055263, |
||||
arbitrumgoerli: 1941997, |
||||
}; |
@ -1,14 +0,0 @@ |
||||
[profile.default] |
||||
src = 'scripts' |
||||
out = 'out' |
||||
libs = ['node_modules', 'lib'] |
||||
test = 'test' |
||||
cache_path = 'forge-cache' |
||||
allow_paths = ["../node_modules"] |
||||
solc = '0.8.17' |
||||
optimizer = true |
||||
optimizer_runs = 999_999 |
||||
fs_permissions = [{ access = "read-write", path = "./"}] |
||||
|
||||
[profile.ci] |
||||
verbosity = 4 |
@ -1,80 +0,0 @@ |
||||
// from https://gist.github.com/rmeissner/76d6345796909ee41fb9f36fdaa4d15f |
||||
pragma solidity >=0.8.0 <0.9.0; |
||||
|
||||
library BytesLib { |
||||
function slice( |
||||
bytes memory _bytes, |
||||
uint256 _start, |
||||
uint256 _length |
||||
) internal pure returns (bytes memory) { |
||||
require(_length + 31 >= _length, "slice_overflow"); |
||||
require(_bytes.length >= _start + _length, "slice_outOfBounds"); |
||||
|
||||
bytes memory tempBytes; |
||||
|
||||
// Check length is 0. `iszero` return 1 for `true` and 0 for `false`. |
||||
assembly { |
||||
switch iszero(_length) |
||||
case 0 { |
||||
// Get a location of some free memory and store it in tempBytes as |
||||
// Solidity does for memory variables. |
||||
tempBytes := mload(0x40) |
||||
|
||||
// Calculate length mod 32 to handle slices that are not a multiple of 32 in size. |
||||
let lengthmod := and(_length, 31) |
||||
|
||||
// tempBytes will have the following format in memory: <length><data> |
||||
// When copying data we will offset the start forward to avoid allocating additional memory |
||||
// Therefore part of the length area will be written, but this will be overwritten later anyways. |
||||
// In case no offset is require, the start is set to the data region (0x20 from the tempBytes) |
||||
// mc will be used to keep track where to copy the data to. |
||||
let mc := add( |
||||
add(tempBytes, lengthmod), |
||||
mul(0x20, iszero(lengthmod)) |
||||
) |
||||
let end := add(mc, _length) |
||||
|
||||
for { |
||||
// Same logic as for mc is applied and additionally the start offset specified for the method is added |
||||
let cc := add( |
||||
add( |
||||
add(_bytes, lengthmod), |
||||
mul(0x20, iszero(lengthmod)) |
||||
), |
||||
_start |
||||
) |
||||
} lt(mc, end) { |
||||
// increase `mc` and `cc` to read the next word from memory |
||||
mc := add(mc, 0x20) |
||||
cc := add(cc, 0x20) |
||||
} { |
||||
// Copy the data from source (cc location) to the slice data (mc location) |
||||
mstore(mc, mload(cc)) |
||||
} |
||||
|
||||
// Store the length of the slice. This will overwrite any partial data that |
||||
// was copied when having slices that are not a multiple of 32. |
||||
mstore(tempBytes, _length) |
||||
|
||||
// update free-memory pointer |
||||
// allocating the array padded to 32 bytes like the compiler does now |
||||
// To set the used memory as a multiple of 32, add 31 to the actual memory usage (mc) |
||||
// and remove the modulo 32 (the `and` with `not(31)`) |
||||
mstore(0x40, and(add(mc, 31), not(31))) |
||||
} |
||||
// if we want a zero-length slice let's just return a zero-length array |
||||
default { |
||||
tempBytes := mload(0x40) |
||||
// zero out the 32 bytes slice we are about to return |
||||
// we need to do it because Solidity does not garbage collect |
||||
mstore(tempBytes, 0) |
||||
|
||||
// update free-memory pointer |
||||
// tempBytes uses 32 bytes in memory (even when empty) for the length. |
||||
mstore(0x40, add(tempBytes, 0x20)) |
||||
} |
||||
} |
||||
|
||||
return tempBytes; |
||||
} |
||||
} |
@ -1,105 +0,0 @@ |
||||
// SPDX-License-Identifier: UNLICENSED |
||||
pragma solidity ^0.8.17; |
||||
import "../lib/forge-std/src/console.sol"; |
||||
|
||||
import {ConfigLib} from "../lib/ConfigLib.sol"; |
||||
import {MultisigIsm} from "@hyperlane-xyz/core/contracts/isms/MultisigIsm.sol"; |
||||
import {TransparentUpgradeableProxy} from "@hyperlane-xyz/core/contracts/upgrade/TransparentUpgradeableProxy.sol"; |
||||
|
||||
library CheckLib { |
||||
function check( |
||||
ConfigLib.HyperlaneDomainConfig memory config, |
||||
ConfigLib.MultisigIsmConfig memory ismConfig |
||||
) internal view { |
||||
checkOwners(config); |
||||
checkAdmins(config); |
||||
checkMailboxIsm(config, ismConfig); |
||||
console.log( |
||||
"Succesfully checked Hyperlane deployment for %s", |
||||
config.chainName |
||||
); |
||||
} |
||||
|
||||
function checkOwners( |
||||
ConfigLib.HyperlaneDomainConfig memory config |
||||
) private view { |
||||
require( |
||||
config.admin.owner() == config.owner, |
||||
"ProxyAdmin owner misconfigured" |
||||
); |
||||
require( |
||||
config.igp.owner() == config.owner, |
||||
"InterchainGasPaymaster owner misconfigured" |
||||
); |
||||
require( |
||||
config.mailbox.owner() == config.owner, |
||||
"Mailbox owner misconfigured" |
||||
); |
||||
} |
||||
|
||||
function checkAdmins( |
||||
ConfigLib.HyperlaneDomainConfig memory config |
||||
) private view { |
||||
require( |
||||
config.admin.getProxyAdmin( |
||||
TransparentUpgradeableProxy(payable(address(config.igp))) |
||||
) == address(config.admin), |
||||
"InterchainGasPaymaster proxy admin misconfigured" |
||||
); |
||||
require( |
||||
config.admin.getProxyAdmin( |
||||
TransparentUpgradeableProxy(payable(address(config.mailbox))) |
||||
) == address(config.admin), |
||||
"Mailbox proxy admin misconfigured" |
||||
); |
||||
} |
||||
|
||||
function checkMailboxIsm( |
||||
ConfigLib.HyperlaneDomainConfig memory config, |
||||
ConfigLib.MultisigIsmConfig memory ismConfig |
||||
) private view { |
||||
MultisigIsm ism = MultisigIsm(address(config.mailbox.defaultIsm())); |
||||
check(ismConfig, ism, config.owner); |
||||
} |
||||
|
||||
function contains( |
||||
address[] memory set, |
||||
address element |
||||
) internal pure returns (bool) { |
||||
for (uint256 i = 0; i < set.length; i++) { |
||||
if (set[i] == element) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
function check( |
||||
ConfigLib.MultisigIsmConfig memory config, |
||||
MultisigIsm ism, |
||||
address owner |
||||
) internal view { |
||||
require(ism.owner() == owner, "MultisigIsm owner misconfigured"); |
||||
for (uint256 i = 0; i < config.domains.length; i++) { |
||||
ConfigLib.MultisigIsmDomainConfig memory domain = config.domains[i]; |
||||
require( |
||||
domain.threshold == ism.threshold(domain.domainId), |
||||
string.concat( |
||||
"Default MultisigIsm threshold misconfigured for", |
||||
domain.chainName |
||||
) |
||||
); |
||||
address[] memory validators = ism.validators(domain.domainId); |
||||
require(domain.validators.length == validators.length); |
||||
for (uint256 j = 0; j < validators.length; j++) { |
||||
require( |
||||
contains(domain.validators, validators[j]), |
||||
string.concat( |
||||
"Default MultisigIsm validator set misconfigured for ", |
||||
domain.chainName |
||||
) |
||||
); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,247 +0,0 @@ |
||||
// SPDX-License-Identifier: UNLICENSED |
||||
pragma solidity ^0.8.17; |
||||
import "../lib/forge-std/src/Script.sol"; |
||||
import {Vm} from "../lib/forge-std/src/Vm.sol"; |
||||
|
||||
import {BytesLib} from "../lib/BytesLib.sol"; |
||||
import {MultisigIsm} from "@hyperlane-xyz/core/contracts/isms/MultisigIsm.sol"; |
||||
import {Mailbox} from "@hyperlane-xyz/core/contracts/Mailbox.sol"; |
||||
import {InterchainGasPaymaster} from "@hyperlane-xyz/core/contracts/igps/InterchainGasPaymaster.sol"; |
||||
import {ValidatorAnnounce} from "@hyperlane-xyz/core/contracts/ValidatorAnnounce.sol"; |
||||
import {ProxyAdmin} from "@hyperlane-xyz/core/contracts/upgrade/ProxyAdmin.sol"; |
||||
import {TransparentUpgradeableProxy} from "@hyperlane-xyz/core/contracts/upgrade/TransparentUpgradeableProxy.sol"; |
||||
import {Create2Factory} from "@hyperlane-xyz/core/contracts/Create2Factory.sol"; |
||||
import {TestRecipient} from "@hyperlane-xyz/core/contracts/test/TestRecipient.sol"; |
||||
|
||||
library ConfigLib { |
||||
using stdJson for string; |
||||
using BytesLib for bytes; |
||||
|
||||
struct HyperlaneDomainConfig { |
||||
string chainName; |
||||
uint32 domainId; |
||||
address owner; |
||||
Mailbox mailbox; |
||||
InterchainGasPaymaster igp; |
||||
ProxyAdmin admin; |
||||
Create2Factory create2; |
||||
ValidatorAnnounce validatorAnnounce; |
||||
TestRecipient testRecipient; |
||||
} |
||||
|
||||
struct MultisigIsmDomainConfig { |
||||
string chainName; |
||||
uint32 domainId; |
||||
uint8 threshold; |
||||
address[] validators; |
||||
} |
||||
|
||||
struct MultisigIsmConfig { |
||||
MultisigIsmDomainConfig[] domains; |
||||
} |
||||
|
||||
function readContractAddress( |
||||
Vm vm, |
||||
string memory chainName, |
||||
string memory contractName |
||||
) private view returns (address) { |
||||
string memory json = vm.readFile("config/networks.json"); |
||||
string memory prefix = ".contracts."; |
||||
try |
||||
vm.parseJson( |
||||
json, |
||||
string.concat( |
||||
".", |
||||
chainName, |
||||
string.concat(prefix, contractName) |
||||
) |
||||
) |
||||
returns (bytes memory result) { |
||||
address parsedAddr = abi.decode(result, (address)); |
||||
return parsedAddr == address(0x20) ? address(0) : parsedAddr; |
||||
} catch { |
||||
return address(0); |
||||
} |
||||
} |
||||
|
||||
function readHyperlaneDomainConfig( |
||||
Vm vm, |
||||
string memory chainName |
||||
) internal view returns (HyperlaneDomainConfig memory) { |
||||
string memory json = vm.readFile("config/networks.json"); |
||||
// console.log(json); |
||||
uint32 domainId = abi.decode( |
||||
vm.parseJson(json, string.concat(".", chainName, ".id")), |
||||
(uint32) |
||||
); |
||||
address owner = abi.decode( |
||||
vm.parseJson(json, string.concat(".", chainName, ".owner")), |
||||
(address) |
||||
); |
||||
Mailbox mailbox = Mailbox( |
||||
readContractAddress(vm, chainName, "mailbox") |
||||
); |
||||
InterchainGasPaymaster igp = InterchainGasPaymaster( |
||||
readContractAddress(vm, chainName, "interchainGasPaymaster") |
||||
); |
||||
ProxyAdmin admin = ProxyAdmin( |
||||
readContractAddress(vm, chainName, "proxyAdmin") |
||||
); |
||||
Create2Factory create2 = Create2Factory( |
||||
readContractAddress(vm, chainName, "create2Factory") |
||||
); |
||||
TestRecipient recipient = TestRecipient( |
||||
readContractAddress(vm, chainName, "testRecipient") |
||||
); |
||||
ValidatorAnnounce validatorAnnounce = ValidatorAnnounce( |
||||
readContractAddress(vm, chainName, "validatorAnnounce") |
||||
); |
||||
return |
||||
HyperlaneDomainConfig( |
||||
chainName, |
||||
domainId, |
||||
owner, |
||||
mailbox, |
||||
igp, |
||||
admin, |
||||
create2, |
||||
validatorAnnounce, |
||||
recipient |
||||
); |
||||
} |
||||
|
||||
function readMultisigIsmDomainConfig( |
||||
Vm vm, |
||||
string memory chainName |
||||
) private view returns (MultisigIsmDomainConfig memory) { |
||||
console.log(chainName); |
||||
string memory json = vm.readFile("config/multisig_ism.json"); |
||||
uint8 threshold = abi.decode( |
||||
vm.parseJson(json, string.concat(".", chainName, ".threshold")), |
||||
(uint8) |
||||
); |
||||
address[] memory validators = abi.decode( |
||||
vm.parseJson(json, string.concat(".", chainName, ".validators")), |
||||
(address[]) |
||||
); |
||||
|
||||
json = vm.readFile("config/networks.json"); |
||||
uint32 domainId = abi.decode( |
||||
vm.parseJson(json, string.concat(".", chainName, ".id")), |
||||
(uint32) |
||||
); |
||||
return |
||||
MultisigIsmDomainConfig(chainName, domainId, threshold, validators); |
||||
} |
||||
|
||||
function readMultisigIsmConfig( |
||||
Vm vm, |
||||
string[] memory chainNames |
||||
) internal view returns (MultisigIsmConfig memory) { |
||||
MultisigIsmDomainConfig[] |
||||
memory domains = new MultisigIsmDomainConfig[](chainNames.length); |
||||
for (uint256 i = 0; i < chainNames.length; i++) { |
||||
string memory chainName = chainNames[i]; |
||||
domains[i] = readMultisigIsmDomainConfig(vm, chainName); |
||||
} |
||||
return MultisigIsmConfig(domains); |
||||
} |
||||
|
||||
function writeAgentConfig( |
||||
HyperlaneDomainConfig memory config, |
||||
Vm vm, |
||||
uint256 startBlock |
||||
) internal { |
||||
string memory baseConfig = "config"; |
||||
vm.serializeString( |
||||
baseConfig, |
||||
"domain", |
||||
vm.toString(uint256(config.domainId)) |
||||
); |
||||
vm.serializeString(baseConfig, "rpcStyle", "ethereum"); |
||||
vm.serializeString(baseConfig, "finalityBlocks", "POPULATE_ME"); |
||||
|
||||
string memory addresses = "addresses"; |
||||
vm.serializeAddress(addresses, "mailbox", address(config.mailbox)); |
||||
vm.serializeAddress( |
||||
addresses, |
||||
"validatorAnnounce", |
||||
address(config.validatorAnnounce) |
||||
); |
||||
vm.serializeString( |
||||
baseConfig, |
||||
"addresses", |
||||
vm.serializeAddress( |
||||
addresses, |
||||
"interchainGasPaymaster", |
||||
address(config.igp) |
||||
) |
||||
); |
||||
|
||||
string memory connection = "connection"; |
||||
vm.serializeString(connection, "type", "http"); |
||||
vm.serializeString( |
||||
baseConfig, |
||||
"connection", |
||||
vm.serializeString(connection, "url", "") |
||||
); |
||||
|
||||
string memory index = "index"; |
||||
vm.serializeString( |
||||
baseConfig, |
||||
"index", |
||||
vm.serializeString(index, "from", vm.toString(startBlock)) |
||||
); |
||||
|
||||
vm.serializeString(baseConfig, "name", config.chainName); |
||||
|
||||
vm |
||||
.serializeString( |
||||
"topLevel", |
||||
"chains", |
||||
vm.serializeString( |
||||
"chainLevel", |
||||
config.chainName, |
||||
vm.serializeString(baseConfig, "protocol", "ethereum") |
||||
) |
||||
) |
||||
.write( |
||||
string.concat( |
||||
"./config/", |
||||
config.chainName, |
||||
"_agent_config.json" |
||||
) |
||||
); |
||||
} |
||||
|
||||
function write(HyperlaneDomainConfig memory config, Vm vm) internal { |
||||
string memory contracts = "contracts"; |
||||
vm.serializeAddress(contracts, "mailbox", address(config.mailbox)); |
||||
vm.serializeAddress( |
||||
contracts, |
||||
"interchainGasPaymaster", |
||||
address(config.igp) |
||||
); |
||||
vm.serializeAddress(contracts, "proxyAdmin", address(config.admin)); |
||||
vm.serializeAddress( |
||||
contracts, |
||||
"validatorAnnounce", |
||||
address(config.validatorAnnounce) |
||||
); |
||||
vm.serializeAddress( |
||||
contracts, |
||||
"testRecipient", |
||||
address(config.testRecipient) |
||||
); |
||||
vm |
||||
.serializeAddress( |
||||
contracts, |
||||
"create2Factory", |
||||
address(config.create2) |
||||
) |
||||
.write( |
||||
"./config/networks.json", |
||||
string.concat(".", config.chainName, ".contracts") |
||||
); |
||||
} |
||||
} |
@ -1,165 +0,0 @@ |
||||
// SPDX-License-Identifier: UNLICENSED |
||||
pragma solidity ^0.8.17; |
||||
import "../lib/forge-std/src/console.sol"; |
||||
|
||||
import {ConfigLib} from "../lib/ConfigLib.sol"; |
||||
import {MultisigIsm} from "@hyperlane-xyz/core/contracts/isms/MultisigIsm.sol"; |
||||
import {Mailbox} from "@hyperlane-xyz/core/contracts/Mailbox.sol"; |
||||
import {InterchainGasPaymaster} from "@hyperlane-xyz/core/contracts/igps/InterchainGasPaymaster.sol"; |
||||
import {ValidatorAnnounce} from "@hyperlane-xyz/core/contracts/ValidatorAnnounce.sol"; |
||||
import {ProxyAdmin} from "@hyperlane-xyz/core/contracts/upgrade/ProxyAdmin.sol"; |
||||
import {TransparentUpgradeableProxy} from "@hyperlane-xyz/core/contracts/upgrade/TransparentUpgradeableProxy.sol"; |
||||
import {Create2Factory} from "@hyperlane-xyz/core/contracts/Create2Factory.sol"; |
||||
import {TestRecipient} from "@hyperlane-xyz/core/contracts/test/TestRecipient.sol"; |
||||
|
||||
library DeployLib { |
||||
function deploy( |
||||
ConfigLib.HyperlaneDomainConfig memory config, |
||||
ConfigLib.MultisigIsmConfig memory ismConfig |
||||
) internal { |
||||
deployProxyAdmin(config); |
||||
deployIgp(config); |
||||
deployMailbox(config, ismConfig); |
||||
deployTestRecipient(config); |
||||
deployValidatorAnnounce(config); |
||||
} |
||||
|
||||
function deployValidatorAnnounce( |
||||
ConfigLib.HyperlaneDomainConfig memory config |
||||
) private { |
||||
if (address(config.validatorAnnounce) == address(0)) { |
||||
config.validatorAnnounce = new ValidatorAnnounce( |
||||
address(config.mailbox) |
||||
); |
||||
console.log( |
||||
"ValidatorAnnounce deployed at address %s", |
||||
address(config.validatorAnnounce) |
||||
); |
||||
} else { |
||||
console.log( |
||||
"Found ValidatorAnnounce at address %s, skipping deployment", |
||||
address(config.validatorAnnounce) |
||||
); |
||||
} |
||||
} |
||||
|
||||
function deployProxyAdmin( |
||||
ConfigLib.HyperlaneDomainConfig memory config |
||||
) private { |
||||
if (address(config.admin) == address(0)) { |
||||
config.admin = new ProxyAdmin(); |
||||
console.log( |
||||
"ProxyAdmin deployed at address %s", |
||||
address(config.admin) |
||||
); |
||||
config.admin.transferOwnership(config.owner); |
||||
} else { |
||||
console.log( |
||||
"Found ProxyAdmin at address %s, skipping deployment", |
||||
address(config.admin) |
||||
); |
||||
} |
||||
} |
||||
|
||||
function deployIgp(ConfigLib.HyperlaneDomainConfig memory config) private { |
||||
require( |
||||
address(config.admin) != address(0), |
||||
"Must deploy ProxyAdmin before InterchainGasPaymaster" |
||||
); |
||||
if (address(config.igp) == address(0)) { |
||||
InterchainGasPaymaster impl = new InterchainGasPaymaster(); |
||||
bytes memory initData = abi.encodeCall( |
||||
InterchainGasPaymaster.initialize, |
||||
() |
||||
); |
||||
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( |
||||
address(impl), |
||||
address(config.admin), |
||||
initData |
||||
); |
||||
console.log( |
||||
"InterchainGasPaymaster deployed at address %s", |
||||
address(proxy) |
||||
); |
||||
config.igp = InterchainGasPaymaster(address(proxy)); |
||||
config.igp.transferOwnership(config.owner); |
||||
} else { |
||||
console.log( |
||||
"Found InterchainGasPaymaster at address %s, skipping deployment", |
||||
address(config.igp) |
||||
); |
||||
} |
||||
} |
||||
|
||||
function deployMailbox( |
||||
ConfigLib.HyperlaneDomainConfig memory config, |
||||
ConfigLib.MultisigIsmConfig memory ismConfig |
||||
) private { |
||||
require( |
||||
address(config.admin) != address(0), |
||||
"Must deploy ProxyAdmin before Mailbox" |
||||
); |
||||
if (address(config.mailbox) == address(0)) { |
||||
MultisigIsm ism = deploy(ismConfig, config.owner); |
||||
|
||||
Mailbox mailbox = new Mailbox(config.domainId); |
||||
bytes memory initData = abi.encodeCall( |
||||
Mailbox.initialize, |
||||
(config.owner, address(ism)) |
||||
); |
||||
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( |
||||
address(mailbox), |
||||
address(config.admin), |
||||
initData |
||||
); |
||||
console.log("Mailbox deployed at address %s", address(proxy)); |
||||
config.mailbox = Mailbox(address(proxy)); |
||||
} else { |
||||
console.log( |
||||
"Found Mailbox at address %s, skipping deployment", |
||||
address(config.igp) |
||||
); |
||||
} |
||||
} |
||||
|
||||
function deployTestRecipient( |
||||
ConfigLib.HyperlaneDomainConfig memory config |
||||
) private { |
||||
if (address(config.testRecipient) == address(0)) { |
||||
config.testRecipient = new TestRecipient(); |
||||
console.log( |
||||
"TestRecipient deployed at address %s", |
||||
address(config.testRecipient) |
||||
); |
||||
} else { |
||||
console.log( |
||||
"Found TestRecipient at address %s, skipping deployment", |
||||
address(config.igp) |
||||
); |
||||
} |
||||
} |
||||
|
||||
function deploy( |
||||
ConfigLib.MultisigIsmConfig memory config, |
||||
address owner |
||||
) internal returns (MultisigIsm) { |
||||
// Deploy a default MultisigIsm and enroll validators for remote |
||||
// networks. |
||||
MultisigIsm ism = new MultisigIsm(); |
||||
console.log("MultisigIsm deployed at address %s", address(ism)); |
||||
uint32[] memory remoteDomainIds = new uint32[](config.domains.length); |
||||
uint8[] memory remoteThresholds = new uint8[](config.domains.length); |
||||
address[][] memory remoteValidators = new address[][]( |
||||
config.domains.length |
||||
); |
||||
for (uint256 i = 0; i < config.domains.length; i++) { |
||||
remoteDomainIds[i] = config.domains[i].domainId; |
||||
remoteThresholds[i] = config.domains[i].threshold; |
||||
remoteValidators[i] = config.domains[i].validators; |
||||
} |
||||
ism.enrollValidators(remoteDomainIds, remoteValidators); |
||||
ism.setThresholds(remoteDomainIds, remoteThresholds); |
||||
ism.transferOwnership(owner); |
||||
return ism; |
||||
} |
||||
} |
@ -1 +0,0 @@ |
||||
Subproject commit 181c0c686a8421cfb530e06bda9d00632c9d8637 |
@ -1,42 +0,0 @@ |
||||
// SPDX-License-Identifier: UNLICENSED |
||||
pragma solidity ^0.8.17; |
||||
|
||||
import "../lib/forge-std/src/Script.sol"; |
||||
import "../lib/forge-std/src/console.sol"; |
||||
|
||||
import {BytesLib} from "../lib/BytesLib.sol"; |
||||
import {ConfigLib} from "../lib/ConfigLib.sol"; |
||||
import {Mailbox} from "@hyperlane-xyz/core/contracts/Mailbox.sol"; |
||||
import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; |
||||
|
||||
// TODO: Maybe take recipient as an arg.. |
||||
contract CheckMessage is Script { |
||||
using TypeCasts for address; |
||||
using BytesLib for bytes; |
||||
|
||||
function run() public view { |
||||
string memory destination = vm.envString("DESTINATION"); |
||||
bytes32 messageId = vm.envBytes32("MESSAGE_ID"); |
||||
Mailbox mailbox = ConfigLib |
||||
.readHyperlaneDomainConfig(vm, destination) |
||||
.mailbox; |
||||
bool delivered = mailbox.delivered(messageId); |
||||
if (delivered) { |
||||
console.log( |
||||
"Message ID '%s' HAS been delivered to %s", |
||||
vm.toString(messageId), |
||||
destination |
||||
); |
||||
} else { |
||||
console.log( |
||||
"Message ID '%s' HAS NOT been delivered to %s", |
||||
vm.toString(messageId), |
||||
destination |
||||
); |
||||
} |
||||
console.log( |
||||
"https://explorer.hyperlane.xyz/message/%s", |
||||
string(abi.encodePacked(vm.toString(messageId)).slice(2, 64)) |
||||
); |
||||
} |
||||
} |
@ -1,35 +0,0 @@ |
||||
// SPDX-License-Identifier: UNLICENSED |
||||
pragma solidity ^0.8.17; |
||||
|
||||
import "../lib/forge-std/src/Script.sol"; |
||||
|
||||
import {ConfigLib} from "../lib/ConfigLib.sol"; |
||||
import {CheckLib} from "../lib/CheckLib.sol"; |
||||
import {DeployLib} from "../lib/DeployLib.sol"; |
||||
|
||||
contract DeployCore is Script { |
||||
using ConfigLib for ConfigLib.HyperlaneDomainConfig; |
||||
using CheckLib for ConfigLib.HyperlaneDomainConfig; |
||||
using DeployLib for ConfigLib.HyperlaneDomainConfig; |
||||
|
||||
function run() public { |
||||
string memory local = vm.envString("LOCAL"); |
||||
string[] memory remotes = vm.envString("REMOTES", ","); |
||||
ConfigLib.HyperlaneDomainConfig memory config = ConfigLib |
||||
.readHyperlaneDomainConfig(vm, local); |
||||
ConfigLib.MultisigIsmConfig memory ismConfig = ConfigLib |
||||
.readMultisigIsmConfig(vm, remotes); |
||||
|
||||
vm.startBroadcast(); |
||||
uint256 startBlock = block.number; |
||||
|
||||
config.deploy(ismConfig); |
||||
config.check(ismConfig); |
||||
|
||||
// Write the output to disk |
||||
config.write(vm); |
||||
config.writeAgentConfig(vm, startBlock); |
||||
|
||||
vm.stopBroadcast(); |
||||
} |
||||
} |
@ -1,39 +0,0 @@ |
||||
// SPDX-License-Identifier: UNLICENSED |
||||
pragma solidity ^0.8.17; |
||||
|
||||
import "../lib/forge-std/src/Script.sol"; |
||||
import "../lib/forge-std/src/console.sol"; |
||||
|
||||
import {ConfigLib} from "../lib/ConfigLib.sol"; |
||||
import {CheckLib} from "../lib/CheckLib.sol"; |
||||
import {DeployLib} from "../lib/DeployLib.sol"; |
||||
import {MultisigIsm} from "@hyperlane-xyz/core/contracts/isms/MultisigIsm.sol"; |
||||
import {TestRecipient} from "@hyperlane-xyz/core/contracts/test/TestRecipient.sol"; |
||||
import {InterchainGasPaymaster} from "@hyperlane-xyz/core/contracts/igps/InterchainGasPaymaster.sol"; |
||||
|
||||
// TODO: Deploy test recipient, maybe write to networks. |
||||
contract DeployMultisigIsm is Script { |
||||
using CheckLib for ConfigLib.MultisigIsmConfig; |
||||
using DeployLib for ConfigLib.MultisigIsmConfig; |
||||
|
||||
function run() public { |
||||
address owner = vm.envAddress("OWNER"); |
||||
string[] memory remotes = vm.envString("REMOTES", ","); |
||||
ConfigLib.MultisigIsmConfig memory config = ConfigLib |
||||
.readMultisigIsmConfig(vm, remotes); |
||||
|
||||
vm.startBroadcast(); |
||||
|
||||
MultisigIsm ism = config.deploy(owner); |
||||
TestRecipient recipient = new TestRecipient(); |
||||
recipient.setInterchainSecurityModule(address(ism)); |
||||
console.log("TestRecipient deployed at address %s", address(recipient)); |
||||
|
||||
InterchainGasPaymaster igp = new InterchainGasPaymaster(); |
||||
console.log( |
||||
"InterchainGasPaymaster deployed at address %s", |
||||
address(igp) |
||||
); |
||||
config.check(ism, owner); |
||||
} |
||||
} |
@ -1,44 +0,0 @@ |
||||
// SPDX-License-Identifier: UNLICENSED |
||||
pragma solidity ^0.8.17; |
||||
|
||||
import "../lib/forge-std/src/Script.sol"; |
||||
import "../lib/forge-std/src/console.sol"; |
||||
|
||||
import {BytesLib} from "../lib/BytesLib.sol"; |
||||
import {ConfigLib} from "../lib/ConfigLib.sol"; |
||||
import {Mailbox} from "@hyperlane-xyz/core/contracts/Mailbox.sol"; |
||||
import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; |
||||
|
||||
contract SendTestMessage is Script { |
||||
using TypeCasts for address; |
||||
using BytesLib for bytes; |
||||
|
||||
function run() public { |
||||
string memory origin = vm.envString("ORIGIN"); |
||||
string memory destination = vm.envString("DESTINATION"); |
||||
address recipient = vm.envAddress("RECIPIENT"); |
||||
string memory body = vm.envString("BODY"); |
||||
Mailbox mailbox = ConfigLib |
||||
.readHyperlaneDomainConfig(vm, origin) |
||||
.mailbox; |
||||
ConfigLib.HyperlaneDomainConfig memory config = ConfigLib |
||||
.readHyperlaneDomainConfig(vm, destination); |
||||
|
||||
vm.startBroadcast(); |
||||
bytes32 messageId = mailbox.dispatch( |
||||
config.domainId, |
||||
address(recipient).addressToBytes32(), |
||||
abi.encode(body) |
||||
); |
||||
console.log( |
||||
"Sent message with ID %s from %s to %s", |
||||
vm.toString(messageId), |
||||
origin, |
||||
destination |
||||
); |
||||
console.log( |
||||
"https://explorer.hyperlane.xyz/message/%s", |
||||
string(abi.encodePacked(vm.toString(messageId)).slice(2, 64)) |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
import { HyperlanePermissionlessDeployer } from '../src/deployer'; |
||||
|
||||
async function main() { |
||||
const deployer = await HyperlanePermissionlessDeployer.fromArgs(); |
||||
try { |
||||
await deployer.deploy(); |
||||
} catch (e) { |
||||
console.error(`Encountered error during deploy`); |
||||
console.error(e); |
||||
} |
||||
} |
||||
|
||||
main() |
||||
.then(() => console.info('Deploy completed successfully')) |
||||
.catch((e) => { |
||||
console.error(e); |
||||
process.exit(1); |
||||
}); |
@ -0,0 +1,167 @@ |
||||
import { |
||||
CoreFactories, |
||||
coreFactories, |
||||
DispatchedMessage, |
||||
HyperlaneAddressesMap, |
||||
HyperlaneApp, |
||||
HyperlaneCore, |
||||
HyperlaneIgp, |
||||
MultiProvider, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { |
||||
igpFactories, |
||||
IgpFactories, |
||||
} from '@hyperlane-xyz/sdk/dist/gas/contracts'; |
||||
import { utils } from '@hyperlane-xyz/utils'; |
||||
import { sleep } from '@hyperlane-xyz/utils/dist/src/utils'; |
||||
import { ethers } from 'ethers'; |
||||
import yargs from 'yargs'; |
||||
import { |
||||
assertBalances, |
||||
assertBytes32, |
||||
getMultiProvider, |
||||
mergedContractAddresses, |
||||
} from '../src/config'; |
||||
|
||||
export function getArgs(multiProvider: MultiProvider) { |
||||
// Only accept chains for which we have both a connection and contract addresses
|
||||
const { intersection } = multiProvider.intersect( |
||||
Object.keys(mergedContractAddresses), |
||||
); |
||||
return yargs(process.argv.slice(2)) |
||||
.describe('chains', 'chain to send message from') |
||||
.choices('chains', intersection) |
||||
.demandOption('chains') |
||||
.array('chains') |
||||
.describe('key', 'hexadecimal private key for transaction signing') |
||||
.string('key') |
||||
.coerce('key', assertBytes32) |
||||
.demandOption('key') |
||||
.describe('timeout', 'timeout in seconds') |
||||
.number('timeout') |
||||
.default('timeout', 10 * 60) |
||||
.middleware(assertBalances(multiProvider, (argv) => argv.chains)).argv; |
||||
} |
||||
|
||||
function coreFromAddressesMap( |
||||
addressesMap: HyperlaneAddressesMap<CoreFactories>, |
||||
_multiProvider: MultiProvider, |
||||
): HyperlaneCore { |
||||
const { contractsMap, multiProvider } = HyperlaneApp.fromAddressesMap( |
||||
addressesMap, |
||||
coreFactories, |
||||
_multiProvider, |
||||
); |
||||
return new HyperlaneCore(contractsMap, multiProvider); |
||||
} |
||||
|
||||
function igpFromAddressesMap( |
||||
addressesMap: HyperlaneAddressesMap<IgpFactories>, |
||||
_multiProvider: MultiProvider, |
||||
): HyperlaneIgp { |
||||
const { contractsMap, multiProvider } = HyperlaneApp.fromAddressesMap( |
||||
addressesMap, |
||||
igpFactories, |
||||
_multiProvider, |
||||
); |
||||
return new HyperlaneIgp(contractsMap, multiProvider); |
||||
} |
||||
|
||||
async function main() { |
||||
let timedOut = false; |
||||
const multiProvider = getMultiProvider(); |
||||
let { chains, key, timeout } = await getArgs(multiProvider); |
||||
const timeoutId = setTimeout(() => { |
||||
timedOut = true; |
||||
}, timeout * 1000); |
||||
const signer = new ethers.Wallet(key); |
||||
multiProvider.setSharedSigner(signer); |
||||
const core = coreFromAddressesMap(mergedContractAddresses, multiProvider); |
||||
const igp = igpFromAddressesMap(mergedContractAddresses, multiProvider); |
||||
const messages: Set<DispatchedMessage> = new Set(); |
||||
for (const origin of chains) { |
||||
const mailbox = core.getContracts(origin).mailbox; |
||||
const defaultIgp = |
||||
igp.getContracts(origin).defaultIsmInterchainGasPaymaster; |
||||
for (const destination of chains) { |
||||
const destinationDomain = multiProvider.getDomainId(destination); |
||||
if (origin === destination) continue; |
||||
try { |
||||
const recipient = mergedContractAddresses[destination] |
||||
.testRecipient as string; |
||||
if (!recipient) { |
||||
throw new Error(`Unable to find TestRecipient for ${destination}`); |
||||
} |
||||
const messageTx = await mailbox.dispatch( |
||||
destinationDomain, |
||||
utils.addressToBytes32(recipient), |
||||
'0xdeadbeef', |
||||
); |
||||
const messageReceipt = await multiProvider.handleTx(origin, messageTx); |
||||
const dispatchedMessages = core.getDispatchedMessages(messageReceipt); |
||||
if (dispatchedMessages.length !== 1) continue; |
||||
const dispatchedMessage = dispatchedMessages[0]; |
||||
console.log( |
||||
`Sent message from ${origin} to ${recipient} on ${destination} with message ID ${dispatchedMessage.id}`, |
||||
); |
||||
// Make gas payment...
|
||||
const gasAmount = 100_000; |
||||
const value = await defaultIgp.quoteGasPayment( |
||||
destinationDomain, |
||||
gasAmount, |
||||
); |
||||
const paymentTx = await defaultIgp.payForGas( |
||||
dispatchedMessage.id, |
||||
destinationDomain, |
||||
gasAmount, |
||||
await multiProvider.getSignerAddress(origin), |
||||
{ value }, |
||||
); |
||||
await paymentTx.wait(); |
||||
messages.add(dispatchedMessage); |
||||
} catch (e) { |
||||
console.error( |
||||
`Encountered error sending message from ${origin} to ${destination}`, |
||||
); |
||||
console.error(e); |
||||
} |
||||
} |
||||
} |
||||
while (messages.size > 0 && !timedOut) { |
||||
for (const message of messages.values()) { |
||||
const origin = multiProvider.getChainName(message.parsed.origin); |
||||
const destination = multiProvider.getChainName( |
||||
message.parsed.destination, |
||||
); |
||||
const mailbox = core.getContracts(destination).mailbox; |
||||
const delivered = await mailbox.delivered(message.id); |
||||
if (delivered) { |
||||
messages.delete(message); |
||||
console.log( |
||||
`Message from ${origin} to ${destination} with ID ${ |
||||
message!.id |
||||
} was delivered`,
|
||||
); |
||||
} else { |
||||
console.log( |
||||
`Message from ${origin} to ${destination} with ID ${ |
||||
message!.id |
||||
} has not yet been delivered`,
|
||||
); |
||||
} |
||||
await sleep(5000); |
||||
} |
||||
} |
||||
clearTimeout(timeoutId); |
||||
if (timedOut) { |
||||
console.error('Timed out waiting for messages to be delivered'); |
||||
process.exit(1); |
||||
} |
||||
} |
||||
|
||||
main() |
||||
.then(() => console.info('Testing complete')) |
||||
.catch((e) => { |
||||
console.error(e); |
||||
process.exit(1); |
||||
}); |
@ -0,0 +1,53 @@ |
||||
import { TestRecipient, TestRecipient__factory } from '@hyperlane-xyz/core'; |
||||
import { |
||||
ChainMap, |
||||
ChainName, |
||||
HyperlaneDeployer, |
||||
MultiProvider, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { types } from '@hyperlane-xyz/utils'; |
||||
import debug from 'debug'; |
||||
|
||||
// Maps chain name to ISM address
|
||||
export type TestRecipientConfig = { |
||||
ism: types.Address; |
||||
}; |
||||
|
||||
export type TestRecipientContracts = { |
||||
testRecipient: TestRecipient; |
||||
}; |
||||
|
||||
export type TestRecipientAddresses = { |
||||
testRecipient: types.Address; |
||||
}; |
||||
|
||||
export const testRecipientFactories = { |
||||
testRecipient: new TestRecipient__factory(), |
||||
}; |
||||
|
||||
export class HyperlaneTestRecipientDeployer extends HyperlaneDeployer< |
||||
TestRecipientConfig, |
||||
typeof testRecipientFactories |
||||
> { |
||||
constructor( |
||||
multiProvider: MultiProvider, |
||||
configMap: ChainMap<TestRecipientConfig>, |
||||
factoriesOverride = testRecipientFactories, |
||||
) { |
||||
super(multiProvider, configMap, factoriesOverride, { |
||||
logger: debug('hyperlane:TestRecipientDeployer'), |
||||
}); |
||||
} |
||||
|
||||
async deployContracts( |
||||
chain: ChainName, |
||||
config: TestRecipientConfig, |
||||
): Promise<TestRecipientContracts> { |
||||
const testRecipient = await this.deployContract(chain, 'testRecipient', []); |
||||
const tx = testRecipient.setInterchainSecurityModule(config.ism); |
||||
await this.multiProvider.handleTx(chain, tx); |
||||
return { |
||||
testRecipient, |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,159 @@ |
||||
import { |
||||
buildAgentConfig, |
||||
ChainMap, |
||||
ChainName, |
||||
CoreConfig, |
||||
defaultMultisigIsmConfigs, |
||||
GasOracleContractType, |
||||
HyperlaneAddressesMap, |
||||
HyperlaneAgentAddresses, |
||||
MultiProvider, |
||||
MultisigIsmConfig, |
||||
multisigIsmVerificationCost, |
||||
objFilter, |
||||
objMerge, |
||||
OverheadIgpConfig, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { hyperlaneEnvironments } from '@hyperlane-xyz/sdk/dist/consts/environments'; |
||||
import { types, utils } from '@hyperlane-xyz/utils'; |
||||
import { ethers } from 'ethers'; |
||||
import artifactAddresses from '../artifacts/addresses.json'; |
||||
import { chains } from '../config/chains'; |
||||
import { multisigIsmConfig } from '../config/multisig_ism'; |
||||
import { readJSON } from './json'; |
||||
|
||||
export function getMultiProvider() { |
||||
const multiProvider = new MultiProvider(); |
||||
for (const metadata of Object.values(chains)) { |
||||
multiProvider.addChain(metadata); |
||||
} |
||||
return multiProvider; |
||||
} |
||||
|
||||
export function assertBytes32(value: string): string { |
||||
if ( |
||||
ethers.utils.isHexString(value) && |
||||
ethers.utils.hexDataLength(value) == 32 |
||||
) { |
||||
return value; |
||||
} |
||||
throw new Error(`Invalid value ${value}, must be a 32 byte hex string`); |
||||
} |
||||
|
||||
export function assertBalances( |
||||
multiProvider: MultiProvider, |
||||
chainsFunc: (argv: any) => ChainName[], |
||||
): (argv: any) => Promise<void> { |
||||
return async (argv: any) => { |
||||
const chains = chainsFunc(argv); |
||||
const signer = new ethers.Wallet(argv.key); |
||||
const address = await signer.getAddress(); |
||||
Promise.all( |
||||
chains.map(async (chain: ChainName) => { |
||||
const balance = await multiProvider |
||||
.getProvider(chain) |
||||
.getBalance(address); |
||||
if (balance.isZero()) |
||||
throw new Error(`${address} has no balance on ${chain}`); |
||||
}), |
||||
); |
||||
}; |
||||
} |
||||
|
||||
export function coerceAddressToBytes32(value: string): string { |
||||
if (ethers.utils.isHexString(value)) { |
||||
const length = ethers.utils.hexDataLength(value); |
||||
if (length == 32) { |
||||
return value; |
||||
} else if (length == 20) { |
||||
return utils.addressToBytes32(value); |
||||
} |
||||
} |
||||
throw new Error(`Invalid value ${value}, must be a 20 or 32 byte hex string`); |
||||
} |
||||
|
||||
export function buildCoreConfig( |
||||
owner: types.Address, |
||||
chains: ChainName[], |
||||
): ChainMap<CoreConfig> { |
||||
const configMap: ChainMap<CoreConfig> = {}; |
||||
for (const local of chains) { |
||||
const multisigIsmConfigs: ChainMap<MultisigIsmConfig> = {}; |
||||
const mergedMultisigIsmConfig: ChainMap<MultisigIsmConfig> = objMerge( |
||||
defaultMultisigIsmConfigs, |
||||
multisigIsmConfig, |
||||
); |
||||
for (const remote of chains) { |
||||
if (local === remote) continue; |
||||
multisigIsmConfigs[remote] = mergedMultisigIsmConfig[remote]; |
||||
} |
||||
configMap[local] = { |
||||
owner, |
||||
multisigIsm: multisigIsmConfigs, |
||||
}; |
||||
} |
||||
return configMap; |
||||
} |
||||
|
||||
export function buildIgpConfig( |
||||
owner: types.Address, |
||||
chains: ChainName[], |
||||
): ChainMap<OverheadIgpConfig> { |
||||
const configMap: ChainMap<OverheadIgpConfig> = {}; |
||||
for (const local of chains) { |
||||
const overhead: ChainMap<number> = {}; |
||||
const gasOracleType: ChainMap<GasOracleContractType> = {}; |
||||
for (const remote of chains) { |
||||
if (local === remote) continue; |
||||
overhead[remote] = multisigIsmVerificationCost( |
||||
multisigIsmConfig[remote].threshold, |
||||
multisigIsmConfig[remote].validators.length, |
||||
); |
||||
gasOracleType[remote] = GasOracleContractType.StorageGasOracle; |
||||
} |
||||
configMap[local] = { |
||||
owner, |
||||
beneficiary: owner, |
||||
gasOracleType, |
||||
overhead, |
||||
}; |
||||
} |
||||
return configMap; |
||||
} |
||||
|
||||
export const sdkContractAddresses = { |
||||
...hyperlaneEnvironments.testnet, |
||||
...hyperlaneEnvironments.mainnet, |
||||
}; |
||||
|
||||
export const mergedContractAddresses = objMerge( |
||||
sdkContractAddresses, |
||||
artifactAddresses, |
||||
); |
||||
|
||||
export function buildOverriddenAgentConfig( |
||||
chains: ChainName[], |
||||
multiProvider: MultiProvider, |
||||
startBlocks: ChainMap<number>, |
||||
) { |
||||
const localAddresses = readJSON('./artifacts', 'addresses.json'); |
||||
const mergedAddresses: HyperlaneAddressesMap<any> = objMerge( |
||||
sdkContractAddresses, |
||||
localAddresses, |
||||
); |
||||
const filteredAddresses: ChainMap<HyperlaneAgentAddresses> = objFilter( |
||||
mergedAddresses, |
||||
(chain, v): v is HyperlaneAgentAddresses => |
||||
chains.includes(chain) && |
||||
!!v.mailbox && |
||||
!!v.interchainGasPaymaster && |
||||
!!v.validatorAnnounce, |
||||
); |
||||
|
||||
return buildAgentConfig( |
||||
chains, |
||||
multiProvider, |
||||
filteredAddresses, |
||||
startBlocks, |
||||
); |
||||
} |
@ -0,0 +1,172 @@ |
||||
import { |
||||
ChainMap, |
||||
ChainName, |
||||
CoreFactories, |
||||
HyperlaneContractsMap, |
||||
HyperlaneCoreDeployer, |
||||
HyperlaneIgpDeployer, |
||||
MultiProvider, |
||||
objMap, |
||||
objMerge, |
||||
serializeContractsMap, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import yargs from 'yargs'; |
||||
|
||||
import { LegacyMultisigIsm } from '@hyperlane-xyz/core'; |
||||
import { ethers } from 'ethers'; |
||||
import { multisigIsmConfig } from '../config/multisig_ism'; |
||||
import { startBlocks } from '../config/start_blocks'; |
||||
import { |
||||
assertBalances, |
||||
assertBytes32, |
||||
buildCoreConfig, |
||||
buildIgpConfig, |
||||
buildOverriddenAgentConfig, |
||||
getMultiProvider, |
||||
} from './config'; |
||||
import { mergeJSON, writeJSON } from './json'; |
||||
import { |
||||
HyperlaneTestRecipientDeployer, |
||||
TestRecipientConfig, |
||||
} from './TestRecipientDeployer'; |
||||
|
||||
export function getArgs(multiProvider: MultiProvider) { |
||||
// For each chain, we need:
|
||||
// - ChainMetadata for the MultiProvider
|
||||
// - A MultisigIsmConfig
|
||||
const { intersection } = multiProvider.intersect( |
||||
Object.keys(multisigIsmConfig), |
||||
); |
||||
|
||||
return yargs(process.argv.slice(2)) |
||||
.describe('local', 'The chain to deploy to') |
||||
.choices('local', intersection) |
||||
.demandOption('local') |
||||
.array('remotes') |
||||
.describe( |
||||
'remotes', |
||||
"The chains with which 'local' will be able to send and receive messages", |
||||
) |
||||
.choices('remotes', intersection) |
||||
.demandOption('remotes') |
||||
.describe('key', 'A hexadecimal private key for transaction signing') |
||||
.string('key') |
||||
.coerce('key', assertBytes32) |
||||
.demandOption('key') |
||||
.middleware( |
||||
assertBalances(multiProvider, (argv) => argv.remotes.concat(argv.local)), |
||||
) |
||||
.describe('write-agent-config', 'Whether or not to write agent config') |
||||
.default('write-agent-config', true) |
||||
.boolean('write-agent-config').argv; |
||||
} |
||||
|
||||
type MultisigIsmContracts = { |
||||
multisigIsm: LegacyMultisigIsm; |
||||
}; |
||||
|
||||
export class HyperlanePermissionlessDeployer { |
||||
constructor( |
||||
public readonly multiProvider: MultiProvider, |
||||
public readonly signer: ethers.Signer, |
||||
public readonly local: ChainName, |
||||
public readonly remotes: ChainName[], |
||||
public readonly writeAgentConfig?: boolean, |
||||
) {} |
||||
|
||||
static async fromArgs(): Promise<HyperlanePermissionlessDeployer> { |
||||
const multiProvider = getMultiProvider(); |
||||
const { local, remotes, key, writeAgentConfig } = await getArgs( |
||||
multiProvider, |
||||
); |
||||
const signer = new ethers.Wallet(key); |
||||
multiProvider.setSharedSigner(signer); |
||||
|
||||
return new HyperlanePermissionlessDeployer( |
||||
multiProvider, |
||||
signer, |
||||
local, |
||||
remotes as unknown as string[], |
||||
writeAgentConfig, |
||||
); |
||||
} |
||||
|
||||
get chains(): ChainName[] { |
||||
return this.remotes.concat([this.local]); |
||||
} |
||||
|
||||
async deploy(): Promise<void> { |
||||
let contracts: HyperlaneContractsMap<CoreFactories> = {}; |
||||
const owner = await this.signer.getAddress(); |
||||
// First, deploy core contracts to the local chain
|
||||
// NB: We create core configs for *all* chains because
|
||||
// we also use coreDeployer to deploy MultisigIsms.
|
||||
// Once we move that out to a HyperlaneIsmDeployer
|
||||
// we can just do:
|
||||
// const coreContracts = await coreDeployer.deploy();
|
||||
const coreConfig = buildCoreConfig(owner, this.chains); |
||||
const coreDeployer = new HyperlaneCoreDeployer( |
||||
this.multiProvider, |
||||
coreConfig, |
||||
); |
||||
const coreContracts: HyperlaneContractsMap<CoreFactories> = {}; |
||||
coreContracts[this.local] = await coreDeployer.deployContracts( |
||||
this.local, |
||||
coreConfig[this.local], |
||||
); |
||||
contracts = objMerge(contracts, coreContracts); |
||||
|
||||
// Next, deploy MultisigIsms to the remote chains
|
||||
// TODO: Would be cleaner if using HyperlaneIsmDeployer
|
||||
const isms: ChainMap<MultisigIsmContracts> = {}; |
||||
isms[this.local] = { |
||||
multisigIsm: coreContracts[this.local].multisigIsm, |
||||
}; |
||||
for (const remote of this.remotes) { |
||||
isms[remote] = { |
||||
multisigIsm: await coreDeployer.deployLegacyMultisigIsm(remote), |
||||
}; |
||||
} |
||||
contracts = objMerge(contracts, isms); |
||||
|
||||
// Next, deploy TestRecipients to all chains
|
||||
const testRecipientConfig: ChainMap<TestRecipientConfig> = objMap( |
||||
isms, |
||||
(chain, ism) => { |
||||
return { ism: ism.multisigIsm.address }; |
||||
}, |
||||
); |
||||
const testRecipientDeployer = new HyperlaneTestRecipientDeployer( |
||||
this.multiProvider, |
||||
testRecipientConfig, |
||||
); |
||||
const testRecipients = await testRecipientDeployer.deploy(); |
||||
contracts = objMerge(contracts, testRecipients); |
||||
|
||||
// Finally, deploy IGPs to all chains
|
||||
// TODO: Reuse ProxyAdmin on local chain... right now *two* ProxyAdmins are deployed
|
||||
const igpConfig = buildIgpConfig(owner, this.chains); |
||||
const igpDeployer = new HyperlaneIgpDeployer(this.multiProvider, igpConfig); |
||||
const igps = await igpDeployer.deploy(); |
||||
contracts = objMerge(contracts, igps); |
||||
|
||||
const addresses = serializeContractsMap(contracts); |
||||
// Write contract address artifacts
|
||||
mergeJSON('./artifacts/', 'addresses.json', addresses); |
||||
|
||||
startBlocks[this.local] = await this.multiProvider |
||||
.getProvider(this.local) |
||||
.getBlockNumber(); |
||||
|
||||
if (this.writeAgentConfig) { |
||||
const agentConfig = buildOverriddenAgentConfig( |
||||
this.chains, |
||||
this.multiProvider, |
||||
startBlocks, |
||||
); |
||||
|
||||
// Write AgentConfig artifacts
|
||||
writeJSON('./artifacts/', 'agent_config.json', agentConfig); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,37 @@ |
||||
import { objMerge } from '@hyperlane-xyz/sdk'; |
||||
import fs from 'fs'; |
||||
import path from 'path'; |
||||
|
||||
export function writeJSON(directory: string, filename: string, obj: any) { |
||||
if (!fs.existsSync(directory)) { |
||||
fs.mkdirSync(directory, { recursive: true }); |
||||
} |
||||
fs.writeFileSync( |
||||
path.join(directory, filename), |
||||
JSON.stringify(obj, null, 2) + '\n', |
||||
); |
||||
} |
||||
|
||||
export function mergeJSON(directory: string, filename: string, obj: any) { |
||||
if (fs.existsSync(path.join(directory, filename))) { |
||||
const previous = readJSON(directory, filename); |
||||
writeJSON(directory, filename, objMerge(previous, obj)); |
||||
} else { |
||||
writeJSON(directory, filename, obj); |
||||
} |
||||
} |
||||
|
||||
export function readFileAtPath(filepath: string) { |
||||
if (!fs.existsSync(filepath)) { |
||||
throw Error(`file doesn't exist at ${filepath}`); |
||||
} |
||||
return fs.readFileSync(filepath, 'utf8'); |
||||
} |
||||
|
||||
export function readJSONAtPath(filepath: string) { |
||||
return JSON.parse(readFileAtPath(filepath)); |
||||
} |
||||
|
||||
export function readJSON(directory: string, filename: string) { |
||||
return readJSONAtPath(path.join(directory, filename)); |
||||
} |
@ -0,0 +1,28 @@ |
||||
{ |
||||
"compilerOptions": { |
||||
"outDir": "./dist/", |
||||
"rootDir": "./", |
||||
"declaration": true, |
||||
"declarationMap": true, |
||||
"esModuleInterop": true, |
||||
"forceConsistentCasingInFileNames": true, |
||||
"incremental": false, |
||||
"lib": ["es2015", "es5", "dom", "es2021"], |
||||
"module": "commonjs", |
||||
"moduleResolution": "node", |
||||
"noEmitOnError": true, |
||||
"noFallthroughCasesInSwitch": true, |
||||
"noImplicitAny": true, |
||||
"noImplicitReturns": true, |
||||
"noUnusedLocals": true, |
||||
"preserveSymlinks": true, |
||||
"preserveWatchOutput": true, |
||||
"pretty": false, |
||||
"resolveJsonModule": true, |
||||
"sourceMap": true, |
||||
"target": "es6", |
||||
"strict": true |
||||
}, |
||||
"exclude": ["./node_modules/", "./dist/"], |
||||
"include": ["./src/**/*.ts", "./config/**/*.ts", "./scripts/*.ts"] |
||||
} |
Loading…
Reference in new issue