@wormhole-foundation/sdk-connect 4.22.0 → 5.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/indexers/__tests__/balances.test.js +1 -0
- package/dist/esm/indexers/__tests__/balances.test.js.map +1 -1
- package/package.json +32 -126
- package/dist/cjs/circle-api.d.ts +0 -20
- package/dist/cjs/circle-api.d.ts.map +0 -1
- package/dist/cjs/circle-api.js +0 -67
- package/dist/cjs/circle-api.js.map +0 -1
- package/dist/cjs/common.d.ts +0 -6
- package/dist/cjs/common.d.ts.map +0 -1
- package/dist/cjs/common.js +0 -60
- package/dist/cjs/common.js.map +0 -1
- package/dist/cjs/config.d.ts +0 -41
- package/dist/cjs/config.d.ts.map +0 -1
- package/dist/cjs/config.js +0 -65
- package/dist/cjs/config.js.map +0 -1
- package/dist/cjs/index.d.ts +0 -22
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/index.js +0 -71
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/indexers/AlchemyClient.d.ts +0 -17
- package/dist/cjs/indexers/AlchemyClient.d.ts.map +0 -1
- package/dist/cjs/indexers/AlchemyClient.js +0 -112
- package/dist/cjs/indexers/AlchemyClient.js.map +0 -1
- package/dist/cjs/indexers/GoldRushClient.d.ts +0 -14
- package/dist/cjs/indexers/GoldRushClient.d.ts.map +0 -1
- package/dist/cjs/indexers/GoldRushClient.js +0 -90
- package/dist/cjs/indexers/GoldRushClient.js.map +0 -1
- package/dist/cjs/indexers/__tests__/balances.test.d.ts +0 -2
- package/dist/cjs/indexers/__tests__/balances.test.d.ts.map +0 -1
- package/dist/cjs/indexers/__tests__/balances.test.js +0 -323
- package/dist/cjs/indexers/__tests__/balances.test.js.map +0 -1
- package/dist/cjs/indexers/__tests__/utils.test.d.ts +0 -2
- package/dist/cjs/indexers/__tests__/utils.test.d.ts.map +0 -1
- package/dist/cjs/indexers/__tests__/utils.test.js +0 -71
- package/dist/cjs/indexers/__tests__/utils.test.js.map +0 -1
- package/dist/cjs/indexers/balances.d.ts +0 -5
- package/dist/cjs/indexers/balances.d.ts.map +0 -1
- package/dist/cjs/indexers/balances.js +0 -70
- package/dist/cjs/indexers/balances.js.map +0 -1
- package/dist/cjs/indexers/index.d.ts +0 -3
- package/dist/cjs/indexers/index.d.ts.map +0 -1
- package/dist/cjs/indexers/index.js +0 -21
- package/dist/cjs/indexers/index.js.map +0 -1
- package/dist/cjs/indexers/types.d.ts +0 -30
- package/dist/cjs/indexers/types.d.ts.map +0 -1
- package/dist/cjs/indexers/types.js +0 -3
- package/dist/cjs/indexers/types.js.map +0 -1
- package/dist/cjs/indexers/utils.d.ts +0 -2
- package/dist/cjs/indexers/utils.d.ts.map +0 -1
- package/dist/cjs/indexers/utils.js +0 -21
- package/dist/cjs/indexers/utils.js.map +0 -1
- package/dist/cjs/package.json +0 -1
- package/dist/cjs/protocols/cctp/cctpTransfer.d.ts +0 -46
- package/dist/cjs/protocols/cctp/cctpTransfer.d.ts.map +0 -1
- package/dist/cjs/protocols/cctp/cctpTransfer.js +0 -498
- package/dist/cjs/protocols/cctp/cctpTransfer.js.map +0 -1
- package/dist/cjs/protocols/gateway/gatewayTransfer.d.ts +0 -43
- package/dist/cjs/protocols/gateway/gatewayTransfer.d.ts.map +0 -1
- package/dist/cjs/protocols/gateway/gatewayTransfer.js +0 -399
- package/dist/cjs/protocols/gateway/gatewayTransfer.js.map +0 -1
- package/dist/cjs/protocols/index.d.ts +0 -5
- package/dist/cjs/protocols/index.d.ts.map +0 -1
- package/dist/cjs/protocols/index.js +0 -21
- package/dist/cjs/protocols/index.js.map +0 -1
- package/dist/cjs/protocols/tokenBridge/tokenTransfer.d.ts +0 -68
- package/dist/cjs/protocols/tokenBridge/tokenTransfer.d.ts.map +0 -1
- package/dist/cjs/protocols/tokenBridge/tokenTransfer.js +0 -800
- package/dist/cjs/protocols/tokenBridge/tokenTransfer.js.map +0 -1
- package/dist/cjs/protocols/wormholeTransfer.d.ts +0 -20
- package/dist/cjs/protocols/wormholeTransfer.d.ts.map +0 -1
- package/dist/cjs/protocols/wormholeTransfer.js +0 -3
- package/dist/cjs/protocols/wormholeTransfer.js.map +0 -1
- package/dist/cjs/routes/cctp/automatic.d.ts +0 -47
- package/dist/cjs/routes/cctp/automatic.d.ts.map +0 -1
- package/dist/cjs/routes/cctp/automatic.js +0 -159
- package/dist/cjs/routes/cctp/automatic.js.map +0 -1
- package/dist/cjs/routes/cctp/index.d.ts +0 -3
- package/dist/cjs/routes/cctp/index.d.ts.map +0 -1
- package/dist/cjs/routes/cctp/index.js +0 -19
- package/dist/cjs/routes/cctp/index.js.map +0 -1
- package/dist/cjs/routes/cctp/manual.d.ts +0 -46
- package/dist/cjs/routes/cctp/manual.d.ts.map +0 -1
- package/dist/cjs/routes/cctp/manual.js +0 -139
- package/dist/cjs/routes/cctp/manual.js.map +0 -1
- package/dist/cjs/routes/common.d.ts +0 -13
- package/dist/cjs/routes/common.d.ts.map +0 -1
- package/dist/cjs/routes/common.js +0 -51
- package/dist/cjs/routes/common.js.map +0 -1
- package/dist/cjs/routes/index.d.ts +0 -10
- package/dist/cjs/routes/index.d.ts.map +0 -1
- package/dist/cjs/routes/index.js +0 -26
- package/dist/cjs/routes/index.js.map +0 -1
- package/dist/cjs/routes/portico/automatic.d.ts +0 -48
- package/dist/cjs/routes/portico/automatic.d.ts.map +0 -1
- package/dist/cjs/routes/portico/automatic.js +0 -232
- package/dist/cjs/routes/portico/automatic.js.map +0 -1
- package/dist/cjs/routes/portico/index.d.ts +0 -3
- package/dist/cjs/routes/portico/index.d.ts.map +0 -1
- package/dist/cjs/routes/portico/index.js +0 -18
- package/dist/cjs/routes/portico/index.js.map +0 -1
- package/dist/cjs/routes/request.d.ts +0 -29
- package/dist/cjs/routes/request.d.ts.map +0 -1
- package/dist/cjs/routes/request.js +0 -72
- package/dist/cjs/routes/request.js.map +0 -1
- package/dist/cjs/routes/resolver.d.ts +0 -14
- package/dist/cjs/routes/resolver.d.ts.map +0 -1
- package/dist/cjs/routes/resolver.js +0 -58
- package/dist/cjs/routes/resolver.js.map +0 -1
- package/dist/cjs/routes/route.d.ts +0 -79
- package/dist/cjs/routes/route.d.ts.map +0 -1
- package/dist/cjs/routes/route.js +0 -63
- package/dist/cjs/routes/route.js.map +0 -1
- package/dist/cjs/routes/tbtc/index.d.ts +0 -2
- package/dist/cjs/routes/tbtc/index.d.ts.map +0 -1
- package/dist/cjs/routes/tbtc/index.js +0 -18
- package/dist/cjs/routes/tbtc/index.js.map +0 -1
- package/dist/cjs/routes/tbtc/tbtc.d.ts +0 -44
- package/dist/cjs/routes/tbtc/tbtc.d.ts.map +0 -1
- package/dist/cjs/routes/tbtc/tbtc.js +0 -244
- package/dist/cjs/routes/tbtc/tbtc.js.map +0 -1
- package/dist/cjs/routes/token.d.ts +0 -13
- package/dist/cjs/routes/token.d.ts.map +0 -1
- package/dist/cjs/routes/token.js +0 -45
- package/dist/cjs/routes/token.js.map +0 -1
- package/dist/cjs/routes/tokenBridge/automatic.d.ts +0 -47
- package/dist/cjs/routes/tokenBridge/automatic.d.ts.map +0 -1
- package/dist/cjs/routes/tokenBridge/automatic.js +0 -205
- package/dist/cjs/routes/tokenBridge/automatic.js.map +0 -1
- package/dist/cjs/routes/tokenBridge/executor.d.ts +0 -107
- package/dist/cjs/routes/tokenBridge/executor.d.ts.map +0 -1
- package/dist/cjs/routes/tokenBridge/executor.js +0 -245
- package/dist/cjs/routes/tokenBridge/executor.js.map +0 -1
- package/dist/cjs/routes/tokenBridge/index.d.ts +0 -4
- package/dist/cjs/routes/tokenBridge/index.d.ts.map +0 -1
- package/dist/cjs/routes/tokenBridge/index.js +0 -20
- package/dist/cjs/routes/tokenBridge/index.js.map +0 -1
- package/dist/cjs/routes/tokenBridge/manual.d.ts +0 -45
- package/dist/cjs/routes/tokenBridge/manual.d.ts.map +0 -1
- package/dist/cjs/routes/tokenBridge/manual.js +0 -135
- package/dist/cjs/routes/tokenBridge/manual.js.map +0 -1
- package/dist/cjs/routes/types.d.ts +0 -59
- package/dist/cjs/routes/types.d.ts.map +0 -1
- package/dist/cjs/routes/types.js +0 -31
- package/dist/cjs/routes/types.js.map +0 -1
- package/dist/cjs/tasks.d.ts +0 -7
- package/dist/cjs/tasks.d.ts.map +0 -1
- package/dist/cjs/tasks.js +0 -65
- package/dist/cjs/tasks.js.map +0 -1
- package/dist/cjs/tokens.d.ts +0 -3
- package/dist/cjs/tokens.d.ts.map +0 -1
- package/dist/cjs/tokens.js +0 -19
- package/dist/cjs/tokens.js.map +0 -1
- package/dist/cjs/types.d.ts +0 -131
- package/dist/cjs/types.d.ts.map +0 -1
- package/dist/cjs/types.js +0 -66
- package/dist/cjs/types.js.map +0 -1
- package/dist/cjs/warnings.d.ts +0 -10
- package/dist/cjs/warnings.d.ts.map +0 -1
- package/dist/cjs/warnings.js +0 -3
- package/dist/cjs/warnings.js.map +0 -1
- package/dist/cjs/whscan-api.d.ts +0 -173
- package/dist/cjs/whscan-api.d.ts.map +0 -1
- package/dist/cjs/whscan-api.js +0 -238
- package/dist/cjs/whscan-api.js.map +0 -1
- package/dist/cjs/wormhole.d.ts +0 -244
- package/dist/cjs/wormhole.d.ts.map +0 -1
- package/dist/cjs/wormhole.js +0 -377
- package/dist/cjs/wormhole.js.map +0 -1
- package/index.d.cts +0 -22
- package/index.d.mts +0 -22
|
@@ -1,800 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TokenTransfer = void 0;
|
|
4
|
-
const sdk_base_1 = require("@wormhole-foundation/sdk-base");
|
|
5
|
-
const sdk_definitions_1 = require("@wormhole-foundation/sdk-definitions");
|
|
6
|
-
const common_js_1 = require("../../common.js");
|
|
7
|
-
const config_js_1 = require("../../config.js");
|
|
8
|
-
const sdk_base_2 = require("@wormhole-foundation/sdk-base");
|
|
9
|
-
const types_js_1 = require("../../types.js");
|
|
10
|
-
const whscan_api_js_1 = require("../../whscan-api.js");
|
|
11
|
-
const wormhole_js_1 = require("../../wormhole.js");
|
|
12
|
-
const sdk_definitions_2 = require("@wormhole-foundation/sdk-definitions");
|
|
13
|
-
class TokenTransfer {
|
|
14
|
-
wh;
|
|
15
|
-
fromChain;
|
|
16
|
-
toChain;
|
|
17
|
-
// state machine tracker
|
|
18
|
-
_state;
|
|
19
|
-
// transfer details
|
|
20
|
-
transfer;
|
|
21
|
-
// txids, populated once transactions are submitted
|
|
22
|
-
txids = [];
|
|
23
|
-
// The corresponding vaa representing the TokenTransfer
|
|
24
|
-
// on the source chain (if its been completed and finalized)
|
|
25
|
-
attestations;
|
|
26
|
-
constructor(wh, transfer, fromChain, toChain) {
|
|
27
|
-
this._state = types_js_1.TransferState.Created;
|
|
28
|
-
this.wh = wh;
|
|
29
|
-
this.transfer = transfer;
|
|
30
|
-
this.fromChain = fromChain ?? wh.getChain(transfer.from.chain);
|
|
31
|
-
this.toChain = toChain ?? wh.getChain(transfer.to.chain);
|
|
32
|
-
}
|
|
33
|
-
getTransferState() {
|
|
34
|
-
return this._state;
|
|
35
|
-
}
|
|
36
|
-
static async from(wh, from, timeout = 6000, fromChain, toChain) {
|
|
37
|
-
if ((0, sdk_definitions_1.isTokenTransferDetails)(from)) {
|
|
38
|
-
fromChain = fromChain ?? wh.getChain(from.from.chain);
|
|
39
|
-
toChain = toChain ?? wh.getChain(from.to.chain);
|
|
40
|
-
// throws if invalid
|
|
41
|
-
TokenTransfer.validateTransferDetails(wh, from, fromChain, toChain);
|
|
42
|
-
// Apply hackery
|
|
43
|
-
from = await TokenTransfer.destinationOverrides(fromChain, toChain, from);
|
|
44
|
-
return new TokenTransfer(wh, from, fromChain, toChain);
|
|
45
|
-
}
|
|
46
|
-
let tt;
|
|
47
|
-
if ((0, sdk_definitions_1.isWormholeMessageId)(from)) {
|
|
48
|
-
tt = await TokenTransfer.fromIdentifier(wh, from, timeout);
|
|
49
|
-
}
|
|
50
|
-
else if ((0, sdk_definitions_1.isTransactionIdentifier)(from)) {
|
|
51
|
-
tt = await TokenTransfer.fromTransaction(wh, from, timeout, fromChain);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
throw new Error("Invalid `from` parameter for TokenTransfer");
|
|
55
|
-
}
|
|
56
|
-
tt.fromChain = fromChain ?? wh.getChain(tt.transfer.from.chain);
|
|
57
|
-
tt.toChain = toChain ?? wh.getChain(tt.transfer.to.chain);
|
|
58
|
-
await tt.fetchAttestation(timeout);
|
|
59
|
-
return tt;
|
|
60
|
-
}
|
|
61
|
-
// init from the seq id
|
|
62
|
-
static async fromIdentifier(wh, id, timeout) {
|
|
63
|
-
const vaa = await TokenTransfer.getTransferVaa(wh, id, timeout);
|
|
64
|
-
if (!vaa)
|
|
65
|
-
throw new Error("VAA not found");
|
|
66
|
-
const protocol = vaa.protocolName;
|
|
67
|
-
// TODO: the `from.address` here is a lie, but we don't
|
|
68
|
-
// immediately have enough info to get the _correct_ one
|
|
69
|
-
let from = { chain: vaa.emitterChain, address: vaa.emitterAddress };
|
|
70
|
-
let { token, to } = vaa.payload;
|
|
71
|
-
let nativeAddress;
|
|
72
|
-
if (token.chain === from.chain) {
|
|
73
|
-
nativeAddress = await wh.getTokenNativeAddress(from.chain, token.chain, token.address);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
const fromChain = await wh.getChain(from.chain);
|
|
77
|
-
const tb = await fromChain.getTokenBridge();
|
|
78
|
-
const wrapped = await tb.getWrappedAsset(token);
|
|
79
|
-
nativeAddress = (0, sdk_definitions_1.toNative)(from.chain, wrapped.toString());
|
|
80
|
-
}
|
|
81
|
-
const decimals = await wh.getDecimals(from.chain, nativeAddress);
|
|
82
|
-
const scaledAmount = sdk_base_1.amount.scale(sdk_base_1.amount.fromBaseUnits(token.amount, Math.min(decimals, TokenTransfer.MAX_DECIMALS)), decimals);
|
|
83
|
-
if (protocol === "AutomaticTokenBridge" || protocol === "ExecutorTokenBridge") {
|
|
84
|
-
from = { chain: vaa.emitterChain, address: vaa.payload.from };
|
|
85
|
-
to = { chain: vaa.payload.to.chain, address: vaa.payload.payload.targetRecipient };
|
|
86
|
-
}
|
|
87
|
-
const details = {
|
|
88
|
-
token: token,
|
|
89
|
-
amount: sdk_base_1.amount.units(scaledAmount),
|
|
90
|
-
from,
|
|
91
|
-
to,
|
|
92
|
-
protocol,
|
|
93
|
-
nativeGas: protocol === "AutomaticTokenBridge" ? vaa.payload.payload.toNativeTokenAmount : 0n, // ExecutorTokenBridge: VAA doesn't contain original nativeGas amount from quote
|
|
94
|
-
};
|
|
95
|
-
// TODO: grab at least the init tx from the api
|
|
96
|
-
const tt = new TokenTransfer(wh, details);
|
|
97
|
-
tt.attestations = [{ id: id, attestation: vaa }];
|
|
98
|
-
tt._state = types_js_1.TransferState.Attested;
|
|
99
|
-
return tt;
|
|
100
|
-
}
|
|
101
|
-
static async fromTransaction(wh, from, timeout, fromChain) {
|
|
102
|
-
fromChain = fromChain ?? wh.getChain(from.chain);
|
|
103
|
-
const msg = await TokenTransfer.getTransferMessage(fromChain, from.txid, timeout);
|
|
104
|
-
const tt = await TokenTransfer.fromIdentifier(wh, msg, timeout);
|
|
105
|
-
tt.txids = [from];
|
|
106
|
-
return tt;
|
|
107
|
-
}
|
|
108
|
-
// start the WormholeTransfer by submitting transactions to the source chain
|
|
109
|
-
// returns a transaction hash
|
|
110
|
-
async initiateTransfer(signer) {
|
|
111
|
-
if (this._state !== types_js_1.TransferState.Created)
|
|
112
|
-
throw new Error("Invalid state transition in `initiateTransfer`");
|
|
113
|
-
this.txids = await TokenTransfer.transfer(this.fromChain, this.transfer, signer);
|
|
114
|
-
this._state = types_js_1.TransferState.SourceInitiated;
|
|
115
|
-
return this.txids.map(({ txid }) => txid);
|
|
116
|
-
}
|
|
117
|
-
// wait for the VAA to be ready
|
|
118
|
-
// returns the sequence number
|
|
119
|
-
async fetchAttestation(timeout) {
|
|
120
|
-
if (this._state < types_js_1.TransferState.SourceInitiated || this._state > types_js_1.TransferState.Attested)
|
|
121
|
-
throw new Error("Invalid state transition in `fetchAttestation`, expected at least `SourceInitiated`");
|
|
122
|
-
if (!this.attestations || this.attestations.length === 0) {
|
|
123
|
-
if (this.txids.length === 0)
|
|
124
|
-
throw new Error("No VAAs set and txids available to look them up");
|
|
125
|
-
// TODO: assuming the _last_ transaction in the list will contain the msg id
|
|
126
|
-
const txid = this.txids[this.txids.length - 1];
|
|
127
|
-
const msgId = await TokenTransfer.getTransferMessage(this.fromChain, txid.txid, timeout);
|
|
128
|
-
this.attestations = [{ id: msgId }];
|
|
129
|
-
}
|
|
130
|
-
for (const idx in this.attestations) {
|
|
131
|
-
// Check if we already have the VAA
|
|
132
|
-
if (this.attestations[idx].attestation)
|
|
133
|
-
continue;
|
|
134
|
-
const vaa = await TokenTransfer.getTransferVaa(this.wh, this.attestations[idx].id, timeout);
|
|
135
|
-
if (!vaa)
|
|
136
|
-
throw new Error("VAA not found");
|
|
137
|
-
this.attestations[idx].attestation = vaa;
|
|
138
|
-
}
|
|
139
|
-
this._state = types_js_1.TransferState.Attested;
|
|
140
|
-
if (this.attestations.length > 0) {
|
|
141
|
-
// Check if the transfer has been completed
|
|
142
|
-
const { attestation } = this.attestations[0];
|
|
143
|
-
const completed = await TokenTransfer.isTransferComplete(this.toChain, attestation);
|
|
144
|
-
if (completed)
|
|
145
|
-
this._state = types_js_1.TransferState.DestinationFinalized;
|
|
146
|
-
}
|
|
147
|
-
return this.attestations.map((vaa) => vaa.id);
|
|
148
|
-
}
|
|
149
|
-
// finish the WormholeTransfer by submitting transactions to the destination chain
|
|
150
|
-
// returns a transaction hash
|
|
151
|
-
async completeTransfer(signer) {
|
|
152
|
-
if (this._state < types_js_1.TransferState.Attested)
|
|
153
|
-
throw new Error("Invalid state transition, must be attested prior to calling `completeTransfer`.");
|
|
154
|
-
if (!this.attestations)
|
|
155
|
-
throw new Error("No VAA details available");
|
|
156
|
-
const { attestation } = this.attestations[0];
|
|
157
|
-
if (!attestation)
|
|
158
|
-
throw new Error(`No VAA found for ${this.attestations[0].id.sequence}`);
|
|
159
|
-
const redeemTxids = await TokenTransfer.redeem(this.toChain, attestation, signer);
|
|
160
|
-
this.txids.push(...redeemTxids);
|
|
161
|
-
this._state = types_js_1.TransferState.DestinationInitiated;
|
|
162
|
-
return redeemTxids.map(({ txid }) => txid);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
exports.TokenTransfer = TokenTransfer;
|
|
166
|
-
(function (TokenTransfer) {
|
|
167
|
-
/** 8 is maximum precision supported by the token bridge VAA */
|
|
168
|
-
TokenTransfer.MAX_DECIMALS = 8;
|
|
169
|
-
// Static method to perform the transfer so a custom RPC may be used
|
|
170
|
-
// Note: this assumes the transfer has already been validated with `validateTransfer`
|
|
171
|
-
async function transfer(fromChain, transfer, signer) {
|
|
172
|
-
const senderAddress = (0, sdk_definitions_1.toNative)(signer.chain(), signer.address());
|
|
173
|
-
const token = (0, sdk_definitions_1.isTokenId)(transfer.token) ? transfer.token.address : transfer.token;
|
|
174
|
-
let xfer;
|
|
175
|
-
if (transfer.protocol === "AutomaticTokenBridge") {
|
|
176
|
-
const tb = await fromChain.getAutomaticTokenBridge();
|
|
177
|
-
xfer = tb.transfer(senderAddress, transfer.to, token, transfer.amount, transfer.nativeGas);
|
|
178
|
-
}
|
|
179
|
-
else if (transfer.protocol === "TokenBridge") {
|
|
180
|
-
const tb = await fromChain.getTokenBridge();
|
|
181
|
-
xfer = tb.transfer(senderAddress, transfer.to, token, transfer.amount, transfer.payload);
|
|
182
|
-
}
|
|
183
|
-
else if (transfer.protocol === "ExecutorTokenBridge") {
|
|
184
|
-
if (!transfer.executorQuote) {
|
|
185
|
-
throw new Error("ExecutorTokenBridge transfer requires an executorQuote");
|
|
186
|
-
}
|
|
187
|
-
const gasDropOffInstruction = transfer.executorQuote.relayInstructions.requests.find((r) => r.request.type === "GasDropOffInstruction");
|
|
188
|
-
if (gasDropOffInstruction &&
|
|
189
|
-
gasDropOffInstruction.request.type === "GasDropOffInstruction" &&
|
|
190
|
-
gasDropOffInstruction.request.recipient.equals(sdk_definitions_1.UniversalAddress.ZERO)) {
|
|
191
|
-
// @ts-ignore
|
|
192
|
-
gasDropOffInstruction.request.recipient = transfer.to.address.toUniversalAddress();
|
|
193
|
-
}
|
|
194
|
-
const tb = await fromChain.getExecutorTokenBridge();
|
|
195
|
-
xfer = tb.transfer(senderAddress, transfer.to, token, transfer.amount, transfer.executorQuote, transfer.referrerFee);
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
throw new Error("Unknown token transfer protocol");
|
|
199
|
-
}
|
|
200
|
-
return (0, common_js_1.signSendWait)(fromChain, xfer, signer);
|
|
201
|
-
}
|
|
202
|
-
TokenTransfer.transfer = transfer;
|
|
203
|
-
// Static method to allow passing a custom RPC
|
|
204
|
-
async function redeem(toChain, vaa, signer) {
|
|
205
|
-
const signerAddress = (0, sdk_definitions_1.toNative)(signer.chain(), signer.address());
|
|
206
|
-
const xfer = vaa.protocolName === "AutomaticTokenBridge"
|
|
207
|
-
? (await toChain.getAutomaticTokenBridge()).redeem(signerAddress, vaa)
|
|
208
|
-
: vaa.protocolName === "ExecutorTokenBridge"
|
|
209
|
-
? (await toChain.getExecutorTokenBridge()).redeem(signerAddress, vaa)
|
|
210
|
-
: (await toChain.getTokenBridge()).redeem(signerAddress, vaa);
|
|
211
|
-
return (0, common_js_1.signSendWait)(toChain, xfer, signer);
|
|
212
|
-
}
|
|
213
|
-
TokenTransfer.redeem = redeem;
|
|
214
|
-
// AsyncGenerator fn that produces status updates through an async generator
|
|
215
|
-
// eventually producing a receipt
|
|
216
|
-
// can be called repeatedly so the receipt is updated as it moves through the
|
|
217
|
-
// steps of the transfer
|
|
218
|
-
async function* track(wh, receipt, timeout = config_js_1.DEFAULT_TASK_TIMEOUT, fromChain, toChain) {
|
|
219
|
-
const start = Date.now();
|
|
220
|
-
const leftover = (start, max) => Math.max(max - (Date.now() - start), 0);
|
|
221
|
-
fromChain = fromChain ?? wh.getChain(receipt.from);
|
|
222
|
-
// Check the source chain for initiation transaction
|
|
223
|
-
// and capture the message id
|
|
224
|
-
if ((0, types_js_1.isSourceInitiated)(receipt)) {
|
|
225
|
-
if (receipt.originTxs.length === 0)
|
|
226
|
-
throw "Origin transactions required to fetch message id";
|
|
227
|
-
const { txid } = receipt.originTxs[receipt.originTxs.length - 1];
|
|
228
|
-
const msg = await TokenTransfer.getTransferMessage(fromChain, txid, leftover(start, timeout));
|
|
229
|
-
receipt = {
|
|
230
|
-
...receipt,
|
|
231
|
-
state: types_js_1.TransferState.SourceFinalized,
|
|
232
|
-
attestation: { id: msg },
|
|
233
|
-
};
|
|
234
|
-
yield receipt;
|
|
235
|
-
}
|
|
236
|
-
// If the source is finalized or in review (governor held), we need to fetch the signed attestation
|
|
237
|
-
// (once it's available) so that we may deliver it to the destination chain
|
|
238
|
-
// or at least track the transfer through its progress
|
|
239
|
-
if ((0, types_js_1.isSourceFinalized)(receipt) || (0, types_js_1.isInReview)(receipt)) {
|
|
240
|
-
if (!receipt.attestation.id)
|
|
241
|
-
throw "Attestation id required to fetch attestation";
|
|
242
|
-
const { id } = receipt.attestation;
|
|
243
|
-
const attestation = await TokenTransfer.getTransferVaa(wh, id, leftover(start, timeout));
|
|
244
|
-
if (attestation) {
|
|
245
|
-
receipt = {
|
|
246
|
-
...receipt,
|
|
247
|
-
attestation: { id, attestation },
|
|
248
|
-
state: types_js_1.TransferState.Attested,
|
|
249
|
-
};
|
|
250
|
-
yield receipt;
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
// If the attestation is not found, check if the transfer is held by the governor
|
|
254
|
-
const isEnqueued = await TokenTransfer.isTransferEnqueued(wh, id);
|
|
255
|
-
if (isEnqueued) {
|
|
256
|
-
receipt = {
|
|
257
|
-
...receipt,
|
|
258
|
-
state: types_js_1.TransferState.InReview,
|
|
259
|
-
};
|
|
260
|
-
yield receipt;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
throw new Error("Attestation not found");
|
|
264
|
-
}
|
|
265
|
-
// First try to grab the tx status from the API
|
|
266
|
-
// Note: this requires a subsequent async step on the backend
|
|
267
|
-
// to have the dest txid populated, so it may be delayed by some time
|
|
268
|
-
if ((0, types_js_1.isAttested)(receipt) ||
|
|
269
|
-
(0, types_js_1.isSourceFinalized)(receipt) ||
|
|
270
|
-
(0, types_js_1.isInReview)(receipt) ||
|
|
271
|
-
(0, types_js_1.isRelayFailed)(receipt)) {
|
|
272
|
-
if (!receipt.attestation?.id)
|
|
273
|
-
throw "Attestation id required to fetch redeem tx";
|
|
274
|
-
const { id } = receipt.attestation;
|
|
275
|
-
const txStatus = await wh.getTransactionStatus(id, leftover(start, timeout));
|
|
276
|
-
if (txStatus && txStatus.globalTx?.destinationTx?.txHash) {
|
|
277
|
-
const { chainId, txHash } = txStatus.globalTx.destinationTx;
|
|
278
|
-
receipt = {
|
|
279
|
-
...receipt,
|
|
280
|
-
destinationTxs: [{ chain: (0, sdk_base_1.toChain)(chainId), txid: txHash }],
|
|
281
|
-
state: types_js_1.TransferState.DestinationInitiated,
|
|
282
|
-
attestation: receipt.attestation,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
yield receipt;
|
|
286
|
-
}
|
|
287
|
-
// Fall back to asking the destination chain if this VAA has been redeemed
|
|
288
|
-
// Note: We do not get any destinationTxs with this method
|
|
289
|
-
if ((0, types_js_1.isAttested)(receipt) || (0, types_js_1.isRedeemed)(receipt) || (0, types_js_1.isRelayFailed)(receipt)) {
|
|
290
|
-
if (!receipt.attestation?.attestation)
|
|
291
|
-
throw "Signed Attestation required to check for redeem";
|
|
292
|
-
if (receipt.attestation.attestation.payloadName === "AttestMeta") {
|
|
293
|
-
throw new Error("Unable to track an AttestMeta receipt");
|
|
294
|
-
}
|
|
295
|
-
let isComplete = await TokenTransfer.isTransferComplete(toChain ?? wh.getChain(receipt.attestation.attestation.payload.to.chain), receipt.attestation.attestation);
|
|
296
|
-
if (isComplete) {
|
|
297
|
-
receipt = {
|
|
298
|
-
...receipt,
|
|
299
|
-
state: types_js_1.TransferState.DestinationFinalized,
|
|
300
|
-
attestation: receipt.attestation,
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
yield receipt;
|
|
304
|
-
}
|
|
305
|
-
// If this is a ExecutorTokenBridge transfer, check if the relay failed
|
|
306
|
-
// If the relay succeeded, then the token bridge will return the transfer as completed
|
|
307
|
-
if ((0, types_js_1.isAttested)(receipt) &&
|
|
308
|
-
receipt.attestation.attestation.protocolName === "ExecutorTokenBridge") {
|
|
309
|
-
const [txStatus] = await wh.getExecutorTxStatus(receipt.originTxs.at(-1).txid, receipt.from);
|
|
310
|
-
if (!txStatus)
|
|
311
|
-
throw new Error("No transaction status found");
|
|
312
|
-
const relayStatus = txStatus.status;
|
|
313
|
-
if (relayStatus === sdk_definitions_2.RelayStatus.Failed ||
|
|
314
|
-
relayStatus === sdk_definitions_2.RelayStatus.Underpaid ||
|
|
315
|
-
relayStatus === sdk_definitions_2.RelayStatus.Unsupported ||
|
|
316
|
-
relayStatus === sdk_definitions_2.RelayStatus.Aborted) {
|
|
317
|
-
receipt = {
|
|
318
|
-
...receipt,
|
|
319
|
-
state: types_js_1.TransferState.Failed,
|
|
320
|
-
error: new types_js_1.RelayFailedError(`Relay failed with status: ${relayStatus}`),
|
|
321
|
-
};
|
|
322
|
-
yield receipt;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
yield receipt;
|
|
326
|
-
}
|
|
327
|
-
TokenTransfer.track = track;
|
|
328
|
-
function getReceipt(xfer) {
|
|
329
|
-
const { transfer } = xfer;
|
|
330
|
-
const from = transfer.from.chain;
|
|
331
|
-
const to = transfer.to.chain;
|
|
332
|
-
let receipt = {
|
|
333
|
-
from: from,
|
|
334
|
-
to: to,
|
|
335
|
-
state: types_js_1.TransferState.Created,
|
|
336
|
-
};
|
|
337
|
-
const originTxs = xfer.txids.filter((txid) => txid.chain === transfer.from.chain);
|
|
338
|
-
if (originTxs.length > 0) {
|
|
339
|
-
receipt = {
|
|
340
|
-
...receipt,
|
|
341
|
-
state: types_js_1.TransferState.SourceInitiated,
|
|
342
|
-
originTxs: originTxs,
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
const att = xfer.attestations && xfer.attestations.length > 0 ? xfer.attestations[0] : undefined;
|
|
346
|
-
const attestation = att && att.id ? { id: att.id, attestation: att.attestation } : undefined;
|
|
347
|
-
if (attestation) {
|
|
348
|
-
if (attestation.id) {
|
|
349
|
-
receipt = {
|
|
350
|
-
...receipt,
|
|
351
|
-
state: types_js_1.TransferState.SourceFinalized,
|
|
352
|
-
attestation: { id: attestation.id },
|
|
353
|
-
};
|
|
354
|
-
if (attestation.attestation) {
|
|
355
|
-
receipt = {
|
|
356
|
-
...receipt,
|
|
357
|
-
state: types_js_1.TransferState.Attested,
|
|
358
|
-
attestation: { id: attestation.id, attestation: attestation.attestation },
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
const destinationTxs = xfer.txids.filter((txid) => txid.chain === transfer.to.chain);
|
|
364
|
-
if (destinationTxs.length > 0) {
|
|
365
|
-
receipt = {
|
|
366
|
-
...receipt,
|
|
367
|
-
state: types_js_1.TransferState.DestinationFinalized,
|
|
368
|
-
destinationTxs: destinationTxs,
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
return receipt;
|
|
372
|
-
}
|
|
373
|
-
TokenTransfer.getReceipt = getReceipt;
|
|
374
|
-
// Lookup the token id for the destination chain given the source chain
|
|
375
|
-
// and token id
|
|
376
|
-
async function lookupDestinationToken(srcChain, dstChain, token) {
|
|
377
|
-
let lookup;
|
|
378
|
-
const tb = await srcChain.getTokenBridge();
|
|
379
|
-
if ((0, sdk_definitions_1.isNative)(token.address)) {
|
|
380
|
-
// if native, get the wrapped asset id
|
|
381
|
-
const wrappedNative = await tb.getWrappedNative();
|
|
382
|
-
lookup = {
|
|
383
|
-
chain: token.chain,
|
|
384
|
-
address: await tb.getTokenUniversalAddress(wrappedNative),
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
try {
|
|
389
|
-
// otherwise, check to see if it is a wrapped token locally
|
|
390
|
-
let address;
|
|
391
|
-
if (sdk_definitions_1.UniversalAddress.instanceof(token.address)) {
|
|
392
|
-
address = (await tb.getWrappedAsset(token));
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
address = token.address;
|
|
396
|
-
}
|
|
397
|
-
lookup = await tb.getOriginalAsset(address);
|
|
398
|
-
}
|
|
399
|
-
catch (e) {
|
|
400
|
-
if (!e.message.includes("not a wrapped asset"))
|
|
401
|
-
throw e;
|
|
402
|
-
// not a from-chain native wormhole-wrapped one
|
|
403
|
-
let address;
|
|
404
|
-
if (sdk_definitions_1.UniversalAddress.instanceof(token.address)) {
|
|
405
|
-
address = await tb.getTokenNativeAddress(srcChain.chain, token.address);
|
|
406
|
-
}
|
|
407
|
-
else {
|
|
408
|
-
address = token.address;
|
|
409
|
-
}
|
|
410
|
-
lookup = { chain: token.chain, address: await tb.getTokenUniversalAddress(address) };
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
// if the token id is actually native to the destination, return it
|
|
414
|
-
const dstTb = await dstChain.getTokenBridge();
|
|
415
|
-
if (lookup.chain === dstChain.chain) {
|
|
416
|
-
const nativeAddress = await dstTb.getTokenNativeAddress(lookup.chain, lookup.address);
|
|
417
|
-
const destWrappedNative = await dstTb.getWrappedNative();
|
|
418
|
-
if ((0, sdk_definitions_1.canonicalAddress)({ chain: dstChain.chain, address: destWrappedNative }) ===
|
|
419
|
-
(0, sdk_definitions_1.canonicalAddress)({ chain: dstChain.chain, address: nativeAddress })) {
|
|
420
|
-
return { chain: dstChain.chain, address: "native" };
|
|
421
|
-
}
|
|
422
|
-
return { chain: dstChain.chain, address: nativeAddress };
|
|
423
|
-
}
|
|
424
|
-
// otherwise, figure out what the token address representing the wormhole-wrapped token we're transferring
|
|
425
|
-
const dstAddress = await dstTb.getWrappedAsset(lookup);
|
|
426
|
-
return { chain: dstChain.chain, address: dstAddress };
|
|
427
|
-
}
|
|
428
|
-
TokenTransfer.lookupDestinationToken = lookupDestinationToken;
|
|
429
|
-
async function isTransferComplete(toChain, vaa) {
|
|
430
|
-
if (vaa.protocolName === "AutomaticTokenBridge" || vaa.protocolName === "ExecutorTokenBridge") {
|
|
431
|
-
vaa = (0, sdk_definitions_1.deserialize)("TokenBridge:TransferWithPayload", (0, sdk_definitions_1.serialize)(vaa));
|
|
432
|
-
}
|
|
433
|
-
const tb = await toChain.getTokenBridge();
|
|
434
|
-
return tb.isTransferCompleted(vaa);
|
|
435
|
-
}
|
|
436
|
-
TokenTransfer.isTransferComplete = isTransferComplete;
|
|
437
|
-
async function getTransferMessage(chain, txid, timeout) {
|
|
438
|
-
// A Single wormhole message will be returned for a standard token transfer
|
|
439
|
-
const whm = await wormhole_js_1.Wormhole.parseMessageFromTx(chain, txid, timeout);
|
|
440
|
-
if (whm.length !== 1)
|
|
441
|
-
throw new Error("Expected a single Wormhole Message, got: " + whm.length);
|
|
442
|
-
return whm[0];
|
|
443
|
-
}
|
|
444
|
-
TokenTransfer.getTransferMessage = getTransferMessage;
|
|
445
|
-
async function getTransferVaa(wh, key, timeout) {
|
|
446
|
-
const vaa = await wh.getVaa(key, sdk_definitions_1.TokenBridge.getTransferDiscriminator(), timeout);
|
|
447
|
-
if (!vaa)
|
|
448
|
-
return null;
|
|
449
|
-
// Check if its automatic and re-de-serialize
|
|
450
|
-
if (vaa.payloadName === "TransferWithPayload") {
|
|
451
|
-
const { chain, address } = vaa.payload.to;
|
|
452
|
-
const { tokenBridgeRelayer } = wh.config.chains[chain].contracts;
|
|
453
|
-
const relayerAddress = tokenBridgeRelayer ? (0, sdk_definitions_1.toUniversal)(chain, tokenBridgeRelayer) : null;
|
|
454
|
-
// If the target address is the relayer address, expect its an automatic token bridge vaa
|
|
455
|
-
if (!!relayerAddress && address.equals(relayerAddress)) {
|
|
456
|
-
return (0, sdk_definitions_1.deserialize)("AutomaticTokenBridge:TransferWithRelay", (0, sdk_definitions_1.serialize)(vaa));
|
|
457
|
-
}
|
|
458
|
-
// We aren't checking the to address here since it could be different than what's hard-coded in the sdk
|
|
459
|
-
try {
|
|
460
|
-
return (0, sdk_definitions_1.deserialize)("ExecutorTokenBridge:TransferWithExecutorRelay", (0, sdk_definitions_1.serialize)(vaa));
|
|
461
|
-
}
|
|
462
|
-
catch { }
|
|
463
|
-
}
|
|
464
|
-
return vaa;
|
|
465
|
-
}
|
|
466
|
-
TokenTransfer.getTransferVaa = getTransferVaa;
|
|
467
|
-
async function isTransferEnqueued(wh, key) {
|
|
468
|
-
return await wh.getIsVaaEnqueued(key);
|
|
469
|
-
}
|
|
470
|
-
TokenTransfer.isTransferEnqueued = isTransferEnqueued;
|
|
471
|
-
function validateTransferDetails(wh, transfer, fromChain, toChain) {
|
|
472
|
-
if (transfer.amount === 0n)
|
|
473
|
-
throw new Error("Amount cannot be 0");
|
|
474
|
-
if (transfer.from.chain === transfer.to.chain)
|
|
475
|
-
throw new Error("Cannot transfer to the same chain");
|
|
476
|
-
fromChain = fromChain ?? wh.getChain(transfer.from.chain);
|
|
477
|
-
toChain = toChain ?? wh.getChain(transfer.to.chain);
|
|
478
|
-
if (transfer.protocol === "AutomaticTokenBridge") {
|
|
479
|
-
if (!fromChain.supportsAutomaticTokenBridge())
|
|
480
|
-
throw new Error(`Automatic Token Bridge not supported on ${transfer.from.chain}`);
|
|
481
|
-
if (!toChain.supportsAutomaticTokenBridge())
|
|
482
|
-
throw new Error(`Automatic Token Bridge not supported on ${transfer.to.chain}`);
|
|
483
|
-
const nativeGas = transfer.nativeGas ?? 0n;
|
|
484
|
-
if (nativeGas > transfer.amount)
|
|
485
|
-
throw new Error(`Native gas amount > amount (${nativeGas} > ${transfer.amount})`);
|
|
486
|
-
}
|
|
487
|
-
else if (transfer.protocol === "TokenBridge") {
|
|
488
|
-
if (!fromChain.supportsTokenBridge())
|
|
489
|
-
throw new Error(`Token Bridge not supported on ${transfer.from.chain}`);
|
|
490
|
-
if (!toChain.supportsTokenBridge())
|
|
491
|
-
throw new Error(`Token Bridge not supported on ${transfer.to.chain}`);
|
|
492
|
-
}
|
|
493
|
-
else if (transfer.protocol === "ExecutorTokenBridge") {
|
|
494
|
-
if (!fromChain.supportsExecutorTokenBridge())
|
|
495
|
-
throw new Error(`Token Bridge Executor not supported on ${transfer.from.chain}`);
|
|
496
|
-
if (!toChain.supportsExecutorTokenBridge())
|
|
497
|
-
throw new Error(`Token Bridge Executor not supported on ${transfer.to.chain}`);
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
throw new Error("Unknown token transfer protocol");
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
TokenTransfer.validateTransferDetails = validateTransferDetails;
|
|
504
|
-
async function quoteTransfer(wh, srcChain, dstChain, transfer, getCapabilities) {
|
|
505
|
-
const srcTb = await srcChain.getTokenBridge();
|
|
506
|
-
let srcToken;
|
|
507
|
-
if ((0, sdk_definitions_1.isNative)(transfer.token.address)) {
|
|
508
|
-
srcToken = await srcTb.getWrappedNative();
|
|
509
|
-
}
|
|
510
|
-
else if (sdk_definitions_1.UniversalAddress.instanceof(transfer.token.address)) {
|
|
511
|
-
try {
|
|
512
|
-
srcToken = (await srcTb.getWrappedAsset(transfer.token));
|
|
513
|
-
}
|
|
514
|
-
catch (e) {
|
|
515
|
-
if (!e.message.includes("not a wrapped asset"))
|
|
516
|
-
throw e;
|
|
517
|
-
srcToken = await srcTb.getTokenNativeAddress(srcChain.chain, transfer.token.address);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
else {
|
|
521
|
-
srcToken = transfer.token.address;
|
|
522
|
-
}
|
|
523
|
-
// @ts-ignore: TS2339
|
|
524
|
-
const srcTokenId = wormhole_js_1.Wormhole.tokenId(srcChain.chain, srcToken.toString());
|
|
525
|
-
const srcDecimals = await srcChain.getDecimals(srcToken);
|
|
526
|
-
const srcAmount = sdk_base_1.amount.fromBaseUnits(transfer.amount, srcDecimals);
|
|
527
|
-
const srcAmountTruncated = sdk_base_1.amount.truncate(srcAmount, TokenTransfer.MAX_DECIMALS);
|
|
528
|
-
// Ensure the transfer would not violate governor transfer limits
|
|
529
|
-
const [tokens, limits] = await Promise.all([
|
|
530
|
-
(0, whscan_api_js_1.getGovernedTokens)(wh.config.api),
|
|
531
|
-
(0, whscan_api_js_1.getGovernorLimits)(wh.config.api),
|
|
532
|
-
]);
|
|
533
|
-
const warnings = [];
|
|
534
|
-
if (limits !== null && srcChain.chain in limits && tokens !== null) {
|
|
535
|
-
let origAsset;
|
|
536
|
-
if ((0, sdk_definitions_1.isNative)(transfer.token.address)) {
|
|
537
|
-
origAsset = {
|
|
538
|
-
chain: srcChain.chain,
|
|
539
|
-
address: await srcTb.getTokenUniversalAddress(srcToken),
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
try {
|
|
544
|
-
origAsset = await srcTb.getOriginalAsset(transfer.token.address);
|
|
545
|
-
}
|
|
546
|
-
catch (e) {
|
|
547
|
-
if (!e.message.includes("not a wrapped asset"))
|
|
548
|
-
throw e;
|
|
549
|
-
origAsset = {
|
|
550
|
-
chain: srcChain.chain,
|
|
551
|
-
address: await srcTb.getTokenUniversalAddress(srcToken),
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
if (origAsset.chain in tokens && origAsset.address.toString() in tokens[origAsset.chain]) {
|
|
556
|
-
const limit = limits[srcChain.chain];
|
|
557
|
-
const tokenPrice = tokens[origAsset.chain][origAsset.address.toString()];
|
|
558
|
-
const notionalTransferAmt = tokenPrice * sdk_base_1.amount.whole(srcAmountTruncated);
|
|
559
|
-
if (limit.maxSize && notionalTransferAmt > limit.maxSize) {
|
|
560
|
-
warnings.push({
|
|
561
|
-
type: "GovernorLimitWarning",
|
|
562
|
-
reason: "ExceedsLargeTransferLimit",
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
if (notionalTransferAmt > limit.available) {
|
|
566
|
-
warnings.push({
|
|
567
|
-
type: "GovernorLimitWarning",
|
|
568
|
-
reason: "ExceedsRemainingNotional",
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
const dstToken = await TokenTransfer.lookupDestinationToken(srcChain, dstChain, transfer.token);
|
|
574
|
-
const dstDecimals = await dstChain.getDecimals(dstToken.address);
|
|
575
|
-
const dstAmountReceivable = sdk_base_1.amount.scale(srcAmountTruncated, dstDecimals);
|
|
576
|
-
const eta = sdk_base_1.finality.estimateFinalityTime(srcChain.chain) + sdk_base_1.guardians.guardianAttestationEta;
|
|
577
|
-
const baseQuote = {
|
|
578
|
-
sourceToken: {
|
|
579
|
-
token: transfer.token,
|
|
580
|
-
amount: sdk_base_1.amount.units(srcAmountTruncated),
|
|
581
|
-
},
|
|
582
|
-
destinationToken: { token: dstToken, amount: sdk_base_1.amount.units(dstAmountReceivable) },
|
|
583
|
-
warnings: warnings.length > 0 ? warnings : undefined,
|
|
584
|
-
eta,
|
|
585
|
-
};
|
|
586
|
-
if (transfer.protocol === "TokenBridge") {
|
|
587
|
-
return {
|
|
588
|
-
...baseQuote,
|
|
589
|
-
expires: sdk_base_1.time.expiration(24, 0, 0), // manual transfer quote is good for 24 hours
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
if (transfer.protocol === "ExecutorTokenBridge") {
|
|
593
|
-
const executorQuote = await getExecutorQuote(wh, srcChain, dstChain, transfer.gasLimit, transfer.msgValue, transfer.nativeGas, getCapabilities);
|
|
594
|
-
const gasDropOffInstruction = executorQuote.relayInstructions.requests.find((r) => r.request.type === "GasDropOffInstruction");
|
|
595
|
-
const destinationNativeGas = gasDropOffInstruction?.request.type === "GasDropOffInstruction"
|
|
596
|
-
? gasDropOffInstruction.request.dropOff
|
|
597
|
-
: undefined;
|
|
598
|
-
let referrerFee;
|
|
599
|
-
if (transfer.referrerFee &&
|
|
600
|
-
(transfer.referrerFee.transferTokenFee > 0n ||
|
|
601
|
-
transfer.referrerFee.nativeTokenFee > 0n)) {
|
|
602
|
-
const { transferTokenFee, nativeTokenFee, referrer } = transfer.referrerFee;
|
|
603
|
-
// Truncate the source-token fee to the bridge's shared decimal precision
|
|
604
|
-
// so the bridged remainder doesn't carry sub-MAX_DECIMALS dust.
|
|
605
|
-
const truncatedFee = sdk_base_1.amount.truncate(sdk_base_1.amount.fromBaseUnits(transferTokenFee, srcDecimals), TokenTransfer.MAX_DECIMALS);
|
|
606
|
-
const truncatedFeeUnits = sdk_base_1.amount.units(truncatedFee);
|
|
607
|
-
const remainingAmount = sdk_base_1.amount.units(srcAmountTruncated) - truncatedFeeUnits;
|
|
608
|
-
if (remainingAmount <= 0n) {
|
|
609
|
-
throw new Error("Remaining amount after referrer fee is <= 0");
|
|
610
|
-
}
|
|
611
|
-
referrerFee = {
|
|
612
|
-
transferTokenFee: truncatedFeeUnits,
|
|
613
|
-
nativeTokenFee,
|
|
614
|
-
remainingAmount,
|
|
615
|
-
referrer,
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
return {
|
|
619
|
-
...baseQuote,
|
|
620
|
-
destinationToken: {
|
|
621
|
-
token: dstToken,
|
|
622
|
-
amount: referrerFee?.remainingAmount
|
|
623
|
-
? sdk_base_1.amount.units(sdk_base_1.amount.scale(sdk_base_1.amount.fromBaseUnits(referrerFee.remainingAmount, srcDecimals), dstDecimals))
|
|
624
|
-
: sdk_base_1.amount.units(dstAmountReceivable),
|
|
625
|
-
},
|
|
626
|
-
expires: executorQuote.signedQuote.quote.expiryTime,
|
|
627
|
-
relayFee: { token: (0, sdk_definitions_1.nativeTokenId)(srcChain.chain), amount: executorQuote.estimatedCost },
|
|
628
|
-
destinationNativeGas,
|
|
629
|
-
referrerFee: referrerFee
|
|
630
|
-
? {
|
|
631
|
-
transferTokenFee: { token: srcTokenId, amount: referrerFee.transferTokenFee },
|
|
632
|
-
nativeTokenFee: {
|
|
633
|
-
token: (0, sdk_definitions_1.nativeTokenId)(srcChain.chain),
|
|
634
|
-
amount: referrerFee.nativeTokenFee,
|
|
635
|
-
},
|
|
636
|
-
referrer: referrerFee.referrer,
|
|
637
|
-
}
|
|
638
|
-
: undefined,
|
|
639
|
-
details: {
|
|
640
|
-
executorQuote,
|
|
641
|
-
referrerFee,
|
|
642
|
-
},
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
// Otherwise automatic
|
|
646
|
-
if (transfer.protocol !== "AutomaticTokenBridge")
|
|
647
|
-
throw new Error("Unknown token transfer protocol");
|
|
648
|
-
// The fee is removed from the amount transferred
|
|
649
|
-
// quoted on the source chain
|
|
650
|
-
const stb = await srcChain.getAutomaticTokenBridge();
|
|
651
|
-
const fee = await stb.getRelayerFee(dstChain.chain, srcToken);
|
|
652
|
-
const feeAmountDest = sdk_base_1.amount.scale(sdk_base_1.amount.truncate(sdk_base_1.amount.fromBaseUnits(fee, srcDecimals), TokenTransfer.MAX_DECIMALS), dstDecimals);
|
|
653
|
-
// nativeGas is in source chain decimals
|
|
654
|
-
const srcNativeGasAmountRequested = transfer.nativeGas ?? 0n;
|
|
655
|
-
// convert to destination chain decimals
|
|
656
|
-
const dstNativeGasAmountRequested = sdk_base_1.amount.units(sdk_base_1.amount.scale(sdk_base_1.amount.truncate(sdk_base_1.amount.fromBaseUnits(srcNativeGasAmountRequested, srcDecimals), TokenTransfer.MAX_DECIMALS), dstDecimals));
|
|
657
|
-
// TODO: consider moving these solana specific checks to its protocol implementation
|
|
658
|
-
const solanaMinBalanceForRentExemptAccount = 890880n;
|
|
659
|
-
let destinationNativeGas = 0n;
|
|
660
|
-
if (transfer.nativeGas) {
|
|
661
|
-
const dtb = await dstChain.getAutomaticTokenBridge();
|
|
662
|
-
// There is a limit applied to the amount of the source
|
|
663
|
-
// token that may be swapped for native gas on the destination
|
|
664
|
-
const [maxNativeAmountIn, _destinationNativeGas] = await Promise.all([
|
|
665
|
-
dtb.maxSwapAmount(dstToken.address),
|
|
666
|
-
// Get the actual amount we should receive
|
|
667
|
-
dtb.nativeTokenAmount(dstToken.address, dstNativeGasAmountRequested),
|
|
668
|
-
]);
|
|
669
|
-
if (dstNativeGasAmountRequested > maxNativeAmountIn)
|
|
670
|
-
throw new Error(`Native gas amount exceeds maximum swap amount: ${sdk_base_1.amount.fmt(dstNativeGasAmountRequested, dstDecimals)}>${sdk_base_1.amount.fmt(maxNativeAmountIn, dstDecimals)}`);
|
|
671
|
-
// when native gas is requested on solana, the amount must be at least the rent-exempt amount
|
|
672
|
-
// or the transaction could fail if the account does not have enough lamports
|
|
673
|
-
if ((0, sdk_base_2.chainToPlatform)(dstChain.chain) === "Solana" &&
|
|
674
|
-
_destinationNativeGas < solanaMinBalanceForRentExemptAccount) {
|
|
675
|
-
throw new Error(`Native gas amount must be at least ${solanaMinBalanceForRentExemptAccount} lamports`);
|
|
676
|
-
}
|
|
677
|
-
destinationNativeGas = _destinationNativeGas;
|
|
678
|
-
}
|
|
679
|
-
const destAmountLessFee = sdk_base_1.amount.units(dstAmountReceivable) - dstNativeGasAmountRequested - sdk_base_1.amount.units(feeAmountDest);
|
|
680
|
-
// when sending wsol to solana, the amount must be at least the rent-exempt amount
|
|
681
|
-
// or the transaction could fail if the account does not have enough lamports
|
|
682
|
-
if ((0, sdk_base_2.chainToPlatform)(dstToken.chain) === "Solana") {
|
|
683
|
-
const nativeWrappedTokenId = await dstChain.getNativeWrappedTokenId();
|
|
684
|
-
const isNativeSol = (0, sdk_definitions_1.isNative)(dstToken.address) || (0, sdk_definitions_1.isSameToken)(dstToken, nativeWrappedTokenId);
|
|
685
|
-
if (isNativeSol && destAmountLessFee < solanaMinBalanceForRentExemptAccount) {
|
|
686
|
-
throw new Error(`Destination amount must be at least ${solanaMinBalanceForRentExemptAccount} lamports`);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
return {
|
|
690
|
-
sourceToken: {
|
|
691
|
-
token: transfer.token,
|
|
692
|
-
amount: sdk_base_1.amount.units(srcAmountTruncated),
|
|
693
|
-
},
|
|
694
|
-
destinationToken: { token: dstToken, amount: destAmountLessFee },
|
|
695
|
-
relayFee: { token: dstToken, amount: sdk_base_1.amount.units(feeAmountDest) },
|
|
696
|
-
destinationNativeGas,
|
|
697
|
-
warnings: warnings.length > 0 ? warnings : undefined,
|
|
698
|
-
eta,
|
|
699
|
-
expires: sdk_base_1.time.expiration(0, 5, 0), // automatic transfer quote is good for 5 minutes
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
TokenTransfer.quoteTransfer = quoteTransfer;
|
|
703
|
-
async function getExecutorGasDropOffLimit(wh, dstChain, getCapabilities) {
|
|
704
|
-
const capabilities = getCapabilities
|
|
705
|
-
? await getCapabilities(wh.network)
|
|
706
|
-
: await wh.getExecutorCapabilities();
|
|
707
|
-
const dstCapabilities = capabilities[(0, sdk_base_1.toChainId)(dstChain.chain)];
|
|
708
|
-
if (!dstCapabilities) {
|
|
709
|
-
throw new Error(`No executor capabilities found for destination chain ${dstChain.chain}`);
|
|
710
|
-
}
|
|
711
|
-
return BigInt(dstCapabilities.gasDropOffLimit);
|
|
712
|
-
}
|
|
713
|
-
TokenTransfer.getExecutorGasDropOffLimit = getExecutorGasDropOffLimit;
|
|
714
|
-
async function getExecutorQuote(wh, srcChain, dstChain, gasLimit, msgValue, nativeGas, getCapabilities) {
|
|
715
|
-
const capabilities = getCapabilities
|
|
716
|
-
? await getCapabilities(wh.network)
|
|
717
|
-
: await wh.getExecutorCapabilities();
|
|
718
|
-
const srcCapabilities = capabilities[(0, sdk_base_1.toChainId)(srcChain.chain)];
|
|
719
|
-
if (!srcCapabilities) {
|
|
720
|
-
throw new Error(`No executor capabilities found for source chain ${srcChain.chain}`);
|
|
721
|
-
}
|
|
722
|
-
const dstCapabilities = capabilities[(0, sdk_base_1.toChainId)(dstChain.chain)];
|
|
723
|
-
if (!dstCapabilities || !dstCapabilities.requestPrefixes.includes("ERV1")) {
|
|
724
|
-
throw new Error(`No executor capabilities found for destination chain ${dstChain.chain}`);
|
|
725
|
-
}
|
|
726
|
-
// Validate nativeGas doesn't exceed limit
|
|
727
|
-
const dropOff = nativeGas ?? 0n;
|
|
728
|
-
if (dropOff > BigInt(dstCapabilities.gasDropOffLimit)) {
|
|
729
|
-
throw new Error(`Native gas amount ${dropOff} exceeds limit ${BigInt(dstCapabilities.gasDropOffLimit)} for destination chain ${dstChain.chain}`);
|
|
730
|
-
}
|
|
731
|
-
const instructions = [];
|
|
732
|
-
instructions.push({
|
|
733
|
-
request: {
|
|
734
|
-
type: "GasInstruction",
|
|
735
|
-
gasLimit,
|
|
736
|
-
msgValue,
|
|
737
|
-
},
|
|
738
|
-
});
|
|
739
|
-
if (dropOff > 0n) {
|
|
740
|
-
instructions.push({
|
|
741
|
-
request: {
|
|
742
|
-
type: "GasDropOffInstruction",
|
|
743
|
-
dropOff,
|
|
744
|
-
// Placeholder recipient for gas drop-off. This will be set at transfer time.
|
|
745
|
-
recipient: sdk_definitions_1.UniversalAddress.ZERO,
|
|
746
|
-
},
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
const relayInstructions = {
|
|
750
|
-
requests: instructions,
|
|
751
|
-
};
|
|
752
|
-
const relayInstructionsBytes = (0, sdk_base_1.serializeLayout)(sdk_definitions_1.relayInstructionsLayout, relayInstructions);
|
|
753
|
-
const executorQuote = await wh.getExecutorQuote(srcChain.chain, dstChain.chain, sdk_base_1.encoding.hex.encode(relayInstructionsBytes, true));
|
|
754
|
-
if (!executorQuote.estimatedCost) {
|
|
755
|
-
throw new Error("No estimated cost");
|
|
756
|
-
}
|
|
757
|
-
const estimatedCost = BigInt(executorQuote.estimatedCost);
|
|
758
|
-
const signedQuote = (0, sdk_base_1.deserializeLayout)(sdk_definitions_1.signedQuoteLayout, sdk_base_1.encoding.hex.decode(executorQuote.signedQuote));
|
|
759
|
-
return {
|
|
760
|
-
signedQuote,
|
|
761
|
-
estimatedCost,
|
|
762
|
-
relayInstructions,
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
TokenTransfer.getExecutorQuote = getExecutorQuote;
|
|
766
|
-
async function destinationOverrides(srcChain, dstChain, transfer) {
|
|
767
|
-
const _transfer = { ...transfer };
|
|
768
|
-
// Bit of (temporary) hackery until solana contracts support being
|
|
769
|
-
// sent a VAA with the primary address
|
|
770
|
-
// Note: Do _not_ override if automatic or if the destination token is native
|
|
771
|
-
// gas token
|
|
772
|
-
if ((0, sdk_base_2.chainToPlatform)(transfer.to.chain) === "Solana" && _transfer.protocol === "TokenBridge") {
|
|
773
|
-
const destinationToken = await TokenTransfer.lookupDestinationToken(srcChain, dstChain, _transfer.token);
|
|
774
|
-
if ((0, sdk_definitions_1.isNative)(destinationToken.address)) {
|
|
775
|
-
const nativeWrappedTokenId = await dstChain.getNativeWrappedTokenId();
|
|
776
|
-
_transfer.to = await dstChain.getTokenAccount(_transfer.to.address, nativeWrappedTokenId.address);
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
_transfer.to = await dstChain.getTokenAccount(_transfer.to.address, destinationToken.address);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
if (_transfer.to.chain === "Sei" && _transfer.protocol === "TokenBridge") {
|
|
783
|
-
if (_transfer.to.chain === "Sei" && _transfer.payload)
|
|
784
|
-
throw new Error("Arbitrary payloads unsupported for Sei");
|
|
785
|
-
// For sei, we reserve the payload for a token transfer through the sei bridge.
|
|
786
|
-
_transfer.payload = sdk_base_1.encoding.bytes.encode(JSON.stringify({
|
|
787
|
-
basic_recipient: {
|
|
788
|
-
recipient: sdk_base_1.encoding.b64.encode(_transfer.to.address.toString()),
|
|
789
|
-
},
|
|
790
|
-
}));
|
|
791
|
-
const translator = dstChain.config.contracts.translator;
|
|
792
|
-
if (translator === undefined || translator === "")
|
|
793
|
-
throw new Error("Unexpected empty translator address");
|
|
794
|
-
_transfer.to = wormhole_js_1.Wormhole.chainAddress(_transfer.to.chain, translator);
|
|
795
|
-
}
|
|
796
|
-
return _transfer;
|
|
797
|
-
}
|
|
798
|
-
TokenTransfer.destinationOverrides = destinationOverrides;
|
|
799
|
-
})(TokenTransfer || (exports.TokenTransfer = TokenTransfer = {}));
|
|
800
|
-
//# sourceMappingURL=tokenTransfer.js.map
|