@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/dist/quote.js CHANGED
@@ -1,14 +1,22 @@
1
+ import { BRIDGE_ERRORS, BridgeError, asBridgeError, reportBridgeError } from './errors/index.js';
2
+ import { InvalidBridgeCalldataError, validateBridgeCalldata, } from './calldata/index.js';
3
+ import { finishCheck, knownCompare } from './calldata/expect.js';
4
+ import { calldataDiagnostics } from './calldata/diagnostics.js';
5
+ import { sanitizeBridgeError } from './errors/sanitize.js';
1
6
  import { BRIDGE_ENUM } from './consts.js';
2
7
  import { getBridgeAltContracts, getBridgeRouter, getBridgeSpender, isSupportedBridgeAggregator, isSupportedBridgeChain, } from './list.js';
3
- export class InvalidBridgeContractError extends Error {
8
+ import { verifyRelayOrder } from './providers/relay.js';
9
+ export class InvalidBridgeContractError extends BridgeError {
4
10
  constructor(kind, aggregatorId, chainId, expected, received, bridgeId) {
5
- super(`Invalid bridge ${kind} for ${aggregatorId} on ${chainId}: expected ${expected}, got ${received}`);
11
+ super(kind === 'router' ? BRIDGE_ERRORS.ROUTER_NOT_ALLOWED : BRIDGE_ERRORS.SPENDER_NOT_ALLOWED, { aggregatorId, chainId, bridgeId });
6
12
  this.kind = kind;
7
13
  this.aggregatorId = aggregatorId;
8
14
  this.chainId = chainId;
9
15
  this.expected = expected;
10
16
  this.received = received;
11
17
  this.bridgeId = bridgeId;
18
+ this.message =
19
+ `Invalid bridge ${kind} for ${aggregatorId} on ${chainId}: expected ${expected}, got ${received}`;
12
20
  this.name = 'InvalidBridgeContractError';
13
21
  }
14
22
  }
@@ -20,18 +28,32 @@ const isSameAddress = (left, right) => {
20
28
  };
21
29
  function assertSupported(aggregatorId, fromChainId) {
22
30
  if (!isSupportedBridgeAggregator(aggregatorId)) {
23
- throw new Error(`${aggregatorId} is not supported`);
31
+ const error = new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_BRIDGE_AGGREGATOR, { aggregatorId, chainId: fromChainId });
32
+ error.message = `${aggregatorId} is not supported`;
33
+ throw error;
24
34
  }
25
35
  if (!isSupportedBridgeChain(aggregatorId, fromChainId)) {
26
- throw new Error(`${aggregatorId} does not support ${fromChainId}`);
36
+ const error = new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_SOURCE_CHAIN, { aggregatorId, chainId: fromChainId });
37
+ error.message = `${aggregatorId} does not support ${fromChainId}`;
38
+ throw error;
27
39
  }
28
40
  }
29
41
  const requireSlippage = (slippage) => {
30
42
  if (!slippage) {
31
- throw new Error('slippage is required for this API call');
43
+ throw new BridgeError(BRIDGE_ERRORS.SLIPPAGE_IS_REQUIRED_FOR_THIS_API_CALL);
32
44
  }
33
45
  return slippage;
34
46
  };
47
+ const readBuildAttachment = (value) => {
48
+ if (!value || typeof value !== 'object')
49
+ return {};
50
+ const record = value;
51
+ const wrapped = Boolean(record.tx && !record.to && !record.data);
52
+ return {
53
+ tx: wrapped ? record.tx : record,
54
+ orderData: record.order_data,
55
+ };
56
+ };
35
57
  const stringifyQuoteKey = (quoteKey) => {
36
58
  if (typeof quoteKey === 'string') {
37
59
  return quoteKey;
@@ -45,9 +67,12 @@ export const validateBridgeQuote = (aggregatorId, fromChainId, quote) => {
45
67
  }
46
68
  const expectedSpender = getBridgeSpender(aggregatorId, fromChainId);
47
69
  const receivedSpender = quote.approve_contract_id;
48
- if (!expectedSpender || !receivedSpender) {
70
+ if (!receivedSpender) {
49
71
  return true;
50
72
  }
73
+ if (!expectedSpender) {
74
+ throw new InvalidBridgeContractError('spender', aggregatorId, fromChainId, 'configured spender', receivedSpender, quote.bridge_id);
75
+ }
51
76
  const allowedSpenders = [
52
77
  expectedSpender,
53
78
  ...getBridgeAltContracts(aggregatorId),
@@ -57,81 +82,219 @@ export const validateBridgeQuote = (aggregatorId, fromChainId, quote) => {
57
82
  }
58
83
  return true;
59
84
  };
60
- export const validateBridgeTx = (aggregatorId, fromChainId, bridgeId, tx) => {
85
+ const quotedToAmount = (quoteKey) => {
86
+ let key = quoteKey;
87
+ if (typeof key === 'string') {
88
+ try {
89
+ key = JSON.parse(key);
90
+ }
91
+ catch {
92
+ return undefined;
93
+ }
94
+ }
95
+ if (!key || typeof key !== 'object' || Array.isArray(key))
96
+ return undefined;
97
+ const amount = key.to_token_raw_amount;
98
+ if (typeof amount === 'string' || (typeof amount === 'number' && Number.isSafeInteger(amount))) {
99
+ return String(amount);
100
+ }
101
+ return undefined;
102
+ };
103
+ const validateBridgeRouter = (aggregatorId, fromChainId, bridgeId, tx) => {
61
104
  if (!shouldValidateBridgeContracts(aggregatorId)) {
62
105
  return true;
63
106
  }
64
107
  const expectedRouter = getBridgeRouter(aggregatorId, fromChainId);
65
- if (!expectedRouter) {
66
- return true;
67
- }
68
108
  const allowedRouters = [
69
- expectedRouter,
109
+ ...(expectedRouter ? [expectedRouter] : []),
70
110
  ...getBridgeAltContracts(aggregatorId),
71
111
  ];
72
112
  if (!allowedRouters.some((addr) => isSameAddress(addr, tx.to))) {
73
- throw new InvalidBridgeContractError('router', aggregatorId, fromChainId, expectedRouter, tx.to, bridgeId);
113
+ throw asBridgeError(new InvalidBridgeContractError('router', aggregatorId, fromChainId, expectedRouter || allowedRouters.join(',') || 'configured router', tx.to, bridgeId), BRIDGE_ERRORS.ROUTER_NOT_ALLOWED, { ...calldataDiagnostics(tx.data),
114
+ pass: false, routerPass: false, fromPass: false, toPass: false, receiverPass: false, blocking: true });
74
115
  }
75
116
  return true;
76
117
  };
77
- export const getQuote = async (aggregatorId, params, api) => {
78
- assertSupported(aggregatorId, params.fromChainId);
79
- const baseParams = {
80
- aggregator_id: aggregatorId,
81
- user_addr: params.userAddress,
82
- from_chain_id: params.fromChainId,
83
- from_token_id: params.fromTokenId,
84
- from_token_raw_amount: params.fromTokenRawAmount,
85
- to_chain_id: params.toChainId,
86
- to_token_id: params.toTokenId,
87
- fee_rate: params.feeRate,
88
- slippage: requireSlippage(params.slippage),
89
- };
90
- const quotes = api.getBridgeQuoteListV2
91
- ? await api.getBridgeQuoteListV2(baseParams)
92
- : await api.getBridgeQuoteV2?.(baseParams);
93
- if (!quotes) {
94
- throw new Error('Bridge API does not provide a quote list method');
118
+ const fieldErrors = {
119
+ fromPass: [BRIDGE_ERRORS.FROM_ADDRESS_MISMATCH, BRIDGE_ERRORS.FROM_CHAIN_MISMATCH,
120
+ BRIDGE_ERRORS.FROM_TOKEN_MISMATCH, BRIDGE_ERRORS.FROM_AMOUNT_MISMATCH,
121
+ BRIDGE_ERRORS.DEPOSITOR_MISMATCH, BRIDGE_ERRORS.MISSING_SOURCE_TOKEN, BRIDGE_ERRORS.MISSING_SOURCE_AMOUNT],
122
+ toPass: [BRIDGE_ERRORS.TO_CHAIN_MISMATCH, BRIDGE_ERRORS.TO_TOKEN_MISMATCH,
123
+ BRIDGE_ERRORS.MIN_RECEIVE_MISMATCH, BRIDGE_ERRORS.MISSING_DESTINATION,
124
+ BRIDGE_ERRORS.MISSING_DESTINATION_CHAIN, BRIDGE_ERRORS.MISSING_OUTPUT_TOKEN,
125
+ BRIDGE_ERRORS.MISSING_MIN_OUTPUT, BRIDGE_ERRORS.UNSUPPORTED_DESTINATION_CALL,
126
+ BRIDGE_ERRORS.UNSUPPORTED_RELAY_ORDER_SHAPE],
127
+ receiverPass: [BRIDGE_ERRORS.RECEIVER_MISMATCH, BRIDGE_ERRORS.MISSING_RECEIVER,
128
+ BRIDGE_ERRORS.REFUND_RECIPIENT_MISMATCH,
129
+ BRIDGE_ERRORS.UNSUPPORTED_DESTINATION_CALL,
130
+ BRIDGE_ERRORS.RELAY_REFUND_RECIPIENT_MISMATCH, BRIDGE_ERRORS.RELAY_ORDER_HASH_MISMATCH,
131
+ BRIDGE_ERRORS.MISSING_RELAY_ORDER_DATA,
132
+ BRIDGE_ERRORS.UNVERIFIED_RELAY_FUNDING, BRIDGE_ERRORS.RELAY_SOURCE_PAYMENT_MISMATCH],
133
+ };
134
+ const bindingErrors = [
135
+ BRIDGE_ERRORS.QUOTE_ID_MISMATCH, BRIDGE_ERRORS.REQUEST_ID_MISMATCH, BRIDGE_ERRORS.SOCKET_RFQ_QUOTE_ID_MISMATCH,
136
+ ];
137
+ const checkBridgeTx = (aggregatorId, fromChainId, bridgeId, tx, expectation, options = {}, operation = 'verifyTx') => {
138
+ const routerPass = validateBridgeRouter(aggregatorId, fromChainId, bridgeId, tx);
139
+ let check;
140
+ try {
141
+ check = validateBridgeCalldata(aggregatorId, fromChainId, tx, expectation);
95
142
  }
96
- return quotes.filter((quote) => {
97
- try {
98
- validateBridgeQuote(aggregatorId, params.fromChainId, quote);
99
- return true;
100
- }
101
- catch (error) {
102
- if (error instanceof InvalidBridgeContractError) {
103
- return false;
104
- }
105
- throw error;
143
+ catch (error) {
144
+ check = { ok: false, aggregatorId, errors: asBridgeError(error, BRIDGE_ERRORS.INVALID_CALLDATA).errors };
145
+ }
146
+ const enforceRelayOrder = aggregatorId === BRIDGE_ENUM.RELAY &&
147
+ (options.allowMissingRelayOrder !== true || expectation?.orderData !== undefined);
148
+ if (enforceRelayOrder) {
149
+ const orderErrors = expectation?.orderData === undefined
150
+ ? [BRIDGE_ERRORS.MISSING_RELAY_ORDER_DATA]
151
+ : check.parsed?.orderId
152
+ ? verifyRelayOrder(expectation.orderData, check.parsed, fromChainId, expectation.userAddress, tx.to)
153
+ : [BRIDGE_ERRORS.MISSING_RELAY_ORDER_ID];
154
+ if (orderErrors.length === 0 && check.parsed) {
155
+ const verified = finishCheck('relay', check.parsed, expectation?.toChainId ? undefined : BRIDGE_ERRORS.MISSING_DESTINATION_CHAIN, expectation, fromChainId, knownCompare(check.parsed));
156
+ check = { ...check, ok: check.ok && verified.ok, unverifiedFields: verified.unverifiedFields,
157
+ errors: [...check.errors, ...verified.errors.filter((error) => !check.errors.some((existing) => existing.error_code === error.error_code))] };
106
158
  }
159
+ check = { ...check, ok: check.ok && orderErrors.length === 0,
160
+ errors: [...check.errors,
161
+ ...orderErrors.filter((error) => !check.errors.some((existing) => existing.error_code === error.error_code))] };
162
+ }
163
+ const unverifiedFields = check.unverifiedFields ?? [
164
+ 'sourceToken', 'sourceAmount', 'outputToken', 'minOutputAmount', 'receiver',
165
+ ...(expectation?.toChainId ? ['destinationChainId'] : []),
166
+ ];
167
+ const externalDestination = check.parsed && ((aggregatorId === BRIDGE_ENUM.RELAY && check.parsed.destinationChainId === undefined) ||
168
+ (aggregatorId === BRIDGE_ENUM.SOCKET &&
169
+ ['receiveERC20', 'receiveNative', 'depositForBurn'].includes(check.parsed.protocolMethod ?? '')));
170
+ // Known local limits remain unverified, without repeating an expected gap as an error.
171
+ const reportableFields = unverifiedFields.filter((field) => {
172
+ if (!check.parsed)
173
+ return true;
174
+ if (field === 'outputToken')
175
+ return false;
176
+ if (field === 'minOutputAmount')
177
+ return check.parsed.minOutputAmount !== undefined;
178
+ return !(externalDestination && ['receiver', 'destinationChainId'].includes(field));
107
179
  });
180
+ // Unclassified structure failures invalidate every field; known field failures stay separate.
181
+ const comparable = Boolean(expectation && check.parsed) && check.errors.every((error) => bindingErrors.some((item) => item.error_code === error.error_code) ||
182
+ Object.values(fieldErrors).some((group) => group.some((item) => item.error_code === error.error_code)));
183
+ const fieldPass = (field, required) => comparable &&
184
+ required.every((item) => !unverifiedFields.includes(item)) &&
185
+ !check.errors.some((error) => fieldErrors[field].some((item) => item.error_code === error.error_code));
186
+ const flags = {
187
+ pass: routerPass && !check.errors.length && !reportableFields.length, routerPass,
188
+ fromPass: fieldPass('fromPass', ['sourceToken', 'sourceAmount']),
189
+ toPass: fieldPass('toPass', ['outputToken', 'minOutputAmount', ...(expectation?.toChainId ? ['destinationChainId'] : [])]),
190
+ receiverPass: fieldPass('receiverPass', ['receiver']),
191
+ };
192
+ const context = {
193
+ ...check.parsed?.diagnostics, ...calldataDiagnostics(tx.data), aggregatorId,
194
+ chainId: fromChainId, toChainId: expectation?.toChainId, bridgeId, operation, method: check.method,
195
+ ...(check.parsed ? { hasDestinationCall: Boolean(check.parsed.hasDestinationCall || check.parsed.destinationCallData !== undefined) } : {}),
196
+ ...flags, blocking: true,
197
+ };
198
+ const errors = [
199
+ ...check.errors,
200
+ ...(reportableFields.length ? [{ ...BRIDGE_ERRORS.UNVERIFIED_BRIDGE_FIELDS, unverifiedFields: reportableFields }] : []),
201
+ ].map((error) => sanitizeBridgeError({ ...error, ...context }));
202
+ if (errors.length) {
203
+ const error = new InvalidBridgeCalldataError(aggregatorId, fromChainId, check.method, check.reason || errors[0].message, errors);
204
+ reportBridgeError(options.onError, error, context);
205
+ throw error;
206
+ }
207
+ return { ...flags, unverifiedFields, errors };
208
+ };
209
+ export const validateBridgeTx = (aggregatorId, fromChainId, bridgeId, tx, expectation, options = {}) => checkBridgeTx(aggregatorId, fromChainId, bridgeId, tx, expectation, options);
210
+ export const getQuote = async (aggregatorId, params, api) => {
211
+ try {
212
+ params = { ...params };
213
+ assertSupported(aggregatorId, params.fromChainId);
214
+ const baseParams = {
215
+ aggregator_id: aggregatorId,
216
+ user_addr: params.userAddress,
217
+ from_chain_id: params.fromChainId,
218
+ from_token_id: params.fromTokenId,
219
+ from_token_raw_amount: params.fromTokenRawAmount,
220
+ to_chain_id: params.toChainId,
221
+ to_token_id: params.toTokenId,
222
+ fee_rate: params.feeRate,
223
+ slippage: requireSlippage(params.slippage),
224
+ };
225
+ const quotes = api.getBridgeQuoteListV2
226
+ ? await api.getBridgeQuoteListV2(baseParams).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_QUOTE_REQUEST_FAILED, { requestKind: 'backend_quote' }); })
227
+ : await api.getBridgeQuoteV2?.(baseParams).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_QUOTE_REQUEST_FAILED, { requestKind: 'backend_quote' }); });
228
+ if (!quotes)
229
+ throw new BridgeError(BRIDGE_ERRORS.MISSING_QUOTE_API);
230
+ return quotes.filter((quote) => {
231
+ try {
232
+ validateBridgeQuote(aggregatorId, params.fromChainId, quote);
233
+ return true;
234
+ }
235
+ catch (error) {
236
+ if (error instanceof InvalidBridgeContractError) {
237
+ reportBridgeError(params.onError, error, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, bridgeId: quote.bridge_id, operation: 'getQuote' });
238
+ return false;
239
+ }
240
+ throw error;
241
+ }
242
+ });
243
+ }
244
+ catch (error) {
245
+ throw asBridgeError(error, BRIDGE_ERRORS.UNKNOWN, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'getQuote' });
246
+ }
108
247
  };
109
248
  export const buildTx = async (aggregatorId, params, api) => {
110
- assertSupported(aggregatorId, params.fromChainId);
111
- const buildParams = {
112
- aggregator_id: aggregatorId,
113
- bridge_id: params.bridgeId,
114
- user_addr: params.userAddress,
115
- from_chain_id: params.fromChainId,
116
- from_token_id: params.fromTokenId,
117
- from_token_raw_amount: params.fromTokenRawAmount,
118
- to_chain_id: params.toChainId,
119
- to_token_id: params.toTokenId,
120
- slippage: requireSlippage(params.slippage),
121
- };
122
- const tx = api.buildBridgeTx
123
- ? await api.buildBridgeTx({
124
- ...buildParams,
125
- quote_key: stringifyQuoteKey(params.quoteKey),
126
- })
127
- : (await api.getBridgeQuoteTxV2?.({
128
- ...buildParams,
129
- }))?.tx;
130
- if (!tx) {
131
- throw new Error('Bridge API does not provide a build tx method');
132
- }
133
- validateBridgeTx(aggregatorId, params.fromChainId, params.bridgeId, tx);
134
- return tx;
249
+ try {
250
+ // Freeze signing intent before awaiting a backend response.
251
+ params = { ...params };
252
+ assertSupported(aggregatorId, params.fromChainId);
253
+ const quoteKey = stringifyQuoteKey(params.quoteKey);
254
+ const expectation = {
255
+ userAddress: params.userAddress,
256
+ fromTokenId: params.fromTokenId,
257
+ fromTokenRawAmount: params.fromTokenRawAmount,
258
+ toChainId: params.toChainId,
259
+ toTokenId: params.toTokenId,
260
+ toTokenRawAmount: params.toTokenRawAmount ?? quotedToAmount(quoteKey),
261
+ // The legacy quote+tx API requotes instead of reusing this selection.
262
+ quoteKey: typeof api.buildBridgeTx === 'function' ? quoteKey : undefined,
263
+ slippage: params.slippage,
264
+ };
265
+ const buildParams = {
266
+ aggregator_id: aggregatorId,
267
+ bridge_id: params.bridgeId,
268
+ user_addr: params.userAddress,
269
+ from_chain_id: params.fromChainId,
270
+ from_token_id: params.fromTokenId,
271
+ from_token_raw_amount: params.fromTokenRawAmount,
272
+ to_chain_id: params.toChainId,
273
+ to_token_id: params.toTokenId,
274
+ slippage: requireSlippage(params.slippage),
275
+ };
276
+ const built = api.buildBridgeTx
277
+ ? await api.buildBridgeTx({
278
+ ...buildParams,
279
+ quote_key: quoteKey,
280
+ }).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_BUILD_REQUEST_FAILED, { requestKind: 'backend_build' }); })
281
+ : await api.getBridgeQuoteTxV2?.({
282
+ ...buildParams,
283
+ }).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_BUILD_REQUEST_FAILED, { requestKind: 'backend_build' }); });
284
+ const attached = readBuildAttachment(built);
285
+ const tx = attached.tx;
286
+ if (!tx) {
287
+ throw new BridgeError(BRIDGE_ERRORS.MISSING_BUILD_API);
288
+ }
289
+ checkBridgeTx(aggregatorId, params.fromChainId, params.bridgeId, tx, {
290
+ ...expectation,
291
+ orderData: attached.orderData ?? params.orderData,
292
+ }, params, 'buildTx');
293
+ return tx;
294
+ }
295
+ catch (error) {
296
+ throw asBridgeError(error, BRIDGE_ERRORS.UNKNOWN, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'buildTx', bridgeId: params.bridgeId });
297
+ }
135
298
  };
136
299
  export const getQuoteListWithTx = async (aggregatorId, params, api) => {
137
300
  const quotes = await getQuote(aggregatorId, params, api);
@@ -141,6 +304,8 @@ export const getQuoteListWithTx = async (aggregatorId, params, api) => {
141
304
  ...params,
142
305
  bridgeId: quote.bridge_id,
143
306
  quoteKey: quote.quote_key,
307
+ toTokenRawAmount: quote.to_token_raw_amount_hex_str ??
308
+ (Number.isSafeInteger(quote.to_token_raw_amount) ? String(quote.to_token_raw_amount) : undefined),
144
309
  }, api);
145
310
  return {
146
311
  ...quote,
@@ -148,7 +313,12 @@ export const getQuoteListWithTx = async (aggregatorId, params, api) => {
148
313
  };
149
314
  }
150
315
  catch (error) {
151
- if (error instanceof InvalidBridgeContractError) {
316
+ if (error instanceof InvalidBridgeContractError ||
317
+ error instanceof InvalidBridgeCalldataError) {
318
+ // Calldata failures were already reported by checkBridgeTx.
319
+ if (error instanceof InvalidBridgeContractError) {
320
+ reportBridgeError(params.onError, error, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, bridgeId: quote.bridge_id, operation: 'buildTx' });
321
+ }
152
322
  return null;
153
323
  }
154
324
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rabby-wallet/rabby-bridge",
3
- "version": "0.0.13",
3
+ "version": "0.0.14-alpha.0",
4
4
  "description": "Bridge quote helpers and contract allowlists for Rabby",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,13 @@
22
22
  "scripts": {
23
23
  "prepublishOnly": "yarn build",
24
24
  "build": "rimraf dist && tsc",
25
- "test": "yarn build && NODE_OPTIONS=--experimental-vm-modules jest --runInBand --watchman=false"
25
+ "test": "yarn build && NODE_OPTIONS=--experimental-vm-modules jest --runInBand --watchman=false",
26
+ "test:bridge-calldata": "yarn build && NODE_OPTIONS=--experimental-vm-modules jest --runInBand --watchman=false tests/calldata",
27
+ "test:bridge-calldata:lifi": "yarn test:bridge-calldata --testNamePattern='\\[LiFi\\]'",
28
+ "test:bridge-calldata:socket": "yarn test:bridge-calldata --testNamePattern='\\[Socket\\]'",
29
+ "test:bridge-calldata:relay": "yarn test:bridge-calldata --testNamePattern='\\[Relay\\]'",
30
+ "test:bridge-calldata:across": "yarn test:bridge-calldata --testNamePattern='\\[Across\\]'",
31
+ "test:bridge-calldata:mayan": "yarn test:bridge-calldata --testNamePattern='\\[Mayan\\]'"
26
32
  },
27
33
  "devDependencies": {
28
34
  "@debank/common": "^0.3.60",
@@ -32,5 +38,9 @@
32
38
  "rimraf": "^3.0.2",
33
39
  "typescript": "^5.8.3"
34
40
  },
35
- "packageManager": "yarn@4.12.0"
41
+ "packageManager": "yarn@4.12.0",
42
+ "dependencies": {
43
+ "@ethersproject/hash": "5.8.0",
44
+ "@ethersproject/strings": "5.8.0"
45
+ }
36
46
  }