@gibs/bridge-sdk 1.13.0 → 1.17.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/README.md +244 -18
- package/abis/package.json +6 -0
- package/bridge-limits/package.json +6 -0
- package/chain-info/package.json +6 -0
- package/chain-names/package.json +6 -0
- package/chains/package.json +6 -0
- package/claims/package.json +6 -0
- package/config/package.json +6 -0
- package/consolidated-quote/package.json +6 -0
- package/destination-calldata/package.json +6 -0
- package/dist/chain-info.d.ts +1 -1
- package/dist/chain-info.js +11 -12
- package/dist/chains.d.ts +25 -0
- package/dist/chains.js +55 -20
- package/dist/cjs/abis.d.ts +1 -0
- package/dist/cjs/abis.js +19 -0
- package/dist/cjs/bridge-limits.d.ts +103 -0
- package/dist/cjs/bridge-limits.js +89 -0
- package/dist/cjs/chain-info.d.ts +169 -0
- package/dist/cjs/chain-info.js +452 -0
- package/dist/cjs/chain-names.d.ts +160 -0
- package/dist/cjs/chain-names.js +207 -0
- package/dist/cjs/chains.d.ts +90 -0
- package/dist/cjs/chains.js +357 -0
- package/dist/cjs/claims.d.ts +198 -0
- package/dist/cjs/claims.js +182 -0
- package/dist/cjs/config.d.ts +240 -0
- package/dist/cjs/config.js +537 -0
- package/dist/cjs/consolidated-quote.d.ts +648 -0
- package/dist/cjs/consolidated-quote.js +1048 -0
- package/dist/cjs/destination-calldata.d.ts +118 -0
- package/dist/cjs/destination-calldata.js +156 -0
- package/dist/cjs/ecosystems.d.ts +217 -0
- package/dist/cjs/ecosystems.js +298 -0
- package/dist/cjs/execution.d.ts +346 -0
- package/dist/cjs/execution.js +468 -0
- package/dist/cjs/fee-type.d.ts +10 -0
- package/dist/cjs/fee-type.js +10 -0
- package/dist/cjs/hyperliquid-execution.d.ts +152 -0
- package/dist/cjs/hyperliquid-execution.js +175 -0
- package/dist/cjs/image-links.d.ts +117 -0
- package/dist/cjs/image-links.js +153 -0
- package/dist/cjs/index.d.ts +23 -0
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/lifi-execution-guard.d.ts +210 -0
- package/dist/cjs/lifi-execution-guard.js +409 -0
- package/dist/cjs/near-intents-conformance.d.ts +85 -0
- package/dist/cjs/near-intents-conformance.js +337 -0
- package/dist/cjs/near-intents-route.d.ts +47 -0
- package/dist/cjs/near-intents-route.js +303 -0
- package/dist/cjs/near-intents.d.ts +524 -0
- package/dist/cjs/near-intents.js +1071 -0
- package/dist/cjs/origin-deposit.d.ts +326 -0
- package/dist/cjs/origin-deposit.js +82 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/pre-quote.d.ts +198 -0
- package/dist/cjs/pre-quote.js +180 -0
- package/dist/cjs/prices.d.ts +107 -0
- package/dist/cjs/prices.js +172 -0
- package/dist/cjs/providers.d.ts +238 -0
- package/dist/cjs/providers.js +171 -0
- package/dist/cjs/quote-fees.d.ts +102 -0
- package/dist/cjs/quote-fees.js +201 -0
- package/dist/cjs/referral.d.ts +85 -0
- package/dist/cjs/referral.js +85 -0
- package/dist/cjs/relay-currencies.d.ts +47 -0
- package/dist/cjs/relay-currencies.js +95 -0
- package/dist/cjs/relay-depository.d.ts +2 -0
- package/dist/cjs/relay-depository.js +85 -0
- package/dist/cjs/relay-quote.d.ts +301 -0
- package/dist/cjs/relay-quote.js +996 -0
- package/dist/cjs/route-steps.d.ts +138 -0
- package/dist/cjs/route-steps.js +310 -0
- package/dist/cjs/routing/amounts.d.ts +80 -0
- package/dist/cjs/routing/amounts.js +126 -0
- package/dist/cjs/routing/candidate-route.d.ts +43 -0
- package/dist/cjs/routing/candidate-route.js +50 -0
- package/dist/cjs/routing/candidate.d.ts +237 -0
- package/dist/cjs/routing/candidate.js +100 -0
- package/dist/cjs/routing/carriers/ask-answer.d.ts +77 -0
- package/dist/cjs/routing/carriers/ask-answer.js +2 -0
- package/dist/cjs/routing/carriers/omnibridge.d.ts +137 -0
- package/dist/cjs/routing/carriers/omnibridge.js +459 -0
- package/dist/cjs/routing/carriers/pulsex-paths.d.ts +80 -0
- package/dist/cjs/routing/carriers/pulsex-paths.js +97 -0
- package/dist/cjs/routing/carriers/pulsex-split.d.ts +175 -0
- package/dist/cjs/routing/carriers/pulsex-split.js +193 -0
- package/dist/cjs/routing/carriers/pulsex.d.ts +159 -0
- package/dist/cjs/routing/carriers/pulsex.js +565 -0
- package/dist/cjs/routing/carriers/wrap.d.ts +25 -0
- package/dist/cjs/routing/carriers/wrap.js +119 -0
- package/dist/cjs/routing/composed-route-quality.d.ts +125 -0
- package/dist/cjs/routing/composed-route-quality.js +144 -0
- package/dist/cjs/routing/entry-chain-anchor.d.ts +88 -0
- package/dist/cjs/routing/entry-chain-anchor.js +59 -0
- package/dist/cjs/routing/format.d.ts +31 -0
- package/dist/cjs/routing/format.js +64 -0
- package/dist/cjs/routing/index.d.ts +38 -0
- package/dist/cjs/routing/index.js +134 -0
- package/dist/cjs/routing/producers.d.ts +770 -0
- package/dist/cjs/routing/producers.js +1617 -0
- package/dist/cjs/routing/providers.d.ts +31 -0
- package/dist/cjs/routing/providers.js +48 -0
- package/dist/cjs/routing/route-data.d.ts +355 -0
- package/dist/cjs/routing/route-data.js +2 -0
- package/dist/cjs/routing/search/compose.d.ts +168 -0
- package/dist/cjs/routing/search/compose.js +104 -0
- package/dist/cjs/routing/search/graph.d.ts +668 -0
- package/dist/cjs/routing/search/graph.js +461 -0
- package/dist/cjs/routing/search/plan-route-data.d.ts +269 -0
- package/dist/cjs/routing/search/plan-route-data.js +848 -0
- package/dist/cjs/routing/search/score.d.ts +356 -0
- package/dist/cjs/routing/search/score.js +445 -0
- package/dist/cjs/routing/swap-transactions.d.ts +91 -0
- package/dist/cjs/routing/swap-transactions.js +131 -0
- package/dist/cjs/routing/usd-value.d.ts +17 -0
- package/dist/cjs/routing/usd-value.js +29 -0
- package/dist/cjs/settings.d.ts +785 -0
- package/dist/cjs/settings.js +1074 -0
- package/dist/cjs/ton-connect.d.ts +98 -0
- package/dist/cjs/ton-connect.js +206 -0
- package/dist/cjs/ton-execution.d.ts +7 -0
- package/dist/cjs/ton-execution.js +23 -0
- package/dist/cjs/ton.d.ts +37 -0
- package/dist/cjs/ton.js +83 -0
- package/dist/cjs/tron-execution.d.ts +146 -0
- package/dist/cjs/tron-execution.js +248 -0
- package/dist/cjs/types.d.ts +51 -0
- package/dist/cjs/types.js +2 -0
- package/dist/claims.d.ts +198 -0
- package/dist/claims.js +140 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +39 -5
- package/dist/consolidated-quote.d.ts +109 -15
- package/dist/consolidated-quote.js +310 -52
- package/dist/execution.d.ts +346 -0
- package/dist/execution.js +428 -0
- package/dist/hyperliquid-execution.js +2 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/lifi-execution-guard.d.ts +13 -2
- package/dist/lifi-execution-guard.js +4 -0
- package/dist/near-intents.js +2 -3
- package/dist/pre-quote.d.ts +6 -6
- package/dist/pre-quote.js +10 -14
- package/dist/providers.d.ts +17 -0
- package/dist/providers.js +6 -0
- package/dist/referral.d.ts +12 -4
- package/dist/referral.js +12 -4
- package/dist/relay-depository.d.ts +2 -0
- package/dist/relay-depository.js +82 -0
- package/dist/relay-quote.js +2 -3
- package/dist/routing/candidate.d.ts +3 -3
- package/dist/routing/candidate.js +3 -3
- package/dist/routing/carriers/ask-answer.d.ts +19 -0
- package/dist/routing/carriers/omnibridge.js +23 -21
- package/dist/routing/carriers/pulsex-paths.d.ts +80 -0
- package/dist/routing/carriers/pulsex-paths.js +92 -0
- package/dist/routing/carriers/pulsex-split.d.ts +175 -0
- package/dist/routing/carriers/pulsex-split.js +185 -0
- package/dist/routing/carriers/pulsex.d.ts +69 -21
- package/dist/routing/carriers/pulsex.js +184 -65
- package/dist/routing/composed-route-quality.d.ts +28 -6
- package/dist/routing/composed-route-quality.js +36 -5
- package/dist/routing/entry-chain-anchor.d.ts +1 -1
- package/dist/routing/entry-chain-anchor.js +1 -1
- package/dist/routing/index.d.ts +7 -3
- package/dist/routing/index.js +7 -3
- package/dist/routing/producers.js +1 -1
- package/dist/routing/providers.d.ts +5 -0
- package/dist/routing/providers.js +5 -0
- package/dist/routing/search/graph.d.ts +29 -0
- package/dist/routing/search/plan-route-data.d.ts +25 -0
- package/dist/routing/search/plan-route-data.js +43 -7
- package/dist/routing/search/score.d.ts +33 -1
- package/dist/routing/search/score.js +41 -6
- package/dist/routing/swap-transactions.d.ts +91 -0
- package/dist/routing/swap-transactions.js +125 -0
- package/dist/settings.d.ts +94 -1
- package/dist/settings.js +95 -3
- package/dist/ton-connect.d.ts +98 -0
- package/dist/ton-connect.js +200 -0
- package/dist/ton-execution.d.ts +4 -114
- package/dist/ton-execution.js +4 -244
- package/dist/ton.d.ts +37 -0
- package/dist/ton.js +65 -0
- package/docs/examples/example-a-ether-to-pulse.ts +300 -0
- package/docs/examples/example-b-token-pulsechain-to-ethereum.ts +270 -0
- package/docs/examples/shared.ts +261 -0
- package/docs/examples/tsconfig.json +18 -0
- package/docs/integration-guide.md +617 -0
- package/ecosystems/package.json +6 -0
- package/execution/package.json +6 -0
- package/fee-type/package.json +6 -0
- package/hyperliquid-execution/package.json +6 -0
- package/image-links/package.json +6 -0
- package/lifi-execution-guard/package.json +6 -0
- package/llms.txt +234 -0
- package/near-intents/package.json +6 -0
- package/near-intents-route/package.json +6 -0
- package/origin-deposit/package.json +6 -0
- package/package.json +818 -10
- package/pre-quote/package.json +6 -0
- package/prices/package.json +6 -0
- package/providers/package.json +6 -0
- package/quote-fees/package.json +6 -0
- package/referral/package.json +6 -0
- package/relay-currencies/package.json +6 -0
- package/relay-quote/package.json +6 -0
- package/route-steps/package.json +6 -0
- package/routing/package.json +6 -0
- package/settings/package.json +6 -0
- package/ton/package.json +6 -0
- package/ton-connect/package.json +6 -0
- package/ton-execution/package.json +6 -0
- package/tron-execution/package.json +6 -0
- package/types/package.json +6 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executing a deposit on The Open Network, as pure data, with no TON library.
|
|
3
|
+
*
|
|
4
|
+
* This module imports nothing outside the package, so it loads anywhere: a
|
|
5
|
+
* browser, Node, or a React Native app under Hermes, with no `Buffer` and no
|
|
6
|
+
* polyfill. It uses only `atob` and `btoa`, which Hermes provides. The one TON
|
|
7
|
+
* helper that needs `@ton/core`, the TEP-467 message hash, lives in
|
|
8
|
+
* `@gibs/bridge-sdk/ton` instead.
|
|
9
|
+
*
|
|
10
|
+
* Relay describes a deposit as a list of internal messages carrying `to`,
|
|
11
|
+
* `value`, `body` and a `bounce` flag. A connected wallet, however, is driven
|
|
12
|
+
* through the TON Connect protocol, whose request shape is different in one way
|
|
13
|
+
* that silently loses money:
|
|
14
|
+
*
|
|
15
|
+
* **TON Connect has no per-message `bounce` field.** A wallet derives whether a
|
|
16
|
+
* message bounces from the FORM OF THE ADDRESS — the bounceable `EQ…` prefix or
|
|
17
|
+
* the non-bounceable `UQ…` one. Passing Relay's address through unchanged while
|
|
18
|
+
* dropping its `bounce: false` therefore sends a deposit to the bounceable form,
|
|
19
|
+
* and the depository bounces the funds straight back to the sender. The transfer
|
|
20
|
+
* looks like it succeeded and the route never fills.
|
|
21
|
+
*
|
|
22
|
+
* {@link buildTonConnectRequest} folds `bounce` into the address so the flag
|
|
23
|
+
* cannot be dropped on the way to a wallet.
|
|
24
|
+
*
|
|
25
|
+
* Kept free of transport and of clock reads so it is fully testable: nothing
|
|
26
|
+
* here signs, fetches, or calls `Date.now`. The caller supplies the deadline and
|
|
27
|
+
* performs the request.
|
|
28
|
+
*
|
|
29
|
+
* The rules encoded here are those of Relay's own wallet adapter, which is the
|
|
30
|
+
* only reference implementation of this mapping.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* The TON Connect network identifier for mainnet. Relay bridges only mainnet, so
|
|
34
|
+
* a request is always tagged with this.
|
|
35
|
+
*/
|
|
36
|
+
export const tonConnectMainnetNetwork = '-239';
|
|
37
|
+
/** How long a request stays valid by default, in seconds, matching Relay's adapter. */
|
|
38
|
+
export const tonConnectDefaultValidForSeconds = 300;
|
|
39
|
+
/** Address tag byte for the bounceable (`EQ…`) form. */
|
|
40
|
+
const bounceableTag = 0x11;
|
|
41
|
+
/** Address tag byte for the non-bounceable (`UQ…`) form. */
|
|
42
|
+
const nonBounceableTag = 0x51;
|
|
43
|
+
/** Bit set on the tag byte for testnet-only addresses (`kQ…` / `0Q…`). */
|
|
44
|
+
const testnetTagFlag = 0x80;
|
|
45
|
+
/** A friendly address is a 36-byte structure: tag, workchain, hash, checksum. */
|
|
46
|
+
const friendlyAddressByteLength = 36;
|
|
47
|
+
/** An account identifier is always 32 bytes, in every address form. */
|
|
48
|
+
const accountHashByteLength = 32;
|
|
49
|
+
/**
|
|
50
|
+
* Cyclic redundancy check (16-bit, the XModem variant) over an address body.
|
|
51
|
+
* The Open Network appends it to every friendly address, and a re-encoded
|
|
52
|
+
* address whose checksum is not recomputed is rejected by wallets as malformed.
|
|
53
|
+
*/
|
|
54
|
+
const checksum = (data) => {
|
|
55
|
+
let remainder = 0;
|
|
56
|
+
for (const byte of data) {
|
|
57
|
+
remainder ^= byte << 8;
|
|
58
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
59
|
+
remainder =
|
|
60
|
+
remainder & 0x8000 ? ((remainder << 1) ^ 0x1021) & 0xffff : (remainder << 1) & 0xffff;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return remainder;
|
|
64
|
+
};
|
|
65
|
+
/** Decodes the url-safe base64 alphabet the friendly address form uses. */
|
|
66
|
+
const decodeBase64Url = (value) => {
|
|
67
|
+
const standard = value.replaceAll('-', '+').replaceAll('_', '/');
|
|
68
|
+
const padded = standard.padEnd(Math.ceil(standard.length / 4) * 4, '=');
|
|
69
|
+
try {
|
|
70
|
+
const binary = atob(padded);
|
|
71
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/** Encodes bytes back into the url-safe, unpadded base64 form. */
|
|
78
|
+
const encodeBase64Url = (bytes) => {
|
|
79
|
+
let binary = '';
|
|
80
|
+
for (const byte of bytes) {
|
|
81
|
+
binary += String.fromCharCode(byte);
|
|
82
|
+
}
|
|
83
|
+
return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
|
|
84
|
+
};
|
|
85
|
+
const parseRawAddress = (value) => {
|
|
86
|
+
const separator = value.indexOf(':');
|
|
87
|
+
if (separator < 0) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const workchain = Number(value.slice(0, separator));
|
|
91
|
+
const hex = value.slice(separator + 1);
|
|
92
|
+
if (!Number.isInteger(workchain) || hex.length !== accountHashByteLength * 2) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const hash = new Uint8Array(accountHashByteLength);
|
|
96
|
+
for (let index = 0; index < accountHashByteLength; index += 1) {
|
|
97
|
+
const byte = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
98
|
+
if (Number.isNaN(byte)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
hash[index] = byte;
|
|
102
|
+
}
|
|
103
|
+
// The raw form carries no network tag, so mainnet is the only safe assumption.
|
|
104
|
+
return { workchain, hash, testOnly: false };
|
|
105
|
+
};
|
|
106
|
+
const parseFriendlyAddress = (value) => {
|
|
107
|
+
const bytes = decodeBase64Url(value);
|
|
108
|
+
if (bytes === null || bytes.length !== friendlyAddressByteLength) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const body = bytes.subarray(0, friendlyAddressByteLength - 2);
|
|
112
|
+
// Every index below is in range: the length was checked to be exactly
|
|
113
|
+
// `friendlyAddressByteLength` above, so the non-null assertions are sound.
|
|
114
|
+
const stated = (bytes[friendlyAddressByteLength - 2] << 8) | bytes[friendlyAddressByteLength - 1];
|
|
115
|
+
if (checksum(body) !== stated) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const tag = bytes[0];
|
|
119
|
+
const withoutTestFlag = tag & ~testnetTagFlag;
|
|
120
|
+
if (withoutTestFlag !== bounceableTag && withoutTestFlag !== nonBounceableTag) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const workchainByte = bytes[1];
|
|
124
|
+
return {
|
|
125
|
+
// The masterchain is workchain -1, which travels as the byte 0xff.
|
|
126
|
+
workchain: workchainByte === 0xff ? -1 : workchainByte,
|
|
127
|
+
hash: bytes.slice(2, 2 + accountHashByteLength),
|
|
128
|
+
testOnly: (tag & testnetTagFlag) !== 0,
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Parses either address form The Open Network uses.
|
|
133
|
+
*
|
|
134
|
+
* @param value - a friendly (`EQ…`/`UQ…`) or raw (`0:…`) address
|
|
135
|
+
* @returns the address parts, or null when it is neither form or fails its checksum
|
|
136
|
+
*/
|
|
137
|
+
export const parseTonAddress = (value) => value.includes(':') ? parseRawAddress(value) : parseFriendlyAddress(value);
|
|
138
|
+
/**
|
|
139
|
+
* Re-encodes an address into the friendly form matching a bounce setting.
|
|
140
|
+
*
|
|
141
|
+
* This is the whole point of the module: the bounce flag has nowhere else to
|
|
142
|
+
* live in a TON Connect request, so it has to be carried by the address itself.
|
|
143
|
+
*
|
|
144
|
+
* @param address - the address in either form
|
|
145
|
+
* @param options.bounceable - whether the message may bounce back to the sender
|
|
146
|
+
* @returns the friendly address, or null when the input cannot be parsed
|
|
147
|
+
*/
|
|
148
|
+
export const encodeTonAddress = (address, { bounceable }) => {
|
|
149
|
+
const parsed = parseTonAddress(address);
|
|
150
|
+
if (parsed === null) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const bytes = new Uint8Array(friendlyAddressByteLength);
|
|
154
|
+
bytes[0] =
|
|
155
|
+
(bounceable ? bounceableTag : nonBounceableTag) | (parsed.testOnly ? testnetTagFlag : 0);
|
|
156
|
+
bytes[1] = parsed.workchain < 0 ? 0xff : parsed.workchain;
|
|
157
|
+
bytes.set(parsed.hash, 2);
|
|
158
|
+
const stated = checksum(bytes.subarray(0, friendlyAddressByteLength - 2));
|
|
159
|
+
bytes[friendlyAddressByteLength - 2] = (stated >> 8) & 0xff;
|
|
160
|
+
bytes[friendlyAddressByteLength - 1] = stated & 0xff;
|
|
161
|
+
return encodeBase64Url(bytes);
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Builds the TON Connect request for a deposit.
|
|
165
|
+
*
|
|
166
|
+
* @param deposit - the parsed origin deposit
|
|
167
|
+
* @param options.validUntil - unix seconds after which the request expires.
|
|
168
|
+
* Supplied by the caller rather than read from a clock here so the transform
|
|
169
|
+
* stays pure; {@link tonConnectDefaultValidForSeconds} is the usual offset.
|
|
170
|
+
* @returns the request, or null when the deposit carries no messages or any
|
|
171
|
+
* destination address cannot be re-encoded — never a request whose bounce
|
|
172
|
+
* setting has been silently dropped
|
|
173
|
+
*/
|
|
174
|
+
export const buildTonConnectRequest = (deposit, { validUntil }) => {
|
|
175
|
+
if (deposit.messages.length === 0) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const messages = [];
|
|
179
|
+
for (const message of deposit.messages) {
|
|
180
|
+
const address = encodeTonAddress(message.to, { bounceable: message.bounce });
|
|
181
|
+
// Refuse rather than fall back to the unencoded address: the fallback is
|
|
182
|
+
// exactly the bounce-losing behaviour this module exists to prevent.
|
|
183
|
+
if (address === null) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
messages.push({
|
|
187
|
+
address,
|
|
188
|
+
amount: message.value.toString(),
|
|
189
|
+
...(message.body ? { payload: message.body } : {}),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// `messages[0]` is present: the empty case returned null above.
|
|
193
|
+
const from = deposit.messages[0].from;
|
|
194
|
+
return {
|
|
195
|
+
validUntil,
|
|
196
|
+
network: tonConnectMainnetNetwork,
|
|
197
|
+
...(from === null ? {} : { from }),
|
|
198
|
+
messages,
|
|
199
|
+
};
|
|
200
|
+
};
|
package/dist/ton-execution.d.ts
CHANGED
|
@@ -1,117 +1,7 @@
|
|
|
1
|
-
import type { TonDeposit } from './origin-deposit.js';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
2
|
+
* The original name of `@gibs/bridge-sdk/ton`, kept so existing imports work.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
|
4
|
+
* @deprecated Import from `@gibs/bridge-sdk/ton` for the full TON surface, or
|
|
5
|
+
* from `@gibs/bridge-sdk/ton-connect` for the helpers that need no TON library.
|
|
115
6
|
*/
|
|
116
|
-
export
|
|
117
|
-
export {};
|
|
7
|
+
export * from './ton.js';
|
package/dist/ton-execution.js
CHANGED
|
@@ -1,247 +1,7 @@
|
|
|
1
|
-
import { Cell, beginCell, loadMessage, storeMessage } from '@ton/core';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
2
|
+
* The original name of `@gibs/bridge-sdk/ton`, kept so existing imports work.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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 const tonConnectMainnetNetwork = '-239';
|
|
32
|
-
/** How long a request stays valid by default, in seconds, matching Relay's adapter. */
|
|
33
|
-
export const tonConnectDefaultValidForSeconds = 300;
|
|
34
|
-
/** Address tag byte for the bounceable (`EQ…`) form. */
|
|
35
|
-
const bounceableTag = 0x11;
|
|
36
|
-
/** Address tag byte for the non-bounceable (`UQ…`) form. */
|
|
37
|
-
const nonBounceableTag = 0x51;
|
|
38
|
-
/** Bit set on the tag byte for testnet-only addresses (`kQ…` / `0Q…`). */
|
|
39
|
-
const testnetTagFlag = 0x80;
|
|
40
|
-
/** A friendly address is a 36-byte structure: tag, workchain, hash, checksum. */
|
|
41
|
-
const friendlyAddressByteLength = 36;
|
|
42
|
-
/** An account identifier is always 32 bytes, in every address form. */
|
|
43
|
-
const accountHashByteLength = 32;
|
|
44
|
-
/**
|
|
45
|
-
* Cyclic redundancy check (16-bit, the XModem variant) over an address body.
|
|
46
|
-
* The Open Network appends it to every friendly address, and a re-encoded
|
|
47
|
-
* address whose checksum is not recomputed is rejected by wallets as malformed.
|
|
48
|
-
*/
|
|
49
|
-
const checksum = (data) => {
|
|
50
|
-
let remainder = 0;
|
|
51
|
-
for (const byte of data) {
|
|
52
|
-
remainder ^= byte << 8;
|
|
53
|
-
for (let bit = 0; bit < 8; bit += 1) {
|
|
54
|
-
remainder =
|
|
55
|
-
remainder & 0x8000 ? ((remainder << 1) ^ 0x1021) & 0xffff : (remainder << 1) & 0xffff;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return remainder;
|
|
59
|
-
};
|
|
60
|
-
/** Decodes the url-safe base64 alphabet the friendly address form uses. */
|
|
61
|
-
const decodeBase64Url = (value) => {
|
|
62
|
-
const standard = value.replaceAll('-', '+').replaceAll('_', '/');
|
|
63
|
-
const padded = standard.padEnd(Math.ceil(standard.length / 4) * 4, '=');
|
|
64
|
-
try {
|
|
65
|
-
const binary = atob(padded);
|
|
66
|
-
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
/** Encodes bytes back into the url-safe, unpadded base64 form. */
|
|
73
|
-
const encodeBase64Url = (bytes) => {
|
|
74
|
-
let binary = '';
|
|
75
|
-
for (const byte of bytes) {
|
|
76
|
-
binary += String.fromCharCode(byte);
|
|
77
|
-
}
|
|
78
|
-
return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
|
|
79
|
-
};
|
|
80
|
-
const parseRawAddress = (value) => {
|
|
81
|
-
const separator = value.indexOf(':');
|
|
82
|
-
if (separator < 0) {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
const workchain = Number(value.slice(0, separator));
|
|
86
|
-
const hex = value.slice(separator + 1);
|
|
87
|
-
if (!Number.isInteger(workchain) || hex.length !== accountHashByteLength * 2) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
const hash = new Uint8Array(accountHashByteLength);
|
|
91
|
-
for (let index = 0; index < accountHashByteLength; index += 1) {
|
|
92
|
-
const byte = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
93
|
-
if (Number.isNaN(byte)) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
hash[index] = byte;
|
|
97
|
-
}
|
|
98
|
-
// The raw form carries no network tag, so mainnet is the only safe assumption.
|
|
99
|
-
return { workchain, hash, testOnly: false };
|
|
100
|
-
};
|
|
101
|
-
const parseFriendlyAddress = (value) => {
|
|
102
|
-
const bytes = decodeBase64Url(value);
|
|
103
|
-
if (bytes === null || bytes.length !== friendlyAddressByteLength) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
const body = bytes.subarray(0, friendlyAddressByteLength - 2);
|
|
107
|
-
// Every index below is in range: the length was checked to be exactly
|
|
108
|
-
// `friendlyAddressByteLength` above, so the non-null assertions are sound.
|
|
109
|
-
const stated = (bytes[friendlyAddressByteLength - 2] << 8) | bytes[friendlyAddressByteLength - 1];
|
|
110
|
-
if (checksum(body) !== stated) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
const tag = bytes[0];
|
|
114
|
-
const withoutTestFlag = tag & ~testnetTagFlag;
|
|
115
|
-
if (withoutTestFlag !== bounceableTag && withoutTestFlag !== nonBounceableTag) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
const workchainByte = bytes[1];
|
|
119
|
-
return {
|
|
120
|
-
// The masterchain is workchain -1, which travels as the byte 0xff.
|
|
121
|
-
workchain: workchainByte === 0xff ? -1 : workchainByte,
|
|
122
|
-
hash: bytes.slice(2, 2 + accountHashByteLength),
|
|
123
|
-
testOnly: (tag & testnetTagFlag) !== 0,
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
/**
|
|
127
|
-
* Parses either address form The Open Network uses.
|
|
128
|
-
*
|
|
129
|
-
* @param value - a friendly (`EQ…`/`UQ…`) or raw (`0:…`) address
|
|
130
|
-
* @returns the address parts, or null when it is neither form or fails its checksum
|
|
131
|
-
*/
|
|
132
|
-
export const parseTonAddress = (value) => value.includes(':') ? parseRawAddress(value) : parseFriendlyAddress(value);
|
|
133
|
-
/**
|
|
134
|
-
* Re-encodes an address into the friendly form matching a bounce setting.
|
|
135
|
-
*
|
|
136
|
-
* This is the whole point of the module: the bounce flag has nowhere else to
|
|
137
|
-
* live in a TON Connect request, so it has to be carried by the address itself.
|
|
138
|
-
*
|
|
139
|
-
* @param address - the address in either form
|
|
140
|
-
* @param options.bounceable - whether the message may bounce back to the sender
|
|
141
|
-
* @returns the friendly address, or null when the input cannot be parsed
|
|
142
|
-
*/
|
|
143
|
-
export const encodeTonAddress = (address, { bounceable }) => {
|
|
144
|
-
const parsed = parseTonAddress(address);
|
|
145
|
-
if (parsed === null) {
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
const bytes = new Uint8Array(friendlyAddressByteLength);
|
|
149
|
-
bytes[0] =
|
|
150
|
-
(bounceable ? bounceableTag : nonBounceableTag) | (parsed.testOnly ? testnetTagFlag : 0);
|
|
151
|
-
bytes[1] = parsed.workchain < 0 ? 0xff : parsed.workchain;
|
|
152
|
-
bytes.set(parsed.hash, 2);
|
|
153
|
-
const stated = checksum(bytes.subarray(0, friendlyAddressByteLength - 2));
|
|
154
|
-
bytes[friendlyAddressByteLength - 2] = (stated >> 8) & 0xff;
|
|
155
|
-
bytes[friendlyAddressByteLength - 1] = stated & 0xff;
|
|
156
|
-
return encodeBase64Url(bytes);
|
|
157
|
-
};
|
|
158
|
-
/**
|
|
159
|
-
* Builds the TON Connect request for a deposit.
|
|
160
|
-
*
|
|
161
|
-
* @param deposit - the parsed origin deposit
|
|
162
|
-
* @param options.validUntil - unix seconds after which the request expires.
|
|
163
|
-
* Supplied by the caller rather than read from a clock here so the transform
|
|
164
|
-
* stays pure; {@link tonConnectDefaultValidForSeconds} is the usual offset.
|
|
165
|
-
* @returns the request, or null when the deposit carries no messages or any
|
|
166
|
-
* destination address cannot be re-encoded — never a request whose bounce
|
|
167
|
-
* setting has been silently dropped
|
|
168
|
-
*/
|
|
169
|
-
export const buildTonConnectRequest = (deposit, { validUntil }) => {
|
|
170
|
-
if (deposit.messages.length === 0) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
const messages = [];
|
|
174
|
-
for (const message of deposit.messages) {
|
|
175
|
-
const address = encodeTonAddress(message.to, { bounceable: message.bounce });
|
|
176
|
-
// Refuse rather than fall back to the unencoded address: the fallback is
|
|
177
|
-
// exactly the bounce-losing behaviour this module exists to prevent.
|
|
178
|
-
if (address === null) {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
messages.push({
|
|
182
|
-
address,
|
|
183
|
-
amount: message.value.toString(),
|
|
184
|
-
...(message.body ? { payload: message.body } : {}),
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
// `messages[0]` is present: the empty case returned null above.
|
|
188
|
-
const from = deposit.messages[0].from;
|
|
189
|
-
return {
|
|
190
|
-
validUntil,
|
|
191
|
-
network: tonConnectMainnetNetwork,
|
|
192
|
-
...(from === null ? {} : { from }),
|
|
193
|
-
messages,
|
|
194
|
-
};
|
|
195
|
-
};
|
|
196
|
-
/**
|
|
197
|
-
* Derives the transaction identifier from a wallet's `sendTransaction` response.
|
|
198
|
-
*
|
|
199
|
-
* A wallet answers with a `boc`: the base64 bag-of-cells of the signed
|
|
200
|
-
* external-in message it broadcast — NOT the resulting transaction, and NOT a
|
|
201
|
-
* hash. The identifier explorers and indexers (toncenter, TonAPI) resolve a
|
|
202
|
-
* transaction by is the hash of that external message, but it must be the
|
|
203
|
-
* NORMALIZED hash of TEP-467, not the hash of the bytes the wallet happened to
|
|
204
|
-
* send.
|
|
205
|
-
*
|
|
206
|
-
* The distinction is the whole point, and getting it wrong loses a resume record
|
|
207
|
-
* for real funds. An external-in message can be serialized several equivalent
|
|
208
|
-
* ways — a wallet may or may not include its own `src`, may set a non-zero
|
|
209
|
-
* `import_fee`, may attach a `stateInit`, may store the body inline or as a
|
|
210
|
-
* reference. The raw cell hash changes with each of those wallet-specific
|
|
211
|
-
* choices, so it frequently does NOT match what an indexer keyed the same
|
|
212
|
-
* transaction under. TEP-467 rebuilds the message with those variable fields
|
|
213
|
-
* stripped (`src` to none, `import_fee` to zero, `init` dropped, body forced to a
|
|
214
|
-
* reference) so the hash is stable across wallets and matches the indexers.
|
|
215
|
-
*
|
|
216
|
-
* @param boc - the wallet's base64 `boc` field from `sendTransaction`
|
|
217
|
-
* @returns the 64-character hex normalized hash, or null when the input is not a
|
|
218
|
-
* parseable external-in message
|
|
4
|
+
* @deprecated Import from `@gibs/bridge-sdk/ton` for the full TON surface, or
|
|
5
|
+
* from `@gibs/bridge-sdk/ton-connect` for the helpers that need no TON library.
|
|
219
6
|
*/
|
|
220
|
-
export
|
|
221
|
-
try {
|
|
222
|
-
const message = loadMessage(Cell.fromBase64(boc).beginParse());
|
|
223
|
-
// Only an external-in message has a normalized form; an internal message or
|
|
224
|
-
// a bare cell is not the wallet's broadcast envelope and cannot be keyed.
|
|
225
|
-
if (message.info.type !== 'external-in') {
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
const normalized = {
|
|
229
|
-
...message,
|
|
230
|
-
// Dropped by TEP-467 so a wallet-attached stateInit does not move the hash.
|
|
231
|
-
init: null,
|
|
232
|
-
// `src` to addr_none (undefined) and the fee to zero: both are the sender's
|
|
233
|
-
// to choose and neither is part of what identifies the message.
|
|
234
|
-
info: { ...message.info, src: undefined, importFee: 0n },
|
|
235
|
-
};
|
|
236
|
-
// `forceRef` stores the body as a reference, as the normalized form requires.
|
|
237
|
-
return beginCell()
|
|
238
|
-
.store(storeMessage(normalized, { forceRef: true }))
|
|
239
|
-
.endCell()
|
|
240
|
-
.hash()
|
|
241
|
-
.toString('hex');
|
|
242
|
-
}
|
|
243
|
-
catch {
|
|
244
|
-
// A malformed base64 payload or a cell tree that is not a message at all.
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
};
|
|
7
|
+
export * from './ton.js';
|
package/dist/ton.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything the bridge SDK offers for The Open Network, in one entry point.
|
|
3
|
+
*
|
|
4
|
+
* It re-exports the pure TON Connect helpers from `./ton-connect.js` and adds
|
|
5
|
+
* {@link tonExternalMessageHash}, the only helper that needs `@ton/core`.
|
|
6
|
+
*
|
|
7
|
+
* Kept apart from the root entry point on purpose. `@ton/core` uses Node's
|
|
8
|
+
* `Buffer`, and its `@ton/crypto` dependency resolves, under React Native, to a
|
|
9
|
+
* build that requires `expo-crypto`. An application that bridges only between
|
|
10
|
+
* Ethereum-style chains never imports this file, so it never pays for either.
|
|
11
|
+
*/
|
|
12
|
+
export * from './ton-connect.js';
|
|
13
|
+
/**
|
|
14
|
+
* Derives the transaction identifier from a wallet's `sendTransaction` response.
|
|
15
|
+
*
|
|
16
|
+
* A wallet answers with a `boc`: the base64 bag-of-cells of the signed
|
|
17
|
+
* external-in message it broadcast — NOT the resulting transaction, and NOT a
|
|
18
|
+
* hash. The identifier explorers and indexers (toncenter, TonAPI) resolve a
|
|
19
|
+
* transaction by is the hash of that external message, but it must be the
|
|
20
|
+
* NORMALIZED hash of TEP-467, not the hash of the bytes the wallet happened to
|
|
21
|
+
* send.
|
|
22
|
+
*
|
|
23
|
+
* The distinction is the whole point, and getting it wrong loses a resume record
|
|
24
|
+
* for real funds. An external-in message can be serialized several equivalent
|
|
25
|
+
* ways — a wallet may or may not include its own `src`, may set a non-zero
|
|
26
|
+
* `import_fee`, may attach a `stateInit`, may store the body inline or as a
|
|
27
|
+
* reference. The raw cell hash changes with each of those wallet-specific
|
|
28
|
+
* choices, so it frequently does NOT match what an indexer keyed the same
|
|
29
|
+
* transaction under. TEP-467 rebuilds the message with those variable fields
|
|
30
|
+
* stripped (`src` to none, `import_fee` to zero, `init` dropped, body forced to a
|
|
31
|
+
* reference) so the hash is stable across wallets and matches the indexers.
|
|
32
|
+
*
|
|
33
|
+
* @param boc - the wallet's base64 `boc` field from `sendTransaction`
|
|
34
|
+
* @returns the 64-character hex normalized hash, or null when the input is not a
|
|
35
|
+
* parseable external-in message
|
|
36
|
+
*/
|
|
37
|
+
export declare const tonExternalMessageHash: (boc: string) => string | null;
|
package/dist/ton.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Cell, beginCell, loadMessage, storeMessage } from '@ton/core';
|
|
2
|
+
/**
|
|
3
|
+
* Everything the bridge SDK offers for The Open Network, in one entry point.
|
|
4
|
+
*
|
|
5
|
+
* It re-exports the pure TON Connect helpers from `./ton-connect.js` and adds
|
|
6
|
+
* {@link tonExternalMessageHash}, the only helper that needs `@ton/core`.
|
|
7
|
+
*
|
|
8
|
+
* Kept apart from the root entry point on purpose. `@ton/core` uses Node's
|
|
9
|
+
* `Buffer`, and its `@ton/crypto` dependency resolves, under React Native, to a
|
|
10
|
+
* build that requires `expo-crypto`. An application that bridges only between
|
|
11
|
+
* Ethereum-style chains never imports this file, so it never pays for either.
|
|
12
|
+
*/
|
|
13
|
+
export * from './ton-connect.js';
|
|
14
|
+
/**
|
|
15
|
+
* Derives the transaction identifier from a wallet's `sendTransaction` response.
|
|
16
|
+
*
|
|
17
|
+
* A wallet answers with a `boc`: the base64 bag-of-cells of the signed
|
|
18
|
+
* external-in message it broadcast — NOT the resulting transaction, and NOT a
|
|
19
|
+
* hash. The identifier explorers and indexers (toncenter, TonAPI) resolve a
|
|
20
|
+
* transaction by is the hash of that external message, but it must be the
|
|
21
|
+
* NORMALIZED hash of TEP-467, not the hash of the bytes the wallet happened to
|
|
22
|
+
* send.
|
|
23
|
+
*
|
|
24
|
+
* The distinction is the whole point, and getting it wrong loses a resume record
|
|
25
|
+
* for real funds. An external-in message can be serialized several equivalent
|
|
26
|
+
* ways — a wallet may or may not include its own `src`, may set a non-zero
|
|
27
|
+
* `import_fee`, may attach a `stateInit`, may store the body inline or as a
|
|
28
|
+
* reference. The raw cell hash changes with each of those wallet-specific
|
|
29
|
+
* choices, so it frequently does NOT match what an indexer keyed the same
|
|
30
|
+
* transaction under. TEP-467 rebuilds the message with those variable fields
|
|
31
|
+
* stripped (`src` to none, `import_fee` to zero, `init` dropped, body forced to a
|
|
32
|
+
* reference) so the hash is stable across wallets and matches the indexers.
|
|
33
|
+
*
|
|
34
|
+
* @param boc - the wallet's base64 `boc` field from `sendTransaction`
|
|
35
|
+
* @returns the 64-character hex normalized hash, or null when the input is not a
|
|
36
|
+
* parseable external-in message
|
|
37
|
+
*/
|
|
38
|
+
export const tonExternalMessageHash = (boc) => {
|
|
39
|
+
try {
|
|
40
|
+
const message = loadMessage(Cell.fromBase64(boc).beginParse());
|
|
41
|
+
// Only an external-in message has a normalized form; an internal message or
|
|
42
|
+
// a bare cell is not the wallet's broadcast envelope and cannot be keyed.
|
|
43
|
+
if (message.info.type !== 'external-in') {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const normalized = {
|
|
47
|
+
...message,
|
|
48
|
+
// Dropped by TEP-467 so a wallet-attached stateInit does not move the hash.
|
|
49
|
+
init: null,
|
|
50
|
+
// `src` to addr_none (undefined) and the fee to zero: both are the sender's
|
|
51
|
+
// to choose and neither is part of what identifies the message.
|
|
52
|
+
info: { ...message.info, src: undefined, importFee: 0n },
|
|
53
|
+
};
|
|
54
|
+
// `forceRef` stores the body as a reference, as the normalized form requires.
|
|
55
|
+
return beginCell()
|
|
56
|
+
.store(storeMessage(normalized, { forceRef: true }))
|
|
57
|
+
.endCell()
|
|
58
|
+
.hash()
|
|
59
|
+
.toString('hex');
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// A malformed base64 payload or a cell tree that is not a message at all.
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
};
|