Skip to content

Commit 45f052f

Browse files
committed
setup-bridging-contracts can handle multiple remote chains
1 parent edd1cee commit 45f052f

File tree

6 files changed

+283
-157
lines changed

6 files changed

+283
-157
lines changed

hardhat.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ module.exports = {
184184
cancun: 0,
185185
},
186186
},
187+
265669101: {
188+
hardforkHistory: {
189+
cancun: 0,
190+
},
191+
},
187192
},
188193
},
189194
},

packages/wormhole-relayer/config.example.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ module.exports = {
55
[wormhole.CHAIN_ID_ARBITRUM_SEPOLIA]: {
66
endpoints: ["http://localhost:8545"],
77
colonyBridgeAddress: "0x633899227A3BC1f79de097149E1E3C8097c07b1a",
8+
payForGas: true,
9+
evmChainId: 265669100,
810
},
911
[wormhole.CHAIN_ID_SEPOLIA]: {
1012
endpoints: ["http://localhost:8546"],
1113
colonyBridgeAddress: "0x161944B5601a7d3004E20d4Ca823F710838Ea1be",
14+
payForGas: true,
15+
evmChainId: 265669101,
1216
},
1317
},
1418
};

packages/wormhole-relayer/index.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ import {
103103
const nonceManager = new NonceManager(wallet);
104104

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

108116

@@ -125,22 +133,35 @@ import {
125133
return next();
126134
}
127135
const hash = ctx.sourceTxHash;
128-
// console.log(vaa);
136+
const [destinationEvmChainId, destinationAddress, payload] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`0x${vaa.payload.toString('hex')}`);
137+
console.log('destinationEvmChainId', destinationEvmChainId);
138+
console.log('destinationAddress', destinationAddress);
139+
console.log('payload', payload);
140+
141+
129142
console.log(
130143
`Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`,
131144
);
132145

133-
let destinationBridge;
146+
const destinationChainConfig = Object.values(config.chains).find((c) => c.evmChainId === destinationEvmChainId.toNumber())
147+
if (!destinationChainConfig) {
148+
console.log('No destination chain config found for chain id', destinationEvmChainId.toNumber());
149+
return next();
150+
}
151+
152+
if (!destinationChainConfig.payForGas) {
153+
console.log('We do not pay for gas on destination chain. Skipping');
154+
return next();
155+
}
134156

135-
if (vaa.emitterChain === CHAIN_ID_ARBITRUM_SEPOLIA) {
136-
destinationBridge = colonyBridges[CHAIN_ID_SEPOLIA];
137-
} else if (vaa.emitterChain === CHAIN_ID_SEPOLIA) {
138-
destinationBridge = colonyBridges[CHAIN_ID_ARBITRUM_SEPOLIA];
139-
} else {
140-
console.log('Unknown chain', vaa.emitterChain);
157+
const destinationWormholeId = Object.keys(config.chains).find((wormholeChainId) => { return config.chains[wormholeChainId].evmChainId === destinationEvmChainId.toNumber() });
158+
if (!destinationWormholeId) {
159+
console.log('No wormhole chain id found for destination chain id', destinationEvmChainId.toNumber());
141160
return next();
142161
}
143162

163+
const destinationBridge = colonyBridges[destinationWormholeId];
164+
144165
try {
145166
// TODO: Explicit gas limit is a nod to tests...
146167
const tx = await destinationBridge.receiveMessage(ctx.vaaBytes, { gasLimit: 1000000 });

scripts/mockGuardianSpy.ts

Lines changed: 99 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { EmitterFilter, FilterEntry, SpyRPCServiceService, SubscribeSignedVAARes
1717

1818

1919
const ethers = require("ethers");
20+
import { Contract } from "ethers";
2021

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

3233
homeRpc: string;
33-
foreignRpc: string;
34+
foreignRpcs: string[];
3435
homeBridgeAddress: string;
35-
foreignBridgeAddress: string;
36+
foreignBridgeAddresses: string[];
3637
homeColonyBridgeAddress: string;
37-
foreignColonyBridgeAddress: string;
38+
foreignColonyBridgeAddresses: string[];
3839
homeBridge: any;
39-
foreignBridge: any;
40+
foreignBridges: Contract[];
4041
homeWormholeBridgeForColony: any;
41-
foreignWormholeBridgeForColony: any;
42+
foreignWormholeBridgesForColony: Contract[];
4243
skipCount: number = 0;
4344
queue: any[] = [];
4445
skipped: any[] = [];
4546
locked: boolean = false;
4647
bridgingPromiseCount: number= 0;
4748
resolveBridgingPromise: any;
4849
signerHome: any;
49-
signerForeign: any;
50+
// signerForeign: any;
5051
server: Server;
5152
subscription: any;
5253
subscriptionFilters: FilterEntry[] = [];
@@ -63,13 +64,13 @@ class MockBridgeMonitor {
6364
* @param {string} homeColonyBridgeAddress The address of the home colony bridge contract
6465
* @param {string} foreignColonyBridgeAddress The address of the foreign colony bridge contract
6566
*/
66-
constructor(homeRpc: string, foreignRpc: string, homeBridgeAddress: string, foreignBridgeAddress: string, homeColonyBridgeAddress: string, foreignColonyBridgeAddress: string) {
67+
constructor(homeRpc: string, foreignRpcs: string[], homeBridgeAddress: string, foreignBridgeAddresses: string[], homeColonyBridgeAddress: string, foreignColonyBridgeAddresses: string[]) {
6768
this.homeRpc = homeRpc;
68-
this.foreignRpc = foreignRpc;
69+
this.foreignRpcs = foreignRpcs;
6970
this.homeBridgeAddress = homeBridgeAddress;
70-
this.foreignBridgeAddress = foreignBridgeAddress;
71+
this.foreignBridgeAddresses = foreignBridgeAddresses;
7172
this.homeColonyBridgeAddress = homeColonyBridgeAddress;
72-
this.foreignColonyBridgeAddress = foreignColonyBridgeAddress;
73+
this.foreignColonyBridgeAddresses = foreignColonyBridgeAddresses;
7374

7475
this.setupListeners();
7576

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

172+
setupForeignBridges(foreignRpc, foreignBridgeAddress, foreignColonyBridgeAddress) {
173+
const signerForeign = new RetryProvider(foreignRpc).getSigner();
174+
const foreignBridge = new ethers.Contract(foreignBridgeAddress, bridgeAbi, signerForeign);
175+
const foreignWormholeBridgeForColony = new ethers.Contract(foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, signerForeign);
176+
177+
this.foreignBridges.push(foreignBridge);
178+
this.foreignWormholeBridgesForColony.push(foreignWormholeBridgeForColony);
179+
}
180+
181+
182+
async getColonyBridgeWithChainId(chainId) {
183+
if ((await this.homeBridge.provider.getNetwork()).chainId === chainId) {
184+
return this.homeBridge;
185+
}
186+
for (const foreignBridge of this.foreignWormholeBridgesForColony) {
187+
if ((await foreignBridge.provider.getNetwork()).chainId === chainId) {
188+
return foreignBridge;
189+
}
190+
}
191+
throw new Error("No bridge found for chainId");
192+
};
193+
194+
getWormholeChainId(chainId) {
195+
// Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids.
196+
// So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia)
197+
// and for chainId 256669101, we use 10002 (which is really sepolia).
198+
// This isn't ideal, but it's the best solution I have for now
199+
if (chainId === 265669100) {
200+
return 10003;
201+
} else if (chainId === 265669101) {
202+
return 10002;
203+
} else if (chainId === 265669102) {
204+
return 10005;
205+
}
206+
throw new Error("Unsupported chainId");
207+
}
208+
171209
setupListeners() {
172210
if (this.homeBridge) {
173211
this.homeBridge.removeAllListeners("LogMessagePublished");
174212
}
175-
if (this.foreignBridge) {
176-
this.foreignBridge.removeAllListeners("LogMessagePublished");
213+
if (this.foreignBridges && this.foreignBridges.length > 0) {
214+
for (const bridge of this.foreignBridges) {
215+
bridge.removeAllListeners("LogMessagePublished");
216+
}
177217
}
178218

219+
this.foreignBridges = [];
220+
this.foreignWormholeBridgesForColony = [];
221+
179222
this.signerHome = new RetryProvider(this.homeRpc).getSigner();
180-
this.signerForeign = new RetryProvider(this.foreignRpc).getSigner();
223+
// this.signerForeign = new RetryProvider(this.foreignRpc).getSigner();
181224
this.homeBridge = new ethers.Contract(this.homeBridgeAddress, bridgeAbi, this.signerHome);
182-
this.foreignBridge = new ethers.Contract(this.foreignBridgeAddress, bridgeAbi, this.signerForeign);
225+
// this.foreignBridge = new ethers.Contract(this.foreignBridgeAddress, bridgeAbi, this.signerForeign);
183226
this.homeWormholeBridgeForColony = new ethers.Contract(this.homeColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerHome);
184-
this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign);
185-
227+
// this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign);
228+
for (let i = 0; i < this.foreignRpcs.length; i++) {
229+
this.setupForeignBridges(this.foreignRpcs[i], this.foreignBridgeAddresses[i], this.foreignColonyBridgeAddresses[i]);
230+
}
186231
this.skipCount = 0;
187232

188233
this.queue = [];
189234
this.skipped = [];
190235
this.locked = false;
191236
this.homeBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => {
192-
const { chainId } = await this.signerHome.provider.getNetwork();
193-
// Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids.
194-
// So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia)
195-
// and for chainId 256669101, we use 10002 (which is really sepolia).
196-
// This isn't ideal, but it's the best solution I have for now
197-
let wormholeChainId;
198-
if (chainId === 265669100) {
199-
wormholeChainId = 10003;
200-
} else if (chainId === 265669101) {
201-
wormholeChainId = 10002;
202-
} else {
203-
throw new Error("Unsupported chainId");
204-
}
237+
try {
238+
const { chainId } = await this.signerHome.provider.getNetwork();
239+
const [destinationEvmChainId, destinationAddress, data] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`${payload.toString('hex')}`);
240+
241+
const destinationBridge = await this.getColonyBridgeWithChainId(destinationEvmChainId.toNumber());
242+
const wormholeChainId = this.getWormholeChainId(chainId);
205243

206-
if (this.skipCount > 0) {
207-
this.skipped.push([this.foreignWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
208-
this.skipCount -= 1;
209-
return;
244+
if (this.skipCount > 0) {
245+
this.skipped.push([destinationBridge, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
246+
this.skipCount -= 1;
247+
return;
248+
}
249+
this.queue.push([destinationBridge, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
250+
await this.processQueue();
251+
} catch (e) {
252+
console.log("Error in LogMessagePublished listener");
253+
console.log(e);
210254
}
211-
this.queue.push([this.foreignWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
212-
await this.processQueue();
213255
});
214256

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

230-
if (this.skipCount > 0) {
231-
this.skipped.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
232-
this.skipCount -= 1;
233-
return;
234-
}
235-
this.queue.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
262+
if (destinationEvmChainId.toNumber() !== 265669100) {
263+
throw new Error("Unsupported chainId - change assumptions in mockGuardianSpy.ts");
264+
}
236265

237-
await this.processQueue();
238-
});
266+
const wormholeChainId = this.getWormholeChainId(chainId);
267+
268+
if (this.skipCount > 0) {
269+
this.skipped.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
270+
this.skipCount -= 1;
271+
return;
272+
}
273+
this.queue.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]);
274+
275+
await this.processQueue();
276+
});
277+
}
239278

240279
console.log("Mock Bridge Monitor running");
241280
console.log("Home bridge address: ", this.homeBridgeAddress);
242-
console.log("Foreign bridge address: ", this.foreignBridgeAddress);
281+
console.log("Foreign bridge addresses: ", this.foreignBridgeAddresses);
243282
}
244283

245284
close() {} // eslint-disable-line class-methods-use-this
@@ -277,10 +316,11 @@ class MockBridgeMonitor {
277316
}
278317

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

282323
this.bridgingPromiseCount -= 1;
283-
284324
if (this.bridgingPromiseCount === 0) {
285325
const receipt = await bridge.provider.getTransactionReceipt(tx.hash);
286326
this.resolveBridgingPromise(receipt);

0 commit comments

Comments
 (0)