@rabby-wallet/rabby-bridge 0.0.12 → 0.0.13-alpha.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.
Files changed (194) hide show
  1. package/README.md +131 -1
  2. package/dist/calldata/abi.d.ts +29 -0
  3. package/dist/calldata/abi.js +160 -0
  4. package/dist/calldata/across/deposit.d.ts +2 -0
  5. package/dist/calldata/across/deposit.js +36 -0
  6. package/dist/calldata/across/depositNative.d.ts +2 -0
  7. package/dist/calldata/across/depositNative.js +45 -0
  8. package/dist/calldata/across/depositNow.d.ts +2 -0
  9. package/dist/calldata/across/depositNow.js +35 -0
  10. package/dist/calldata/across/depositV3.d.ts +2 -0
  11. package/dist/calldata/across/depositV3.js +35 -0
  12. package/dist/calldata/across/depositV3Now.d.ts +2 -0
  13. package/dist/calldata/across/depositV3Now.js +34 -0
  14. package/dist/calldata/across/index.d.ts +4 -0
  15. package/dist/calldata/across/index.js +31 -0
  16. package/dist/calldata/across/swapAndBridge.d.ts +2 -0
  17. package/dist/calldata/across/swapAndBridge.js +70 -0
  18. package/dist/calldata/across/validate.d.ts +3 -0
  19. package/dist/calldata/across/validate.js +30 -0
  20. package/dist/calldata/chains.d.ts +2 -0
  21. package/dist/calldata/chains.js +35 -0
  22. package/dist/calldata/destination.d.ts +6 -0
  23. package/dist/calldata/destination.js +148 -0
  24. package/dist/calldata/diagnostics.d.ts +4 -0
  25. package/dist/calldata/diagnostics.js +18 -0
  26. package/dist/calldata/expect.d.ts +25 -0
  27. package/dist/calldata/expect.js +129 -0
  28. package/dist/calldata/hex.d.ts +6 -0
  29. package/dist/calldata/hex.js +46 -0
  30. package/dist/calldata/index.d.ts +20 -0
  31. package/dist/calldata/index.js +85 -0
  32. package/dist/calldata/lifi/across/acrossV4Erc20Min.d.ts +2 -0
  33. package/dist/calldata/lifi/across/acrossV4Erc20Min.js +26 -0
  34. package/dist/calldata/lifi/across/acrossV4NativeMin.d.ts +2 -0
  35. package/dist/calldata/lifi/across/acrossV4NativeMin.js +22 -0
  36. package/dist/calldata/lifi/across/index.d.ts +6 -0
  37. package/dist/calldata/lifi/across/index.js +19 -0
  38. package/dist/calldata/lifi/allbridge/index.d.ts +3 -0
  39. package/dist/calldata/lifi/allbridge/index.js +26 -0
  40. package/dist/calldata/lifi/celer/index.d.ts +4 -0
  41. package/dist/calldata/lifi/celer/index.js +10 -0
  42. package/dist/calldata/lifi/centrifuge/index.d.ts +3 -0
  43. package/dist/calldata/lifi/centrifuge/index.js +22 -0
  44. package/dist/calldata/lifi/frax/index.d.ts +4 -0
  45. package/dist/calldata/lifi/frax/index.js +29 -0
  46. package/dist/calldata/lifi/garden/index.d.ts +3 -0
  47. package/dist/calldata/lifi/garden/index.js +22 -0
  48. package/dist/calldata/lifi/gaszip/index.d.ts +6 -0
  49. package/dist/calldata/lifi/gaszip/index.js +15 -0
  50. package/dist/calldata/lifi/index.d.ts +4 -0
  51. package/dist/calldata/lifi/index.js +60 -0
  52. package/dist/calldata/lifi/intents/index.d.ts +4 -0
  53. package/dist/calldata/lifi/intents/index.js +10 -0
  54. package/dist/calldata/lifi/layerswap/index.d.ts +4 -0
  55. package/dist/calldata/lifi/layerswap/index.js +10 -0
  56. package/dist/calldata/lifi/m0/index.d.ts +3 -0
  57. package/dist/calldata/lifi/m0/index.js +33 -0
  58. package/dist/calldata/lifi/mayan/index.d.ts +4 -0
  59. package/dist/calldata/lifi/mayan/index.js +10 -0
  60. package/dist/calldata/lifi/method.d.ts +6 -0
  61. package/dist/calldata/lifi/method.js +16 -0
  62. package/dist/calldata/lifi/native/index.d.ts +4 -0
  63. package/dist/calldata/lifi/native/index.js +80 -0
  64. package/dist/calldata/lifi/params.d.ts +30 -0
  65. package/dist/calldata/lifi/params.js +370 -0
  66. package/dist/calldata/lifi/paxos/index.d.ts +4 -0
  67. package/dist/calldata/lifi/paxos/index.js +10 -0
  68. package/dist/calldata/lifi/polymer/index.d.ts +4 -0
  69. package/dist/calldata/lifi/polymer/index.js +10 -0
  70. package/dist/calldata/lifi/relay/index.d.ts +6 -0
  71. package/dist/calldata/lifi/relay/index.js +15 -0
  72. package/dist/calldata/lifi/shape.d.ts +4 -0
  73. package/dist/calldata/lifi/shape.js +31 -0
  74. package/dist/calldata/lifi/squid/index.d.ts +6 -0
  75. package/dist/calldata/lifi/squid/index.js +15 -0
  76. package/dist/calldata/lifi/stargate/index.d.ts +5 -0
  77. package/dist/calldata/lifi/stargate/index.js +15 -0
  78. package/dist/calldata/lifi/swap/index.d.ts +5 -0
  79. package/dist/calldata/lifi/swap/index.js +18 -0
  80. package/dist/calldata/lifi/swap/swapMethod.d.ts +2 -0
  81. package/dist/calldata/lifi/swap/swapMethod.js +40 -0
  82. package/dist/calldata/match.d.ts +3 -0
  83. package/dist/calldata/match.js +21 -0
  84. package/dist/calldata/mayan/destination.d.ts +2 -0
  85. package/dist/calldata/mayan/destination.js +13 -0
  86. package/dist/calldata/mayan/fastMctpBridge.d.ts +3 -0
  87. package/dist/calldata/mayan/fastMctpBridge.js +43 -0
  88. package/dist/calldata/mayan/fastMctpCreateOrder.d.ts +3 -0
  89. package/dist/calldata/mayan/fastMctpCreateOrder.js +39 -0
  90. package/dist/calldata/mayan/forward.d.ts +8 -0
  91. package/dist/calldata/mayan/forward.js +35 -0
  92. package/dist/calldata/mayan/forwardERC20.d.ts +2 -0
  93. package/dist/calldata/mayan/forwardERC20.js +19 -0
  94. package/dist/calldata/mayan/forwardEth.d.ts +2 -0
  95. package/dist/calldata/mayan/forwardEth.js +16 -0
  96. package/dist/calldata/mayan/index.d.ts +4 -0
  97. package/dist/calldata/mayan/index.js +21 -0
  98. package/dist/calldata/mayan/mctp.d.ts +5 -0
  99. package/dist/calldata/mayan/mctp.js +41 -0
  100. package/dist/calldata/mayan/readProtocol.d.ts +2 -0
  101. package/dist/calldata/mayan/readProtocol.js +29 -0
  102. package/dist/calldata/mayan/swapAndForwardERC20.d.ts +2 -0
  103. package/dist/calldata/mayan/swapAndForwardERC20.js +24 -0
  104. package/dist/calldata/mayan/swapAndForwardEth.d.ts +2 -0
  105. package/dist/calldata/mayan/swapAndForwardEth.js +23 -0
  106. package/dist/calldata/mayan/swiftCreateOrder.d.ts +3 -0
  107. package/dist/calldata/mayan/swiftCreateOrder.js +49 -0
  108. package/dist/calldata/mayan/validate.d.ts +3 -0
  109. package/dist/calldata/mayan/validate.js +20 -0
  110. package/dist/calldata/protocols.d.ts +9 -0
  111. package/dist/calldata/protocols.js +56 -0
  112. package/dist/calldata/relay/calls.d.ts +13 -0
  113. package/dist/calldata/relay/calls.js +54 -0
  114. package/dist/calldata/relay/depositErc20.d.ts +2 -0
  115. package/dist/calldata/relay/depositErc20.js +22 -0
  116. package/dist/calldata/relay/depositErc20Allowance.d.ts +2 -0
  117. package/dist/calldata/relay/depositErc20Allowance.js +20 -0
  118. package/dist/calldata/relay/depositNative.d.ts +2 -0
  119. package/dist/calldata/relay/depositNative.js +20 -0
  120. package/dist/calldata/relay/index.d.ts +4 -0
  121. package/dist/calldata/relay/index.js +40 -0
  122. package/dist/calldata/relay/multicall.d.ts +2 -0
  123. package/dist/calldata/relay/multicall.js +15 -0
  124. package/dist/calldata/relay/permit2TransferAndMulticall.d.ts +2 -0
  125. package/dist/calldata/relay/permit2TransferAndMulticall.js +42 -0
  126. package/dist/calldata/relay/permit3009TransferAndMulticall.d.ts +2 -0
  127. package/dist/calldata/relay/permit3009TransferAndMulticall.js +41 -0
  128. package/dist/calldata/relay/permit3009TransferAndMulticallSigned.d.ts +2 -0
  129. package/dist/calldata/relay/permit3009TransferAndMulticallSigned.js +40 -0
  130. package/dist/calldata/relay/permitTransferAndMulticall.d.ts +2 -0
  131. package/dist/calldata/relay/permitTransferAndMulticall.js +34 -0
  132. package/dist/calldata/relay/permitTransferAndMulticallLegacy.d.ts +2 -0
  133. package/dist/calldata/relay/permitTransferAndMulticallLegacy.js +35 -0
  134. package/dist/calldata/relay/transferAndMulticall.d.ts +2 -0
  135. package/dist/calldata/relay/transferAndMulticall.js +26 -0
  136. package/dist/calldata/relay/validate.d.ts +3 -0
  137. package/dist/calldata/relay/validate.js +18 -0
  138. package/dist/calldata/resolve.d.ts +2 -0
  139. package/dist/calldata/resolve.js +28 -0
  140. package/dist/calldata/socket/bridge.d.ts +2 -0
  141. package/dist/calldata/socket/bridge.js +28 -0
  142. package/dist/calldata/socket/exec.d.ts +2 -0
  143. package/dist/calldata/socket/exec.js +71 -0
  144. package/dist/calldata/socket/index.d.ts +4 -0
  145. package/dist/calldata/socket/index.js +13 -0
  146. package/dist/calldata/socket/params.d.ts +5 -0
  147. package/dist/calldata/socket/params.js +14 -0
  148. package/dist/calldata/socket/protocols/ccip.d.ts +2 -0
  149. package/dist/calldata/socket/protocols/ccip.js +32 -0
  150. package/dist/calldata/socket/protocols/cctp.d.ts +2 -0
  151. package/dist/calldata/socket/protocols/cctp.js +24 -0
  152. package/dist/calldata/socket/protocols/eco.d.ts +2 -0
  153. package/dist/calldata/socket/protocols/eco.js +20 -0
  154. package/dist/calldata/socket/protocols/index.d.ts +3 -0
  155. package/dist/calldata/socket/protocols/index.js +81 -0
  156. package/dist/calldata/socket/protocols/native.d.ts +2 -0
  157. package/dist/calldata/socket/protocols/native.js +70 -0
  158. package/dist/calldata/socket/protocols/oft.d.ts +2 -0
  159. package/dist/calldata/socket/protocols/oft.js +31 -0
  160. package/dist/calldata/socket/protocols/swapsXYZ.d.ts +2 -0
  161. package/dist/calldata/socket/protocols/swapsXYZ.js +62 -0
  162. package/dist/calldata/socket/swapAndBridge.d.ts +2 -0
  163. package/dist/calldata/socket/swapAndBridge.js +37 -0
  164. package/dist/calldata/socket/validate.d.ts +3 -0
  165. package/dist/calldata/socket/validate.js +21 -0
  166. package/dist/calldata/types.d.ts +82 -0
  167. package/dist/calldata/types.js +20 -0
  168. package/dist/errors/constants.d.ts +876 -0
  169. package/dist/errors/constants.js +883 -0
  170. package/dist/errors/index.d.ts +61 -0
  171. package/dist/errors/index.js +53 -0
  172. package/dist/errors/sanitize.d.ts +3 -0
  173. package/dist/errors/sanitize.js +75 -0
  174. package/dist/index.d.ts +7 -1
  175. package/dist/index.js +3 -0
  176. package/dist/providers/data.d.ts +23 -0
  177. package/dist/providers/data.js +116 -0
  178. package/dist/providers/errors.d.ts +7 -0
  179. package/dist/providers/errors.js +10 -0
  180. package/dist/providers/index.d.ts +7 -0
  181. package/dist/providers/index.js +340 -0
  182. package/dist/providers/layerswap.d.ts +9 -0
  183. package/dist/providers/layerswap.js +95 -0
  184. package/dist/providers/relay.d.ts +59 -0
  185. package/dist/providers/relay.js +185 -0
  186. package/dist/providers/request.d.ts +23 -0
  187. package/dist/providers/request.js +69 -0
  188. package/dist/providers/socket.d.ts +5 -0
  189. package/dist/providers/socket.js +57 -0
  190. package/dist/providers/types.d.ts +36 -0
  191. package/dist/providers/types.js +1 -0
  192. package/dist/quote.d.ts +18 -4
  193. package/dist/quote.js +282 -65
  194. package/package.json +13 -3
package/README.md CHANGED
@@ -5,8 +5,138 @@ Minimal bridge SDK for Rabby:
5
5
  - enumerates supported bridge aggregators and chains
6
6
  - maintains per-chain spender/router allowlists
7
7
  - validates backend bridge quotes before returning them
8
- - does not implement calldata decoding
8
+ - decodes source transactions for Across, LI.FI, Relay, Socket and Mayan
9
+ - keeps chain-state-dependent output fields explicitly unverified, without RPC requests
10
+ - validates Relay orders, three Socket routes and LI.FI LayerSwap through direct provider requests
11
+
12
+ See [calldata parsing](src/calldata/README.md) for supported paths, integration and verification boundaries.
13
+ See [direct provider integration](src/providers/README.md) for the shared extension/mobile
14
+ transport interface, quote binding, application-fee constraints and signing checks.
9
15
 
10
16
  ## Test
11
17
 
12
18
  Run `yarn test` to build the package and execute the Jest suite.
19
+
20
+ See [reachable route discovery](tests/fixtures/route-discovery-20261104/README.md) for the latest parsing changes, fixture coverage and remaining verification limits.
21
+
22
+ ## Lite rollout
23
+
24
+ Pass `lite: true` to `getQuote`, `buildTx` or `getQuoteListWithTx` to observe new
25
+ validation without letting it change the business decisions from `main`.
26
+ The default is `false`, retaining strict behavior.
27
+
28
+ ```ts
29
+ const quotes = await getQuoteListWithTx(aggregatorId, {
30
+ ...params,
31
+ lite: true,
32
+ onError: (report) => reportToBusiness(report),
33
+ }, api);
34
+ ```
35
+
36
+ - Quotes and transactions come from the existing backend flow, including Relay
37
+ and Socket. Provider quotes never replace the selected backend quote in lite mode.
38
+ - Configured Router/Spender mismatches, unsupported source chains/aggregators,
39
+ missing required slippage, missing backend results and backend request failures
40
+ keep their original blocking behavior.
41
+ - Calldata parsing, intent comparison, missing-field checks, stricter missing
42
+ contract configuration and provider verification still run against the returned
43
+ transaction. Their failures are reported without discarding that quote/transaction.
44
+ - No parser is reported as unsupported, including aggregators that strict mode
45
+ does not currently parse. It is never reported as a successful parse.
46
+ - Relay/Socket verification uses available proof/cache for the selected transaction.
47
+ If that proof or client is missing, the original missing-proof error is reported;
48
+ dependent hash/order checks cannot complete. An unrelated fresh provider quote
49
+ is not fetched or used as evidence for the backend transaction.
50
+ - LayerSwap lookup runs in the background in lite mode. Its result is reported
51
+ through `onError` and does not delay returning the backend transaction.
52
+
53
+ `validateBridgeQuote` also accepts `{ lite, onError }` as its fourth argument;
54
+ `validateBridgeTx` accepts it after the optional expectation as its sixth argument.
55
+ Raw decode/calldata-check results remain truthful (`ok: false` on failure).
56
+ Direct provider-client methods remain strict; use the high-level entry points for
57
+ lite rollout decisions.
58
+
59
+ Error codes and details are preserved. Lite observations carry `lite: true` and
60
+ `blocking: false`; legacy failures carry `blocking: true` when reported by the
61
+ high-level flow. Keep the callback installed while background checks are pending.
62
+ Dependent checks still stop at their first unusable prerequisite, and independent
63
+ stages may emit multiple reports. These are failure-event/path counts, not distinct
64
+ user counts or a denominator for all successful transactions. No user identifiers
65
+ are added. See error reporting below for catching failures that still throw.
66
+
67
+ ## Error reporting
68
+
69
+ All stable error codes, stages and messages are defined in
70
+ [`BRIDGE_ERRORS`](src/errors/constants.ts). Codes are explicit constants; callers
71
+ must not classify errors by matching message text. Existing error classes and
72
+ legacy `reason` fields remain available.
73
+
74
+ - `decodeBridgeCalldataResult(aggregatorId, tx)` returns a detailed success/failure
75
+ result. The existing `decodeBridgeCalldata` still returns `null` on failure.
76
+ - `validateBridgeCalldata` returns `errors`, plus `error_code` and `stage` on failure.
77
+ Independent intent mismatches are collected together; decoding or dependent
78
+ protocol operations stop at their first failure.
79
+ - Thrown SDK errors extend `BridgeError`. Use `error.toJSON()` for a report.
80
+ - `onError` receives failures filtered from `getQuote` / `getQuoteListWithTx`.
81
+ Directly thrown errors are handled by the caller, avoiding duplicate reporting.
82
+ Reporting callback failures do not affect validation or quote filtering.
83
+
84
+ ```ts
85
+ import { BridgeError, getQuoteListWithTx } from '@rabby-wallet/rabby-bridge';
86
+
87
+ try {
88
+ const quotes = await getQuoteListWithTx(aggregatorId, {
89
+ ...params,
90
+ onError: (report) => reportToBusiness(report),
91
+ }, api);
92
+ } catch (error) {
93
+ if (error instanceof BridgeError) reportToBusiness(error.toJSON());
94
+ throw error;
95
+ }
96
+ ```
97
+
98
+ A report contains a primary `error_code` / `stage` and an `errors` list. The primary
99
+ code is the first failure; inspect the list for every collected failure. Each detail
100
+ contains its fixed message and available context: aggregator, chain, bridge,
101
+ provider, operation, method, selector, nested protocol selector, missing fields or
102
+ HTTP status. SDK reports omit wallet addresses, amounts, raw calldata, response
103
+ bodies and arbitrary transport exception messages. Existing Error messages are
104
+ retained for compatibility; prefer `toJSON()` for reporting.
105
+
106
+ Stages distinguish input checks, contract allowlists, ABI decoding, protocol
107
+ structure, intent comparison, provider requests, provider responses, transaction
108
+ binding and completeness. Unexpected uncategorized failures use `BRIDGE_UNKNOWN`.
109
+ Deferred chain-state output fields remain in `unverifiedFields`; they do not
110
+ become errors merely because RPC validation is postponed.
111
+
112
+ ### Privacy-safe diagnostics
113
+
114
+ Use `toBridgeErrorReport(resultOrError)` to convert a failed calldata check,
115
+ detailed decode result or exception into a report. Passing a successful result
116
+ returns `undefined`. The `onError` callback and `BridgeError.toJSON()` use the same
117
+ runtime allowlist. Do not upload the entire check, exception, parsed transaction,
118
+ request parameters or quote response.
119
+
120
+ ```ts
121
+ const check = validateBridgeCalldata(aggregatorId, chainId, tx, expectation);
122
+ const report = toBridgeErrorReport(check);
123
+ if (report) reportToBusiness(report);
124
+ ```
125
+
126
+ Reports can retain recognized aggregator/bridge/method labels, source/destination
127
+ chains, outer/router/protocol selectors, call-data format and byte lengths, ABI
128
+ failure offsets, bounded destination-call selectors/counts, unsupported command
129
+ bytes/counts, static request categories and HTTP status. Nested diagnostics are
130
+ collected where the parser knows the call layout; arbitrary payload bytes are not
131
+ scanned for possible selectors. Destination selector lists are deduplicated and
132
+ limited to 16 entries.
133
+
134
+ Unknown labels become `unknown` or are omitted. Messages and stages are restored
135
+ from the error-code catalogue, even if an error was modified after construction.
136
+ Arbitrary properties are dropped, including wallet/receiver/refund addresses,
137
+ token addresses, amounts, balances, approvals, transaction/order/quote/request IDs,
138
+ signatures, full calldata, URL/query/header/body contents, raw exception messages
139
+ and stack traces. Contract addresses and hashes are not reported either.
140
+
141
+ These reports identify unsupported function families and failure stages. They
142
+ intentionally cannot reconstruct or identify the original user's transaction.
@@ -0,0 +1,29 @@
1
+ import type { ParsedBridgeCall } from './types.js';
2
+ export type AbiParam = {
3
+ name: string;
4
+ kind: 'address' | 'uint' | 'bool' | 'bytes' | 'string' | 'array' | 'tuple';
5
+ size?: number;
6
+ length?: number;
7
+ item?: AbiParam;
8
+ components?: AbiParam[];
9
+ };
10
+ export type AbiValue = string | bigint | boolean | AbiValue[] | {
11
+ [key: string]: AbiValue;
12
+ };
13
+ export declare const decodeCall: (params: AbiParam[], calldata: string) => {
14
+ [key: string]: AbiValue;
15
+ };
16
+ export declare const decodeParameters: (params: AbiParam[], data: string) => {
17
+ [key: string]: AbiValue;
18
+ };
19
+ export declare const asRecord: (value: AbiValue) => {
20
+ [key: string]: AbiValue;
21
+ };
22
+ export declare const asArray: (value: AbiValue) => AbiValue[];
23
+ export declare const asBigint: (value: AbiValue) => bigint;
24
+ export declare const asString: (value: AbiValue) => string;
25
+ export type CalldataMethod = {
26
+ name: string;
27
+ selector: string;
28
+ decode: (data: string, value: bigint) => ParsedBridgeCall;
29
+ };
@@ -0,0 +1,160 @@
1
+ import { BRIDGE_ERRORS, BridgeError } from '../errors/index.js';
2
+ import { toUtf8String, Utf8ErrorFuncs } from '@ethersproject/strings';
3
+ import { bytesToHex, hexToBytes } from './hex.js';
4
+ const isDynamic = (param) => {
5
+ if (param.kind === 'string') {
6
+ return true;
7
+ }
8
+ if (param.kind === 'bytes') {
9
+ return param.size === undefined;
10
+ }
11
+ if (param.kind === 'array') {
12
+ return param.length === undefined || isDynamic(param.item);
13
+ }
14
+ if (param.kind === 'tuple') {
15
+ return (param.components ?? []).some(isDynamic);
16
+ }
17
+ return false;
18
+ };
19
+ const staticWordCount = (param) => {
20
+ if (isDynamic(param)) {
21
+ return 1;
22
+ }
23
+ if (param.kind === 'tuple') {
24
+ return (param.components ?? []).reduce((sum, item) => sum + staticWordCount(item), 0);
25
+ }
26
+ if (param.kind === 'array') {
27
+ return (param.length ?? 0) * staticWordCount(param.item);
28
+ }
29
+ return 1;
30
+ };
31
+ const readWord = (buf, offset) => {
32
+ if (offset < 0 || offset + 32 > buf.length) {
33
+ throw new BridgeError(BRIDGE_ERRORS.ABI_WORD_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
34
+ }
35
+ let value = 0n;
36
+ for (let index = 0; index < 32; index += 1) {
37
+ value = (value << 8n) + BigInt(buf[offset + index]);
38
+ }
39
+ return value;
40
+ };
41
+ const toAddress = (word) => {
42
+ if (word >= (1n << 160n))
43
+ throw new BridgeError(BRIDGE_ERRORS.INVALID_ABI_ADDRESS);
44
+ return `0x${word.toString(16).padStart(40, '0')}`;
45
+ };
46
+ const readByteLength = (buf, offset) => {
47
+ const length = readWord(buf, offset);
48
+ if (length > BigInt(buf.length)) {
49
+ throw new BridgeError(BRIDGE_ERRORS.ABI_BYTES_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
50
+ }
51
+ return Number(length);
52
+ };
53
+ const readDynamicBytes = (buf, offset) => {
54
+ const length = readByteLength(buf, offset);
55
+ const start = offset + 32;
56
+ if (start + length > buf.length) {
57
+ throw new BridgeError(BRIDGE_ERRORS.ABI_BYTES_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
58
+ }
59
+ return bytesToHex(buf.subarray(start, start + length));
60
+ };
61
+ const decodeSequence = (params, buf, base) => {
62
+ let cursor = base;
63
+ const locations = [];
64
+ for (const param of params) {
65
+ if (isDynamic(param)) {
66
+ const relative = readWord(buf, cursor);
67
+ if (relative > BigInt(buf.length)) {
68
+ throw new BridgeError(BRIDGE_ERRORS.ABI_OFFSET_OUT_OF_BOUNDS, { abiOffset: cursor, abiBufferBytes: buf.length });
69
+ }
70
+ locations.push(base + Number(relative));
71
+ cursor += 32;
72
+ }
73
+ else {
74
+ locations.push(cursor);
75
+ cursor += staticWordCount(param) * 32;
76
+ }
77
+ }
78
+ return params.map((param, index) => decodeAt(param, buf, locations[index]));
79
+ };
80
+ const decodeAt = (param, buf, offset) => {
81
+ switch (param.kind) {
82
+ case 'address':
83
+ return toAddress(readWord(buf, offset));
84
+ case 'uint':
85
+ return readWord(buf, offset);
86
+ case 'bool': {
87
+ const word = readWord(buf, offset);
88
+ if (word !== 0n && word !== 1n) {
89
+ throw new BridgeError(BRIDGE_ERRORS.INVALID_BOOL);
90
+ }
91
+ return word === 1n;
92
+ }
93
+ case 'bytes': {
94
+ if (param.size === undefined) {
95
+ return readDynamicBytes(buf, offset);
96
+ }
97
+ if (offset + 32 > buf.length) {
98
+ throw new BridgeError(BRIDGE_ERRORS.ABI_WORD_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
99
+ }
100
+ return bytesToHex(buf.subarray(offset, offset + param.size));
101
+ }
102
+ case 'string':
103
+ return toUtf8String(readDynamicBytes(buf, offset), Utf8ErrorFuncs.replace).replace(/^\uFEFF/, '');
104
+ case 'tuple':
105
+ return decodeTuple(param.components ?? [], buf, offset);
106
+ case 'array': {
107
+ const item = param.item;
108
+ if (param.length === undefined) {
109
+ const length = readByteLength(buf, offset);
110
+ if (length > 2048) {
111
+ throw new BridgeError(BRIDGE_ERRORS.ABI_ARRAY_TOO_LONG);
112
+ }
113
+ return decodeSequence(Array.from({ length }, () => item), buf, offset + 32);
114
+ }
115
+ return decodeSequence(Array.from({ length: param.length }, () => item), buf, offset);
116
+ }
117
+ default:
118
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_ABI_TYPE);
119
+ }
120
+ };
121
+ const decodeTuple = (components, buf, offset) => {
122
+ const values = decodeSequence(components, buf, offset);
123
+ return Object.fromEntries(components.map((component, index) => [component.name, values[index]]));
124
+ };
125
+ export const decodeCall = (params, calldata) => {
126
+ const bytes = hexToBytes(calldata);
127
+ if (bytes.length < 4) {
128
+ throw new BridgeError(BRIDGE_ERRORS.MISSING_SELECTOR);
129
+ }
130
+ const values = decodeSequence(params, bytes.subarray(4), 0);
131
+ return Object.fromEntries(params.map((param, index) => [param.name, values[index]]));
132
+ };
133
+ export const decodeParameters = (params, data) => {
134
+ const values = decodeSequence(params, hexToBytes(data), 0);
135
+ return Object.fromEntries(params.map((param, index) => [param.name, values[index]]));
136
+ };
137
+ export const asRecord = (value) => {
138
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
139
+ throw new BridgeError(BRIDGE_ERRORS.EXPECTED_TUPLE);
140
+ }
141
+ return value;
142
+ };
143
+ export const asArray = (value) => {
144
+ if (!Array.isArray(value)) {
145
+ throw new BridgeError(BRIDGE_ERRORS.EXPECTED_ARRAY);
146
+ }
147
+ return value;
148
+ };
149
+ export const asBigint = (value) => {
150
+ if (typeof value !== 'bigint') {
151
+ throw new BridgeError(BRIDGE_ERRORS.EXPECTED_UINT);
152
+ }
153
+ return value;
154
+ };
155
+ export const asString = (value) => {
156
+ if (typeof value !== 'string') {
157
+ throw new BridgeError(BRIDGE_ERRORS.EXPECTED_STRING);
158
+ }
159
+ return value;
160
+ };
@@ -0,0 +1,2 @@
1
+ import type { CalldataMethod } from '../abi.js';
2
+ export declare const deposit: CalldataMethod;
@@ -0,0 +1,36 @@
1
+ import { applyAcrossMessage } from '../destination.js';
2
+ import { asBigint, asString, decodeCall } from '../abi.js';
3
+ import { accountFromBytes32 } from '../hex.js';
4
+ const fields = [
5
+ { name: 'depositor', kind: 'bytes', size: 32 },
6
+ { name: 'recipient', kind: 'bytes', size: 32 },
7
+ { name: 'inputToken', kind: 'bytes', size: 32 },
8
+ { name: 'outputToken', kind: 'bytes', size: 32 },
9
+ { name: 'inputAmount', kind: 'uint' },
10
+ { name: 'outputAmount', kind: 'uint' },
11
+ { name: 'destinationChainId', kind: 'uint' },
12
+ { name: 'exclusiveRelayer', kind: 'bytes', size: 32 },
13
+ { name: 'quoteTimestamp', kind: 'uint' },
14
+ { name: 'fillDeadline', kind: 'uint' },
15
+ { name: 'exclusivityParameter', kind: 'uint' },
16
+ { name: 'message', kind: 'bytes' },
17
+ ];
18
+ export const deposit = {
19
+ name: 'deposit',
20
+ selector: '0xad5425c6',
21
+ decode(data) {
22
+ const args = decodeCall(fields, data);
23
+ return applyAcrossMessage({
24
+ aggregatorId: 'across',
25
+ method: 'deposit',
26
+ kind: 'bridge',
27
+ depositor: accountFromBytes32(asString(args.depositor)),
28
+ sourceToken: accountFromBytes32(asString(args.inputToken)),
29
+ sourceAmount: asBigint(args.inputAmount),
30
+ destinationChainId: asBigint(args.destinationChainId),
31
+ receiver: accountFromBytes32(asString(args.recipient)),
32
+ outputToken: accountFromBytes32(asString(args.outputToken)),
33
+ minOutputAmount: asBigint(args.outputAmount),
34
+ }, asString(args.message));
35
+ },
36
+ };
@@ -0,0 +1,2 @@
1
+ import type { CalldataMethod } from '../abi.js';
2
+ export declare const depositNative: CalldataMethod;
@@ -0,0 +1,45 @@
1
+ import { BRIDGE_ERRORS, BridgeError } from '../../errors/index.js';
2
+ import { applyAcrossMessage } from '../destination.js';
3
+ import { asBigint, asString, decodeCall } from '../abi.js';
4
+ import { accountFromBytes32 } from '../hex.js';
5
+ import { NATIVE_TOKEN } from '../protocols.js';
6
+ const fields = [
7
+ { name: 'spokePool', kind: 'address' },
8
+ { name: 'depositor', kind: 'address' },
9
+ { name: 'recipient', kind: 'bytes', size: 32 },
10
+ { name: 'inputToken', kind: 'address' },
11
+ { name: 'inputAmount', kind: 'uint' },
12
+ { name: 'outputToken', kind: 'bytes', size: 32 },
13
+ { name: 'outputAmount', kind: 'uint' },
14
+ { name: 'destinationChainId', kind: 'uint' },
15
+ { name: 'exclusiveRelayer', kind: 'bytes', size: 32 },
16
+ { name: 'quoteTimestamp', kind: 'uint' },
17
+ { name: 'fillDeadline', kind: 'uint' },
18
+ { name: 'exclusivityParameter', kind: 'uint' },
19
+ { name: 'message', kind: 'bytes' },
20
+ ];
21
+ export const depositNative = {
22
+ name: 'depositNative',
23
+ selector: '0x609ea081',
24
+ decode(data, value) {
25
+ const args = decodeCall(fields, data);
26
+ const inputAmount = asBigint(args.inputAmount);
27
+ if (inputAmount > 0n && value !== inputAmount)
28
+ throw new BridgeError(BRIDGE_ERRORS.ACROSS_NATIVE_VALUE_MISMATCH);
29
+ return applyAcrossMessage({
30
+ aggregatorId: 'across',
31
+ method: 'depositNative',
32
+ kind: 'bridge',
33
+ depositor: asString(args.depositor),
34
+ sourceToken: NATIVE_TOKEN,
35
+ sourceAmount: inputAmount > 0n ? inputAmount : value,
36
+ bridgeToken: asString(args.inputToken),
37
+ bridgeAmount: inputAmount > 0n ? inputAmount : value,
38
+ destinationChainId: asBigint(args.destinationChainId),
39
+ receiver: accountFromBytes32(asString(args.recipient)),
40
+ outputToken: accountFromBytes32(asString(args.outputToken)),
41
+ minOutputAmount: asBigint(args.outputAmount),
42
+ spokePool: asString(args.spokePool),
43
+ }, asString(args.message));
44
+ },
45
+ };
@@ -0,0 +1,2 @@
1
+ import type { CalldataMethod } from '../abi.js';
2
+ export declare const depositNow: CalldataMethod;
@@ -0,0 +1,35 @@
1
+ import { applyAcrossMessage } from '../destination.js';
2
+ import { asBigint, asString, decodeCall } from '../abi.js';
3
+ import { accountFromBytes32 } from '../hex.js';
4
+ const fields = [
5
+ { name: 'depositor', kind: 'bytes', size: 32 },
6
+ { name: 'recipient', kind: 'bytes', size: 32 },
7
+ { name: 'inputToken', kind: 'bytes', size: 32 },
8
+ { name: 'outputToken', kind: 'bytes', size: 32 },
9
+ { name: 'inputAmount', kind: 'uint' },
10
+ { name: 'outputAmount', kind: 'uint' },
11
+ { name: 'destinationChainId', kind: 'uint' },
12
+ { name: 'exclusiveRelayer', kind: 'bytes', size: 32 },
13
+ { name: 'fillDeadlineOffset', kind: 'uint' },
14
+ { name: 'exclusivityParameter', kind: 'uint' },
15
+ { name: 'message', kind: 'bytes' },
16
+ ];
17
+ export const depositNow = {
18
+ name: 'depositNow',
19
+ selector: '0xea86bd46',
20
+ decode(data) {
21
+ const args = decodeCall(fields, data);
22
+ return applyAcrossMessage({
23
+ aggregatorId: 'across',
24
+ method: 'depositNow',
25
+ kind: 'bridge',
26
+ depositor: accountFromBytes32(asString(args.depositor)),
27
+ sourceToken: accountFromBytes32(asString(args.inputToken)),
28
+ sourceAmount: asBigint(args.inputAmount),
29
+ destinationChainId: asBigint(args.destinationChainId),
30
+ receiver: accountFromBytes32(asString(args.recipient)),
31
+ outputToken: accountFromBytes32(asString(args.outputToken)),
32
+ minOutputAmount: asBigint(args.outputAmount),
33
+ }, asString(args.message));
34
+ },
35
+ };
@@ -0,0 +1,2 @@
1
+ import type { CalldataMethod } from '../abi.js';
2
+ export declare const depositV3: CalldataMethod;
@@ -0,0 +1,35 @@
1
+ import { applyAcrossMessage } from '../destination.js';
2
+ import { asBigint, asString, decodeCall } from '../abi.js';
3
+ const fields = [
4
+ { name: 'depositor', kind: 'address' },
5
+ { name: 'recipient', kind: 'address' },
6
+ { name: 'inputToken', kind: 'address' },
7
+ { name: 'outputToken', kind: 'address' },
8
+ { name: 'inputAmount', kind: 'uint' },
9
+ { name: 'outputAmount', kind: 'uint' },
10
+ { name: 'destinationChainId', kind: 'uint' },
11
+ { name: 'exclusiveRelayer', kind: 'address' },
12
+ { name: 'quoteTimestamp', kind: 'uint' },
13
+ { name: 'fillDeadline', kind: 'uint' },
14
+ { name: 'exclusivityParameter', kind: 'uint' },
15
+ { name: 'message', kind: 'bytes' },
16
+ ];
17
+ export const depositV3 = {
18
+ name: 'depositV3',
19
+ selector: '0x7b939232',
20
+ decode(data) {
21
+ const args = decodeCall(fields, data);
22
+ return applyAcrossMessage({
23
+ aggregatorId: 'across',
24
+ method: 'depositV3',
25
+ kind: 'bridge',
26
+ depositor: asString(args.depositor),
27
+ sourceToken: asString(args.inputToken),
28
+ sourceAmount: asBigint(args.inputAmount),
29
+ destinationChainId: asBigint(args.destinationChainId),
30
+ receiver: asString(args.recipient),
31
+ outputToken: asString(args.outputToken),
32
+ minOutputAmount: asBigint(args.outputAmount),
33
+ }, asString(args.message));
34
+ },
35
+ };
@@ -0,0 +1,2 @@
1
+ import type { CalldataMethod } from '../abi.js';
2
+ export declare const depositV3Now: CalldataMethod;
@@ -0,0 +1,34 @@
1
+ import { applyAcrossMessage } from '../destination.js';
2
+ import { asBigint, asString, decodeCall } from '../abi.js';
3
+ const fields = [
4
+ { name: 'depositor', kind: 'address' },
5
+ { name: 'recipient', kind: 'address' },
6
+ { name: 'inputToken', kind: 'address' },
7
+ { name: 'outputToken', kind: 'address' },
8
+ { name: 'inputAmount', kind: 'uint' },
9
+ { name: 'outputAmount', kind: 'uint' },
10
+ { name: 'destinationChainId', kind: 'uint' },
11
+ { name: 'exclusiveRelayer', kind: 'address' },
12
+ { name: 'fillDeadlineOffset', kind: 'uint' },
13
+ { name: 'exclusivityParameter', kind: 'uint' },
14
+ { name: 'message', kind: 'bytes' },
15
+ ];
16
+ export const depositV3Now = {
17
+ name: 'depositV3Now',
18
+ selector: '0x7aef642c',
19
+ decode(data) {
20
+ const args = decodeCall(fields, data);
21
+ return applyAcrossMessage({
22
+ aggregatorId: 'across',
23
+ method: 'depositV3Now',
24
+ kind: 'bridge',
25
+ depositor: asString(args.depositor),
26
+ sourceToken: asString(args.inputToken),
27
+ sourceAmount: asBigint(args.inputAmount),
28
+ destinationChainId: asBigint(args.destinationChainId),
29
+ receiver: asString(args.recipient),
30
+ outputToken: asString(args.outputToken),
31
+ minOutputAmount: asBigint(args.outputAmount),
32
+ }, asString(args.message));
33
+ },
34
+ };
@@ -0,0 +1,4 @@
1
+ import type { BridgeCallExpectation } from '../expect.js';
2
+ import type { BridgeCheck, DecodeHit } from '../types.js';
3
+ export declare const decodeAcross: (data: string, value: bigint) => DecodeHit;
4
+ export declare const validateAcross: (data: string, value: bigint, expectation?: BridgeCallExpectation, chainId?: string) => BridgeCheck;
@@ -0,0 +1,31 @@
1
+ import { matchMethod } from '../match.js';
2
+ import { finishCheck } from '../expect.js';
3
+ import { deposit } from './deposit.js';
4
+ import { depositNative } from './depositNative.js';
5
+ import { depositNow } from './depositNow.js';
6
+ import { depositV3 } from './depositV3.js';
7
+ import { depositV3Now } from './depositV3Now.js';
8
+ import { swapAndBridge } from './swapAndBridge.js';
9
+ import { acrossFailure } from './validate.js';
10
+ const METHODS = [
11
+ depositV3,
12
+ deposit,
13
+ depositV3Now,
14
+ depositNow,
15
+ depositNative,
16
+ swapAndBridge,
17
+ ];
18
+ export const decodeAcross = (data, value) => matchMethod(METHODS, data, value);
19
+ export const validateAcross = (data, value, expectation, chainId = '') => {
20
+ const hit = decodeAcross(data, value);
21
+ if (!hit.ok) {
22
+ return { ...hit, aggregatorId: 'across' };
23
+ }
24
+ return finishCheck('across', hit.parsed, acrossFailure(hit.parsed, chainId, expectation?.userAddress), expectation, chainId, {
25
+ fromToken: true,
26
+ fromAmount: true,
27
+ toToken: true,
28
+ toAmount: true,
29
+ receiver: true,
30
+ });
31
+ };
@@ -0,0 +1,2 @@
1
+ import type { CalldataMethod } from '../abi.js';
2
+ export declare const swapAndBridge: CalldataMethod;
@@ -0,0 +1,70 @@
1
+ import { BRIDGE_ERRORS, BridgeError } from '../../errors/index.js';
2
+ import { applyAcrossMessage } from '../destination.js';
3
+ import { asBigint, asRecord, asString, decodeCall } from '../abi.js';
4
+ import { accountFromBytes32 } from '../hex.js';
5
+ import { NATIVE_TOKEN } from '../protocols.js';
6
+ const swapAndDepositData = {
7
+ name: 'swapAndDepositData',
8
+ kind: 'tuple',
9
+ components: [
10
+ {
11
+ name: 'submissionFees',
12
+ kind: 'tuple',
13
+ components: [
14
+ { name: 'amount', kind: 'uint' },
15
+ { name: 'recipient', kind: 'address' },
16
+ ],
17
+ },
18
+ {
19
+ name: 'depositData',
20
+ kind: 'tuple',
21
+ components: [
22
+ { name: 'inputToken', kind: 'address' },
23
+ { name: 'outputToken', kind: 'bytes', size: 32 },
24
+ { name: 'outputAmount', kind: 'uint' },
25
+ { name: 'depositor', kind: 'address' },
26
+ { name: 'recipient', kind: 'bytes', size: 32 },
27
+ { name: 'destinationChainId', kind: 'uint' },
28
+ { name: 'exclusiveRelayer', kind: 'bytes', size: 32 },
29
+ { name: 'quoteTimestamp', kind: 'uint' },
30
+ { name: 'fillDeadline', kind: 'uint' },
31
+ { name: 'exclusivityParameter', kind: 'uint' },
32
+ { name: 'message', kind: 'bytes' },
33
+ ],
34
+ },
35
+ { name: 'swapToken', kind: 'address' },
36
+ { name: 'exchange', kind: 'address' },
37
+ { name: 'transferType', kind: 'uint' },
38
+ { name: 'swapTokenAmount', kind: 'uint' },
39
+ { name: 'minExpectedInputTokenAmount', kind: 'uint' },
40
+ { name: 'routerCalldata', kind: 'bytes' },
41
+ { name: 'enableProportionalAdjustment', kind: 'bool' },
42
+ { name: 'spokePool', kind: 'address' },
43
+ { name: 'nonce', kind: 'uint' },
44
+ ],
45
+ };
46
+ export const swapAndBridge = {
47
+ name: 'swapAndBridge',
48
+ selector: '0x110560ad',
49
+ decode(data, value) {
50
+ const args = asRecord(decodeCall([swapAndDepositData], data).swapAndDepositData);
51
+ const depositData = asRecord(args.depositData);
52
+ const sourceAmount = asBigint(args.swapTokenAmount);
53
+ if (value > 0n && value !== sourceAmount)
54
+ throw new BridgeError(BRIDGE_ERRORS.ACROSS_SWAP_VALUE_MISMATCH);
55
+ return applyAcrossMessage({
56
+ aggregatorId: 'across',
57
+ method: 'swapAndBridge',
58
+ kind: 'bridge',
59
+ depositor: asString(depositData.depositor),
60
+ sourceToken: value > 0n ? NATIVE_TOKEN : asString(args.swapToken),
61
+ sourceAmount,
62
+ destinationChainId: asBigint(depositData.destinationChainId),
63
+ receiver: accountFromBytes32(asString(depositData.recipient)),
64
+ outputToken: accountFromBytes32(asString(depositData.outputToken)),
65
+ minOutputAmount: asBigint(depositData.outputAmount),
66
+ spokePool: asString(args.spokePool),
67
+ innerTarget: asString(args.exchange),
68
+ }, asString(depositData.message));
69
+ },
70
+ };
@@ -0,0 +1,3 @@
1
+ import type { BridgeErrorDefinition } from '../../errors/index.js';
2
+ import type { ParsedBridgeCall } from '../types.js';
3
+ export declare const acrossFailure: (parsed: ParsedBridgeCall, chainId: string, expectedDepositor?: string) => BridgeErrorDefinition | undefined;