Skip to content

Commit

Permalink
setup-bridging-contracts can handle multiple remote chains
Browse files Browse the repository at this point in the history
  • Loading branch information
area committed Aug 12, 2024
1 parent edd1cee commit 55439f8
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 156 deletions.
5 changes: 5 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ module.exports = {
cancun: 0,
},
},
265669101: {
hardforkHistory: {
cancun: 0,
},
},
},
},
},
Expand Down
4 changes: 4 additions & 0 deletions packages/wormhole-relayer/config.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ module.exports = {
[wormhole.CHAIN_ID_ARBITRUM_SEPOLIA]: {
endpoints: ["http://localhost:8545"],
colonyBridgeAddress: "0x633899227A3BC1f79de097149E1E3C8097c07b1a",
payForGas: true,
evmChainId: 265669100,
},
[wormhole.CHAIN_ID_SEPOLIA]: {
endpoints: ["http://localhost:8546"],
colonyBridgeAddress: "0x161944B5601a7d3004E20d4Ca823F710838Ea1be",
payForGas: true,
evmChainId: 265669101,
},
},
};
37 changes: 29 additions & 8 deletions packages/wormhole-relayer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ import {
const nonceManager = new NonceManager(wallet);

colonyBridges[chainId] = new ethers.Contract(colonyBridgeAddress, colonyBridgeContractDef.abi, nonceManager);
const networkDetails = await wallet.provider.getNetwork();
console.log('networkDetails', networkDetails)
if (networkDetails.chainId !== config.chains[chainId].evmChainId) {
console.log('Network details do not match config for chain', chainId);
console.log('Got an evmChainId of', networkDetails.chainId, 'but expected', config.chains[chainId].evmChainId);
console.log('Exiting');
process.exit(1);
}
}


Expand All @@ -125,22 +133,35 @@ import {
return next();
}
const hash = ctx.sourceTxHash;
// console.log(vaa);
const [destinationEvmChainId, destinationAddress, payload] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`0x${vaa.payload.toString('hex')}`);
console.log('destinationEvmChainId', destinationEvmChainId);
console.log('destinationAddress', destinationAddress);
console.log('payload', payload);


console.log(
`Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`,
);

let destinationBridge;
const destinationChainConfig = Object.values(config.chains).find((c) => c.evmChainId === destinationEvmChainId.toNumber())
if (!destinationChainConfig) {
console.log('No destination chain config found for chain id', destinationEvmChainId.toNumber());
return next();
}

if (!destinationChainConfig.payForGas) {
console.log('We do not pay for gas on destination chain. Skipping');
return next();
}

if (vaa.emitterChain === CHAIN_ID_ARBITRUM_SEPOLIA) {
destinationBridge = colonyBridges[CHAIN_ID_SEPOLIA];
} else if (vaa.emitterChain === CHAIN_ID_SEPOLIA) {
destinationBridge = colonyBridges[CHAIN_ID_ARBITRUM_SEPOLIA];
} else {
console.log('Unknown chain', vaa.emitterChain);
const destinationWormholeId = Object.keys(config.chains).find((wormholeChainId) => { return config.chains[wormholeChainId].evmChainId === destinationEvmChainId.toNumber() });
if (!destinationWormholeId) {
console.log('No wormhole chain id found for destination chain id', destinationEvmChainId.toNumber());
return next();
}

const destinationBridge = colonyBridges[destinationWormholeId];

try {
// TODO: Explicit gas limit is a nod to tests...
const tx = await destinationBridge.receiveMessage(ctx.vaaBytes, { gasLimit: 1000000 });
Expand Down
158 changes: 99 additions & 59 deletions scripts/mockGuardianSpy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { EmitterFilter, FilterEntry, SpyRPCServiceService, SubscribeSignedVAARes


const ethers = require("ethers");
import { Contract } from "ethers";

// eslint-disable-next-line import/no-unresolved
const bridgeAbi = require("../artifacts/contracts/testHelpers/WormholeMock.sol/WormholeMock.json").abi;
Expand All @@ -30,23 +31,23 @@ function ethereumAddressToWormholeAddress(address: string) {
class MockBridgeMonitor {

homeRpc: string;
foreignRpc: string;
foreignRpcs: string[];
homeBridgeAddress: string;
foreignBridgeAddress: string;
foreignBridgeAddresses: string[];
homeColonyBridgeAddress: string;
foreignColonyBridgeAddress: string;
foreignColonyBridgeAddresses: string[];
homeBridge: any;
foreignBridge: any;
foreignBridges: Contract[];
homeWormholeBridgeForColony: any;
foreignWormholeBridgeForColony: any;
foreignWormholeBridgesForColony: Contract[];
skipCount: number = 0;
queue: any[] = [];
skipped: any[] = [];
locked: boolean = false;
bridgingPromiseCount: number= 0;
resolveBridgingPromise: any;
signerHome: any;
signerForeign: any;
// signerForeign: any;
server: Server;
subscription: any;
subscriptionFilters: FilterEntry[] = [];
Expand All @@ -63,13 +64,13 @@ class MockBridgeMonitor {
* @param {string} homeColonyBridgeAddress The address of the home colony bridge contract
* @param {string} foreignColonyBridgeAddress The address of the foreign colony bridge contract
*/
constructor(homeRpc: string, foreignRpc: string, homeBridgeAddress: string, foreignBridgeAddress: string, homeColonyBridgeAddress: string, foreignColonyBridgeAddress: string) {
constructor(homeRpc: string, foreignRpcs: string[], homeBridgeAddress: string, foreignBridgeAddresses: string[], homeColonyBridgeAddress: string, foreignColonyBridgeAddresses: string[]) {
this.homeRpc = homeRpc;
this.foreignRpc = foreignRpc;
this.foreignRpcs = foreignRpcs;
this.homeBridgeAddress = homeBridgeAddress;
this.foreignBridgeAddress = foreignBridgeAddress;
this.foreignBridgeAddresses = foreignBridgeAddresses;
this.homeColonyBridgeAddress = homeColonyBridgeAddress;
this.foreignColonyBridgeAddress = foreignColonyBridgeAddress;
this.foreignColonyBridgeAddresses = foreignColonyBridgeAddresses;

this.setupListeners();

Expand Down Expand Up @@ -168,78 +169,116 @@ class MockBridgeMonitor {
// return signatures.toString('hex').slice(2);
}

setupForeignBridges(foreignRpc, foreignBridgeAddress, foreignColonyBridgeAddress) {
const signerForeign = new RetryProvider(foreignRpc).getSigner();
const foreignBridge = new ethers.Contract(foreignBridgeAddress, bridgeAbi, signerForeign);
const foreignWormholeBridgeForColony = new ethers.Contract(foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, signerForeign);

this.foreignBridges.push(foreignBridge);
this.foreignWormholeBridgesForColony.push(foreignWormholeBridgeForColony);
}


async getColonyBridgeWithChainId(chainId) {
if ((await this.homeBridge.provider.getNetwork()).chainId === chainId) {
return this.homeBridge;
}
for (const foreignBridge of this.foreignWormholeBridgesForColony) {
if ((await foreignBridge.provider.getNetwork()).chainId === chainId) {
return foreignBridge;
}
}
throw new Error("No bridge found for chainId");
};

getWormholeChainId(chainId) {
// Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids.
// So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia)
// and for chainId 256669101, we use 10002 (which is really sepolia).
// This isn't ideal, but it's the best solution I have for now
if (chainId === 265669100) {
return 10003;
} else if (chainId === 265669101) {
return 10002;
} else if (chainId === 265669102) {
return 10005;
}
throw new Error("Unsupported chainId");
}

setupListeners() {
if (this.homeBridge) {
this.homeBridge.removeAllListeners("LogMessagePublished");
}
if (this.foreignBridge) {
this.foreignBridge.removeAllListeners("LogMessagePublished");
if (this.foreignBridges && this.foreignBridges.length > 0) {
for (const bridge of this.foreignBridges) {
bridge.removeAllListeners("LogMessagePublished");
}
}

this.foreignBridges = [];
this.foreignWormholeBridgesForColony = [];

this.signerHome = new RetryProvider(this.homeRpc).getSigner();
this.signerForeign = new RetryProvider(this.foreignRpc).getSigner();
// this.signerForeign = new RetryProvider(this.foreignRpc).getSigner();
this.homeBridge = new ethers.Contract(this.homeBridgeAddress, bridgeAbi, this.signerHome);
this.foreignBridge = new ethers.Contract(this.foreignBridgeAddress, bridgeAbi, this.signerForeign);
// this.foreignBridge = new ethers.Contract(this.foreignBridgeAddress, bridgeAbi, this.signerForeign);
this.homeWormholeBridgeForColony = new ethers.Contract(this.homeColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerHome);
this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign);

// this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign);
for (let i = 0; i < this.foreignRpcs.length; i++) {
this.setupForeignBridges(this.foreignRpcs[i], this.foreignBridgeAddresses[i], this.foreignColonyBridgeAddresses[i]);
}
this.skipCount = 0;

this.queue = [];
this.skipped = [];
this.locked = false;
this.homeBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => {
const { chainId } = await this.signerHome.provider.getNetwork();
// Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids.
// So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia)
// and for chainId 256669101, we use 10002 (which is really sepolia).
// This isn't ideal, but it's the best solution I have for now
let wormholeChainId;
if (chainId === 265669100) {
wormholeChainId = 10003;
} else if (chainId === 265669101) {
wormholeChainId = 10002;
} else {
throw new Error("Unsupported chainId");
}
try {
const { chainId } = await this.signerHome.provider.getNetwork();
const [destinationEvmChainId, destinationAddress, data] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`${payload.toString('hex')}`);

const destinationBridge = await this.getColonyBridgeWithChainId(destinationEvmChainId.toNumber());
const wormholeChainId = this.getWormholeChainId(chainId);

if (this.skipCount > 0) {
this.skipped.push([this.foreignWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
this.skipCount -= 1;
return;
if (this.skipCount > 0) {
this.skipped.push([destinationBridge, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
this.skipCount -= 1;
return;
}
this.queue.push([destinationBridge, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
await this.processQueue();
} catch (e) {
console.log("Error in LogMessagePublished listener");
console.log(e);
}
this.queue.push([this.foreignWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
await this.processQueue();
});

this.foreignBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => {
const { chainId } = await this.signerForeign.provider.getNetwork();
// Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids.
// So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia)
// and for chainId 256669101, we use 10002 (which is really sepolia).
// This isn't ideal, but it's the best solution I have for now
let wormholeChainId;
if (chainId === 265669100) {
wormholeChainId = 10003;
} else if (chainId === 265669101) {
wormholeChainId = 10002;
} else {
throw new Error("Unsupported chainId");
}
for (const foreignBridge of this.foreignBridges) {
foreignBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => {
const { chainId } = await foreignBridge.provider.getNetwork();
const [destinationEvmChainId, destinationAddress, data] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`${payload.toString('hex')}`);

if (this.skipCount > 0) {
this.skipped.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
this.skipCount -= 1;
return;
}
this.queue.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
if (destinationEvmChainId.toNumber() !== 265669100) {
throw new Error("Unsupported chainId - change assumptions in mockGuardianSpy.ts");
}

await this.processQueue();
});
const wormholeChainId = this.getWormholeChainId(chainId);

if (this.skipCount > 0) {
this.skipped.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
this.skipCount -= 1;
return;
}
this.queue.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);

await this.processQueue();
});
}

console.log("Mock Bridge Monitor running");
console.log("Home bridge address: ", this.homeBridgeAddress);
console.log("Foreign bridge address: ", this.foreignBridgeAddress);
console.log("Foreign bridge addresses: ", this.foreignBridgeAddresses);
}

close() {} // eslint-disable-line class-methods-use-this
Expand Down Expand Up @@ -277,10 +316,11 @@ class MockBridgeMonitor {
}

tx = await this.getTransactionFromAddressWithNonce(bridge.provider, this.relayerAddress, relayerNonce)
} else {
console.log('not sending, didnt pass filter');
}

this.bridgingPromiseCount -= 1;

if (this.bridgingPromiseCount === 0) {
const receipt = await bridge.provider.getTransactionReceipt(tx.hash);
this.resolveBridgingPromise(receipt);
Expand Down
Loading

0 comments on commit 55439f8

Please sign in to comment.