Adds VAA self signing logic and updates implementation addresses (#42)
* evm: remove obsolete packages. * evm: fixes shebang for shell scripts. * evm: forward arguments in deploy shell scripts. * evm: adjustments in `tsconfig.json` * evm: adds script to self sign testnet upgrade. * evm: yarn.lock update * evm: Adds registrations to circle integration initializer temporarily. * evm: fixes env var for RPC URLs * evm: rename environment files. * evm: adds RPC URLs for arbitrum and eth testnets * evm: updates the CircleIntegration implementation addresses. * evm: switches to ESM. * evm: yarn.lock update * evm: reverts testnet hardcoded cross registrations. * evm: removes `ts-node` in favour of `tsx` * minor fixes to submit_testnet_registraion script * adds base deployment configuration * fix env variable on base mainnet deployment * base mainnet configuration --------- Co-authored-by: solanoe <solanoepalacio@gmail.com>
This commit is contained in:
parent
2e0831c6c1
commit
105ad59bad
|
@ -34,7 +34,7 @@ Then run the following command to deploy (and set up) the proxy contract:
|
||||||
|
|
||||||
```
|
```
|
||||||
# sample deployment command
|
# sample deployment command
|
||||||
. env/put_your_env_file_here.env && PRIVATE_KEY=put_your_private_key_here bash shell-scripts/deploy_contracts.sh
|
. env/put_your_env_file_here.env && shell-scripts/deploy_contracts.sh --private-key put_your_private_key_here
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test Suite
|
## Test Suite
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
export CONFIGURE_CCTP_RPC="https://base.publicnode.com"
|
||||||
|
|
||||||
|
### Contract addresses
|
||||||
|
export RELEASE_WORMHOLE_ADDRESS=0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6
|
||||||
|
export RELEASE_CIRCLE_BRIDGE_ADDRESS=0x1682Ae6375C4E4A97e4B583BC394c861A46D8962 # token messenger
|
||||||
|
export RELEASE_WORMHOLE_FINALITY=1
|
||||||
|
|
||||||
|
### Deployed Circle Integration addresses
|
||||||
|
export CIRCLE_INTEGRATION_IMPLEMENTATION="0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c"
|
||||||
|
export CIRCLE_INTEGRATION_SETUP="0xD73aFD826D6bDD4D2fEF326DF5091451A5d8130a"
|
||||||
|
export CIRCLE_INTEGRATION_PROXY="0x03faBB06Fa052557143dC28eFCFc63FC12843f1D"
|
||||||
|
|
||||||
|
# Used in verification scripts
|
||||||
|
export RELEASE_EVM_CHAIN_ID=8453
|
|
@ -1,4 +1,4 @@
|
||||||
export RPC=""
|
export CONFIGURE_CCTP_RPC="https://goerli-rollup.arbitrum.io/rpc"
|
||||||
|
|
||||||
### Contract addresses
|
### Contract addresses
|
||||||
export RELEASE_WORMHOLE_ADDRESS=0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e
|
export RELEASE_WORMHOLE_ADDRESS=0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e
|
||||||
|
@ -6,7 +6,7 @@ export RELEASE_CIRCLE_BRIDGE_ADDRESS=0x12dcfd3fe2e9eac2859fd1ed86d2ab8c5a2f9352
|
||||||
export RELEASE_WORMHOLE_FINALITY=1
|
export RELEASE_WORMHOLE_FINALITY=1
|
||||||
|
|
||||||
### Deployed Circle Integration addresses
|
### Deployed Circle Integration addresses
|
||||||
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xa098368aaadc0fdf3e309cda710d7a5f8bdeecd9
|
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xD73aFD826D6bDD4D2fEF326DF5091451A5d8130a
|
||||||
export CIRCLE_INTEGRATION_SETUP=0xb0a9feeaf74f2e8e2966bf774466ca3575ec8a6d
|
export CIRCLE_INTEGRATION_SETUP=0xb0a9feeaf74f2e8e2966bf774466ca3575ec8a6d
|
||||||
export CIRCLE_INTEGRATION_PROXY=0x2E8F5E00a9C5D450A72700546B89E2b70DfB00f2
|
export CIRCLE_INTEGRATION_PROXY=0x2E8F5E00a9C5D450A72700546B89E2b70DfB00f2
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export RPC="https://api.avax-test.network/ext/bc/C/rpc"
|
export CONFIGURE_CCTP_RPC="https://api.avax-test.network/ext/bc/C/rpc"
|
||||||
|
|
||||||
### Contract addresses
|
### Contract addresses
|
||||||
export RELEASE_WORMHOLE_ADDRESS=0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C
|
export RELEASE_WORMHOLE_ADDRESS=0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C
|
||||||
|
@ -6,7 +6,7 @@ export RELEASE_CIRCLE_BRIDGE_ADDRESS=0xeb08f243e5d3fcff26a9e38ae5520a669f4019d0
|
||||||
export RELEASE_WORMHOLE_FINALITY=1
|
export RELEASE_WORMHOLE_FINALITY=1
|
||||||
|
|
||||||
### Deployed Circle Integration addresses
|
### Deployed Circle Integration addresses
|
||||||
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xf0ff9898918351148ffd97c7ddb412086505eae1
|
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xD73aFD826D6bDD4D2fEF326DF5091451A5d8130a
|
||||||
export CIRCLE_INTEGRATION_SETUP=0xd66a83c1cd3cf85a071daa6a4bcbea32e22931c0
|
export CIRCLE_INTEGRATION_SETUP=0xd66a83c1cd3cf85a071daa6a4bcbea32e22931c0
|
||||||
export CIRCLE_INTEGRATION_PROXY=0x58f4c17449c90665891c42e14d34aae7a26a472e
|
export CIRCLE_INTEGRATION_PROXY=0x58f4c17449c90665891c42e14d34aae7a26a472e
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
export CONFIGURE_CCTP_RPC="https://goerli.base.org"
|
||||||
|
|
||||||
|
### Contract addresses
|
||||||
|
export RELEASE_WORMHOLE_ADDRESS=0x23908A62110e21C04F3A4e011d24F901F911744A
|
||||||
|
export RELEASE_CIRCLE_BRIDGE_ADDRESS=0x877b8e8c9e2383077809787ED6F279ce01CB4cc8
|
||||||
|
export RELEASE_WORMHOLE_FINALITY=1
|
||||||
|
|
||||||
|
|
||||||
|
### Deployed Circle Integration addresses
|
||||||
|
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xD73aFD826D6bDD4D2fEF326DF5091451A5d8130a
|
||||||
|
export CIRCLE_INTEGRATION_SETUP=0xC5180b274Ead8aC34131B6dDa0323e403d671De7
|
||||||
|
export CIRCLE_INTEGRATION_PROXY=0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c
|
||||||
|
|
||||||
|
# Used in verification scripts
|
||||||
|
export RELEASE_EVM_CHAIN_ID=84531
|
|
@ -1,4 +1,4 @@
|
||||||
export RPC=""
|
export CONFIGURE_CCTP_RPC="https://rpc.ankr.com/eth_goerli"
|
||||||
|
|
||||||
### Contract addresses
|
### Contract addresses
|
||||||
export RELEASE_WORMHOLE_ADDRESS=0x706abc4E45D419950511e474C7B9Ed348A4a716c
|
export RELEASE_WORMHOLE_ADDRESS=0x706abc4E45D419950511e474C7B9Ed348A4a716c
|
||||||
|
@ -6,7 +6,7 @@ export RELEASE_CIRCLE_BRIDGE_ADDRESS=0xd0c3da58f55358142b8d3e06c1c30c5c6114efe8
|
||||||
export RELEASE_WORMHOLE_FINALITY=200
|
export RELEASE_WORMHOLE_FINALITY=200
|
||||||
|
|
||||||
### Deployed Circle Integration addresses
|
### Deployed Circle Integration addresses
|
||||||
export CIRCLE_INTEGRATION_IMPLEMENTATION=0x4fa4b2c3744b29D0e4F1AaFE8B758F953FaCf1A4
|
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xD73aFD826D6bDD4D2fEF326DF5091451A5d8130a
|
||||||
export CIRCLE_INTEGRATION_SETUP=0xB9aCd3891EBf91EC09cfe337EE5A8EEfF4317846
|
export CIRCLE_INTEGRATION_SETUP=0xB9aCd3891EBf91EC09cfe337EE5A8EEfF4317846
|
||||||
export CIRCLE_INTEGRATION_PROXY=0x0a69146716b3a21622287efa1607424c663069a4
|
export CIRCLE_INTEGRATION_PROXY=0x0a69146716b3a21622287efa1607424c663069a4
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
export RPC="https://goerli.optimism.io"
|
export CONFIGURE_CCTP_RPC="https://rpc.goerli.optimism.gateway.fm"
|
||||||
|
|
||||||
### Contract addresses
|
### Contract addresses
|
||||||
export RELEASE_WORMHOLE_ADDRESS=0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35
|
export RELEASE_WORMHOLE_ADDRESS=0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35
|
||||||
export RELEASE_CIRCLE_BRIDGE_ADDRESS=0x23a04d5935ed8bc8e3eb78db3541f0abfb001c6e
|
export RELEASE_CIRCLE_BRIDGE_ADDRESS=0x23a04d5935ed8bc8e3eb78db3541f0abfb001c6e
|
||||||
export RELEASE_WORMHOLE_FINALITY=1
|
export RELEASE_WORMHOLE_FINALITY=1
|
||||||
|
|
||||||
|
|
||||||
### Deployed Circle Integration addresses
|
### Deployed Circle Integration addresses
|
||||||
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xD73aFD826D6bDD4D2fEF326DF5091451A5d8130a
|
export CIRCLE_INTEGRATION_IMPLEMENTATION=0xD4Bab83eDe934973d22f1da04819fe2E5b5a9Ef4
|
||||||
export CIRCLE_INTEGRATION_SETUP=0xC5180b274Ead8aC34131B6dDa0323e403d671De7
|
export CIRCLE_INTEGRATION_SETUP=0xC5180b274Ead8aC34131B6dDa0323e403d671De7
|
||||||
export CIRCLE_INTEGRATION_PROXY=0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c
|
export CIRCLE_INTEGRATION_PROXY=0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c
|
||||||
|
|
|
@ -3,29 +3,28 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.11.17",
|
|
||||||
"chai": "^4.3.6",
|
|
||||||
"ethers": "^5.7.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@certusone/wormhole-sdk": "^0.9.0",
|
|
||||||
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
|
|
||||||
"@openzeppelin/contracts": "^4.7.3",
|
|
||||||
"@typechain/ethers-v5": "^10.1.1",
|
|
||||||
"@types/argparse": "^2.0.10",
|
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.3",
|
||||||
"@types/mocha": "^9.1.1",
|
"@types/mocha": "^9.1.1",
|
||||||
"argparse": "^2.0.1",
|
"@types/node": "^20.0.0",
|
||||||
"axios": "^1.1.3",
|
"@types/yargs": "^17.0.24",
|
||||||
"dotenv": "^16.0.3",
|
"chai": "^4.3.6",
|
||||||
"elliptic": "^6.5.4",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"mocha": "^10.0.0",
|
"mocha": "^10.0.0",
|
||||||
"ts-mocha": "^10.0.0",
|
"ts-mocha": "^10.0.0",
|
||||||
"ts-node": "^10.9.1",
|
"tsx": "^3.13.0",
|
||||||
"typechain": "^8.1.1",
|
"typescript": "^5.2.2"
|
||||||
"typescript": "^4.8.3"
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@certusone/wormhole-sdk": "^0.10.3",
|
||||||
|
"@noble/secp256k1": "^2.0.0",
|
||||||
|
"@openzeppelin/contracts": "^4.7.3",
|
||||||
|
"@typechain/ethers-v5": "^11.1.1",
|
||||||
|
"@xlabs-xyz/ledger-signer": "0.0.3",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"ethers": "^5.7.1",
|
||||||
|
"typechain": "^8.3.1",
|
||||||
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-types": "bash shell-scripts/make_ethers_types.sh",
|
"build-types": "bash shell-scripts/make_ethers_types.sh",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
forge script $(dirname $0)/../forge-scripts/deploy_contracts.sol \
|
forge script $(dirname $0)/../forge-scripts/deploy_contracts.sol \
|
||||||
--rpc-url $RPC \
|
--rpc-url $CONFIGURE_CCTP_RPC \
|
||||||
--private-key $PRIVATE_KEY \
|
--broadcast --slow $@
|
||||||
--broadcast --slow
|
|
|
@ -1,9 +1,8 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
forge script $(dirname $0)/../forge-scripts/deploy_implementation_only.sol \
|
forge script $(dirname $0)/../forge-scripts/deploy_implementation_only.sol \
|
||||||
-vv \
|
-vv \
|
||||||
--rpc-url $RPC \
|
--rpc-url $CONFIGURE_CCTP_RPC \
|
||||||
--private-key $PRIVATE_KEY \
|
--broadcast --slow $@
|
||||||
--broadcast --slow
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
SRC=$(dirname $0)/../out
|
SRC=$(dirname $0)/../out
|
||||||
DST=$(dirname $0)/../ts/src/ethers-contracts
|
DST=$(dirname $0)/../ts/src/ethers-contracts
|
||||||
|
|
||||||
typechain --target=ethers-v5 --out-dir=$DST $SRC/*/*.json
|
typechain --target=ethers-v5 --node16-modules --out-dir=$DST $SRC/*/*.json
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
pgrep anvil > /dev/null
|
pgrep anvil > /dev/null
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Usage: ./submit_testnet_registration <target chain> <foreign chain> <foreign emitter> <foreign domain> <forge script args (keys)>
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
@ -8,8 +10,9 @@ export FOREIGN_EMITTER=$3
|
||||||
export FOREIGN_DOMAIN=$4
|
export FOREIGN_DOMAIN=$4
|
||||||
export SIGNER_KEY=$5
|
export SIGNER_KEY=$5
|
||||||
|
|
||||||
forge script $(dirname $0)/../forge-scripts/generate_registration_vaa.sol \
|
shift 5 # <- remove 5 first arguments
|
||||||
|
|
||||||
|
forge script $(dirname $0)/../forge-scripts/submit_testnet_registration.sol \
|
||||||
-vv \
|
-vv \
|
||||||
--rpc-url $RPC \
|
--rpc-url $CONFIGURE_CCTP_RPC \
|
||||||
--private-key $PRIVATE_KEY \
|
--broadcast --slow $@
|
||||||
--broadcast --slow
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ts-node $(dirname $0)/../ts/scripts/upgrade_proxy.ts $@
|
npx tsx $(dirname $0)/../ts/scripts/upgrade_proxy.ts $@
|
|
@ -1,4 +1,4 @@
|
||||||
#/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
etherscan_key=$1
|
etherscan_key=$1
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
etherscan_key=$1
|
etherscan_key=$1
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
etherscan_key=$1
|
etherscan_key=$1
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
import {tryNativeToUint8Array} from "@certusone/wormhole-sdk";
|
import {tryNativeToUint8Array} from "@certusone/wormhole-sdk";
|
||||||
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
|
import {MockGuardians} from "@certusone/wormhole-sdk/lib/esm/mock";
|
||||||
import {CircleGovernanceEmitter} from "../test/helpers/mock";
|
import {CircleGovernanceEmitter} from "../test/helpers/mock.js";
|
||||||
import {abi as WORMHOLE_ABI} from "../../out/IWormhole.sol/IWormhole.json";
|
import {abi as WORMHOLE_ABI} from "../../out/IWormhole.sol/IWormhole.json";
|
||||||
import {abi as CIRCLE_INTEGRATION_ABI} from "../../out/CircleIntegration.sol/CircleIntegration.json";
|
import {abi as CIRCLE_INTEGRATION_ABI} from "../../out/CircleIntegration.sol/CircleIntegration.json";
|
||||||
import {getTimeNow} from "../test/helpers/utils";
|
import {getTimeNow} from "../test/helpers/utils.js";
|
||||||
import {expect} from "chai";
|
import {expect} from "chai";
|
||||||
|
|
||||||
require("dotenv").config({path: process.argv.slice(2)[0]});
|
require("dotenv").config({path: process.argv.slice(2)[0]});
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { tryNativeToHexString } from "@certusone/wormhole-sdk";
|
||||||
|
import { signAsync } from "@noble/secp256k1";
|
||||||
|
|
||||||
|
const circleIntegrationModule =
|
||||||
|
"0x000000000000000000000000000000436972636c65496e746567726174696f6e";
|
||||||
|
const GOVERNANCE_UPGRADE_ACTION = 3;
|
||||||
|
const governanceChainId = 1;
|
||||||
|
const governanceContract =
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000004";
|
||||||
|
|
||||||
|
export interface Guardian {
|
||||||
|
/**
|
||||||
|
* Private key in hexadecimal string 0x encoded.
|
||||||
|
*/
|
||||||
|
key: string;
|
||||||
|
/**
|
||||||
|
* Index of the public key in the current Guardian set.
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GuardianSet {
|
||||||
|
guardians: Guardian[];
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doubleKeccak256(body: ethers.BytesLike) {
|
||||||
|
return ethers.utils.keccak256(ethers.utils.keccak256(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCircleIntegrationUpgradeVAA(
|
||||||
|
chainId: number,
|
||||||
|
newAddress: string,
|
||||||
|
guardianSet: GuardianSet,
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
bytes32 module;
|
||||||
|
uint8 action;
|
||||||
|
uint16 chain;
|
||||||
|
bytes32 newContract; //listed as address in the struct, but is actually bytes32 inside the VAA
|
||||||
|
*/
|
||||||
|
|
||||||
|
const payload = ethers.utils.solidityPack(
|
||||||
|
["bytes32", "uint8", "uint16", "bytes32"],
|
||||||
|
[
|
||||||
|
circleIntegrationModule,
|
||||||
|
GOVERNANCE_UPGRADE_ACTION,
|
||||||
|
chainId,
|
||||||
|
"0x" + tryNativeToHexString(newAddress, "ethereum"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return encodeAndSignGovernancePayload(payload, guardianSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function encodeAndSignGovernancePayload(
|
||||||
|
payload: string,
|
||||||
|
guardianSet: GuardianSet,
|
||||||
|
): Promise<string> {
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
const nonce = 1;
|
||||||
|
const sequence = 1;
|
||||||
|
const consistencyLevel = 1;
|
||||||
|
const vaaVersion = 1;
|
||||||
|
|
||||||
|
const encodedVAABody = ethers.utils.solidityPack(
|
||||||
|
["uint32", "uint32", "uint16", "bytes32", "uint64", "uint8", "bytes"],
|
||||||
|
[
|
||||||
|
timestamp,
|
||||||
|
nonce,
|
||||||
|
governanceChainId,
|
||||||
|
governanceContract,
|
||||||
|
sequence,
|
||||||
|
consistencyLevel,
|
||||||
|
payload,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const hash = doubleKeccak256(encodedVAABody).substring(2);
|
||||||
|
|
||||||
|
const signatures = (await Promise.all(guardianSet.guardians
|
||||||
|
.map(async ({ key, index }) => {
|
||||||
|
const signature = await signAsync(hash, key);
|
||||||
|
if (signature.recovery === undefined)
|
||||||
|
throw new Error(`Failed to sign message: missing recovery id`);
|
||||||
|
|
||||||
|
// Remember that each signature is accompanied by the guardian index.
|
||||||
|
const packSig = ethers.utils.solidityPack(
|
||||||
|
["uint8", "bytes32", "bytes32", "uint8"],
|
||||||
|
[
|
||||||
|
index,
|
||||||
|
ethers.utils.hexZeroPad(ethers.utils.hexlify(signature.r), 32),
|
||||||
|
ethers.utils.hexZeroPad(ethers.utils.hexlify(signature.s), 32),
|
||||||
|
signature.recovery,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return packSig.substring(2);
|
||||||
|
})))
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
const vm = [
|
||||||
|
ethers.utils
|
||||||
|
.solidityPack(
|
||||||
|
["uint8", "uint32", "uint8"],
|
||||||
|
[
|
||||||
|
vaaVersion,
|
||||||
|
// guardianSetIndex
|
||||||
|
guardianSet.id,
|
||||||
|
// number of signers
|
||||||
|
guardianSet.guardians.length,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.substring(2),
|
||||||
|
signatures,
|
||||||
|
encodedVAABody.substring(2),
|
||||||
|
].join("");
|
||||||
|
|
||||||
|
return "0x" + vm;
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import yargs from "yargs";
|
||||||
|
import type { Argv } from "yargs";
|
||||||
|
import { hideBin } from "yargs/helpers";
|
||||||
|
|
||||||
|
export type SignerArguments =
|
||||||
|
| {
|
||||||
|
useLedger: true;
|
||||||
|
derivationPath: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
useLedger: false;
|
||||||
|
privateKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Use this to enrich your argument parsing with signer options
|
||||||
|
*/
|
||||||
|
export function addSignerArgsParser<T>(parser: Argv<T>) {
|
||||||
|
return parser
|
||||||
|
.option("ledger", {
|
||||||
|
boolean: true,
|
||||||
|
default: false,
|
||||||
|
description: "Use ledger to sign transactions",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("derivation-path", {
|
||||||
|
string: true,
|
||||||
|
description:
|
||||||
|
"BIP32 derivation path to use. Used only with ledger devices.",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("private-key", {
|
||||||
|
string: true,
|
||||||
|
description: "EVM Private Key.",
|
||||||
|
required: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParsedSignerArgs = Awaited<
|
||||||
|
ReturnType<ReturnType<typeof addSignerArgsParser>["parse"]>
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Use this if you don't parse any arguments and need to provide
|
||||||
|
* signer options.
|
||||||
|
*/
|
||||||
|
export async function parseSignerArgs() {
|
||||||
|
const signerArgsParser = addSignerArgsParser(yargs())
|
||||||
|
.help("h")
|
||||||
|
.alias("h", "help");
|
||||||
|
const args = await signerArgsParser.parse(hideBin(process.argv));
|
||||||
|
return validateSignerArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateSignerArgs(
|
||||||
|
args: ParsedSignerArgs,
|
||||||
|
): SignerArguments {
|
||||||
|
if ((args.privateKey !== undefined) === args.ledger) {
|
||||||
|
throw new Error(
|
||||||
|
"Exactly one signing method must be provided. Use either the '--ledger' or the '--privateKey' options.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.ledger) {
|
||||||
|
if (args.derivationPath === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
"An account must be selected using the '--derivation-path' option when signing with a ledger device.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
useLedger: true,
|
||||||
|
derivationPath: args.derivationPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
useLedger: false,
|
||||||
|
// The private key cannot be undefined at this point but typescript's type narrowing is a bit lacking to determine that.
|
||||||
|
privateKey: args.privateKey!,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSigner(
|
||||||
|
args: SignerArguments,
|
||||||
|
provider: ethers.providers.Provider,
|
||||||
|
): Promise<ethers.Signer> {
|
||||||
|
if (args.useLedger) {
|
||||||
|
const { LedgerSigner } = await import("@xlabs-xyz/ledger-signer");
|
||||||
|
return LedgerSigner.create(provider, args.derivationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ethers.Wallet(args.privateKey, provider);
|
||||||
|
}
|
|
@ -1,67 +1,69 @@
|
||||||
import { ArgumentParser, Namespace } from "argparse";
|
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
import yargs from "yargs";
|
||||||
import {
|
import {
|
||||||
ICircleIntegration,
|
ICircleIntegration,
|
||||||
ICircleIntegration__factory,
|
ICircleIntegration__factory,
|
||||||
} from "../src/ethers-contracts";
|
} from "../src/ethers-contracts/index.js";
|
||||||
|
import { addSignerArgsParser, validateSignerArgs, getSigner } from "./signer.js";
|
||||||
|
|
||||||
interface Setup {
|
interface Setup {
|
||||||
circleIntegration: ICircleIntegration;
|
circleIntegration: ICircleIntegration;
|
||||||
governanceMessage: Buffer;
|
governanceMessage: Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUp(): Setup {
|
async function setUp(): Promise<Setup> {
|
||||||
const parser = new ArgumentParser({
|
const parser = addSignerArgsParser(yargs())
|
||||||
description: "Upgrade Circle Integration Proxy",
|
.help("help", "Upgrade Circle Integration Proxy")
|
||||||
|
.env("CONFIGURE_CCTP")
|
||||||
|
.option("proxy", {
|
||||||
|
string: true,
|
||||||
|
required: true,
|
||||||
|
description: "Proxy Contract Address",
|
||||||
|
})
|
||||||
|
.option("governance-message", {
|
||||||
|
required: true,
|
||||||
|
string: true,
|
||||||
|
description: "Signed Governance Message in base64 format",
|
||||||
|
})
|
||||||
|
.option("rpc", {
|
||||||
|
string: true,
|
||||||
|
required: true,
|
||||||
|
description: "EVM RPC URL",
|
||||||
});
|
});
|
||||||
|
|
||||||
parser.add_argument("-m", "--governance-message", {
|
const parsedArgs = await parser.argv;
|
||||||
required: true,
|
const signerArgs = validateSignerArgs(parsedArgs);
|
||||||
help: "Signed Governance Message",
|
|
||||||
});
|
|
||||||
parser.add_argument("-p", "--proxy", {
|
|
||||||
required: true,
|
|
||||||
help: "Proxy Contract Address",
|
|
||||||
});
|
|
||||||
parser.add_argument("--rpc-url", { required: true, help: "EVM RPC" });
|
|
||||||
parser.add_argument("--private-key", {
|
|
||||||
required: true,
|
|
||||||
help: "EVM Private Key",
|
|
||||||
});
|
|
||||||
|
|
||||||
const args: Namespace = parser.parse_args();
|
const provider = new ethers.providers.StaticJsonRpcProvider(parsedArgs.rpc);
|
||||||
|
const signer = await getSigner(signerArgs, provider);
|
||||||
const provider = new ethers.providers.StaticJsonRpcProvider(args.rpc_url);
|
|
||||||
const wallet = new ethers.Wallet(args.private_key, provider);
|
|
||||||
const circleIntegration = ICircleIntegration__factory.connect(
|
const circleIntegration = ICircleIntegration__factory.connect(
|
||||||
args.proxy,
|
parsedArgs.proxy,
|
||||||
wallet
|
signer,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
circleIntegration,
|
circleIntegration,
|
||||||
governanceMessage: Buffer.from(args.governance_message, "hex"),
|
governanceMessage: Buffer.from(parsedArgs.governanceMessage, "base64"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const { circleIntegration, governanceMessage } = setUp();
|
const { circleIntegration, governanceMessage } = await setUp();
|
||||||
|
|
||||||
const chainId = await circleIntegration.chainId();
|
const chainId = await circleIntegration.chainId();
|
||||||
console.log(chainId);
|
console.log(
|
||||||
|
`Executing mainnet CircleIntegration upgrade on chainId=${chainId}`,
|
||||||
|
);
|
||||||
|
|
||||||
const tx = circleIntegration
|
const tx = await circleIntegration.upgradeContract(governanceMessage);
|
||||||
.upgradeContract(governanceMessage)
|
console.log(`Upgrade transaction sent txHash=${tx.hash}`);
|
||||||
.then((tx) => tx.wait())
|
|
||||||
.catch((msg) => {
|
const receipt = await tx.wait();
|
||||||
// should not happen
|
if (receipt.status !== 1) {
|
||||||
console.log(msg);
|
console.log("Failed transaction");
|
||||||
return null;
|
|
||||||
});
|
|
||||||
if (tx === null) {
|
|
||||||
console.log("failed transaction");
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
|
console.log("Transaction successful");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import yargs from "yargs";
|
||||||
|
import { hideBin } from "yargs/helpers";
|
||||||
|
import {
|
||||||
|
ICircleIntegration,
|
||||||
|
ICircleIntegration__factory,
|
||||||
|
} from "../src/ethers-contracts/index.js";
|
||||||
|
import { addSignerArgsParser, validateSignerArgs, getSigner } from "./signer.js";
|
||||||
|
import { createCircleIntegrationUpgradeVAA, GuardianSet } from "./sign_vaa.js";
|
||||||
|
|
||||||
|
interface Setup {
|
||||||
|
circleIntegration: ICircleIntegration;
|
||||||
|
newImplementation: string;
|
||||||
|
guardians: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setUp(): Promise<Setup> {
|
||||||
|
const parser = addSignerArgsParser(yargs())
|
||||||
|
.help("help", "Upgrade testnet Circle Integration Proxy")
|
||||||
|
.env("CONFIGURE_CCTP")
|
||||||
|
.option("proxy", {
|
||||||
|
string: true,
|
||||||
|
required: true,
|
||||||
|
description: "Proxy Contract Address",
|
||||||
|
})
|
||||||
|
.option("new-implementation", {
|
||||||
|
string: true,
|
||||||
|
required: true,
|
||||||
|
description: "New implementation contract address",
|
||||||
|
})
|
||||||
|
.option("guardian", {
|
||||||
|
array: true,
|
||||||
|
string: true,
|
||||||
|
required: true,
|
||||||
|
description: `Guardian private key in hexadecimal format.
|
||||||
|
If there is more than one guardian, they must be sorted by their guardian set index.
|
||||||
|
Skipping indexes in the guardian set is not supported.`,
|
||||||
|
})
|
||||||
|
.option("rpc", {
|
||||||
|
string: true,
|
||||||
|
required: true,
|
||||||
|
description: "EVM RPC URL",
|
||||||
|
});
|
||||||
|
|
||||||
|
const parsedArgs = await parser.parse(hideBin(process.argv));
|
||||||
|
if (!ethers.utils.isAddress(parsedArgs.newImplementation)) {
|
||||||
|
throw new Error(
|
||||||
|
`The implementation address is invalid: ${parsedArgs.newImplementation}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const signerArgs = validateSignerArgs(parsedArgs);
|
||||||
|
|
||||||
|
const provider = new ethers.providers.StaticJsonRpcProvider(parsedArgs.rpc);
|
||||||
|
const signer = await getSigner(signerArgs, provider);
|
||||||
|
const circleIntegration = ICircleIntegration__factory.connect(
|
||||||
|
parsedArgs.proxy,
|
||||||
|
signer,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
circleIntegration,
|
||||||
|
newImplementation: parsedArgs.newImplementation,
|
||||||
|
guardians: parsedArgs.guardian,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const { circleIntegration, newImplementation, guardians } = await setUp();
|
||||||
|
|
||||||
|
const chainId = await circleIntegration.chainId();
|
||||||
|
console.log(
|
||||||
|
`Executing testnet CircleIntegration upgrade on chainId=${chainId}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const guardianSet: GuardianSet = {
|
||||||
|
id: 0,
|
||||||
|
guardians: guardians.map((guardian, index) => {
|
||||||
|
return { key: guardian, index };
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const governanceMessage = await createCircleIntegrationUpgradeVAA(
|
||||||
|
chainId,
|
||||||
|
newImplementation,
|
||||||
|
guardianSet,
|
||||||
|
);
|
||||||
|
|
||||||
|
const tx = await circleIntegration.upgradeContract(governanceMessage);
|
||||||
|
console.log(`Upgrade transaction sent txHash=${tx.hash}`);
|
||||||
|
|
||||||
|
const receipt = await tx.wait();
|
||||||
|
if (receipt.status !== 1) {
|
||||||
|
console.log("Failed transaction");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
console.log("Transaction successful");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
|
@ -1,2 +1,2 @@
|
||||||
export * from "./ethers-contracts";
|
export * from "./ethers-contracts/index.js";
|
||||||
export * from "./types";
|
export * from "./types.js";
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
IMessageTransmitter__factory,
|
IMessageTransmitter__factory,
|
||||||
IUSDC__factory,
|
IUSDC__factory,
|
||||||
IWormhole__factory,
|
IWormhole__factory,
|
||||||
} from "../src/ethers-contracts";
|
} from "../src/ethers-contracts/index.js";
|
||||||
import {
|
import {
|
||||||
AVAX_CIRCLE_BRIDGE_ADDRESS,
|
AVAX_CIRCLE_BRIDGE_ADDRESS,
|
||||||
AVAX_FORK_CHAIN_ID,
|
AVAX_FORK_CHAIN_ID,
|
||||||
|
@ -26,7 +26,7 @@ import {
|
||||||
WALLET_PRIVATE_KEY,
|
WALLET_PRIVATE_KEY,
|
||||||
WORMHOLE_GUARDIAN_SET_INDEX,
|
WORMHOLE_GUARDIAN_SET_INDEX,
|
||||||
WORMHOLE_MESSAGE_FEE,
|
WORMHOLE_MESSAGE_FEE,
|
||||||
} from "./helpers/consts";
|
} from "./helpers/consts.js";
|
||||||
|
|
||||||
describe("Environment Test", () => {
|
describe("Environment Test", () => {
|
||||||
describe("Global", () => {
|
describe("Global", () => {
|
||||||
|
|
|
@ -9,12 +9,12 @@ import {
|
||||||
AVAX_LOCALHOST,
|
AVAX_LOCALHOST,
|
||||||
ETH_FORK_CHAIN_ID,
|
ETH_FORK_CHAIN_ID,
|
||||||
AVAX_FORK_CHAIN_ID,
|
AVAX_FORK_CHAIN_ID,
|
||||||
} from "./helpers/consts";
|
} from "./helpers/consts.js";
|
||||||
import {ICircleIntegration__factory} from "../src/ethers-contracts";
|
import {ICircleIntegration__factory} from "../src/ethers-contracts/index.js";
|
||||||
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
|
import {MockGuardians} from "@certusone/wormhole-sdk/lib/esm/mock";
|
||||||
|
|
||||||
import {CircleGovernanceEmitter} from "./helpers/mock";
|
import {CircleGovernanceEmitter} from "./helpers/mock.js";
|
||||||
import {getTimeNow, readCircleIntegrationProxyAddress} from "./helpers/utils";
|
import {getTimeNow, readCircleIntegrationProxyAddress} from "./helpers/utils.js";
|
||||||
|
|
||||||
describe("Circle Integration Registration", () => {
|
describe("Circle Integration Registration", () => {
|
||||||
// ethereum wallet, CircleIntegration contract and USDC contract
|
// ethereum wallet, CircleIntegration contract and USDC contract
|
||||||
|
|
|
@ -19,23 +19,23 @@ import {
|
||||||
AVAX_FORK_CHAIN_ID,
|
AVAX_FORK_CHAIN_ID,
|
||||||
ETH_WORMHOLE_ADDRESS,
|
ETH_WORMHOLE_ADDRESS,
|
||||||
AVAX_WORMHOLE_ADDRESS,
|
AVAX_WORMHOLE_ADDRESS,
|
||||||
} from "./helpers/consts";
|
} from "./helpers/consts.js";
|
||||||
import {
|
import {
|
||||||
ICircleIntegration__factory,
|
ICircleIntegration__factory,
|
||||||
IUSDC__factory,
|
IUSDC__factory,
|
||||||
IMockIntegration__factory,
|
IMockIntegration__factory,
|
||||||
IWormhole__factory,
|
IWormhole__factory,
|
||||||
} from "../src/ethers-contracts";
|
} from "../src/ethers-contracts/index.js";
|
||||||
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
|
import {MockGuardians} from "@certusone/wormhole-sdk/lib/esm/mock";
|
||||||
import {RedeemParameters, TransferParameters} from "../src";
|
import {RedeemParameters, TransferParameters} from "../src/index.js";
|
||||||
import {findCircleMessageInLogs} from "../src/logs";
|
import {findCircleMessageInLogs} from "../src/logs.js";
|
||||||
import {
|
import {
|
||||||
MockCircleAttester,
|
MockCircleAttester,
|
||||||
readCircleIntegrationProxyAddress,
|
readCircleIntegrationProxyAddress,
|
||||||
readMockIntegrationAddress,
|
readMockIntegrationAddress,
|
||||||
findWormholeMessageInLogs,
|
findWormholeMessageInLogs,
|
||||||
findRedeemEventInLogs,
|
findRedeemEventInLogs,
|
||||||
} from "./helpers/utils";
|
} from "./helpers/utils.js";
|
||||||
|
|
||||||
describe("Circle Integration Send and Receive", () => {
|
describe("Circle Integration Send and Receive", () => {
|
||||||
// ethereum wallet, CircleIntegration contract and USDC contract
|
// ethereum wallet, CircleIntegration contract and USDC contract
|
||||||
|
|
|
@ -9,12 +9,12 @@ import {
|
||||||
AVAX_LOCALHOST,
|
AVAX_LOCALHOST,
|
||||||
ETH_FORK_CHAIN_ID,
|
ETH_FORK_CHAIN_ID,
|
||||||
AVAX_FORK_CHAIN_ID,
|
AVAX_FORK_CHAIN_ID,
|
||||||
} from "./helpers/consts";
|
} from "./helpers/consts.js";
|
||||||
import {ICircleIntegration__factory} from "../src/ethers-contracts";
|
import {ICircleIntegration__factory} from "../src/ethers-contracts/index.js";
|
||||||
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
|
import {MockGuardians} from "@certusone/wormhole-sdk/lib/esm/mock";
|
||||||
|
|
||||||
import {CircleGovernanceEmitter} from "./helpers/mock";
|
import {CircleGovernanceEmitter} from "./helpers/mock.js";
|
||||||
import {getTimeNow, readCircleIntegrationProxyAddress} from "./helpers/utils";
|
import {getTimeNow, readCircleIntegrationProxyAddress} from "./helpers/utils.js";
|
||||||
|
|
||||||
const {execSync} = require("child_process");
|
const {execSync} = require("child_process");
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {GovernanceEmitter} from "@certusone/wormhole-sdk/lib/cjs/mock";
|
import {GovernanceEmitter} from "@certusone/wormhole-sdk/lib/esm/mock";
|
||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
|
|
||||||
export interface Transfer {
|
export interface Transfer {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {tryNativeToHexString} from "@certusone/wormhole-sdk";
|
import {tryNativeToHexString} from "@certusone/wormhole-sdk";
|
||||||
import {ethSignWithPrivate} from "@certusone/wormhole-sdk/lib/cjs/mock";
|
import {ethSignWithPrivate} from "@certusone/wormhole-sdk/lib/esm/mock";
|
||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": ["node", "mocha", "chai"],
|
"types": ["node", "mocha", "chai"],
|
||||||
"typeRoots": ["./node_modules/@types"],
|
"typeRoots": ["./node_modules/@types"],
|
||||||
"lib": ["es2020"],
|
"lib": ["ES2022", "dom"],
|
||||||
"module": "CommonJS",
|
"module": "NodeNext",
|
||||||
"target": "es2020",
|
"target": "ES2022",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"moduleResolution": "node"
|
"moduleResolution": "NodeNext"
|
||||||
}
|
},
|
||||||
|
"include": ["ts"]
|
||||||
}
|
}
|
||||||
|
|
2313
evm/yarn.lock
2313
evm/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue