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:
scnale 2023-10-27 11:26:55 -03:00 committed by GitHub
parent 2e0831c6c1
commit 105ad59bad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1653 additions and 1242 deletions

View File

@ -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

14
evm/env/mainnet/base.env vendored Normal file
View File

@ -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

View File

@ -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

View File

@ -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

15
evm/env/testnet/base.env vendored Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 $@

View File

@ -1,4 +1,4 @@
#/usr/bin/env bash #!/usr/bin/env bash
etherscan_key=$1 etherscan_key=$1

View File

@ -1,4 +1,4 @@
#/usr/bin/env bash #!/usr/bin/env bash
etherscan_key=$1 etherscan_key=$1

View File

@ -1,4 +1,4 @@
#/usr/bin/env bash #!/usr/bin/env bash
etherscan_key=$1 etherscan_key=$1

View File

@ -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]});

120
evm/ts/scripts/sign_vaa.ts Normal file
View File

@ -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;
}

95
evm/ts/scripts/signer.ts Normal file
View File

@ -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);
}

View File

@ -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;
} }
} }

View File

@ -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();

View File

@ -1,2 +1,2 @@
export * from "./ethers-contracts"; export * from "./ethers-contracts/index.js";
export * from "./types"; export * from "./types.js";

View File

@ -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", () => {

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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 {

View File

@ -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";

View File

@ -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"]
} }

File diff suppressed because it is too large Load Diff