improve deployment
This commit is contained in:
parent
c119f88024
commit
4cdeca0d34
|
@ -1 +1,2 @@
|
|||
pkeys.sh
|
||||
pkeys.sh
|
||||
.vscode/settings.json
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
"**/.DS_Store": true,
|
||||
"**/Thumbs.db": true,
|
||||
"node_modules/": true,
|
||||
"**/node_modules": true
|
||||
"**/node_modules": false
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ export type Deployment = {
|
|||
export let env = ""
|
||||
let lastRunOverride: boolean | undefined
|
||||
|
||||
export function init(overrides?: { lastRunOverride?: boolean }): string {
|
||||
export function init(overrides: { lastRunOverride?: boolean } = {}): string {
|
||||
env = get_env_var("ENV")
|
||||
if (!env) {
|
||||
console.log("No environment was specified, using default environment files")
|
||||
|
|
|
@ -11,7 +11,6 @@ WORKDIR /usr/src/app
|
|||
# Install app dependencies
|
||||
# COPY . /src
|
||||
|
||||
COPY ../sdk ../sdk
|
||||
COPY . .
|
||||
|
||||
CMD [ "npm", "run", "k8s-testnet" ]
|
|
@ -1,4 +1,4 @@
|
|||
bash deploy-redis.sh
|
||||
# bash deploy-redis.sh
|
||||
kubectl apply -f ./spy-service.yaml
|
||||
source ../../pkeys.sh
|
||||
bash inject-private-keys.sh
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
# typically source ../../pkeys.sh in project root first
|
||||
|
||||
# kubectl delete secret private-keys --ignore-not-found
|
||||
kubectl delete secret private-keys --ignore-not-found
|
||||
|
||||
kubectl create secret generic private-keys \
|
||||
--from-literal=PRIVATE_KEYS_CHAIN_14=${PRIVATE_KEYS_CHAIN_14} \
|
||||
--from-literal=PRIVATE_KEYS_CHAIN_6=${PRIVATE_KEYS_CHAIN_6}
|
||||
--from-literal=PRIVATE_KEYS_CHAIN_4=${PRIVATE_KEYS_CHAIN_4} \
|
||||
--from-literal=PRIVATE_KEYS_CHAIN_5=${PRIVATE_KEYS_CHAIN_5} \
|
||||
--from-literal=PRIVATE_KEYS_CHAIN_6=${PRIVATE_KEYS_CHAIN_6} \
|
||||
--from-literal=PRIVATE_KEYS_CHAIN_14=${PRIVATE_KEYS_CHAIN_14}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"mode": "BOTH",
|
||||
"logLevel": "info",
|
||||
"logLevel": "debug",
|
||||
"storeType": "InMemory",
|
||||
"redisPort": 6379,
|
||||
"redisHost": "redis-master.default.svc.cluster.local",
|
||||
|
@ -16,6 +16,16 @@
|
|||
"chainId": 14,
|
||||
"chainName": "Celo",
|
||||
"nodeUrl": "https://alfajores-forno.celo-testnet.org"
|
||||
},
|
||||
{
|
||||
"chainName": "BSC",
|
||||
"chainId": 4,
|
||||
"nodeUrl": "https://bsc-testnet.public.blastapi.io"
|
||||
},
|
||||
{
|
||||
"chainName": "Mumbai",
|
||||
"chainId": 5,
|
||||
"nodeUrl": "https://matic-mumbai.chainstacklabs.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -4,31 +4,55 @@
|
|||
"relayProviders": [
|
||||
{
|
||||
"chainId": 6,
|
||||
"address": "0x302f4D287204b8c383a79BA86Ad1fD1F81fb00E2"
|
||||
"address": "0xb19eBF87f3b957B6e99d8bACC994c987805C74E2"
|
||||
},
|
||||
{
|
||||
"chainId": 14,
|
||||
"address": "0x1A7d2aCBa5Ae7ad19e4DA7a512d369CC4aEFe66B"
|
||||
"address": "0xb4e6BcD5dBbD502443bb4480aA5213DfCC9F1fC8"
|
||||
},
|
||||
{
|
||||
"chainId": 4,
|
||||
"address": "0x128eE477E3DEC6b97978E7EC41a95C5cBE111c44"
|
||||
},
|
||||
{
|
||||
"chainId": 5,
|
||||
"address": "0xde609ED85F6EaaD58AA422Fd95Fea213cDA83650"
|
||||
}
|
||||
],
|
||||
"coreRelayers": [
|
||||
{
|
||||
"chainId": 6,
|
||||
"address": "0xDED10060E839c497B8D71C3091f9f24dCe4110cF"
|
||||
"address": "0x74D59cBFEBAf363769C78E2e2165503228F2b92F"
|
||||
},
|
||||
{
|
||||
"chainId": 14,
|
||||
"address": "0xDED10060E839c497B8D71C3091f9f24dCe4110cF"
|
||||
"address": "0x22baa6Ff6454C1B5f514CfE09DFDc77f44a6be56"
|
||||
},
|
||||
{
|
||||
"chainId": 4,
|
||||
"address": "0x8bCce102F34C6DbF53655958586d7Bc196dCB3c5"
|
||||
},
|
||||
{
|
||||
"chainId": 5,
|
||||
"address": "0x128eE477E3DEC6b97978E7EC41a95C5cBE111c44"
|
||||
}
|
||||
],
|
||||
"mockIntegrations": [
|
||||
{
|
||||
"chainId": 6,
|
||||
"address": "0x62C4143AB8BEe162eBF6166a679A746cAE1D1385"
|
||||
"address": "0xbFEA8140309070f0dB56c1970fc9de61970F464E"
|
||||
},
|
||||
{
|
||||
"chainId": 14,
|
||||
"address": "0xbeD6e30Ff857944931F3eF0B26EdC0B616e92d57"
|
||||
"address": "0x3Af9D81E5AE37fE3828234E3dBC06c7b2983c7fD"
|
||||
},
|
||||
{
|
||||
"chainId": 4,
|
||||
"address": "0x26Dc6Fa3a53Ab99187176e0E445065E054ca5CB3"
|
||||
},
|
||||
{
|
||||
"chainId": 5,
|
||||
"address": "0x660fF93943d6741ECc60a97D695760F13f094628"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"privateKeys": {
|
||||
"4": [""],
|
||||
"5": [""],
|
||||
"6": [""],
|
||||
"14": [""]
|
||||
}
|
||||
|
|
|
@ -3,13 +3,9 @@
|
|||
"shouldRest": false,
|
||||
"logWatcherSleepMs": 300000,
|
||||
"supportedChains": {
|
||||
"6": {
|
||||
"relayerAddress": "0x932848Aed98d8af0f3eA685533966dA4551851db",
|
||||
"mockIntegrationContractAddress": "0xF5C9730B9F8B4D3a352D0cC6358896B1e56E656C"
|
||||
},
|
||||
"14": {
|
||||
"relayerAddress": "0x06ced51D388A66ff3d968818C4ea58e5CC199B0B",
|
||||
"mockIntegrationContractAddress": "0xB7078f7384d4bb353A147a1035990eD6CDAF011E"
|
||||
}
|
||||
"4": {},
|
||||
"5": {},
|
||||
"6": {},
|
||||
"14": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ configMapGenerator:
|
|||
- k8s-testnet/common.json
|
||||
- k8s-testnet/executor.json
|
||||
- k8s-testnet/listener.json
|
||||
- name: relayer-contracts
|
||||
files:
|
||||
- k8s-testnet/contracts.json
|
||||
- name: generic-relayer-plugin-config
|
||||
files:
|
||||
- k8s-testnet/k8s-testnet.json
|
|
@ -19,27 +19,36 @@ spec:
|
|||
containers:
|
||||
- name: simple-gr
|
||||
image: joehowarth/simple-gr:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
imagePullPolicy: Always
|
||||
# uncomment to explore filesystem during crash loop
|
||||
# command: [ "/bin/sh", "-c", "--" ]
|
||||
# args: [ "while true; do sleep 30; done;" ]
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1
|
||||
memory: 100Mi
|
||||
cpu: 1000m
|
||||
memory: 600Mi
|
||||
limits:
|
||||
cpu: 2
|
||||
cpu: 1000m
|
||||
memory: 2000Mi
|
||||
volumeMounts:
|
||||
- name: relayer-contracts
|
||||
mountPath: /usr/src/ethereum/ts-scripts/config/k8s-testnet
|
||||
- name: relayer-engine-config
|
||||
mountPath: /usr/src/app/engine_config/k8s-testnet
|
||||
- name: generic-relayer-plugin-config
|
||||
mountPath: /usr/src/app/src/plugin/config
|
||||
env:
|
||||
- name: PRIVATE_KEYS_CHAIN_14
|
||||
- name: PRIVATE_KEYS_CHAIN_4
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: private-keys
|
||||
key: PRIVATE_KEYS_CHAIN_14
|
||||
key: PRIVATE_KEYS_CHAIN_4
|
||||
optional: false
|
||||
- name: PRIVATE_KEYS_CHAIN_5
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: private-keys
|
||||
key: PRIVATE_KEYS_CHAIN_5
|
||||
optional: false
|
||||
- name: PRIVATE_KEYS_CHAIN_6
|
||||
valueFrom:
|
||||
|
@ -47,7 +56,16 @@ spec:
|
|||
name: private-keys
|
||||
key: PRIVATE_KEYS_CHAIN_6
|
||||
optional: false
|
||||
- name: PRIVATE_KEYS_CHAIN_14
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: private-keys
|
||||
key: PRIVATE_KEYS_CHAIN_14
|
||||
optional: false
|
||||
volumes:
|
||||
- name: relayer-contracts
|
||||
configMap:
|
||||
name: relayer-contracts
|
||||
- name: relayer-engine-config
|
||||
configMap:
|
||||
name: relayer-engine-config
|
||||
|
|
|
@ -14,6 +14,16 @@
|
|||
"chainId": 14,
|
||||
"chainName": "Celo",
|
||||
"nodeUrl": "https://alfajores-forno.celo-testnet.org"
|
||||
},
|
||||
{
|
||||
"chainName": "BSC",
|
||||
"chainId": 4,
|
||||
"nodeUrl": "https://bsc-testnet.public.blastapi.io"
|
||||
},
|
||||
{
|
||||
"chainName": "Mumbai",
|
||||
"chainId": 5,
|
||||
"nodeUrl": "https://matic-mumbai.chainstacklabs.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"privateKeys": {
|
||||
"4": [""],
|
||||
"5": [""],
|
||||
"6": [""],
|
||||
"14": [""]
|
||||
}
|
||||
|
|
|
@ -10,9 +10,7 @@
|
|||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@certusone/wormhole-sdk": "^0.9.6",
|
||||
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
|
||||
"@wormhole-foundation/relayer-engine": "github:wormhole-foundation/relayer-engine#f6491e6e59e905a9c9590cd8b6f62a58f730b4d6",
|
||||
"lodash": "^4.17.21",
|
||||
"ts-retry": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -6,20 +6,18 @@
|
|||
"types": "lib/main.d.ts",
|
||||
"scripts": {
|
||||
"k8s-testnet": "ts-node src/main --k8s-testnet",
|
||||
"testnet": "bash build.sh; ts-node src/main --testnet",
|
||||
"testnet-watch": "bash build.sh; nodemon src/main --testnet",
|
||||
"tilt": "bash build.sh; ts-node src/main --tilt",
|
||||
"mainnet": "bash build.sh; ts-node src/main --mainnet",
|
||||
"testnet": "ts-node src/main --testnet",
|
||||
"testnet-watch": "nodemon src/main --testnet",
|
||||
"tilt": "ts-node src/main --tilt",
|
||||
"mainnet": "ts-node src/main --mainnet",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "bash build.sh; tsc",
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch",
|
||||
"start": "ts-node src/main.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certusone/wormhole-sdk": "^0.9.6",
|
||||
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
|
||||
"@wormhole-foundation/relayer-engine": "github:wormhole-foundation/relayer-engine#f6491e6e59e905a9c9590cd8b6f62a58f730b4d6",
|
||||
"lodash": "^4.17.21",
|
||||
"ts-retry": "^4.1.1"
|
||||
},
|
||||
"author": "Chase Moran",
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
lib
|
||||
node_modules
|
||||
src/ethers-contracts
|
||||
src/ethers-contracts
|
|
@ -2,7 +2,8 @@
|
|||
"name": "generic-relayer-sdk",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "networks.js",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"devDependencies": {
|
||||
"@openzeppelin/contracts": "^4.7.3",
|
||||
"@poanet/solidity-flattener": "^3.0.8",
|
||||
|
@ -15,6 +16,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf node_modules src/ethers-contracts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"tsc": "tsc",
|
||||
"build": "bash scripts/make_ethers_types.sh",
|
||||
"test": "ts-mocha src/__tests__/*.ts --timeout 60000"
|
||||
},
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { ethers } from "ethers";
|
||||
import {
|
||||
BSC_FORGE_BROADCAST,
|
||||
BSC_RPC,
|
||||
WORMHOLE_RPCS,
|
||||
ETH_FORGE_BROADCAST,
|
||||
ETH_RPC,
|
||||
DEPLOYER_PRIVATE_KEY,
|
||||
ZERO_ADDRESS_BYTES,
|
||||
TARGET_GAS_LIMIT,
|
||||
} from "./helpers/consts";
|
||||
import { RelayerArgs } from "./helpers/structs";
|
||||
import {
|
||||
makeCoreRelayerFromForgeBroadcast,
|
||||
makeGasOracleFromForgeBroadcast,
|
||||
makeMockRelayerIntegrationFromForgeBroadcast,
|
||||
resolvePath,
|
||||
} from "./helpers/utils";
|
||||
import {
|
||||
CHAIN_ID_BSC,
|
||||
CHAIN_ID_ETH,
|
||||
getSignedBatchVAAWithRetry,
|
||||
tryNativeToUint8Array,
|
||||
tryNativeToHexString,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
||||
|
||||
describe("ETH <> BSC Generic Relayer Integration Test", () => {
|
||||
const ethProvider = new ethers.providers.StaticJsonRpcProvider(ETH_RPC);
|
||||
const bscProvider = new ethers.providers.StaticJsonRpcProvider(BSC_RPC);
|
||||
|
||||
// core relayers
|
||||
const ethCoreRelayer = makeCoreRelayerFromForgeBroadcast(
|
||||
resolvePath(ETH_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, ethProvider)
|
||||
);
|
||||
|
||||
const bscCoreRelayer = makeCoreRelayerFromForgeBroadcast(
|
||||
resolvePath(BSC_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, bscProvider)
|
||||
);
|
||||
|
||||
// relayer integrators
|
||||
const ethRelayerIntegrator = makeMockRelayerIntegrationFromForgeBroadcast(
|
||||
resolvePath(ETH_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, ethProvider)
|
||||
);
|
||||
|
||||
const bscRelayerIntegrator = makeMockRelayerIntegrationFromForgeBroadcast(
|
||||
resolvePath(BSC_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, bscProvider)
|
||||
);
|
||||
|
||||
// gas oracles
|
||||
const ownedGasOracles = [
|
||||
// eth
|
||||
makeGasOracleFromForgeBroadcast(
|
||||
resolvePath(ETH_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, ethProvider)
|
||||
),
|
||||
// bsc
|
||||
makeGasOracleFromForgeBroadcast(
|
||||
resolvePath(BSC_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, bscProvider)
|
||||
),
|
||||
];
|
||||
|
||||
const readonlyGasOracles = [
|
||||
// eth
|
||||
makeGasOracleFromForgeBroadcast(resolvePath(ETH_FORGE_BROADCAST), ethProvider),
|
||||
// bsc
|
||||
makeGasOracleFromForgeBroadcast(resolvePath(BSC_FORGE_BROADCAST), bscProvider),
|
||||
];
|
||||
|
||||
const ethPrice = ethers.utils.parseUnits("2000.00", 8);
|
||||
const bscPrice = ethers.utils.parseUnits("400.00", 8);
|
||||
|
||||
before("Setup Gas Oracle Prices And Register Relayer Contracts", async () => {
|
||||
// now fetch gas prices from each provider
|
||||
const gasPrices = await Promise.all(ownedGasOracles.map((oracle) => oracle.provider.getGasPrice()));
|
||||
|
||||
const updates = [
|
||||
{
|
||||
chainId: CHAIN_ID_ETH,
|
||||
gasPrice: gasPrices.at(0)!,
|
||||
nativeCurrencyPrice: ethPrice,
|
||||
},
|
||||
{
|
||||
chainId: CHAIN_ID_BSC,
|
||||
gasPrice: gasPrices.at(1)!,
|
||||
nativeCurrencyPrice: bscPrice,
|
||||
},
|
||||
];
|
||||
|
||||
const oracleTxs = await Promise.all(
|
||||
ownedGasOracles.map((oracle) => oracle.updatePrices(updates).then((tx: ethers.ContractTransaction) => tx.wait()))
|
||||
);
|
||||
|
||||
// query the core relayer contracts to see if relayers have been registered
|
||||
const registeredCoreRelayerOnBsc = await bscCoreRelayer.registeredRelayer(CHAIN_ID_ETH);
|
||||
const registeredCoreRelayerOnEth = await ethCoreRelayer.registeredRelayer(CHAIN_ID_BSC);
|
||||
|
||||
// register the core relayer contracts
|
||||
if (registeredCoreRelayerOnBsc == ZERO_ADDRESS_BYTES) {
|
||||
await bscCoreRelayer
|
||||
.registerChain(CHAIN_ID_ETH, tryNativeToUint8Array(ethCoreRelayer.address, CHAIN_ID_ETH))
|
||||
.then((tx) => tx.wait());
|
||||
}
|
||||
|
||||
if (registeredCoreRelayerOnEth == ZERO_ADDRESS_BYTES) {
|
||||
await ethCoreRelayer
|
||||
.registerChain(CHAIN_ID_BSC, tryNativeToUint8Array(bscCoreRelayer.address, CHAIN_ID_BSC))
|
||||
.then((tx) => tx.wait());
|
||||
}
|
||||
});
|
||||
|
||||
describe("Send from Ethereum and Deliver to BSC", () => {
|
||||
// batch Vaa payloads to relay to the target contract
|
||||
let batchVaaPayloads: ethers.utils.BytesLike[] = [];
|
||||
|
||||
// save the batch VAA info
|
||||
let batchToBscReceipt: ethers.ContractReceipt;
|
||||
let batchVaaFromEth: ethers.utils.BytesLike;
|
||||
|
||||
it("Check Gas Oracles", async () => {
|
||||
const chainIds = await Promise.all(readonlyGasOracles.map((oracle) => oracle.chainId()));
|
||||
expect(chainIds.at(0)).is.not.undefined;
|
||||
expect(chainIds.at(0)!).to.equal(CHAIN_ID_ETH);
|
||||
expect(chainIds.at(1)).is.not.undefined;
|
||||
expect(chainIds.at(1)!).to.equal(CHAIN_ID_BSC);
|
||||
|
||||
const ethPrices = await Promise.all(readonlyGasOracles.map((oracle) => oracle.gasPrice(CHAIN_ID_ETH)));
|
||||
const bscPrices = await Promise.all(readonlyGasOracles.map((oracle) => oracle.gasPrice(CHAIN_ID_BSC)));
|
||||
for (let i = 0; i < 2; ++i) {
|
||||
expect(ethPrices.at(i)).is.not.undefined;
|
||||
expect(ethPrices.at(i)?.toString()).to.equal("20000000000");
|
||||
expect(bscPrices.at(i)).is.not.undefined;
|
||||
expect(bscPrices.at(i)?.toString()).to.equal("20000000000");
|
||||
}
|
||||
});
|
||||
|
||||
it("Generate batch VAA with delivery instructions on Ethereum", async () => {
|
||||
// estimate the relayer cost to relay a batch to BSC
|
||||
const estimatedGasCost = await ethRelayerIntegrator.estimateRelayCosts(CHAIN_ID_BSC, TARGET_GAS_LIMIT);
|
||||
|
||||
// create an array of messages to deliver to the BSC target contract
|
||||
batchVaaPayloads = [
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff0")),
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff1")),
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff2")),
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff3")),
|
||||
];
|
||||
const batchVaaConsistencyLevels = [15, 15, 15, 15];
|
||||
|
||||
// create relayerArgs interface to call the mock integration contract with
|
||||
const relayerArgs: RelayerArgs = {
|
||||
nonce: 69,
|
||||
targetChainId: CHAIN_ID_BSC,
|
||||
targetAddress: bscRelayerIntegrator.address,
|
||||
targetGasLimit: TARGET_GAS_LIMIT,
|
||||
consistencyLevel: batchVaaConsistencyLevels[0],
|
||||
};
|
||||
|
||||
// call the mock integration contract and send the batch VAA
|
||||
batchToBscReceipt = await ethRelayerIntegrator
|
||||
.sendBatchToTargetChain(batchVaaPayloads, batchVaaConsistencyLevels, relayerArgs, {
|
||||
value: estimatedGasCost,
|
||||
})
|
||||
.then((tx) => tx.wait());
|
||||
});
|
||||
|
||||
it("Fetch batch VAA from Ethereum", async () => {
|
||||
// fetch the batch VAA with getSignedBatchVAAWithRetry
|
||||
const batchVaaRes = await getSignedBatchVAAWithRetry(
|
||||
WORMHOLE_RPCS,
|
||||
CHAIN_ID_ETH,
|
||||
batchToBscReceipt.transactionHash,
|
||||
{
|
||||
transport: NodeHttpTransport(),
|
||||
}
|
||||
);
|
||||
batchVaaFromEth = batchVaaRes.batchVaaBytes;
|
||||
});
|
||||
|
||||
it("Wait for off-chain relayer to deliver the batch VAA to BSC", async () => {
|
||||
// parse the batch VAA
|
||||
const parsedBatch = await ethRelayerIntegrator.parseWormholeBatch(batchVaaFromEth);
|
||||
|
||||
// Check to see if the batch VAA was delivered by querying the contract
|
||||
// for the first payload sent in the batch.
|
||||
let isBatchDelivered: boolean = false;
|
||||
const targetVm3 = await ethRelayerIntegrator.parseWormholeObservation(parsedBatch.observations[0]);
|
||||
while (!isBatchDelivered) {
|
||||
// query the contract to see if the batch was delivered
|
||||
const storedPayload = await bscRelayerIntegrator.getPayload(targetVm3.hash);
|
||||
if (storedPayload == targetVm3.payload) {
|
||||
isBatchDelivered = true;
|
||||
}
|
||||
}
|
||||
|
||||
// confirm that the remaining payloads are stored in the contract
|
||||
for (const observation of parsedBatch.observations) {
|
||||
const vm3 = await bscRelayerIntegrator.parseWormholeObservation(observation);
|
||||
|
||||
// skip delivery instructions VM
|
||||
if (vm3.emitterAddress == "0x" + tryNativeToHexString(ethCoreRelayer.address, CHAIN_ID_ETH)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// query the contract to see if the batch was delivered
|
||||
const storedPayload = await bscRelayerIntegrator.getPayload(vm3.hash);
|
||||
expect(storedPayload).to.equal(vm3.payload);
|
||||
|
||||
// clear the payload from the mock integration contract
|
||||
await bscRelayerIntegrator.clearPayload(vm3.hash);
|
||||
const emptyStoredPayload = await bscRelayerIntegrator.getPayload(vm3.hash);
|
||||
expect(emptyStoredPayload).to.equal("0x");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
// rpc
|
||||
export const ETH_RPC = "http://localhost:8545";
|
||||
export const BSC_RPC = "http://localhost:8546";
|
||||
export const WORMHOLE_RPCS = ["http://localhost:7071"];
|
||||
|
||||
export const ETH_EVM_CHAINID = 1337;
|
||||
export const BSC_EVM_CHAINID = 1397;
|
||||
|
||||
// evm wallets
|
||||
export const DEPLOYER_PRIVATE_KEY = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"; // account 0
|
||||
export const EVM_PRIVATE_KEY = "0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c"; // account 2
|
||||
|
||||
// io
|
||||
export const ETHEREUM_ROOT = `${__dirname}/../../../../ethereum`; // holy parent directories, batman
|
||||
export const ETH_FORGE_BROADCAST = `${ETHEREUM_ROOT}/broadcast/deploy_contracts.sol/${ETH_EVM_CHAINID}/run-latest.json`;
|
||||
export const BSC_FORGE_BROADCAST = `${ETHEREUM_ROOT}/broadcast/deploy_contracts.sol/${BSC_EVM_CHAINID}/run-latest.json`;
|
||||
|
||||
// misc
|
||||
export const ZERO_ADDRESS_BYTES = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
// the amount of gas that the target relayer contract will invoke the wormhole receiver with
|
||||
export const TARGET_GAS_LIMIT = 500000; // evm gas units
|
||||
|
||||
// wormhole event ABIs
|
||||
export const WORMHOLE_MESSAGE_EVENT_ABI = [
|
||||
"event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel)",
|
||||
];
|
|
@ -1,24 +0,0 @@
|
|||
import { ethers } from "ethers";
|
||||
|
||||
export interface RelayerArgs {
|
||||
nonce: number;
|
||||
targetChainId: number;
|
||||
targetAddress: string;
|
||||
targetGasLimit: number;
|
||||
consistencyLevel: number;
|
||||
}
|
||||
|
||||
export interface TargetDeliveryParameters {
|
||||
encodedVM: ethers.utils.BytesLike;
|
||||
deliveryIndex: number;
|
||||
targetCallGasOverride: ethers.BigNumber;
|
||||
}
|
||||
|
||||
export interface DeliveryStatus {
|
||||
payloadId: number;
|
||||
batchHash: ethers.utils.BytesLike;
|
||||
emitterAddress: ethers.utils.BytesLike;
|
||||
sequence: number;
|
||||
deliveryCount: number;
|
||||
deliverySuccess: number;
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
import {ethers} from "ethers";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import {DeliveryStatus} from "./structs";
|
||||
import {WORMHOLE_RPCS, WORMHOLE_MESSAGE_EVENT_ABI} from "./consts";
|
||||
import {NodeHttpTransport} from "@improbable-eng/grpc-web-node-http-transport";
|
||||
import {ChainId, getEmitterAddressEth, getSignedVAAWithRetry} from "@certusone/wormhole-sdk";
|
||||
import {
|
||||
GasOracle,
|
||||
GasOracle__factory,
|
||||
MockRelayerIntegration,
|
||||
MockRelayerIntegration__factory,
|
||||
CoreRelayer,
|
||||
CoreRelayer__factory,
|
||||
} from "../../";
|
||||
|
||||
export function makeGasOracleFromForgeBroadcast(
|
||||
broadcastPath: string,
|
||||
signerOrProvider: ethers.Signer | ethers.providers.Provider
|
||||
): GasOracle {
|
||||
const address = getContractAddressFromForgeBroadcast(broadcastPath, "GasOracle");
|
||||
return GasOracle__factory.connect(address, signerOrProvider);
|
||||
}
|
||||
|
||||
export function makeMockRelayerIntegrationFromForgeBroadcast(
|
||||
broadcastPath: string,
|
||||
signerOrProvider: ethers.Signer | ethers.providers.Provider
|
||||
): MockRelayerIntegration {
|
||||
const address = getContractAddressFromForgeBroadcast(broadcastPath, "MockRelayerIntegration");
|
||||
return MockRelayerIntegration__factory.connect(address, signerOrProvider);
|
||||
}
|
||||
|
||||
export function makeCoreRelayerFromForgeBroadcast(
|
||||
broadcastPath: string,
|
||||
signerOrProvider: ethers.Signer | ethers.providers.Provider
|
||||
): CoreRelayer {
|
||||
const address = getContractAddressFromForgeBroadcast(broadcastPath, "ERC1967Proxy");
|
||||
return CoreRelayer__factory.connect(address, signerOrProvider);
|
||||
}
|
||||
|
||||
function readForgeBroadcast(broadcastPath: string): any {
|
||||
if (!fs.existsSync(broadcastPath)) {
|
||||
throw new Error("broadcastPath does not exist");
|
||||
}
|
||||
|
||||
return JSON.parse(fs.readFileSync(broadcastPath, "utf8"));
|
||||
}
|
||||
|
||||
function getContractAddressFromForgeBroadcast(broadcastPath: string, contractName: string) {
|
||||
const transactions: any[] = readForgeBroadcast(broadcastPath).transactions;
|
||||
const result = transactions.find((tx) => tx.contractName == contractName && tx.transactionType == "CREATE");
|
||||
if (result == undefined) {
|
||||
throw new Error("transaction.find == undefined");
|
||||
}
|
||||
return result.contractAddress;
|
||||
}
|
||||
|
||||
export function resolvePath(fp: string) {
|
||||
return path.resolve(fp);
|
||||
}
|
||||
|
||||
export async function parseWormholeEventsFromReceipt(
|
||||
receipt: ethers.ContractReceipt
|
||||
): Promise<ethers.utils.LogDescription[]> {
|
||||
// create the wormhole message interface
|
||||
const wormholeMessageInterface = new ethers.utils.Interface(WORMHOLE_MESSAGE_EVENT_ABI);
|
||||
|
||||
// loop through the logs and parse the events that were emitted
|
||||
const logDescriptions: ethers.utils.LogDescription[] = await Promise.all(
|
||||
receipt.logs.map(async (log) => {
|
||||
return wormholeMessageInterface.parseLog(log);
|
||||
})
|
||||
);
|
||||
|
||||
return logDescriptions;
|
||||
}
|
||||
|
||||
export async function getSignedVaaFromReceiptOnEth(
|
||||
receipt: ethers.ContractReceipt,
|
||||
emitterChainId: ChainId,
|
||||
contractAddress: ethers.BytesLike
|
||||
): Promise<Uint8Array> {
|
||||
const messageEvents = await parseWormholeEventsFromReceipt(receipt);
|
||||
|
||||
// grab the sequence from the parsed message log
|
||||
if (messageEvents.length !== 1) {
|
||||
throw Error("more than one message found in log");
|
||||
}
|
||||
const sequence = messageEvents[0].args.sequence;
|
||||
|
||||
// fetch the signed VAA
|
||||
const result = await getSignedVAAWithRetry(
|
||||
WORMHOLE_RPCS,
|
||||
emitterChainId,
|
||||
getEmitterAddressEth(contractAddress),
|
||||
sequence.toString(),
|
||||
{
|
||||
transport: NodeHttpTransport(),
|
||||
}
|
||||
);
|
||||
return result.vaaBytes;
|
||||
}
|
||||
|
||||
export function parseDeliveryStatusVaa(payload: ethers.BytesLike): DeliveryStatus {
|
||||
// confirm that the payload is formatted correctly
|
||||
let index: number = 0;
|
||||
|
||||
// interface that we will parse the bytes into
|
||||
let deliveryStatus: DeliveryStatus = {} as DeliveryStatus;
|
||||
|
||||
// grab the payloadID = 2
|
||||
deliveryStatus.payloadId = parseInt(ethers.utils.hexDataSlice(payload, index, index + 1));
|
||||
index += 1;
|
||||
|
||||
// delivery batch hash
|
||||
deliveryStatus.batchHash = ethers.utils.hexDataSlice(payload, index, index + 32);
|
||||
index += 32;
|
||||
|
||||
// deliveryId emitter address
|
||||
deliveryStatus.emitterAddress = ethers.utils.hexDataSlice(payload, index, index + 32);
|
||||
index += 32;
|
||||
|
||||
// deliveryId sequence
|
||||
deliveryStatus.sequence = parseInt(ethers.utils.hexDataSlice(payload, index, index + 8));
|
||||
index += 8;
|
||||
|
||||
// delivery count
|
||||
deliveryStatus.deliveryCount = parseInt(ethers.utils.hexDataSlice(payload, index, index + 2));
|
||||
index += 2;
|
||||
|
||||
// grab the success boolean
|
||||
deliveryStatus.deliverySuccess = parseInt(ethers.utils.hexDataSlice(payload, index, index + 1));
|
||||
index += 1;
|
||||
|
||||
return deliveryStatus;
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { ethers } from "ethers";
|
||||
import {
|
||||
BSC_FORGE_BROADCAST,
|
||||
BSC_RPC,
|
||||
WORMHOLE_RPCS,
|
||||
ETH_FORGE_BROADCAST,
|
||||
ETH_RPC,
|
||||
DEPLOYER_PRIVATE_KEY,
|
||||
ZERO_ADDRESS_BYTES,
|
||||
TARGET_GAS_LIMIT,
|
||||
} from "../__tests__/helpers/consts";
|
||||
import { DeliveryStatus, RelayerArgs, TargetDeliveryParameters } from "../__tests__/helpers/structs";
|
||||
import {
|
||||
makeCoreRelayerFromForgeBroadcast,
|
||||
makeGasOracleFromForgeBroadcast,
|
||||
makeMockRelayerIntegrationFromForgeBroadcast,
|
||||
resolvePath,
|
||||
getSignedVaaFromReceiptOnEth,
|
||||
parseDeliveryStatusVaa,
|
||||
} from "../__tests__/helpers/utils";
|
||||
import { CHAIN_ID_BSC, CHAIN_ID_ETH, tryNativeToHexString, getSignedBatchVAAWithRetry } from "@certusone/wormhole-sdk";
|
||||
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
||||
|
||||
async function main() {
|
||||
const ethProvider = new ethers.providers.StaticJsonRpcProvider(ETH_RPC);
|
||||
const bscProvider = new ethers.providers.StaticJsonRpcProvider(BSC_RPC);
|
||||
|
||||
// core relayers
|
||||
const ethCoreRelayer = makeCoreRelayerFromForgeBroadcast(
|
||||
resolvePath(ETH_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, ethProvider)
|
||||
);
|
||||
|
||||
const bscCoreRelayer = makeCoreRelayerFromForgeBroadcast(
|
||||
resolvePath(BSC_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, bscProvider)
|
||||
);
|
||||
|
||||
// relayer integrators
|
||||
const ethRelayerIntegrator = makeMockRelayerIntegrationFromForgeBroadcast(
|
||||
resolvePath(ETH_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, ethProvider)
|
||||
);
|
||||
|
||||
const bscRelayerIntegrator = makeMockRelayerIntegrationFromForgeBroadcast(
|
||||
resolvePath(BSC_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, bscProvider)
|
||||
);
|
||||
|
||||
// gas oracles
|
||||
const ownedGasOracles = [
|
||||
// eth
|
||||
makeGasOracleFromForgeBroadcast(
|
||||
resolvePath(ETH_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, ethProvider)
|
||||
),
|
||||
// bsc
|
||||
makeGasOracleFromForgeBroadcast(
|
||||
resolvePath(BSC_FORGE_BROADCAST),
|
||||
new ethers.Wallet(DEPLOYER_PRIVATE_KEY, bscProvider)
|
||||
),
|
||||
];
|
||||
|
||||
// setup gas oracles and register if needed
|
||||
{
|
||||
const ethPrice = ethers.utils.parseUnits("2000.00", 8);
|
||||
const bscPrice = ethers.utils.parseUnits("400.00", 8);
|
||||
|
||||
// now fetch gas prices from each provider
|
||||
const gasPrices = await Promise.all(ownedGasOracles.map((oracle) => oracle.provider.getGasPrice()));
|
||||
|
||||
const updates = [
|
||||
{
|
||||
chainId: CHAIN_ID_ETH,
|
||||
gasPrice: gasPrices.at(0)!,
|
||||
nativeCurrencyPrice: ethPrice,
|
||||
},
|
||||
{
|
||||
chainId: CHAIN_ID_BSC,
|
||||
gasPrice: gasPrices.at(1)!,
|
||||
nativeCurrencyPrice: bscPrice,
|
||||
},
|
||||
];
|
||||
|
||||
const oracleTxs = await Promise.all(
|
||||
ownedGasOracles.map((oracle) => oracle.updatePrices(updates).then((tx: ethers.ContractTransaction) => tx.wait()))
|
||||
);
|
||||
|
||||
// query the core relayer contracts to see if relayers have been registered
|
||||
const registeredCoreRelayerOnBsc = await bscCoreRelayer.registeredRelayer(CHAIN_ID_ETH);
|
||||
const registeredCoreRelayerOnEth = await ethCoreRelayer.registeredRelayer(CHAIN_ID_BSC);
|
||||
|
||||
// register the core relayer contracts
|
||||
if (registeredCoreRelayerOnBsc == ZERO_ADDRESS_BYTES) {
|
||||
const bscRegistrationTx = await bscCoreRelayer.registerChain(
|
||||
CHAIN_ID_ETH,
|
||||
"0x" + tryNativeToHexString(ethCoreRelayer.address, CHAIN_ID_ETH)
|
||||
);
|
||||
await bscRegistrationTx.wait();
|
||||
}
|
||||
|
||||
if (registeredCoreRelayerOnEth == ZERO_ADDRESS_BYTES) {
|
||||
const ethRegistrationTx = await ethCoreRelayer.registerChain(
|
||||
CHAIN_ID_BSC,
|
||||
"0x" + tryNativeToHexString(bscCoreRelayer.address, CHAIN_ID_BSC)
|
||||
);
|
||||
await ethRegistrationTx.wait();
|
||||
}
|
||||
|
||||
// Query the mock relayer integration contracts to see if trusted mock relayer
|
||||
// integration contracts have been registered.
|
||||
const trustedSenderOnBsc = await bscRelayerIntegrator.trustedSender(CHAIN_ID_ETH);
|
||||
const trustedSenderOnEth = await ethRelayerIntegrator.trustedSender(CHAIN_ID_BSC);
|
||||
|
||||
// register the trusted mock relayer integration contracts
|
||||
if (trustedSenderOnBsc == ZERO_ADDRESS_BYTES) {
|
||||
const bscRegistrationTx = await bscRelayerIntegrator.registerTrustedSender(
|
||||
CHAIN_ID_ETH,
|
||||
"0x" + tryNativeToHexString(ethRelayerIntegrator.address, CHAIN_ID_ETH)
|
||||
);
|
||||
await bscRegistrationTx.wait();
|
||||
}
|
||||
|
||||
if (trustedSenderOnEth == ZERO_ADDRESS_BYTES) {
|
||||
const ethRegistrationTx = await ethRelayerIntegrator.registerTrustedSender(
|
||||
CHAIN_ID_BSC,
|
||||
"0x" + tryNativeToHexString(bscRelayerIntegrator.address, CHAIN_ID_BSC)
|
||||
);
|
||||
await ethRegistrationTx.wait();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// batch Vaa payloads to relay to the target contract
|
||||
let batchVaaPayloads: ethers.utils.BytesLike[] = [];
|
||||
|
||||
// REVIEW: these should be removed when the off-chain relayer is implemented
|
||||
let batchToBscReceipt: ethers.ContractReceipt;
|
||||
let targetDeliveryParamsOnBsc: TargetDeliveryParameters = {} as TargetDeliveryParameters;
|
||||
|
||||
{
|
||||
// estimate the relayer cost to relay a batch to BSC
|
||||
const estimatedGasCost: ethers.BigNumber = await ethRelayerIntegrator.estimateRelayCosts(
|
||||
CHAIN_ID_BSC,
|
||||
TARGET_GAS_LIMIT
|
||||
);
|
||||
|
||||
// create an array of messages to deliver to the BSC target contract
|
||||
batchVaaPayloads = [
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff0")),
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff1")),
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff2")),
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes("SuperCoolCrossChainStuff3")),
|
||||
];
|
||||
const batchVaaConsistencyLevels = [15, 15, 15, 15];
|
||||
|
||||
// create relayerArgs interface to call the mock integration contract with
|
||||
const relayerArgs: RelayerArgs = {
|
||||
nonce: 69,
|
||||
targetChainId: CHAIN_ID_BSC,
|
||||
targetAddress: bscRelayerIntegrator.address,
|
||||
targetGasLimit: TARGET_GAS_LIMIT,
|
||||
consistencyLevel: batchVaaConsistencyLevels[0],
|
||||
deliveryListIndices: [] as number[], // no indices specified for full batch delivery
|
||||
};
|
||||
|
||||
// call the mock integration contract and send the batch VAA
|
||||
const tx = await ethRelayerIntegrator.sendBatchToTargetChain(
|
||||
batchVaaPayloads,
|
||||
batchVaaConsistencyLevels,
|
||||
relayerArgs,
|
||||
{
|
||||
value: estimatedGasCost,
|
||||
}
|
||||
);
|
||||
batchToBscReceipt = await tx.wait();
|
||||
|
||||
console.log("emitterChain", CHAIN_ID_ETH, "emitterAddress", ethCoreRelayer.address);
|
||||
console.log("transaction", batchToBscReceipt.transactionHash);
|
||||
}
|
||||
|
||||
{
|
||||
// fetch the batch VAA with getSignedBatchVAAWithRetry
|
||||
const batchVaaRes = await getSignedBatchVAAWithRetry(
|
||||
WORMHOLE_RPCS,
|
||||
CHAIN_ID_ETH,
|
||||
batchToBscReceipt.transactionHash,
|
||||
{
|
||||
transport: NodeHttpTransport(),
|
||||
}
|
||||
);
|
||||
const batchVaaFromEth: ethers.utils.BytesLike = batchVaaRes.batchVaaBytes;
|
||||
console.log("vaa", Buffer.from(batchVaaFromEth as Uint8Array).toString("hex"));
|
||||
|
||||
// parse the batch VAA
|
||||
const parsedBatch = await ethRelayerIntegrator.parseBatchVM(batchVaaFromEth);
|
||||
console.log("parsed", parsedBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,13 +1,29 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2020"],
|
||||
"module": "CommonJS",
|
||||
"target": "es2020",
|
||||
"types": [
|
||||
"mocha",
|
||||
"chai"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
],
|
||||
"outDir": "lib",
|
||||
"esModuleInterop": true,
|
||||
"target": "esnext",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"declaration": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node"
|
||||
}
|
||||
}
|
||||
"downlevelIteration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
}
|
Loading…
Reference in New Issue