Skip to content

Commit bdca974

Browse files
authored
SDK: Add Routes and Examples (#413)
1 parent b7d8c09 commit bdca974

35 files changed

+2696
-457
lines changed

Tiltfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ load('ext://namespace', 'namespace_create', 'namespace_inject')
22
load('ext://git_resource', 'git_checkout')
33

44
git_checkout('https://github.com/wormhole-foundation/wormhole.git#main', '.wormhole/', unsafe_mode=True)
5-
local(['sed','-i','/{chainId: vaa.ChainIDEthereum, addr: "0000000000000000000000006f84742680311cef5ba42bc10a71a4708b4561d1"},/i \\\\t\\t\\t{chainId: vaa.ChainIDSolana, addr: "8bf0b547c96edc5c1d512ca25c5c1d1812a180438a0046e511d1fb61561d5cdf"},\\n\\t\\t\\t{chainId: vaa.ChainIDSolana, addr: "0a490691c21334ca173d9ce386e2a86774ce173f351db10d5d0cccc5c4875376"},\\n\\t\\t\\t{chainId: vaa.ChainIDEthereum, addr: "000000000000000000000000c5afe31ae505594b190ac71ea689b58139d1c354"},\\n\\t\\t\\t{chainId: vaa.ChainIDEthereum, addr: "0000000000000000000000008be8dfcdc90f50562b5022de1f8d83fe93b0b055"},\\n\\t\\t\\t{chainId: vaa.ChainIDBSC, addr: "0000000000000000000000006f84742680311cef5ba42bc10a71a4708b4561d1"},\\n\\t\\t\\t{chainId: vaa.ChainIDBSC, addr: "00000000000000000000000071e7ec880873af0fe33ad988f862be200fdd85cc"},', '.wormhole/node/pkg/accountant/ntt_config.go'])
5+
local(['sed','-i','/{chainId: vaa.ChainIDEthereum, addr: "000000000000000000000000855FA758c77D68a04990E992aA4dcdeF899F654A"},/i {chainId: vaa.ChainIDSolana, addr: "8bf0b547c96edc5c1d512ca25c5c1d1812a180438a0046e511d1fb61561d5cdf"},{chainId: vaa.ChainIDSolana, addr: "0a490691c21334ca173d9ce386e2a86774ce173f351db10d5d0cccc5c4875376"},{chainId: vaa.ChainIDEthereum, addr: "0000000000000000000000006f84742680311cef5ba42bc10a71a4708b4561d1"},{chainId: vaa.ChainIDEthereum, addr: "000000000000000000000000c3ef4965b788cc4b905084d01f2eb7d4b6e93abf"},{chainId: vaa.ChainIDBSC, addr: "0000000000000000000000006f84742680311cef5ba42bc10a71a4708b4561d1"},{chainId: vaa.ChainIDBSC, addr: "0000000000000000000000003f4e941ef5071a1d09c2eb4a24da1fc43f76fcff"},', '.wormhole/node/pkg/accountant/ntt_config.go'])
66

77
load(".wormhole/Tiltfile", "namespace", "k8s_yaml_with_ns")
88

sdk/__tests__/index.test.ts

+29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Chain } from "@wormhole-foundation/sdk";
22
import { deploy, link, transferWithChecks, wh } from "./utils.js";
3+
import { submitAccountantVAA } from "./accountant.js";
34

45
// Note: Currently, in order for this to run, the evm bindings with extra contracts must be build
56
// To do that, at the root, run `npm run generate:test`
@@ -10,7 +11,35 @@ const cases = [
1011
// ["Bsc", ["Ethereum", "Solana"]],
1112
];
1213

14+
async function registerRelayers() {
15+
try {
16+
await submitAccountantVAA(
17+
Buffer.from(
18+
"010000000001006c9967aee739944b30ffcc01653f2030ea02c038adda26a8f5a790f191134dff1e1e48368af121a34806806140d4f56ec09e25067006e69c95b0c4c08b8897990000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000001ce9cf010000000000000000000000000000000000576f726d686f6c6552656c61796572010000000200000000000000000000000053855d4b64e9a3cf59a84bc768ada716b5536bc5",
19+
"hex"
20+
)
21+
);
22+
} catch (e) {
23+
console.log(e);
24+
}
25+
26+
try {
27+
await submitAccountantVAA(
28+
Buffer.from(
29+
"01000000000100894be2c33626547e665cee73684854fbd8fc2eb79ec9ad724b1fb10d6cd24aaa590393870e6655697cd69d5553881ac8519e1282e7d3ae5fc26d7452d097651c00000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000445fb0b010000000000000000000000000000000000576f726d686f6c6552656c61796572010000000400000000000000000000000053855d4b64e9a3cf59a84bc768ada716b5536bc5",
30+
"hex"
31+
)
32+
);
33+
} catch (e) {
34+
console.log(e);
35+
}
36+
}
37+
1338
describe("Hub and Spoke Tests", function () {
39+
beforeAll(async () => {
40+
await registerRelayers();
41+
});
42+
1443
test.each(cases)("Test %s Hub", async (source, destinations) => {
1544
// Get chain context objects
1645
const hubChain = wh.getChain(source as Chain);

sdk/__tests__/utils.ts

+106-75
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import {
55
ChainAddress,
66
ChainContext,
77
NativeSigner,
8+
Platform,
89
Signer,
910
VAA,
1011
Wormhole,
1112
WormholeMessageId,
1213
amount,
1314
chainToPlatform,
1415
encoding,
16+
keccak256,
1517
serialize,
1618
signSendWait as ssw,
1719
toChainId,
@@ -24,6 +26,7 @@ import { ethers } from "ethers";
2426
import { DummyTokenMintAndBurn__factory } from "../evm/ethers-ci-contracts/factories/DummyToken.sol/DummyTokenMintAndBurn__factory.js";
2527
import { DummyToken__factory } from "../evm/ethers-ci-contracts/factories/DummyToken.sol/DummyToken__factory.js";
2628
import { ERC1967Proxy__factory } from "../evm/ethers-ci-contracts/factories/ERC1967Proxy__factory.js";
29+
import { IWormholeRelayer__factory } from "../evm/ethers-ci-contracts/factories/IWormholeRelayer.sol/IWormholeRelayer__factory.js";
2730
import { NttManager__factory } from "../evm/ethers-ci-contracts/factories/NttManager__factory.js";
2831
import { TransceiverStructs__factory } from "../evm/ethers-ci-contracts/factories/TransceiverStructs__factory.js";
2932
import { TrimmedAmountLib__factory } from "../evm/ethers-ci-contracts/factories/TrimmedAmount.sol/TrimmedAmountLib__factory.js";
@@ -44,14 +47,21 @@ export const NETWORK: "Devnet" = "Devnet";
4447

4548
const ETH_PRIVATE_KEY =
4649
"0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"; // Ganache default private key
50+
4751
const SOL_PRIVATE_KEY = web3.Keypair.fromSecretKey(
4852
new Uint8Array(solanaTiltKey)
4953
);
5054

51-
interface Signers {
55+
type NativeSdkSigner<P extends Platform> = P extends "Evm"
56+
? ethers.Wallet
57+
: P extends "Solana"
58+
? web3.Keypair
59+
: never;
60+
61+
interface Signers<P extends Platform = Platform> {
5262
address: ChainAddress;
5363
signer: Signer;
54-
nativeSigner: any;
64+
nativeSigner: NativeSdkSigner<P>;
5565
}
5666

5767
interface StartingCtx {
@@ -98,9 +108,20 @@ export async function link(chainInfos: Ctx[]) {
98108

99109
// first submit hub init to accountant
100110
const hub = chainInfos[0]!;
101-
await submitAccountantVAA(serialize(await getVaa(hub, 0n)));
102111
const hubChain = hub.context.chain;
103112

113+
const msgId: WormholeMessageId = {
114+
chain: hubChain,
115+
emitter: Wormhole.chainAddress(
116+
hubChain,
117+
hub.contracts!.transceiver.wormhole
118+
).address.toUniversalAddress(),
119+
sequence: 0n,
120+
};
121+
122+
const vaa = await wh.getVaa(msgId, "Ntt:TransceiverInfo");
123+
await submitAccountantVAA(serialize(vaa!));
124+
104125
// [target, peer, vaa]
105126
const registrations: [string, string, VAA<"Ntt:TransceiverRegistration">][] =
106127
[];
@@ -172,32 +193,7 @@ export async function link(chainInfos: Ctx[]) {
172193
}
173194
}
174195

175-
async function getVaa(ctx: Ctx, sequence: bigint): Promise<VAA> {
176-
const chain = ctx.context.chain;
177-
const msgId = {
178-
chain,
179-
emitter: Wormhole.chainAddress(
180-
chain,
181-
ctx.contracts!.transceiver.wormhole
182-
).address.toUniversalAddress(),
183-
sequence,
184-
};
185-
186-
const vaa = await wh.getVaa(
187-
msgId,
188-
sequence === 0n ? "Ntt:TransceiverInfo" : "Ntt:TransceiverRegistration"
189-
);
190-
if (!vaa)
191-
throw new Error(`Failed to get VAA for: ${msgId.chain}: ${msgId.sequence}`);
192-
193-
return vaa;
194-
}
195-
196-
export async function transferWithChecks(
197-
sourceCtx: Ctx,
198-
destinationCtx: Ctx,
199-
useRelayer: boolean = false
200-
) {
196+
export async function transferWithChecks(sourceCtx: Ctx, destinationCtx: Ctx) {
201197
const sendAmt = "0.01";
202198

203199
const srcAmt = amount.units(
@@ -221,9 +217,17 @@ export async function transferWithChecks(
221217
dstSigner.address()
222218
);
223219

220+
const useRelayer =
221+
chainToPlatform(sourceCtx.context.chain) === "Evm" &&
222+
chainToPlatform(destinationCtx.context.chain) === "Evm";
223+
224224
console.log("Calling transfer on: ", sourceCtx.context.chain);
225225
const srcNtt = await getNtt(sourceCtx);
226-
const transferTxs = srcNtt.transfer(sender.address, srcAmt, receiver, false);
226+
const transferTxs = srcNtt.transfer(sender.address, srcAmt, receiver, {
227+
queue: false,
228+
automatic: useRelayer,
229+
gasDropoff: 0n,
230+
});
227231
const txids = await signSendWait(sourceCtx.context, transferTxs, srcSigner);
228232

229233
const srcCore = await sourceCtx.context.getWormholeCore();
@@ -232,6 +236,7 @@ export async function transferWithChecks(
232236
)[0]!;
233237

234238
if (!useRelayer) await receive(msgId, destinationCtx);
239+
else await waitForRelay(msgId, destinationCtx);
235240

236241
const [managerBalanceAfterSend, userBalanceAfterSend] =
237242
await getManagerAndUserBalance(sourceCtx);
@@ -253,6 +258,38 @@ export async function transferWithChecks(
253258
);
254259
}
255260

261+
async function waitForRelay(
262+
msgId: WormholeMessageId,
263+
dst: Ctx,
264+
retryTime: number = 2000
265+
) {
266+
console.log("Sleeping for 1 min to allow signing of VAA");
267+
await new Promise((resolve) => setTimeout(resolve, 60 * 1000));
268+
269+
// long timeout because the relayer has consistency level set to 15
270+
const vaa = await wh.getVaa(msgId, "Uint8Array", 2 * 60 * 1000);
271+
const deliveryHash = keccak256(vaa!.hash);
272+
273+
const wormholeRelayer = IWormholeRelayer__factory.connect(
274+
dst.context.config.contracts.relayer!,
275+
await dst.context.getRpc()
276+
);
277+
278+
let success = false;
279+
while (!success) {
280+
try {
281+
const successBlock = await wormholeRelayer.deliverySuccessBlock(
282+
deliveryHash
283+
);
284+
if (successBlock > 0) success = true;
285+
console.log("Relayer delivery: ", success);
286+
} catch (e) {
287+
console.error(e);
288+
}
289+
await new Promise((resolve) => setTimeout(resolve, retryTime));
290+
}
291+
}
292+
256293
// Wrap signSendWait from sdk to provide full error message
257294
async function signSendWait(
258295
ctx: ChainContext<typeof NETWORK>,
@@ -270,17 +307,7 @@ async function signSendWait(
270307
async function getNtt(
271308
ctx: Ctx
272309
): Promise<Ntt<typeof NETWORK, typeof ctx.context.chain>> {
273-
const ctor = ctx.context.platform.getProtocolInitializer("Ntt");
274-
// @ts-ignore
275-
return new ctor(
276-
ctx.context.network,
277-
ctx.context.chain,
278-
await ctx.context.getRpc(),
279-
{
280-
...ctx.context.config.contracts!,
281-
ntt: ctx.contracts,
282-
}
283-
);
310+
return ctx.context.getProtocol("Ntt", { ntt: ctx.contracts });
284311
}
285312

286313
function getNativeSigner(ctx: Partial<Ctx>): any {
@@ -304,7 +331,7 @@ async function getSigners(ctx: Partial<Ctx>): Promise<Signers> {
304331
switch (platform) {
305332
case "Evm":
306333
signer = await evm.getSigner(rpc, nativeSigner);
307-
nativeSigner = (signer as NativeSigner).unwrap() as ethers.Wallet;
334+
nativeSigner = (signer as NativeSigner).unwrap();
308335
break;
309336
case "Solana":
310337
signer = await solana.getSigner(rpc, nativeSigner);
@@ -323,7 +350,7 @@ async function getSigners(ctx: Partial<Ctx>): Promise<Signers> {
323350
}
324351

325352
async function deployEvm(ctx: Ctx): Promise<Ctx> {
326-
const { signer, nativeSigner: wallet } = ctx.signers;
353+
const { signer, nativeSigner: wallet } = ctx.signers as Signers<"Evm">;
327354

328355
// Deploy libraries used by various things
329356
console.log("Deploying transceiverStructs");
@@ -412,14 +439,15 @@ async function deployEvm(ctx: Ctx): Promise<Ctx> {
412439
// // Setup with the proxy
413440
console.log("Deploy transceiver proxy");
414441
const transceiverProxyFactory = new ERC1967Proxy__factory(wallet);
415-
const transceiverProxyAddress = await transceiverProxyFactory.deploy(
442+
const transceiverProxyDeployment = await transceiverProxyFactory.deploy(
416443
await WormholeTransceiverAddress.getAddress(),
417444
"0x"
418445
);
419-
await transceiverProxyAddress.waitForDeployment();
446+
await transceiverProxyDeployment.waitForDeployment();
420447

448+
const transceiverProxyAddress = await transceiverProxyDeployment.getAddress();
421449
const transceiver = WormholeTransceiver__factory.connect(
422-
await transceiverProxyAddress.getAddress(),
450+
transceiverProxyAddress,
423451
wallet
424452
);
425453

@@ -431,9 +459,7 @@ async function deployEvm(ctx: Ctx): Promise<Ctx> {
431459

432460
// Setup the initial calls, like transceivers for the manager
433461
console.log("Set transceiver for manager");
434-
await tryAndWaitThrice(() =>
435-
transceiver.getAddress().then((addr) => manager.setTransceiver(addr))
436-
);
462+
await tryAndWaitThrice(() => manager.setTransceiver(transceiverProxyAddress));
437463

438464
console.log("Set outbound limit for manager");
439465
await tryAndWaitThrice(() =>
@@ -444,7 +470,7 @@ async function deployEvm(ctx: Ctx): Promise<Ctx> {
444470
...ctx,
445471
contracts: {
446472
transceiver: {
447-
wormhole: await transceiverProxyAddress.getAddress(),
473+
wormhole: transceiverProxyAddress,
448474
},
449475
manager: await managerProxyAddress.getAddress(),
450476
token: ERC20NTTAddress,
@@ -453,7 +479,7 @@ async function deployEvm(ctx: Ctx): Promise<Ctx> {
453479
}
454480

455481
async function deploySolana(ctx: Ctx): Promise<Ctx> {
456-
const { signer, nativeSigner: keypair } = ctx.signers;
482+
const { signer, nativeSigner: keypair } = ctx.signers as Signers<"Solana">;
457483
const connection = (await ctx.context.getRpc()) as Connection;
458484
const address = new PublicKey(signer.address());
459485
console.log(`Using public key: ${address}`);
@@ -543,55 +569,59 @@ async function deploySolana(ctx: Ctx): Promise<Ctx> {
543569
async function setupPeer(targetCtx: Ctx, peerCtx: Ctx) {
544570
const target = targetCtx.context;
545571
const peer = peerCtx.context;
546-
547-
console.log(targetCtx.contracts);
548-
console.log(peerCtx.contracts);
549-
550572
const {
551573
manager,
552574
transceiver: { wormhole: transceiver },
553575
} = peerCtx.contracts!;
554576

555-
const managerAddress = Wormhole.chainAddress(peer.chain, manager);
556-
const transceiverEmitter = Wormhole.chainAddress(peer.chain, transceiver);
577+
const peerManager = Wormhole.chainAddress(peer.chain, manager);
578+
const peerTransceiver = Wormhole.chainAddress(peer.chain, transceiver);
557579

558580
const tokenDecimals = target.config.nativeTokenDecimals;
559581
const inboundLimit = amount.units(amount.parse("1000", tokenDecimals));
560582

561583
const { signer, address: sender } = targetCtx.signers;
562584

563-
// TODO:
564-
// const expectedXcvrAddress = manager.pdas.transceiverPeerAccount(peer.chain);
565-
// console.log("Check me", expectedXcvrAddress);
566-
// const expectedMgrAddress = manager.pdas.peerAccount(peer.chain);
567-
// console.log("Check me", expectedMgrAddress);
568-
569585
const nttManager = await getNtt(targetCtx);
570586
const setPeerTxs = nttManager.setPeer(
571-
managerAddress,
587+
peerManager,
572588
tokenDecimals,
573589
inboundLimit,
574590
sender.address
575591
);
576592
await signSendWait(target, setPeerTxs, signer);
577593

578594
const setXcvrPeerTxs = nttManager.setWormholeTransceiverPeer(
579-
transceiverEmitter,
595+
peerTransceiver,
580596
sender.address
581597
);
582598
const xcvrPeerTxids = await signSendWait(target, setXcvrPeerTxs, signer);
583599
const [whm] = await target.parseTransaction(xcvrPeerTxids[0]!.txid);
584-
return await wh.getVaa(whm!, "Ntt:TransceiverRegistration");
600+
console.log("Set peers for: ", target.chain, peer.chain);
585601

586-
// TODO:
587-
// if (targetPlatform === "Evm" && peerPlatform === "Evm") {
588-
// await tryAndWaitThrice(() =>
589-
// transceiver.setIsWormholeEvmChain(peerChainId, true)
590-
// );
591-
// await tryAndWaitThrice(() =>
592-
// transceiver.setIsWormholeRelayingEnabled(peerChainId, true)
593-
// );
594-
// }
602+
if (
603+
chainToPlatform(target.chain) === "Evm" &&
604+
chainToPlatform(peer.chain) === "Evm"
605+
) {
606+
const nativeSigner = (signer as NativeSigner).unwrap();
607+
const xcvr = WormholeTransceiver__factory.connect(
608+
targetCtx.contracts!.transceiver.wormhole,
609+
nativeSigner.signer
610+
);
611+
const peerChainId = toChainId(peer.chain);
612+
613+
console.log("Setting isEvmChain for: ", peer.chain);
614+
await tryAndWaitThrice(() =>
615+
xcvr.setIsWormholeEvmChain.send(peerChainId, true)
616+
);
617+
618+
console.log("Setting wormhole relaying for: ", peer.chain);
619+
await tryAndWaitThrice(() =>
620+
xcvr.setIsWormholeRelayingEnabled.send(peerChainId, true)
621+
);
622+
}
623+
624+
return await wh.getVaa(whm!, "Ntt:TransceiverRegistration");
595625
}
596626

597627
async function receive(msgId: WormholeMessageId, destination: Ctx) {
@@ -665,6 +695,7 @@ async function tryAndWaitThrice(
665695
try {
666696
return await (await txGen()).wait();
667697
} catch (e) {
698+
console.error(e);
668699
attempts++;
669700
if (attempts < 3) {
670701
console.log(`retry ${attempts}...`);

0 commit comments

Comments
 (0)