@rabby-wallet/rabby-bridge 0.0.13 → 0.0.14-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 (200) hide show
  1. package/README.md +163 -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 +34 -0
  20. package/dist/calldata/chains.d.ts +2 -0
  21. package/dist/calldata/chains.js +36 -0
  22. package/dist/calldata/destination.d.ts +6 -0
  23. package/dist/calldata/destination.js +227 -0
  24. package/dist/calldata/destinationSwap.d.ts +14 -0
  25. package/dist/calldata/destinationSwap.js +83 -0
  26. package/dist/calldata/destinationUniswap.d.ts +7 -0
  27. package/dist/calldata/destinationUniswap.js +57 -0
  28. package/dist/calldata/diagnostics.d.ts +4 -0
  29. package/dist/calldata/diagnostics.js +18 -0
  30. package/dist/calldata/expect.d.ts +29 -0
  31. package/dist/calldata/expect.js +157 -0
  32. package/dist/calldata/hex.d.ts +6 -0
  33. package/dist/calldata/hex.js +46 -0
  34. package/dist/calldata/index.d.ts +20 -0
  35. package/dist/calldata/index.js +88 -0
  36. package/dist/calldata/lifi/across/acrossV4Erc20Min.d.ts +2 -0
  37. package/dist/calldata/lifi/across/acrossV4Erc20Min.js +27 -0
  38. package/dist/calldata/lifi/across/acrossV4NativeMin.d.ts +2 -0
  39. package/dist/calldata/lifi/across/acrossV4NativeMin.js +23 -0
  40. package/dist/calldata/lifi/across/index.d.ts +6 -0
  41. package/dist/calldata/lifi/across/index.js +19 -0
  42. package/dist/calldata/lifi/allbridge/index.d.ts +3 -0
  43. package/dist/calldata/lifi/allbridge/index.js +26 -0
  44. package/dist/calldata/lifi/celer/index.d.ts +4 -0
  45. package/dist/calldata/lifi/celer/index.js +10 -0
  46. package/dist/calldata/lifi/centrifuge/index.d.ts +3 -0
  47. package/dist/calldata/lifi/centrifuge/index.js +22 -0
  48. package/dist/calldata/lifi/destination.d.ts +5 -0
  49. package/dist/calldata/lifi/destination.js +40 -0
  50. package/dist/calldata/lifi/frax/index.d.ts +4 -0
  51. package/dist/calldata/lifi/frax/index.js +29 -0
  52. package/dist/calldata/lifi/garden/index.d.ts +3 -0
  53. package/dist/calldata/lifi/garden/index.js +22 -0
  54. package/dist/calldata/lifi/gaszip/index.d.ts +6 -0
  55. package/dist/calldata/lifi/gaszip/index.js +15 -0
  56. package/dist/calldata/lifi/index.d.ts +4 -0
  57. package/dist/calldata/lifi/index.js +62 -0
  58. package/dist/calldata/lifi/intents/index.d.ts +4 -0
  59. package/dist/calldata/lifi/intents/index.js +10 -0
  60. package/dist/calldata/lifi/layerswap/index.d.ts +4 -0
  61. package/dist/calldata/lifi/layerswap/index.js +10 -0
  62. package/dist/calldata/lifi/m0/index.d.ts +3 -0
  63. package/dist/calldata/lifi/m0/index.js +33 -0
  64. package/dist/calldata/lifi/mayan/index.d.ts +4 -0
  65. package/dist/calldata/lifi/mayan/index.js +10 -0
  66. package/dist/calldata/lifi/method.d.ts +6 -0
  67. package/dist/calldata/lifi/method.js +16 -0
  68. package/dist/calldata/lifi/native/index.d.ts +4 -0
  69. package/dist/calldata/lifi/native/index.js +80 -0
  70. package/dist/calldata/lifi/params.d.ts +28 -0
  71. package/dist/calldata/lifi/params.js +349 -0
  72. package/dist/calldata/lifi/paxos/index.d.ts +4 -0
  73. package/dist/calldata/lifi/paxos/index.js +10 -0
  74. package/dist/calldata/lifi/polymer/index.d.ts +4 -0
  75. package/dist/calldata/lifi/polymer/index.js +10 -0
  76. package/dist/calldata/lifi/relay/index.d.ts +6 -0
  77. package/dist/calldata/lifi/relay/index.js +15 -0
  78. package/dist/calldata/lifi/shape.d.ts +4 -0
  79. package/dist/calldata/lifi/shape.js +31 -0
  80. package/dist/calldata/lifi/squid/index.d.ts +6 -0
  81. package/dist/calldata/lifi/squid/index.js +15 -0
  82. package/dist/calldata/lifi/stargate/index.d.ts +5 -0
  83. package/dist/calldata/lifi/stargate/index.js +15 -0
  84. package/dist/calldata/lifi/swap/index.d.ts +5 -0
  85. package/dist/calldata/lifi/swap/index.js +18 -0
  86. package/dist/calldata/lifi/swap/swapMethod.d.ts +2 -0
  87. package/dist/calldata/lifi/swap/swapMethod.js +40 -0
  88. package/dist/calldata/lifi/swapData.d.ts +5 -0
  89. package/dist/calldata/lifi/swapData.js +33 -0
  90. package/dist/calldata/match.d.ts +3 -0
  91. package/dist/calldata/match.js +21 -0
  92. package/dist/calldata/mayan/destination.d.ts +2 -0
  93. package/dist/calldata/mayan/destination.js +13 -0
  94. package/dist/calldata/mayan/fastMctpBridge.d.ts +3 -0
  95. package/dist/calldata/mayan/fastMctpBridge.js +48 -0
  96. package/dist/calldata/mayan/fastMctpCreateOrder.d.ts +3 -0
  97. package/dist/calldata/mayan/fastMctpCreateOrder.js +39 -0
  98. package/dist/calldata/mayan/forward.d.ts +8 -0
  99. package/dist/calldata/mayan/forward.js +35 -0
  100. package/dist/calldata/mayan/forwardERC20.d.ts +2 -0
  101. package/dist/calldata/mayan/forwardERC20.js +19 -0
  102. package/dist/calldata/mayan/forwardEth.d.ts +2 -0
  103. package/dist/calldata/mayan/forwardEth.js +16 -0
  104. package/dist/calldata/mayan/index.d.ts +4 -0
  105. package/dist/calldata/mayan/index.js +21 -0
  106. package/dist/calldata/mayan/mctp.d.ts +5 -0
  107. package/dist/calldata/mayan/mctp.js +41 -0
  108. package/dist/calldata/mayan/readProtocol.d.ts +2 -0
  109. package/dist/calldata/mayan/readProtocol.js +29 -0
  110. package/dist/calldata/mayan/swapAndForwardERC20.d.ts +2 -0
  111. package/dist/calldata/mayan/swapAndForwardERC20.js +24 -0
  112. package/dist/calldata/mayan/swapAndForwardEth.d.ts +2 -0
  113. package/dist/calldata/mayan/swapAndForwardEth.js +23 -0
  114. package/dist/calldata/mayan/swiftCreateOrder.d.ts +3 -0
  115. package/dist/calldata/mayan/swiftCreateOrder.js +49 -0
  116. package/dist/calldata/mayan/validate.d.ts +3 -0
  117. package/dist/calldata/mayan/validate.js +20 -0
  118. package/dist/calldata/protocols.d.ts +9 -0
  119. package/dist/calldata/protocols.js +58 -0
  120. package/dist/calldata/relay/calls.d.ts +13 -0
  121. package/dist/calldata/relay/calls.js +58 -0
  122. package/dist/calldata/relay/depositErc20.d.ts +2 -0
  123. package/dist/calldata/relay/depositErc20.js +22 -0
  124. package/dist/calldata/relay/depositErc20Allowance.d.ts +2 -0
  125. package/dist/calldata/relay/depositErc20Allowance.js +20 -0
  126. package/dist/calldata/relay/depositNative.d.ts +2 -0
  127. package/dist/calldata/relay/depositNative.js +20 -0
  128. package/dist/calldata/relay/index.d.ts +4 -0
  129. package/dist/calldata/relay/index.js +45 -0
  130. package/dist/calldata/relay/multicall.d.ts +2 -0
  131. package/dist/calldata/relay/multicall.js +15 -0
  132. package/dist/calldata/relay/nestedDeposit.d.ts +8 -0
  133. package/dist/calldata/relay/nestedDeposit.js +107 -0
  134. package/dist/calldata/relay/permit2TransferAndMulticall.d.ts +2 -0
  135. package/dist/calldata/relay/permit2TransferAndMulticall.js +42 -0
  136. package/dist/calldata/relay/permit3009TransferAndMulticall.d.ts +2 -0
  137. package/dist/calldata/relay/permit3009TransferAndMulticall.js +41 -0
  138. package/dist/calldata/relay/permit3009TransferAndMulticallSigned.d.ts +2 -0
  139. package/dist/calldata/relay/permit3009TransferAndMulticallSigned.js +40 -0
  140. package/dist/calldata/relay/permitTransferAndMulticall.d.ts +2 -0
  141. package/dist/calldata/relay/permitTransferAndMulticall.js +34 -0
  142. package/dist/calldata/relay/permitTransferAndMulticallLegacy.d.ts +2 -0
  143. package/dist/calldata/relay/permitTransferAndMulticallLegacy.js +35 -0
  144. package/dist/calldata/relay/transferAndMulticall.d.ts +2 -0
  145. package/dist/calldata/relay/transferAndMulticall.js +26 -0
  146. package/dist/calldata/relay/validate.d.ts +3 -0
  147. package/dist/calldata/relay/validate.js +18 -0
  148. package/dist/calldata/resolve.d.ts +2 -0
  149. package/dist/calldata/resolve.js +28 -0
  150. package/dist/calldata/socket/addresses.d.ts +9 -0
  151. package/dist/calldata/socket/addresses.js +9 -0
  152. package/dist/calldata/socket/bridge.d.ts +2 -0
  153. package/dist/calldata/socket/bridge.js +28 -0
  154. package/dist/calldata/socket/exec.d.ts +2 -0
  155. package/dist/calldata/socket/exec.js +71 -0
  156. package/dist/calldata/socket/index.d.ts +4 -0
  157. package/dist/calldata/socket/index.js +13 -0
  158. package/dist/calldata/socket/params.d.ts +5 -0
  159. package/dist/calldata/socket/params.js +14 -0
  160. package/dist/calldata/socket/protocols/ccip.d.ts +2 -0
  161. package/dist/calldata/socket/protocols/ccip.js +32 -0
  162. package/dist/calldata/socket/protocols/cctp.d.ts +2 -0
  163. package/dist/calldata/socket/protocols/cctp.js +25 -0
  164. package/dist/calldata/socket/protocols/eco.d.ts +2 -0
  165. package/dist/calldata/socket/protocols/eco.js +49 -0
  166. package/dist/calldata/socket/protocols/index.d.ts +3 -0
  167. package/dist/calldata/socket/protocols/index.js +87 -0
  168. package/dist/calldata/socket/protocols/native.d.ts +2 -0
  169. package/dist/calldata/socket/protocols/native.js +71 -0
  170. package/dist/calldata/socket/protocols/oft.d.ts +2 -0
  171. package/dist/calldata/socket/protocols/oft.js +33 -0
  172. package/dist/calldata/socket/protocols/swapsXYZ.d.ts +2 -0
  173. package/dist/calldata/socket/protocols/swapsXYZ.js +62 -0
  174. package/dist/calldata/socket/swapAndBridge.d.ts +2 -0
  175. package/dist/calldata/socket/swapAndBridge.js +41 -0
  176. package/dist/calldata/socket/validate.d.ts +3 -0
  177. package/dist/calldata/socket/validate.js +24 -0
  178. package/dist/calldata/types.d.ts +86 -0
  179. package/dist/calldata/types.js +20 -0
  180. package/dist/errors/constants.d.ts +906 -0
  181. package/dist/errors/constants.js +913 -0
  182. package/dist/errors/index.d.ts +65 -0
  183. package/dist/errors/index.js +53 -0
  184. package/dist/errors/sanitize.d.ts +3 -0
  185. package/dist/errors/sanitize.js +74 -0
  186. package/dist/index.d.ts +6 -1
  187. package/dist/index.js +3 -0
  188. package/dist/providers/data.d.ts +23 -0
  189. package/dist/providers/data.js +116 -0
  190. package/dist/providers/errors.d.ts +7 -0
  191. package/dist/providers/errors.js +10 -0
  192. package/dist/providers/layerswap.d.ts +9 -0
  193. package/dist/providers/layerswap.js +95 -0
  194. package/dist/providers/relay.d.ts +62 -0
  195. package/dist/providers/relay.js +237 -0
  196. package/dist/providers/socket.d.ts +5 -0
  197. package/dist/providers/socket.js +58 -0
  198. package/dist/quote.d.ts +30 -3
  199. package/dist/quote.js +236 -66
  200. package/package.json +13 -3
package/README.md CHANGED
@@ -5,8 +5,170 @@ 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
+ - performs local validation only; no direct-provider HTTP requests or RPC reads
11
+
12
+ See [calldata parsing](src/calldata/README.md) for supported paths, integration and verification boundaries.
13
+ See [offline provider evidence](src/providers/README.md) for retained historical fixtures and verification limits.
9
14
 
10
15
  ## Test
11
16
 
12
17
  Run `yarn test` to build the package and execute the Jest suite.
18
+
19
+ See [reachable route discovery](tests/fixtures/route-discovery-20261104/README.md) for the latest parsing changes, fixture coverage and remaining verification limits.
20
+
21
+ ## Validation rollout
22
+
23
+ Router/Spender allowlists and required calldata checks are always enforced.
24
+ Reported calldata failures block: `validateBridgeTx` and `buildTx` throw
25
+ `InvalidBridgeCalldataError`, and `getQuoteListWithTx` filters the failed quote.
26
+ No provider API or RPC requests are made.
27
+
28
+ ```ts
29
+ const quotes = await getQuoteListWithTx(aggregatorId, {
30
+ ...params,
31
+ onError: (report) => reportToBusiness(report),
32
+ }, api);
33
+ ```
34
+
35
+ - Quotes and transactions come from the existing backend flow, including Relay
36
+ and Socket. The SDK no longer accepts `providerClient` or exposes
37
+ a direct-provider client/fetch adapter.
38
+ - 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
+ - Source token, source amount, supplied sender/chain and protocol structure are
42
+ checked locally. Missing source fields and mismatches are reported.
43
+ - Socket quote IDs (including CCTP), RFQ outer/vault IDs and Relay multicall
44
+ request IDs are compared locally when a selected quote provides a matching
45
+ identifier. Mismatches block without changing the three field flags;
46
+ missing/unrecognized keys are skipped. See the [binding limits](src/calldata/README.md).
47
+ - Known destination tokens, minimum amounts and chains are compared. Unknown
48
+ destination paths remain unverified and are reported with available method,
49
+ selector and nested-call diagnostics.
50
+ - Locally derivable final receivers are compared. External order data is skipped
51
+ without a provider request; a skipped receiver is never counted as verified.
52
+ - Parse failures, known mismatches and unexpected missing fields are reported with
53
+ `pass: false, blocking: true`.
54
+ - Relay and Socket RFQ/CCTP external destination fields are skipped rather than
55
+ fetched. LayerSwap uses its local receiver; no background order lookup runs.
56
+ Skipping an external receiver does not claim order-hash or provider verification.
57
+ - Known unresolved output token/minimum fields and Relay/Socket external
58
+ receiver/chain fields stay in `unverifiedFields` without producing reports.
59
+ Missing source fields, unknown receivers/paths and a missing expected amount
60
+ for a locally available minimum use `BRIDGE_UNVERIFIED_BRIDGE_FIELDS`.
61
+ Actual mismatches retain their existing error codes. Repeated failures are not
62
+ deduplicated, sampled or rate-limited by the SDK.
63
+
64
+ `validateBridgeTx` accepts `{ onError }` after the optional expectation as its sixth
65
+ argument. A missing Router allowlist or an approval address without a configured
66
+ spender is rejected.
67
+
68
+ `validateBridgeTx` now returns `BridgeValidationResult` instead of `true`:
69
+
70
+ ```ts
71
+ const result = validateBridgeTx(aggregatorId, chainId, bridgeId, tx, expectation);
72
+ // Throws on required-check failures; otherwise result.pass is true.
73
+ // result.fromPass / result.toPass / result.receiverPass
74
+ // result.unverifiedFields / result.errors
75
+ ```
76
+
77
+ The three field flags are true only when all required local comparisons for that
78
+ group succeeded. False means a failure or an incomplete check; `errors` and
79
+ `unverifiedFields` distinguish them. Known skipped fields can leave `errors` empty
80
+ while a field flag is false. Without an expectation, field flags are false.
81
+ Validation rejects reportable failures, not every false field flag: known skipped
82
+ fields remain allowed. Router rejection still throws
83
+ `InvalidBridgeContractError`; its sanitized detail carries `pass: false`,
84
+ `routerPass: false` and `blocking: true`.
85
+
86
+ Raw decode/calldata-check results retain `ok: false` on failure. Failure
87
+ details include all five pass flags, path diagnostics and `blocking`.
88
+ These are failure-event/path counts, not distinct user counts or a denominator for
89
+ all successful transactions. See error reporting below for failures that still throw.
90
+
91
+ ## Error reporting
92
+
93
+ All stable error codes, stages and messages are defined in
94
+ [`BRIDGE_ERRORS`](src/errors/constants.ts). Codes are explicit constants; callers
95
+ must not classify errors by matching message text. Existing error classes and
96
+ legacy `reason` fields remain available.
97
+
98
+ - `decodeBridgeCalldataResult(aggregatorId, tx)` returns a detailed success/failure
99
+ result. The existing `decodeBridgeCalldata` still returns `null` on failure.
100
+ - `validateBridgeCalldata` returns `errors`, plus `error_code` and `stage` on failure.
101
+ Independent intent mismatches are collected together; decoding or dependent
102
+ protocol operations stop at their first failure.
103
+ - Thrown SDK errors extend `BridgeError`. Use `error.toJSON()` for a report.
104
+ - `onError` receives failures filtered from `getQuote` / `getQuoteListWithTx`,
105
+ plus local validation failures and unexpected missing fields from transaction checks.
106
+ Calldata failures are reported once before throwing; do not report those
107
+ errors again in a caller's catch when an `onError` reporter is supplied.
108
+ Other directly thrown errors are handled by the caller.
109
+ Reporting callback failures do not affect validation or quote filtering.
110
+
111
+ ```ts
112
+ import { BridgeError, getQuoteListWithTx } from '@rabby-wallet/rabby-bridge';
113
+
114
+ try {
115
+ const quotes = await getQuoteListWithTx(aggregatorId, {
116
+ ...params,
117
+ onError: (report) => reportToBusiness(report),
118
+ }, api);
119
+ } catch (error) {
120
+ if (error instanceof BridgeError) reportToBusiness(error.toJSON());
121
+ throw error;
122
+ }
123
+ ```
124
+
125
+ A report contains a primary `error_code` / `stage` and an `errors` list. The primary
126
+ code is the first failure; inspect the list for every collected failure. Each detail
127
+ contains its fixed message and available context: aggregator, chain, bridge,
128
+ provider, operation, method, selector, nested protocol selector, missing fields or
129
+ HTTP status. SDK reports omit wallet addresses, amounts, raw calldata, response
130
+ bodies and arbitrary transport exception messages. Existing Error messages are
131
+ retained for compatibility; prefer `toJSON()` for reporting.
132
+
133
+ Stages distinguish input checks, contract allowlists, ABI decoding, protocol
134
+ structure, intent comparison, provider requests, provider responses, transaction
135
+ binding and completeness. Unexpected uncategorized failures use `BRIDGE_UNKNOWN`.
136
+ Deferred chain-state output fields remain in `unverifiedFields` without producing
137
+ reports merely because a known local limitation prevents verification.
138
+
139
+ ### Privacy-safe diagnostics
140
+
141
+ Use `toBridgeErrorReport(resultOrError)` to convert a calldata check,
142
+ transaction validation result, detailed decode result or exception into a report.
143
+ Results without reportable failures return `undefined`,
144
+ including known skipped destination fields.
145
+ The `onError` callback and `BridgeError.toJSON()` use the same
146
+ runtime allowlist. Do not upload the entire check, exception, parsed transaction,
147
+ request parameters or quote response.
148
+
149
+ ```ts
150
+ try {
151
+ validateBridgeTx(aggregatorId, chainId, bridgeId, tx, expectation);
152
+ } catch (error) {
153
+ reportToBusiness(toBridgeErrorReport(error));
154
+ throw error;
155
+ }
156
+ ```
157
+
158
+ Reports can retain recognized aggregator/bridge/method labels, source/destination
159
+ chains, outer/router/protocol selectors, call-data format and byte lengths, ABI
160
+ failure offsets, bounded destination-call selectors/counts, unsupported command
161
+ bytes/counts, static request categories and HTTP status. Nested diagnostics are
162
+ collected where the parser knows the call layout; arbitrary payload bytes are not
163
+ scanned for possible selectors. Destination selector lists are deduplicated and
164
+ limited to 16 entries.
165
+
166
+ Unknown labels become `unknown` or are omitted. Messages and stages are restored
167
+ from the error-code catalogue, even if an error was modified after construction.
168
+ Arbitrary properties are dropped, including wallet/receiver/refund addresses,
169
+ token addresses, amounts, balances, approvals, transaction/order/quote/request IDs,
170
+ signatures, full calldata, URL/query/header/body contents, raw exception messages
171
+ and stack traces. Contract addresses and hashes are not reported either.
172
+
173
+ These reports identify unsupported function families and failure stages. They
174
+ 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;