@gibs/bridge-sdk 1.10.1 → 1.13.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/LICENSE +15 -0
- package/README.md +234 -432
- package/dist/abis.d.ts +1 -0
- package/dist/abis.js +3 -0
- package/dist/bridge-limits.d.ts +103 -0
- package/dist/bridge-limits.js +84 -0
- package/dist/{src/chain-info.d.ts → chain-info.d.ts} +66 -27
- package/dist/{src/chain-info.js → chain-info.js} +106 -46
- package/dist/chain-names.d.ts +160 -0
- package/dist/chain-names.js +200 -0
- package/dist/chains.d.ts +65 -0
- package/dist/chains.js +284 -0
- package/dist/config.d.ts +216 -0
- package/dist/{src/config.js → config.js} +151 -44
- package/dist/consolidated-quote.d.ts +554 -0
- package/dist/consolidated-quote.js +773 -0
- package/dist/destination-calldata.d.ts +118 -0
- package/dist/destination-calldata.js +152 -0
- package/dist/ecosystems.d.ts +217 -0
- package/dist/ecosystems.js +288 -0
- package/dist/hyperliquid-execution.d.ts +152 -0
- package/dist/hyperliquid-execution.js +168 -0
- package/dist/image-links.d.ts +117 -0
- package/dist/image-links.js +142 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/lifi-execution-guard.d.ts +199 -0
- package/dist/lifi-execution-guard.js +400 -0
- package/dist/near-intents-conformance.d.ts +85 -0
- package/dist/near-intents-conformance.js +329 -0
- package/dist/near-intents-route.d.ts +47 -0
- package/dist/near-intents-route.js +265 -0
- package/dist/near-intents.d.ts +524 -0
- package/dist/near-intents.js +1055 -0
- package/dist/origin-deposit.d.ts +326 -0
- package/dist/origin-deposit.js +68 -0
- package/dist/pre-quote.d.ts +198 -0
- package/dist/pre-quote.js +175 -0
- package/dist/prices.d.ts +107 -0
- package/dist/prices.js +162 -0
- package/dist/providers.d.ts +221 -0
- package/dist/providers.js +157 -0
- package/dist/quote-fees.d.ts +102 -0
- package/dist/quote-fees.js +195 -0
- package/dist/referral.d.ts +77 -0
- package/dist/referral.js +72 -0
- package/dist/relay-currencies.d.ts +47 -0
- package/dist/relay-currencies.js +89 -0
- package/dist/relay-quote.d.ts +301 -0
- package/dist/relay-quote.js +983 -0
- package/dist/route-steps.d.ts +138 -0
- package/dist/route-steps.js +305 -0
- package/dist/routing/amounts.d.ts +80 -0
- package/dist/routing/amounts.js +120 -0
- package/dist/routing/candidate-route.d.ts +43 -0
- package/dist/routing/candidate-route.js +45 -0
- package/dist/routing/candidate.d.ts +237 -0
- package/dist/routing/candidate.js +96 -0
- package/dist/routing/carriers/ask-answer.d.ts +58 -0
- package/dist/routing/carriers/omnibridge.d.ts +137 -0
- package/dist/routing/carriers/omnibridge.js +419 -0
- package/dist/routing/carriers/pulsex.d.ts +111 -0
- package/dist/routing/carriers/pulsex.js +404 -0
- package/dist/routing/carriers/wrap.d.ts +25 -0
- package/dist/routing/carriers/wrap.js +115 -0
- package/dist/routing/composed-route-quality.d.ts +103 -0
- package/dist/routing/composed-route-quality.js +108 -0
- package/dist/routing/entry-chain-anchor.d.ts +88 -0
- package/dist/routing/entry-chain-anchor.js +55 -0
- package/dist/routing/format.d.ts +31 -0
- package/dist/routing/format.js +58 -0
- package/dist/routing/index.d.ts +34 -0
- package/dist/routing/index.js +42 -0
- package/dist/routing/producers.d.ts +770 -0
- package/dist/routing/producers.js +1564 -0
- package/dist/routing/providers.d.ts +26 -0
- package/dist/routing/providers.js +38 -0
- package/dist/routing/route-data.d.ts +355 -0
- package/dist/routing/route-data.js +1 -0
- package/dist/routing/search/compose.d.ts +168 -0
- package/dist/routing/search/compose.js +98 -0
- package/dist/routing/search/graph.d.ts +639 -0
- package/dist/routing/search/graph.js +445 -0
- package/dist/routing/search/plan-route-data.d.ts +244 -0
- package/dist/routing/search/plan-route-data.js +767 -0
- package/dist/routing/search/score.d.ts +324 -0
- package/dist/routing/search/score.js +400 -0
- package/dist/routing/usd-value.d.ts +17 -0
- package/dist/routing/usd-value.js +25 -0
- package/dist/settings.d.ts +692 -0
- package/dist/{src/settings.js → settings.js} +338 -71
- package/dist/ton-execution.d.ts +117 -0
- package/dist/ton-execution.js +247 -0
- package/dist/tron-execution.d.ts +146 -0
- package/dist/tron-execution.js +239 -0
- package/dist/{src/types.d.ts → types.d.ts} +8 -1
- package/dist/types.js +1 -0
- package/package.json +210 -23
- package/dist/src/abis.d.ts +0 -384
- package/dist/src/abis.js +0 -68
- package/dist/src/chains.d.ts +0 -6
- package/dist/src/chains.js +0 -86
- package/dist/src/config.d.ts +0 -136
- package/dist/src/image-links.d.ts +0 -41
- package/dist/src/image-links.js +0 -45
- package/dist/src/index.d.ts +0 -8
- package/dist/src/index.js +0 -8
- package/dist/src/settings.d.ts +0 -357
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/abis.ts +0 -70
- package/src/chain-info.ts +0 -464
- package/src/chains.ts +0 -88
- package/src/config.ts +0 -480
- package/src/fee-type.ts +0 -14
- package/src/image-links.ts +0 -61
- package/src/index.ts +0 -8
- package/src/settings.ts +0 -898
- package/src/types.ts +0 -48
- package/tsconfig.json +0 -16
- /package/dist/{src/fee-type.d.ts → fee-type.d.ts} +0 -0
- /package/dist/{src/fee-type.js → fee-type.js} +0 -0
- /package/dist/{src/types.js → routing/carriers/ask-answer.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { concatHex, encodeAbiParameters, encodeFunctionData, getAddress, isAddress, zeroAddress } from
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
1
|
+
import { concatHex, encodeAbiParameters, encodeFunctionData, getAddress, isAddress, isAddressEqual, zeroAddress, } from 'viem';
|
|
2
|
+
import * as abis from './abis.js';
|
|
3
|
+
import { isNative, nativeAssetOut, pathway } from './config.js';
|
|
4
|
+
import { FeeType } from './fee-type.js';
|
|
5
5
|
export const oneEther = 10n ** 18n;
|
|
6
6
|
export const basisPoints = 10000n;
|
|
7
7
|
/**
|
|
@@ -11,7 +11,7 @@ export const basisPoints = 10000n;
|
|
|
11
11
|
* @param assetIn - the asset in to bridge
|
|
12
12
|
* @returns the bridge pathway
|
|
13
13
|
*/
|
|
14
|
-
export const bridgePathway = ({ bridgeKey, isProd, assetIn, }) =>
|
|
14
|
+
export const bridgePathway = ({ bridgeKey, isProd, assetIn, }) => pathway(bridgeKey, isProd, assetIn?.address) ?? null;
|
|
15
15
|
/**
|
|
16
16
|
* Retrieves the amount to bridge from the amount and asset in. If either of them is nil, returns 0n
|
|
17
17
|
* @param amountIn - the amount to bridge
|
|
@@ -126,7 +126,7 @@ export const estimatedTokenNetworkCost = ({ estimatedNativeNetworkCost, priceCor
|
|
|
126
126
|
* @param assetIn - the asset in to bridge
|
|
127
127
|
* @returns the one token int
|
|
128
128
|
*/
|
|
129
|
-
export const oneTokenInt = ({ assetIn
|
|
129
|
+
export const oneTokenInt = ({ assetIn }) => {
|
|
130
130
|
if (!assetIn)
|
|
131
131
|
return 1n;
|
|
132
132
|
return 10n ** BigInt(assetIn.decimals);
|
|
@@ -154,7 +154,11 @@ export const networkSwitchAssetOutAddress = ({ assetOut, unwrap, toChain, }) =>
|
|
|
154
154
|
return null;
|
|
155
155
|
if (!unwrap)
|
|
156
156
|
return assetOut.address;
|
|
157
|
-
|
|
157
|
+
const wrappedNative = nativeAssetOut[toChain];
|
|
158
|
+
// Addresses arrive in whatever case their source used (a checksummed literal
|
|
159
|
+
// here, a lower-cased list entry there), so equality must ignore case.
|
|
160
|
+
const isWrappedNative = !!wrappedNative && isAddress(assetOut.address, { strict: false }) && isAddressEqual(wrappedNative, assetOut.address);
|
|
161
|
+
return isWrappedNative ? zeroAddress : assetOut.address;
|
|
158
162
|
};
|
|
159
163
|
/**
|
|
160
164
|
* Calculates the desired excess compensation basis points from the path, asset in, asset out, bridge key, fee type, list, and asset out address
|
|
@@ -171,7 +175,7 @@ export const desiredExcessCompensationBasisPoints = ({ path, assetIn, assetOut,
|
|
|
171
175
|
if (!path?.requiresDelivery) {
|
|
172
176
|
return 0n;
|
|
173
177
|
}
|
|
174
|
-
return
|
|
178
|
+
return !assetIn
|
|
175
179
|
? 0n
|
|
176
180
|
: feeType === FeeType.PERCENT
|
|
177
181
|
? 1000n
|
|
@@ -180,36 +184,51 @@ export const desiredExcessCompensationBasisPoints = ({ path, assetIn, assetOut,
|
|
|
180
184
|
: list.has(getAddress(assetIn?.address ?? '')) ||
|
|
181
185
|
list.has(assetOutAddress ? getAddress(assetOutAddress) : '0x')
|
|
182
186
|
? 5000n
|
|
183
|
-
: 10000n
|
|
187
|
+
: 10000n;
|
|
184
188
|
};
|
|
185
189
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
190
|
+
* Works out the Gibs delivery fee for a transfer, as a {@link DeliveryFee}.
|
|
191
|
+
*
|
|
192
|
+
* Each mode reads the SAME figure the delivered estimate subtracts
|
|
193
|
+
* ({@link estimatedFee}), so a route's cost and its delivered value reconcile:
|
|
194
|
+
*
|
|
195
|
+
* - percent: the arriving value times the rate. The router computes
|
|
196
|
+
* `value * multiplier / 1 ether`.
|
|
197
|
+
* - fixed: the fixed amount. The router takes `limit` as the fee.
|
|
198
|
+
* - gas: the limit. The router takes the gas cost, capped at the limit, so the
|
|
199
|
+
* limit is the most the reader pays and the figure the delivered estimate
|
|
200
|
+
* already assumes.
|
|
201
|
+
*
|
|
202
|
+
* `percentFee` must be null when the rate is not known. Passing a zero stand-in
|
|
203
|
+
* would state a free delivery.
|
|
204
|
+
*
|
|
205
|
+
* @param options.shouldDeliver - whether the reader asked for delivery
|
|
206
|
+
* @param options.routeOffersDelivery - whether this pathway delivers for a fee at all
|
|
207
|
+
* @param options.feeType - the delivery fee mode
|
|
208
|
+
* @param options.amountAfterBridgeFee - the value that reaches the destination router, or null
|
|
209
|
+
* @param options.percentFee - the percent rate as a fraction of one ether, or null when unknown
|
|
210
|
+
* @param options.fixedFee - the fixed fee in base units, or null when unknown
|
|
211
|
+
* @param options.limit - the gas mode cap in base units, or null when unknown
|
|
212
|
+
* @returns the delivery fee
|
|
195
213
|
*/
|
|
196
|
-
export const
|
|
197
|
-
if (!shouldDeliver) {
|
|
198
|
-
return
|
|
214
|
+
export const deliveryFee = ({ shouldDeliver, routeOffersDelivery, feeType, amountAfterBridgeFee, percentFee, fixedFee, limit, }) => {
|
|
215
|
+
if (!shouldDeliver || !routeOffersDelivery) {
|
|
216
|
+
return { status: 'not-requested' };
|
|
199
217
|
}
|
|
200
|
-
if (feeType === FeeType.
|
|
201
|
-
|
|
202
|
-
return null;
|
|
203
|
-
return (amountAfterBridgeFee * (percentFee ?? 0n)) / oneEther;
|
|
218
|
+
if (feeType === FeeType.FIXED) {
|
|
219
|
+
return { status: 'requested', amount: fixedFee, rate: null };
|
|
204
220
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return null;
|
|
221
|
+
if (feeType === FeeType.GAS_TIP) {
|
|
222
|
+
return { status: 'requested', amount: limit, rate: null };
|
|
208
223
|
}
|
|
209
|
-
if (
|
|
210
|
-
return null;
|
|
224
|
+
if (amountAfterBridgeFee === null || percentFee === null) {
|
|
225
|
+
return { status: 'requested', amount: null, rate: percentFee };
|
|
211
226
|
}
|
|
212
|
-
return
|
|
227
|
+
return {
|
|
228
|
+
status: 'requested',
|
|
229
|
+
amount: (amountAfterBridgeFee * percentFee) / oneEther,
|
|
230
|
+
rate: percentFee,
|
|
231
|
+
};
|
|
213
232
|
};
|
|
214
233
|
/**
|
|
215
234
|
* Calculates the reasonable fixed fee from the estimated token network cost and desired excess compensation basis points
|
|
@@ -221,8 +240,7 @@ export const reasonableFixedFee = ({ estimatedTokenNetworkCost, desiredExcessCom
|
|
|
221
240
|
if (!estimatedTokenNetworkCost) {
|
|
222
241
|
return null;
|
|
223
242
|
}
|
|
224
|
-
return ((estimatedTokenNetworkCost * (25000n + desiredExcessCompensationBasisPoints)) /
|
|
225
|
-
basisPoints);
|
|
243
|
+
return ((estimatedTokenNetworkCost * (25000n + desiredExcessCompensationBasisPoints)) / basisPoints);
|
|
226
244
|
};
|
|
227
245
|
/**
|
|
228
246
|
* Calculates the reasonable percent on gas limit from the gas tip fee and estimated token network cost
|
|
@@ -231,8 +249,7 @@ export const reasonableFixedFee = ({ estimatedTokenNetworkCost, desiredExcessCom
|
|
|
231
249
|
* @returns the reasonable percent on gas limit
|
|
232
250
|
*/
|
|
233
251
|
export const reasonablePercentOnGasLimit = ({ gasTipFee, estimatedTokenNetworkCost, }) => {
|
|
234
|
-
return ((25000n *
|
|
235
|
-
((oneEther + (gasTipFee ?? 0n)) * (estimatedTokenNetworkCost ?? 0n))) /
|
|
252
|
+
return ((25000n * ((oneEther + (gasTipFee ?? 0n)) * (estimatedTokenNetworkCost ?? 0n))) /
|
|
236
253
|
(oneEther * basisPoints));
|
|
237
254
|
};
|
|
238
255
|
/**
|
|
@@ -306,8 +323,7 @@ export const feeDirectorStructEncoded = ({ recipient, bridgePathway, feeType, pr
|
|
|
306
323
|
let multiplier = 0n;
|
|
307
324
|
if (feeType === FeeType.GAS_TIP && priceCorrective > 0n) {
|
|
308
325
|
multiplier =
|
|
309
|
-
((oneEther + (gasTipFee ?? 0n)) * 10n ** BigInt(assetOut.decimals)) /
|
|
310
|
-
priceCorrective;
|
|
326
|
+
((oneEther + (gasTipFee ?? 0n)) * 10n ** BigInt(assetOut.decimals)) / priceCorrective;
|
|
311
327
|
}
|
|
312
328
|
else if (feeType === FeeType.PERCENT) {
|
|
313
329
|
multiplier = percentFee ?? 0n;
|
|
@@ -340,15 +356,12 @@ export const destinationDataParam = ({ recipient, bridgePathway, assetIn, destin
|
|
|
340
356
|
return null;
|
|
341
357
|
}
|
|
342
358
|
if (!destinationRouter) {
|
|
343
|
-
console.log('no destination router');
|
|
344
359
|
return null;
|
|
345
360
|
}
|
|
346
361
|
if (bridgePathway.requiresDelivery && !feeDirectorStructEncoded) {
|
|
347
|
-
console.log('no fee director struct encoded');
|
|
348
362
|
return null;
|
|
349
363
|
}
|
|
350
364
|
if (!assetLink) {
|
|
351
|
-
console.log('no asset link');
|
|
352
365
|
return null;
|
|
353
366
|
}
|
|
354
367
|
if (bridgePathway.requiresDelivery) {
|
|
@@ -375,7 +388,7 @@ export const destinationDataParam = ({ recipient, bridgePathway, assetIn, destin
|
|
|
375
388
|
* @returns the reasonable percent on top of gas fee
|
|
376
389
|
*/
|
|
377
390
|
export const reasonablePercentOnTopOfGasFee = ({ desiredExcessCompensationBasisPoints, }) => {
|
|
378
|
-
return
|
|
391
|
+
return desiredExcessCompensationBasisPoints * (oneEther / basisPoints);
|
|
379
392
|
};
|
|
380
393
|
/**
|
|
381
394
|
* Calculates the desired compensation ratio from the desired excess compensation basis points
|
|
@@ -436,8 +449,279 @@ export const isUndercompensated = ({ estimatedTokenNetworkCost, feeType, limit,
|
|
|
436
449
|
// Undercompensated if desired compensation exceeds the limit
|
|
437
450
|
return desiredCompensationForNetworkCost > limit;
|
|
438
451
|
};
|
|
452
|
+
/**
|
|
453
|
+
* Why a bridge key must not be built into a transaction for the pair on screen,
|
|
454
|
+
* or null when the two agree.
|
|
455
|
+
*
|
|
456
|
+
* WHY THIS EXISTS, in the reader's words: "i just tried to swap from weth from
|
|
457
|
+
* ethereum on pls, to pls, and the calldata that i must have signed was to
|
|
458
|
+
* bridge back from pls to eth". A selection of PulseChain on BOTH sides is a
|
|
459
|
+
* swap; it has no pathway, so the bridge key kept whatever crossing it last
|
|
460
|
+
* held — or the address bar was repaired to a crossing — and the settings
|
|
461
|
+
* cascade went on building a real, signable transfer of the reader's bridged
|
|
462
|
+
* wrapped ether to Ethereum while the screen still named PulseChain.
|
|
463
|
+
*
|
|
464
|
+
* Three refusals, and each one is a separate way the two can part company:
|
|
465
|
+
*
|
|
466
|
+
* - A SAME-CHAIN SELECTION IS NEVER A CROSSING. Origin and destination on one
|
|
467
|
+
* chain describes a swap. No omnibridge pathway can serve it, so any pathway
|
|
468
|
+
* reached for here belongs to some other journey.
|
|
469
|
+
* - THE PATHWAY MUST LEAVE THE CHAIN THE READER'S FUNDS ARE ON. A key whose
|
|
470
|
+
* source chain differs spends a token the reader is not looking at.
|
|
471
|
+
* - THE PATHWAY MUST LAND WHERE THE READER ASKED. This is the reported defect:
|
|
472
|
+
* the destination silently became Ethereum.
|
|
473
|
+
*
|
|
474
|
+
* `signedCrossing` is `null` for a call no reader composed — the aggregator's
|
|
475
|
+
* executor call built by `buildDestinationCalldata`, where the chain pair IS
|
|
476
|
+
* the pathway and there is no screen to disagree with. Nullable rather than
|
|
477
|
+
* optional so every caller has to decide, and the decision is visible at the
|
|
478
|
+
* call site.
|
|
479
|
+
*
|
|
480
|
+
* @param bridgeKey - the key the pathway was resolved from
|
|
481
|
+
* @param signedCrossing - the pair on screen, or null when no surface composed it
|
|
482
|
+
* @returns the reason to refuse, or null when the two agree
|
|
483
|
+
*/
|
|
484
|
+
export const crossingRefusal = ({ bridgeKey, signedCrossing, }) => {
|
|
485
|
+
// `undefined` is treated as `null` here, and only here. The field is REQUIRED
|
|
486
|
+
// by the type, so no compiled caller can omit it — but a test double that
|
|
487
|
+
// returns a partial object can, and a crash inside the settings cascade blanks
|
|
488
|
+
// the page rather than refusing a transaction.
|
|
489
|
+
if (signedCrossing === null || signedCrossing === undefined) {
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
const { originChainId, destinationChainId } = signedCrossing;
|
|
493
|
+
if (originChainId === destinationChainId) {
|
|
494
|
+
return `Refusing to build a crossing for a selection that stays on chain ${originChainId}. Origin and destination are the same chain, which is a swap, not a bridge.`;
|
|
495
|
+
}
|
|
496
|
+
if (!bridgeKey) {
|
|
497
|
+
return `Refusing to build a crossing from chain ${originChainId} to chain ${destinationChainId}: no bridge key was resolved for it.`;
|
|
498
|
+
}
|
|
499
|
+
const pathwayOriginChainId = Number(bridgeKey[1]);
|
|
500
|
+
const pathwayDestinationChainId = Number(bridgeKey[2]);
|
|
501
|
+
if (pathwayOriginChainId !== originChainId) {
|
|
502
|
+
return `Refusing to build a crossing that leaves chain ${pathwayOriginChainId} while the selection starts on chain ${originChainId}.`;
|
|
503
|
+
}
|
|
504
|
+
if (pathwayDestinationChainId !== destinationChainId) {
|
|
505
|
+
return `Refusing to build a crossing that delivers on chain ${pathwayDestinationChainId} while the selection asks for chain ${destinationChainId}.`;
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
};
|
|
509
|
+
/**
|
|
510
|
+
* Throws when the bridge key and the pair on screen describe different journeys.
|
|
511
|
+
*
|
|
512
|
+
* The LOUD half of {@link crossingRefusal}. {@link transactionInputs} runs
|
|
513
|
+
* during render, where a throw would blank the application, so it refuses
|
|
514
|
+
* quietly by returning null and the button it feeds stays inert. This is for
|
|
515
|
+
* the broadcast funnel, where a disagreement means something reconstructed the
|
|
516
|
+
* call another way and the reader is one signature from a transfer they never
|
|
517
|
+
* asked for — there, silence is the worse failure.
|
|
518
|
+
*
|
|
519
|
+
* @param bridgeKey - the key the pathway was resolved from
|
|
520
|
+
* @param signedCrossing - the pair on screen, or null when no surface composed it
|
|
521
|
+
* @throws when the two describe different journeys
|
|
522
|
+
*/
|
|
523
|
+
export const assertCrossingAgrees = ({ bridgeKey, signedCrossing, }) => {
|
|
524
|
+
const refusal = crossingRefusal({ bridgeKey, signedCrossing });
|
|
525
|
+
if (refusal !== null) {
|
|
526
|
+
throw new Error(refusal);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
/**
|
|
530
|
+
* Why a same-chain PulseX swap transaction must not be built for the leg on
|
|
531
|
+
* screen, or null when they agree. The swap-side sibling of
|
|
532
|
+
* {@link crossingRefusal}.
|
|
533
|
+
*
|
|
534
|
+
* Two refusals, each a separate way the two can part company:
|
|
535
|
+
*
|
|
536
|
+
* - THE LEG ON SCREEN NAMES A CROSSING. A caller building a swap transaction
|
|
537
|
+
* while the reader is looking at a two-chain crossing would sign the wrong
|
|
538
|
+
* kind of thing for what is on screen.
|
|
539
|
+
* - THE SWAP DOES NOT EXECUTE ON THE CHAIN THE READER IS LOOKING AT. A stale
|
|
540
|
+
* candidate built for a chain the address bar has since left.
|
|
541
|
+
*
|
|
542
|
+
* @param signedLeg - what the surface believes this signature does, or null
|
|
543
|
+
* when no surface composed it
|
|
544
|
+
* @param chainId - the chain the candidate swap transaction actually executes on
|
|
545
|
+
* @returns the reason to refuse, or null when the two agree
|
|
546
|
+
*/
|
|
547
|
+
export const swapRefusal = ({ signedLeg, chainId, }) => {
|
|
548
|
+
// `undefined` treated as `null`, matching `crossingRefusal`'s own guard: the
|
|
549
|
+
// field is required by the type, so only a test double or a crash inside the
|
|
550
|
+
// cascade could omit it.
|
|
551
|
+
if (signedLeg === null || signedLeg === undefined) {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
if (signedLeg.kind !== 'swap') {
|
|
555
|
+
return `Refusing to build a same-chain swap for a selection that names a ${signedLeg.kind} (chain ${signedLeg.originChainId} to chain ${signedLeg.destinationChainId}).`;
|
|
556
|
+
}
|
|
557
|
+
if (chainId === null) {
|
|
558
|
+
return `Refusing to build a same-chain swap: no chain was resolved for it.`;
|
|
559
|
+
}
|
|
560
|
+
if (signedLeg.chainId !== chainId) {
|
|
561
|
+
return `Refusing to build a swap on chain ${chainId} while the selection names chain ${signedLeg.chainId}.`;
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* Throws when the leg on screen and the candidate swap transaction describe
|
|
567
|
+
* different journeys. The swap-side sibling of {@link assertCrossingAgrees} —
|
|
568
|
+
* loud where {@link swapRefusal} is quiet, for the same reason: this runs one
|
|
569
|
+
* step before the wallet, where silence is what lets a reader sign.
|
|
570
|
+
*
|
|
571
|
+
* @param signedLeg - what the surface believes this signature does, or null
|
|
572
|
+
* @param chainId - the chain the candidate swap transaction actually executes on
|
|
573
|
+
* @throws when the two describe different journeys
|
|
574
|
+
*/
|
|
575
|
+
export const assertSwapAgrees = ({ signedLeg, chainId, }) => {
|
|
576
|
+
const refusal = swapRefusal({ signedLeg, chainId });
|
|
577
|
+
if (refusal !== null) {
|
|
578
|
+
throw new Error(refusal);
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
/**
|
|
582
|
+
* Why a candidate must not be signed for the leg and section a caller is
|
|
583
|
+
* about to build calldata for, or null when they agree. The candidate-pick
|
|
584
|
+
* sibling of {@link crossingRefusal}/{@link swapRefusal}.
|
|
585
|
+
*
|
|
586
|
+
* UNLIKE THOSE TWO, A MISSING PICK IS ITSELF A REFUSAL. `crossingRefusal` and
|
|
587
|
+
* `swapRefusal` let a null leg through because some calls compose no screen at
|
|
588
|
+
* all (the aggregator's own executor call). A candidate-signing sender always
|
|
589
|
+
* has a screen — the route the reader picked — so a candidate id with no
|
|
590
|
+
* recorded leg is exactly the gap this type exists to close: `candidateId`
|
|
591
|
+
* alone used to be treated as sufficient identity, and this function is what
|
|
592
|
+
* stops it from being treated that way again.
|
|
593
|
+
*
|
|
594
|
+
* @param pick - what the route recorded it picked, or null/undefined when the
|
|
595
|
+
* producer that built the route never set one
|
|
596
|
+
* @param leg - the leg the caller is about to build calldata for
|
|
597
|
+
* @param sectionIndex - the route section the caller is about to build
|
|
598
|
+
* calldata for
|
|
599
|
+
* @returns the reason to refuse, or null when they agree
|
|
600
|
+
*/
|
|
601
|
+
export const candidatePickRefusal = ({ pick, leg, sectionIndex, }) => {
|
|
602
|
+
if (pick === null || pick === undefined) {
|
|
603
|
+
return `Refusing to sign a ${leg} at section ${sectionIndex}: the route named no candidate to sign.`;
|
|
604
|
+
}
|
|
605
|
+
if (pick.leg !== leg) {
|
|
606
|
+
return `Refusing to sign candidate ${pick.candidateId}'s ${pick.leg} leg as a ${leg}: the route picked a different kind of leg.`;
|
|
607
|
+
}
|
|
608
|
+
if (pick.sectionIndex !== sectionIndex) {
|
|
609
|
+
return `Refusing to sign candidate ${pick.candidateId} for section ${sectionIndex}: the route picked it for section ${pick.sectionIndex}.`;
|
|
610
|
+
}
|
|
611
|
+
return null;
|
|
612
|
+
};
|
|
613
|
+
/**
|
|
614
|
+
* Throws when a candidate pick and the leg/section a caller is about to build
|
|
615
|
+
* calldata for describe different signatures. The candidate-pick sibling of
|
|
616
|
+
* {@link assertCrossingAgrees}/{@link assertSwapAgrees} — loud where {@link
|
|
617
|
+
* candidatePickRefusal} is quiet, for the same reason: this runs one step
|
|
618
|
+
* before the wallet, where silence is what lets a reader sign the wrong thing.
|
|
619
|
+
*
|
|
620
|
+
* @param pick - what the route recorded it picked, or null
|
|
621
|
+
* @param leg - the leg the caller is about to build calldata for
|
|
622
|
+
* @param sectionIndex - the route section the caller is about to build
|
|
623
|
+
* calldata for
|
|
624
|
+
* @throws when the pick and the leg/section being built describe different
|
|
625
|
+
* signatures
|
|
626
|
+
*/
|
|
627
|
+
export const assertCandidatePickAgrees = ({ pick, leg, sectionIndex, }) => {
|
|
628
|
+
const refusal = candidatePickRefusal({ pick, leg, sectionIndex });
|
|
629
|
+
if (refusal !== null) {
|
|
630
|
+
throw new Error(refusal);
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
/**
|
|
634
|
+
* Calculates the same-chain PulseX swap transaction inputs, or null when the
|
|
635
|
+
* inputs cannot form a call — including when the leg on screen and the swap's
|
|
636
|
+
* own chain disagree. The swap-side sibling of {@link transactionInputs}.
|
|
637
|
+
*
|
|
638
|
+
* SAME SHAPE AS `transactionInputs`, deliberately: `{ to, value, data } | null`,
|
|
639
|
+
* so a caller can dispatch either builder's result through one `sendTransaction`
|
|
640
|
+
* without a second shape to handle.
|
|
641
|
+
*
|
|
642
|
+
* WRAPPING AND UNWRAPPING THE NATIVE COIN USE THE ROUTER'S OWN NATIVE-COIN
|
|
643
|
+
* FUNCTIONS, not a separate deposit/withdraw step — one router contract wraps
|
|
644
|
+
* on the way in (`swapExactETHForTokens`) and unwraps on the way out
|
|
645
|
+
* (`swapExactTokensForETH`), the same contract `swapExactTokensForTokens`
|
|
646
|
+
* already targets when neither side is native.
|
|
647
|
+
*
|
|
648
|
+
* @param signedLeg - what the surface believes this signature does, or null
|
|
649
|
+
* @param chainId - the chain the swap actually executes on
|
|
650
|
+
* @param routerAddress - the PulseX router this swap is sent to, or null when
|
|
651
|
+
* none is deployed for this chain
|
|
652
|
+
* @param fromTokenAddress - the input token address, or the zero address for
|
|
653
|
+
* the chain's native coin
|
|
654
|
+
* @param toTokenAddress - the output token address, or the zero address for
|
|
655
|
+
* the chain's native coin
|
|
656
|
+
* @param swapPath - the ordered swap path (see `pulseXConversionSwapPath`),
|
|
657
|
+
* already expressed in the router's own token addresses — the wrapped-native
|
|
658
|
+
* token stands in for either native end
|
|
659
|
+
* @param amountIn - the amount to swap, in the input token's base units
|
|
660
|
+
* @param minAmountOut - the minimum acceptable output after slippage, in the
|
|
661
|
+
* output token's base units
|
|
662
|
+
* @param recipient - the address the swap delivers to
|
|
663
|
+
* @param deadline - the unix-seconds deadline the router enforces
|
|
664
|
+
* @returns the transaction inputs, or null when the inputs cannot form a call
|
|
665
|
+
*/
|
|
666
|
+
export const pulseXSwapTransactionInputs = ({ signedLeg, chainId, routerAddress, fromTokenAddress, toTokenAddress, swapPath, amountIn, minAmountOut, recipient, deadline, }) => {
|
|
667
|
+
// FIRST, BEFORE ANY ENCODING — see `transactionInputs`'s identical note.
|
|
668
|
+
if (swapRefusal({ signedLeg, chainId }) !== null) {
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
671
|
+
if (!routerAddress) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
if (!recipient || !isAddress(recipient) || recipient === zeroAddress) {
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
677
|
+
if (swapPath.length < 2) {
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
if (amountIn <= 0n) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
const fromIsNative = fromTokenAddress === zeroAddress;
|
|
684
|
+
const toIsNative = toTokenAddress === zeroAddress;
|
|
685
|
+
// Native-to-native names no real swap — there is nothing for the router to do.
|
|
686
|
+
if (fromIsNative && toIsNative) {
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
if (fromIsNative) {
|
|
690
|
+
return {
|
|
691
|
+
to: routerAddress,
|
|
692
|
+
value: amountIn,
|
|
693
|
+
data: encodeFunctionData({
|
|
694
|
+
abi: abis.univ2Router,
|
|
695
|
+
functionName: 'swapExactETHForTokens',
|
|
696
|
+
args: [minAmountOut, swapPath, recipient, deadline],
|
|
697
|
+
}),
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
if (toIsNative) {
|
|
701
|
+
return {
|
|
702
|
+
to: routerAddress,
|
|
703
|
+
value: 0n,
|
|
704
|
+
data: encodeFunctionData({
|
|
705
|
+
abi: abis.univ2Router,
|
|
706
|
+
functionName: 'swapExactTokensForETH',
|
|
707
|
+
args: [amountIn, minAmountOut, swapPath, recipient, deadline],
|
|
708
|
+
}),
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
to: routerAddress,
|
|
713
|
+
value: 0n,
|
|
714
|
+
data: encodeFunctionData({
|
|
715
|
+
abi: abis.univ2Router,
|
|
716
|
+
functionName: 'swapExactTokensForTokens',
|
|
717
|
+
args: [amountIn, minAmountOut, swapPath, recipient, deadline],
|
|
718
|
+
}),
|
|
719
|
+
};
|
|
720
|
+
};
|
|
439
721
|
/**
|
|
440
722
|
* Calculates the transaction inputs from the bridge pathway, asset in, recipient, account, asset link, destination data param, amount to bridge, fee director struct encoded, and should deliver
|
|
723
|
+
* @param bridgeKey - the key the pathway was resolved from, for {@link crossingRefusal}
|
|
724
|
+
* @param signedCrossing - the chain pair on screen, or null when no surface composed it
|
|
441
725
|
* @param bridgePathway - the bridge pathway to use
|
|
442
726
|
* @param assetIn - the asset in to bridge
|
|
443
727
|
* @param recipient - the recipient of the bridge
|
|
@@ -447,35 +731,32 @@ export const isUndercompensated = ({ estimatedTokenNetworkCost, feeType, limit,
|
|
|
447
731
|
* @param amountToBridge - the amount to bridge
|
|
448
732
|
* @param feeDirectorStructEncoded - the fee director struct encoded to use
|
|
449
733
|
* @param shouldDeliver - whether to deliver the tokens to the recipient
|
|
450
|
-
* @returns the transaction inputs
|
|
734
|
+
* @returns the transaction inputs, or null when the inputs cannot form a call —
|
|
735
|
+
* including when the pathway crosses somewhere other than the pair on screen
|
|
451
736
|
*/
|
|
452
|
-
export const transactionInputs = ({ bridgePathway, assetIn, recipient, account, assetLink, destinationDataParam, amountToBridge, feeDirectorStructEncoded, shouldDeliver, }) => {
|
|
737
|
+
export const transactionInputs = ({ bridgeKey, signedCrossing, bridgePathway, assetIn, recipient, account, assetLink, destinationDataParam, amountToBridge, feeDirectorStructEncoded, shouldDeliver, }) => {
|
|
738
|
+
// FIRST, BEFORE ANY ENCODING. Every branch below writes a real mediator call;
|
|
739
|
+
// by the time one is chosen the disagreement is already encoded.
|
|
740
|
+
if (crossingRefusal({ bridgeKey, signedCrossing }) !== null) {
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
453
743
|
if (!bridgePathway) {
|
|
454
|
-
console.log('no bridge pathway');
|
|
455
744
|
return null;
|
|
456
745
|
}
|
|
457
746
|
// check that recipient is valid
|
|
458
|
-
if (!recipient ||
|
|
459
|
-
!isAddress(recipient) ||
|
|
460
|
-
account === zeroAddress) {
|
|
461
|
-
console.log('no recipient');
|
|
747
|
+
if (!recipient || !isAddress(recipient) || recipient === zeroAddress) {
|
|
462
748
|
return null;
|
|
463
749
|
}
|
|
464
750
|
// check that wallet account is valid
|
|
465
|
-
if (!account ||
|
|
466
|
-
!isAddress(account) ||
|
|
467
|
-
account === zeroAddress) {
|
|
468
|
-
console.log('no account');
|
|
751
|
+
if (!account || !isAddress(account) || account === zeroAddress) {
|
|
469
752
|
return null;
|
|
470
753
|
}
|
|
471
754
|
// check that asset in is valid
|
|
472
755
|
if (!assetIn) {
|
|
473
|
-
console.log('no asset in');
|
|
474
756
|
return null;
|
|
475
757
|
}
|
|
476
758
|
// check that asset link is valid
|
|
477
759
|
if (!assetLink) {
|
|
478
|
-
console.log('no asset link');
|
|
479
760
|
return null;
|
|
480
761
|
}
|
|
481
762
|
let value = 0n;
|
|
@@ -500,12 +781,7 @@ export const transactionInputs = ({ bridgePathway, assetIn, recipient, account,
|
|
|
500
781
|
? encodeFunctionData({
|
|
501
782
|
abi: abis.erc677ExtraInput,
|
|
502
783
|
functionName: 'transferAndCall',
|
|
503
|
-
args: [
|
|
504
|
-
bridgePathway.from,
|
|
505
|
-
amountToBridge,
|
|
506
|
-
destinationDataParam,
|
|
507
|
-
account,
|
|
508
|
-
],
|
|
784
|
+
args: [bridgePathway.from, amountToBridge, destinationDataParam, account],
|
|
509
785
|
})
|
|
510
786
|
: encodeFunctionData({
|
|
511
787
|
abi: abis.erc677,
|
|
@@ -526,11 +802,7 @@ export const transactionInputs = ({ bridgePathway, assetIn, recipient, account,
|
|
|
526
802
|
? encodeFunctionData({
|
|
527
803
|
abi: abis.nativeRouterExtraInput,
|
|
528
804
|
functionName: 'relayTokensAndCall',
|
|
529
|
-
args: [
|
|
530
|
-
bridgePathway.destinationRouter,
|
|
531
|
-
feeDirectorStructEncoded,
|
|
532
|
-
account,
|
|
533
|
-
],
|
|
805
|
+
args: [bridgePathway.destinationRouter, feeDirectorStructEncoded, account],
|
|
534
806
|
})
|
|
535
807
|
: encodeFunctionData({
|
|
536
808
|
abi: abis.nativeRouter,
|
|
@@ -588,12 +860,7 @@ export const transactionInputs = ({ bridgePathway, assetIn, recipient, account,
|
|
|
588
860
|
? encodeFunctionData({
|
|
589
861
|
abi: abis.inputBridgeExtraInput,
|
|
590
862
|
functionName: 'relayTokens',
|
|
591
|
-
args: [
|
|
592
|
-
assetIn.address,
|
|
593
|
-
recipient,
|
|
594
|
-
amountToBridge,
|
|
595
|
-
account,
|
|
596
|
-
],
|
|
863
|
+
args: [assetIn.address, recipient, amountToBridge, account],
|
|
597
864
|
})
|
|
598
865
|
: encodeFunctionData({
|
|
599
866
|
abi: abis.inputBridge,
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { TonDeposit } from './origin-deposit.js';
|
|
2
|
+
/**
|
|
3
|
+
* Executing a deposit on The Open Network, as pure data.
|
|
4
|
+
*
|
|
5
|
+
* Relay describes a deposit as a list of internal messages carrying `to`,
|
|
6
|
+
* `value`, `body` and a `bounce` flag. A connected wallet, however, is driven
|
|
7
|
+
* through the TON Connect protocol, whose request shape is different in one way
|
|
8
|
+
* that silently loses money:
|
|
9
|
+
*
|
|
10
|
+
* **TON Connect has no per-message `bounce` field.** A wallet derives whether a
|
|
11
|
+
* message bounces from the FORM OF THE ADDRESS — the bounceable `EQ…` prefix or
|
|
12
|
+
* the non-bounceable `UQ…` one. Passing Relay's address through unchanged while
|
|
13
|
+
* dropping its `bounce: false` therefore sends a deposit to the bounceable form,
|
|
14
|
+
* and the depository bounces the funds straight back to the sender. The transfer
|
|
15
|
+
* looks like it succeeded and the route never fills.
|
|
16
|
+
*
|
|
17
|
+
* {@link buildTonConnectRequest} folds `bounce` into the address so the flag
|
|
18
|
+
* cannot be dropped on the way to a wallet.
|
|
19
|
+
*
|
|
20
|
+
* Kept free of transport and of clock reads so it is fully testable: nothing
|
|
21
|
+
* here signs, fetches, or calls `Date.now`. The caller supplies the deadline and
|
|
22
|
+
* performs the request.
|
|
23
|
+
*
|
|
24
|
+
* The rules encoded here are those of Relay's own wallet adapter, which is the
|
|
25
|
+
* only reference implementation of this mapping.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* The TON Connect network identifier for mainnet. Relay bridges only mainnet, so
|
|
29
|
+
* a request is always tagged with this.
|
|
30
|
+
*/
|
|
31
|
+
export declare const tonConnectMainnetNetwork = "-239";
|
|
32
|
+
/** How long a request stays valid by default, in seconds, matching Relay's adapter. */
|
|
33
|
+
export declare const tonConnectDefaultValidForSeconds = 300;
|
|
34
|
+
/** An address split into the parts that survive a change of encoding. */
|
|
35
|
+
type ParsedTonAddress = {
|
|
36
|
+
workchain: number;
|
|
37
|
+
hash: Uint8Array;
|
|
38
|
+
/** Preserved rather than assumed: re-encoding a testnet address as mainnet would misdirect it. */
|
|
39
|
+
testOnly: boolean;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Parses either address form The Open Network uses.
|
|
43
|
+
*
|
|
44
|
+
* @param value - a friendly (`EQ…`/`UQ…`) or raw (`0:…`) address
|
|
45
|
+
* @returns the address parts, or null when it is neither form or fails its checksum
|
|
46
|
+
*/
|
|
47
|
+
export declare const parseTonAddress: (value: string) => ParsedTonAddress | null;
|
|
48
|
+
/**
|
|
49
|
+
* Re-encodes an address into the friendly form matching a bounce setting.
|
|
50
|
+
*
|
|
51
|
+
* This is the whole point of the module: the bounce flag has nowhere else to
|
|
52
|
+
* live in a TON Connect request, so it has to be carried by the address itself.
|
|
53
|
+
*
|
|
54
|
+
* @param address - the address in either form
|
|
55
|
+
* @param options.bounceable - whether the message may bounce back to the sender
|
|
56
|
+
* @returns the friendly address, or null when the input cannot be parsed
|
|
57
|
+
*/
|
|
58
|
+
export declare const encodeTonAddress: (address: string, { bounceable }: {
|
|
59
|
+
bounceable: boolean;
|
|
60
|
+
}) => string | null;
|
|
61
|
+
/** One message of a TON Connect request. Note `address`/`amount`, not `to`/`value`. */
|
|
62
|
+
export type TonConnectMessage = {
|
|
63
|
+
address: string;
|
|
64
|
+
/** Attached amount in nanotons, as a decimal string. */
|
|
65
|
+
amount: string;
|
|
66
|
+
/** Base64 bag-of-cells body, omitted when the message carries none. */
|
|
67
|
+
payload?: string;
|
|
68
|
+
};
|
|
69
|
+
/** The request handed to a wallet's TON Connect `sendTransaction` call. */
|
|
70
|
+
export type TonConnectRequest = {
|
|
71
|
+
/** Unix seconds after which a wallet must refuse the request. */
|
|
72
|
+
validUntil: number;
|
|
73
|
+
network: string;
|
|
74
|
+
/** The sending account, when the deposit names one. */
|
|
75
|
+
from?: string;
|
|
76
|
+
messages: TonConnectMessage[];
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Builds the TON Connect request for a deposit.
|
|
80
|
+
*
|
|
81
|
+
* @param deposit - the parsed origin deposit
|
|
82
|
+
* @param options.validUntil - unix seconds after which the request expires.
|
|
83
|
+
* Supplied by the caller rather than read from a clock here so the transform
|
|
84
|
+
* stays pure; {@link tonConnectDefaultValidForSeconds} is the usual offset.
|
|
85
|
+
* @returns the request, or null when the deposit carries no messages or any
|
|
86
|
+
* destination address cannot be re-encoded — never a request whose bounce
|
|
87
|
+
* setting has been silently dropped
|
|
88
|
+
*/
|
|
89
|
+
export declare const buildTonConnectRequest: (deposit: TonDeposit, { validUntil }: {
|
|
90
|
+
validUntil: number;
|
|
91
|
+
}) => TonConnectRequest | null;
|
|
92
|
+
/**
|
|
93
|
+
* Derives the transaction identifier from a wallet's `sendTransaction` response.
|
|
94
|
+
*
|
|
95
|
+
* A wallet answers with a `boc`: the base64 bag-of-cells of the signed
|
|
96
|
+
* external-in message it broadcast — NOT the resulting transaction, and NOT a
|
|
97
|
+
* hash. The identifier explorers and indexers (toncenter, TonAPI) resolve a
|
|
98
|
+
* transaction by is the hash of that external message, but it must be the
|
|
99
|
+
* NORMALIZED hash of TEP-467, not the hash of the bytes the wallet happened to
|
|
100
|
+
* send.
|
|
101
|
+
*
|
|
102
|
+
* The distinction is the whole point, and getting it wrong loses a resume record
|
|
103
|
+
* for real funds. An external-in message can be serialized several equivalent
|
|
104
|
+
* ways — a wallet may or may not include its own `src`, may set a non-zero
|
|
105
|
+
* `import_fee`, may attach a `stateInit`, may store the body inline or as a
|
|
106
|
+
* reference. The raw cell hash changes with each of those wallet-specific
|
|
107
|
+
* choices, so it frequently does NOT match what an indexer keyed the same
|
|
108
|
+
* transaction under. TEP-467 rebuilds the message with those variable fields
|
|
109
|
+
* stripped (`src` to none, `import_fee` to zero, `init` dropped, body forced to a
|
|
110
|
+
* reference) so the hash is stable across wallets and matches the indexers.
|
|
111
|
+
*
|
|
112
|
+
* @param boc - the wallet's base64 `boc` field from `sendTransaction`
|
|
113
|
+
* @returns the 64-character hex normalized hash, or null when the input is not a
|
|
114
|
+
* parseable external-in message
|
|
115
|
+
*/
|
|
116
|
+
export declare const tonExternalMessageHash: (boc: string) => string | null;
|
|
117
|
+
export {};
|