Skip to content

Commit aa72f14

Browse files
authored
sdk: Added resume transfer methods to routes (#487)
* sdk: Added resume transfer methods to routes * bump wormhole sdk to 0.9.0 * add eta to quote * bump wormhole sdk to 0.9.1 * fix test
1 parent a6fcaea commit aa72f14

File tree

11 files changed

+449
-554
lines changed

11 files changed

+449
-554
lines changed

evm/ts/package.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,19 @@
4444
"test": "jest --config ./jest.config.ts"
4545
},
4646
"dependencies": {
47-
"@wormhole-foundation/sdk-base": "^0.8",
48-
"@wormhole-foundation/sdk-definitions": "^0.8",
47+
"@wormhole-foundation/sdk-base": "^0.9.1",
48+
"@wormhole-foundation/sdk-definitions": "^0.9.1",
4949
"@wormhole-foundation/sdk-definitions-ntt": "0.1.0-beta.0",
50-
"@wormhole-foundation/sdk-evm": "^0.8",
51-
"@wormhole-foundation/sdk-evm-core": "^0.8",
50+
"@wormhole-foundation/sdk-evm": "^0.9.1",
51+
"@wormhole-foundation/sdk-evm-core": "^0.9.1",
5252
"ethers": "^6.5.1"
5353
},
5454
"peerDependencies": {
5555
"@wormhole-foundation/sdk-definitions-ntt": "0.1.0-beta.0",
56-
"@wormhole-foundation/sdk-base": "^0.8",
57-
"@wormhole-foundation/sdk-definitions": "^0.8",
58-
"@wormhole-foundation/sdk-evm": "^0.8",
59-
"@wormhole-foundation/sdk-evm-core": "^0.8"
56+
"@wormhole-foundation/sdk-base": "^0.9.1",
57+
"@wormhole-foundation/sdk-definitions": "^0.9.1",
58+
"@wormhole-foundation/sdk-evm": "^0.9.1",
59+
"@wormhole-foundation/sdk-evm-core": "^0.9.1"
6060
},
6161
"devDependencies": {
6262
"@typechain/ethers-v6": "^0.5.1",

package-lock.json

+221-529
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"version": "tsx setSdkVersion.ts"
1818
},
1919
"devDependencies": {
20-
"@wormhole-foundation/sdk": "^0.8",
20+
"@wormhole-foundation/sdk": "^0.9.1",
2121
"@solana/spl-token": "0.3.9",
2222
"@solana/web3.js": "1.91.7",
2323
"@types/jest": "^29.5.12",

sdk/definitions/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@
4949
"test": "jest --config ./jest.config.ts"
5050
},
5151
"dependencies": {
52-
"@wormhole-foundation/sdk-base": "^0.8",
53-
"@wormhole-foundation/sdk-definitions": "^0.8"
52+
"@wormhole-foundation/sdk-base": "^0.9.1",
53+
"@wormhole-foundation/sdk-definitions": "^0.9.1"
5454
},
5555
"peerDependencies": {
56-
"@wormhole-foundation/sdk-base": "^0.8",
57-
"@wormhole-foundation/sdk-definitions": "^0.8"
56+
"@wormhole-foundation/sdk-base": "^0.9.1",
57+
"@wormhole-foundation/sdk-definitions": "^0.9.1"
5858
},
5959
"type": "module"
6060
}

sdk/examples/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"tsx": "^4.7.2"
3333
},
3434
"dependencies": {
35-
"@wormhole-foundation/sdk": "^0.8",
35+
"@wormhole-foundation/sdk": "^0.9.1",
3636
"@wormhole-foundation/sdk-definitions-ntt": "0.1.0-beta.0",
3737
"@wormhole-foundation/sdk-evm-ntt": "0.1.0-beta.0",
3838
"@wormhole-foundation/sdk-solana-ntt": "0.1.0-beta.0",

sdk/route/__tests__/route.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ describe("Manual Route Tests", function () {
150150
"params",
151151
"sourceToken",
152152
"destinationToken",
153+
"eta",
153154
]);
154155
});
155156
});
@@ -249,6 +250,7 @@ describe("Automatic Route Tests", function () {
249250
"destinationToken",
250251
"relayFee",
251252
"destinationNativeGas",
253+
"eta",
252254
]);
253255
});
254256
});

sdk/route/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@
4848
"@wormhole-foundation/sdk-definitions-ntt": "0.1.0-beta.0",
4949
"@wormhole-foundation/sdk-solana-ntt": "0.1.0-beta.0",
5050
"@wormhole-foundation/sdk-evm-ntt": "0.1.0-beta.0",
51-
"@wormhole-foundation/sdk-connect": "^0.8"
51+
"@wormhole-foundation/sdk-connect": "^0.9.1"
5252
},
5353
"peerDependencies": {
54-
"@wormhole-foundation/sdk-connect": "^0.8",
54+
"@wormhole-foundation/sdk-connect": "^0.9.1",
5555
"@wormhole-foundation/sdk-definitions-ntt": "0.1.0-beta.0",
5656
"@wormhole-foundation/sdk-solana-ntt": "0.1.0-beta.0",
5757
"@wormhole-foundation/sdk-evm-ntt": "0.1.0-beta.0"
@@ -69,4 +69,4 @@
6969
}
7070
}
7171
}
72-
}
72+
}

sdk/route/src/automatic.ts

+85
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ import {
99
RedeemedTransferReceipt,
1010
Signer,
1111
TokenId,
12+
TransactionId,
1213
TransferState,
1314
Wormhole,
1415
WormholeMessageId,
1516
amount,
17+
canonicalAddress,
1618
chainToPlatform,
19+
finality,
1720
isAttested,
1821
isDestinationQueued,
1922
isRedeemed,
@@ -179,6 +182,7 @@ export class NttAutomaticRoute<N extends Network>
179182
params.normalizedParams.options.gasDropoff ?? 0n,
180183
toChain.config.nativeTokenDecimals
181184
),
185+
eta: finality.estimateFinalityTime(request.fromChain.chain),
182186
};
183187
const dstNtt = await toChain.getProtocol("Ntt", {
184188
ntt: params.normalizedParams.destinationContracts,
@@ -235,6 +239,87 @@ export class NttAutomaticRoute<N extends Network>
235239
};
236240
}
237241

242+
async resume(tx: TransactionId): Promise<R> {
243+
const vaa = await this.wh.getVaa(
244+
tx.txid,
245+
"Ntt:WormholeTransferStandardRelayer"
246+
);
247+
if (!vaa) throw new Error("No VAA found for transaction: " + tx.txid);
248+
249+
const msgId: WormholeMessageId = {
250+
chain: vaa.emitterChain,
251+
emitter: vaa.emitterAddress,
252+
sequence: vaa.sequence,
253+
};
254+
255+
const { payload } = vaa.payload;
256+
const { recipientChain, trimmedAmount } =
257+
payload["nttManagerPayload"].payload;
258+
259+
const token = canonicalAddress({
260+
chain: vaa.emitterChain,
261+
address: payload["nttManagerPayload"].payload.sourceToken,
262+
});
263+
const manager = canonicalAddress({
264+
chain: vaa.emitterChain,
265+
address: payload["sourceNttManager"],
266+
});
267+
const whTransceiver =
268+
vaa.emitterChain === "Solana"
269+
? manager
270+
: canonicalAddress({
271+
chain: vaa.emitterChain,
272+
address: vaa.emitterAddress,
273+
});
274+
275+
const dstInfo = NttRoute.resolveDestinationNttContracts(
276+
this.staticConfig,
277+
{
278+
chain: vaa.emitterChain,
279+
address: payload["sourceNttManager"],
280+
},
281+
recipientChain
282+
);
283+
284+
const amt = amount.fromBaseUnits(
285+
trimmedAmount.amount,
286+
trimmedAmount.decimals
287+
);
288+
289+
return {
290+
from: vaa.emitterChain,
291+
to: recipientChain,
292+
state: TransferState.Attested,
293+
originTxs: [tx],
294+
attestation: {
295+
id: msgId,
296+
attestation: vaa,
297+
},
298+
params: {
299+
amount: amount.display(amt),
300+
options: { automatic: true },
301+
normalizedParams: {
302+
amount: amt,
303+
options: { queue: false, automatic: true },
304+
sourceContracts: {
305+
token,
306+
manager,
307+
transceiver: {
308+
wormhole: whTransceiver,
309+
},
310+
},
311+
destinationContracts: {
312+
token: dstInfo.token,
313+
manager: dstInfo.manager,
314+
transceiver: {
315+
wormhole: dstInfo.transceiver.wormhole,
316+
},
317+
},
318+
},
319+
},
320+
};
321+
}
322+
238323
// Even though this is an automatic route, the transfer may need to be
239324
// manually finalized if it was queued
240325
async finalize(signer: Signer, receipt: R): Promise<R> {

sdk/route/src/manual.ts

+81
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ import {
99
RedeemedTransferReceipt,
1010
Signer,
1111
TokenId,
12+
TransactionId,
1213
TransferState,
1314
Wormhole,
1415
WormholeMessageId,
1516
amount,
17+
canonicalAddress,
1618
isAttested,
1719
isDestinationQueued,
1820
isRedeemed,
1921
isSourceFinalized,
2022
isSourceInitiated,
2123
routes,
2224
signSendWait,
25+
finality,
2326
} from "@wormhole-foundation/sdk-connect";
2427
import "@wormhole-foundation/sdk-definitions-ntt";
2528
import { NttRoute } from "./types.js";
@@ -145,6 +148,7 @@ export class NttManualRoute<N extends Network>
145148
token: request.destination.id,
146149
amount: amount.parse(params.amount, request.destination.decimals),
147150
},
151+
eta: finality.estimateFinalityTime(request.fromChain.chain),
148152
};
149153
const { fromChain, toChain } = request;
150154
const dstNtt = await toChain.getProtocol("Ntt", {
@@ -224,6 +228,83 @@ export class NttManualRoute<N extends Network>
224228
};
225229
}
226230

231+
async resume(tx: TransactionId): Promise<R> {
232+
const vaa = await this.wh.getVaa(tx.txid, "Ntt:WormholeTransfer");
233+
if (!vaa) throw new Error("No VAA found for transaction: " + tx.txid);
234+
235+
const msgId: WormholeMessageId = {
236+
chain: vaa.emitterChain,
237+
emitter: vaa.emitterAddress,
238+
sequence: vaa.sequence,
239+
};
240+
241+
const { recipientChain, trimmedAmount } =
242+
vaa.payload["nttManagerPayload"].payload;
243+
244+
const token = canonicalAddress({
245+
chain: vaa.emitterChain,
246+
address: vaa.payload["nttManagerPayload"].payload.sourceToken,
247+
});
248+
const manager = canonicalAddress({
249+
chain: vaa.emitterChain,
250+
address: vaa.payload["sourceNttManager"],
251+
});
252+
const whTransceiver =
253+
vaa.emitterChain === "Solana"
254+
? manager
255+
: canonicalAddress({
256+
chain: vaa.emitterChain,
257+
address: vaa.emitterAddress,
258+
});
259+
260+
const dstInfo = NttRoute.resolveDestinationNttContracts(
261+
this.staticConfig,
262+
{
263+
chain: vaa.emitterChain,
264+
address: vaa.payload["sourceNttManager"],
265+
},
266+
recipientChain
267+
);
268+
269+
const amt = amount.fromBaseUnits(
270+
trimmedAmount.amount,
271+
trimmedAmount.decimals
272+
);
273+
274+
return {
275+
from: vaa.emitterChain,
276+
to: recipientChain,
277+
state: TransferState.Attested,
278+
originTxs: [tx],
279+
attestation: {
280+
id: msgId,
281+
attestation: vaa,
282+
},
283+
params: {
284+
amount: amount.display(amt),
285+
options: { automatic: false },
286+
normalizedParams: {
287+
amount: amt,
288+
options: { queue: false },
289+
sourceContracts: {
290+
token,
291+
manager,
292+
transceiver: {
293+
wormhole: whTransceiver,
294+
},
295+
},
296+
destinationContracts: {
297+
token: dstInfo.token,
298+
manager: dstInfo.manager,
299+
transceiver: {
300+
wormhole: dstInfo.transceiver.wormhole,
301+
},
302+
},
303+
},
304+
},
305+
};
306+
}
307+
227308
async finalize(signer: Signer, receipt: R): Promise<R> {
228309
if (!isDestinationQueued(receipt)) {
229310
throw new Error(

sdk/route/src/types.ts

+35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
Chain,
3+
ChainAddress,
34
ChainContext,
45
Network,
56
TokenId,
@@ -168,6 +169,40 @@ export namespace NttRoute {
168169
throw new Error("Cannot find Ntt contracts in config for: " + address);
169170
}
170171

172+
export function resolveDestinationNttContracts<C extends Chain>(
173+
config: Config,
174+
srcManager: ChainAddress<C>,
175+
dstChain: Chain
176+
): Ntt.Contracts {
177+
const cfg = Object.values(config.tokens);
178+
const address = canonicalAddress(srcManager);
179+
for (const tokens of cfg) {
180+
const found = tokens.find(
181+
(tc) =>
182+
tc.manager.toLowerCase() === address.toLowerCase() &&
183+
tc.chain === srcManager.chain
184+
);
185+
if (found) {
186+
const remote = tokens.find((tc) => tc.chain === dstChain);
187+
if (!remote) {
188+
throw new Error(
189+
`Cannot find destination Ntt contracts in config for: ${address}`
190+
);
191+
}
192+
return {
193+
token: remote.token,
194+
manager: remote.manager,
195+
transceiver: {
196+
wormhole: remote.transceiver.find((v) => v.type === "wormhole")!
197+
.address,
198+
},
199+
quoter: remote.quoter,
200+
};
201+
}
202+
}
203+
throw new Error("Cannot find Ntt contracts in config for: " + address);
204+
}
205+
171206
// returns true if the amount is greater than 95% of the capacity
172207
// useful for warning about the possibility of a transfer being queued
173208
export function isCapacityThresholdExceeded(

solana/package.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@
5454
"@solana/spl-token": "0.4.0",
5555
"@solana/web3.js": "1.91.7",
5656
"bn.js": "5.2.1",
57-
"@wormhole-foundation/sdk-base": "^0.8",
58-
"@wormhole-foundation/sdk-definitions": "^0.8",
59-
"@wormhole-foundation/sdk-solana": "^0.8",
60-
"@wormhole-foundation/sdk-solana-core": "^0.8"
57+
"@wormhole-foundation/sdk-base": "^0.9.1",
58+
"@wormhole-foundation/sdk-definitions": "^0.9.1",
59+
"@wormhole-foundation/sdk-solana": "^0.9.1",
60+
"@wormhole-foundation/sdk-solana-core": "^0.9.1"
6161
},
6262
"peerDependencies": {
63-
"@wormhole-foundation/sdk-base": "^0.8",
64-
"@wormhole-foundation/sdk-definitions": "^0.8",
65-
"@wormhole-foundation/sdk-solana": "^0.8",
66-
"@wormhole-foundation/sdk-solana-core": "^0.8",
63+
"@wormhole-foundation/sdk-base": "^0.9.1",
64+
"@wormhole-foundation/sdk-definitions": "^0.9.1",
65+
"@wormhole-foundation/sdk-solana": "^0.9.1",
66+
"@wormhole-foundation/sdk-solana-core": "^0.9.1",
6767
"@wormhole-foundation/sdk-definitions-ntt": "0.1.0-beta.0"
6868
},
6969
"type": "module",

0 commit comments

Comments
 (0)