@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
@@ -0,0 +1,237 @@
1
+ import { BRIDGE_ERRORS, BridgeError } from '../errors/index.js';
2
+ import { _TypedDataEncoder } from '@ethersproject/hash';
3
+ import { getBridgeRouter } from '../list.js';
4
+ import { evmChainId } from '../calldata/chains.js';
5
+ import { decodeBridgeCalldataResult } from '../calldata/index.js';
6
+ import { asBigint, asString, decodeCall } from '../calldata/abi.js';
7
+ import { relayFundingRouter } from '../calldata/relay/nestedDeposit.js';
8
+ import { address, array, bytes, chain, object, text, tokenAddress, transaction, uint } from './data.js';
9
+ // Relay settlement-sdk 0.0.152 ORDER_EIP712_TYPES. EVM addresses are 20-byte
10
+ // dynamic bytes, not bytes32. Unknown order versions/VMs fail closed.
11
+ // Deadlines remain part of the order hash; they are not checked against the clock.
12
+ const ORDER_TYPES = {
13
+ Order: [
14
+ { name: 'version', type: 'string' }, { name: 'solverChainId', type: 'string' },
15
+ { name: 'solver', type: 'address' }, { name: 'salt', type: 'uint256' },
16
+ { name: 'inputs', type: 'Input[]' }, { name: 'output', type: 'Output' }, { name: 'fees', type: 'Fee[]' },
17
+ ],
18
+ Input: [{ name: 'payment', type: 'InputPayment' }, { name: 'refunds', type: 'InputRefund[]' }],
19
+ InputPayment: [
20
+ { name: 'chainId', type: 'string' }, { name: 'currency', type: 'bytes' },
21
+ { name: 'amount', type: 'uint256' }, { name: 'weight', type: 'uint256' },
22
+ ],
23
+ InputRefund: [
24
+ { name: 'chainId', type: 'string' }, { name: 'recipient', type: 'bytes' }, { name: 'currency', type: 'bytes' },
25
+ { name: 'minimumAmount', type: 'uint256' }, { name: 'deadline', type: 'uint32' }, { name: 'extraData', type: 'bytes' },
26
+ ],
27
+ Output: [
28
+ { name: 'chainId', type: 'string' }, { name: 'payments', type: 'OutputPayment[]' },
29
+ { name: 'deadline', type: 'uint32' }, { name: 'calls', type: 'bytes[]' }, { name: 'extraData', type: 'bytes' },
30
+ ],
31
+ OutputPayment: [
32
+ { name: 'recipient', type: 'bytes' }, { name: 'currency', type: 'bytes' },
33
+ { name: 'minimumAmount', type: 'uint256' }, { name: 'expectedAmount', type: 'uint256' },
34
+ ],
35
+ Fee: [
36
+ { name: 'recipientChainId', type: 'string' }, { name: 'recipient', type: 'bytes' },
37
+ { name: 'currencyChainId', type: 'string' }, { name: 'currency', type: 'bytes' }, { name: 'amount', type: 'uint256' },
38
+ ],
39
+ };
40
+ // Pin protocol identifiers independently of the service's mutable /chains response.
41
+ const RELAY_EVM_CHAIN_IDS = {
42
+ ethereum: 1n, optimism: 10n, cronos: 25n, bnb: 56n, gnosis: 100n, unichain: 130n,
43
+ polygon: 137n, monad: 143n, sonic: 146n, manta: 169n, xlayer: 196n, boba: 288n,
44
+ zksync: 324n, shape: 360n, worldchain: 480n, flow: 747n, stable: 988n, hyperevm: 999n,
45
+ metis: 1088n, soneium: 1868n, ronin: 2020n, abstract: 2741n, morph: 2818n,
46
+ tempo: 4217n, megaeth: 4326n, robinhood: 4663n, mantle: 5000n, somnia: 5031n,
47
+ arc: 5042n, base: 8453n, plasma: 9745n, apechain: 33139n, mode: 34443n,
48
+ mythos: 42018n, arbitrum: 42161n, celo: 42220n, avalanche: 43114n, zircuit: 48900n,
49
+ ink: 57073n, linea: 59144n, bob: 60808n, berachain: 80094n, doma: 97477n,
50
+ plume: 98866n, scroll: 534352n, gensyn: 685689n, katana: 747474n,
51
+ ethereal: 5064014n, zora: 7777777n,
52
+ };
53
+ export const relayChains = (value) => {
54
+ const result = [];
55
+ for (const raw of array(object(value).chains)) {
56
+ const c = object(raw);
57
+ if (c.vmType !== 'evm' || !c.protocol)
58
+ continue;
59
+ const v2 = object(c.protocol).v2;
60
+ if (!v2)
61
+ continue;
62
+ const protocol = object(v2);
63
+ const entry = { evmId: uint(c.id), protocolId: text(protocol.chainId), depository: address(protocol.depository) };
64
+ if (!Object.prototype.hasOwnProperty.call(RELAY_EVM_CHAIN_IDS, entry.protocolId))
65
+ continue;
66
+ if (RELAY_EVM_CHAIN_IDS[entry.protocolId] !== entry.evmId)
67
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_PROTOCOL_CHAIN_MISMATCH);
68
+ if (result.some((other) => other.evmId === entry.evmId || other.protocolId === entry.protocolId))
69
+ throw new BridgeError(BRIDGE_ERRORS.AMBIGUOUS_RELAY_CHAIN);
70
+ result.push(entry);
71
+ }
72
+ return result;
73
+ };
74
+ export const relayOrder = (raw, metadata) => {
75
+ const o = object(raw);
76
+ if (o.version !== 'v1')
77
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_ORDER_VERSION);
78
+ const evm = (id) => {
79
+ const protocolId = text(id);
80
+ if (!metadata.some((c) => c.protocolId === protocolId))
81
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_VM);
82
+ return protocolId;
83
+ };
84
+ const output = object(o.output);
85
+ return {
86
+ version: o.version,
87
+ solverChainId: evm(o.solverChainId), solver: address(o.solver), salt: uint(o.salt).toString(),
88
+ inputs: array(o.inputs).map((rawInput) => {
89
+ const input = object(rawInput), payment = object(input.payment);
90
+ return {
91
+ payment: { chainId: evm(payment.chainId), currency: address(payment.currency), amount: uint(payment.amount).toString(), weight: uint(payment.weight).toString() },
92
+ refunds: array(input.refunds).map((rawRefund) => {
93
+ const r = object(rawRefund);
94
+ return { chainId: evm(r.chainId), recipient: address(r.recipient), currency: address(r.currency),
95
+ minimumAmount: uint(r.minimumAmount).toString(), deadline: uint(r.deadline, 32).toString(), extraData: bytes(r.extraData) };
96
+ }),
97
+ };
98
+ }),
99
+ output: {
100
+ chainId: evm(output.chainId), deadline: uint(output.deadline, 32).toString(), extraData: bytes(output.extraData),
101
+ calls: array(output.calls).map((call) => bytes(call)),
102
+ payments: array(output.payments).map((rawPayment) => {
103
+ const p = object(rawPayment);
104
+ return { recipient: address(p.recipient), currency: address(p.currency),
105
+ minimumAmount: uint(p.minimumAmount).toString(), expectedAmount: uint(p.expectedAmount).toString() };
106
+ }),
107
+ },
108
+ fees: array(o.fees).map((rawFee) => {
109
+ const f = object(rawFee);
110
+ return { recipientChainId: evm(f.recipientChainId), recipient: address(f.recipient),
111
+ currencyChainId: evm(f.currencyChainId), currency: address(f.currency), amount: uint(f.amount).toString() };
112
+ }),
113
+ };
114
+ };
115
+ const relayOrderMetadata = () => Object.entries(RELAY_EVM_CHAIN_IDS).map(([protocolId, evmId]) => ({
116
+ protocolId, evmId, depository: '0x0000000000000000000000000000000000000000',
117
+ }));
118
+ /** Verify the order and its funding before exposing its committed destination fields. */
119
+ export const verifyRelayOrder = (orderData, parsed, fromChainId, userAddress, target) => {
120
+ let order;
121
+ try {
122
+ order = relayOrder(orderData, relayOrderMetadata());
123
+ }
124
+ catch {
125
+ return [BRIDGE_ERRORS.RELAY_ORDER_HASH_MISMATCH];
126
+ }
127
+ const hash = _TypedDataEncoder.hashStruct('Order', ORDER_TYPES, order).toLowerCase();
128
+ const user = userAddress.toLowerCase();
129
+ const errors = [];
130
+ if (hash !== parsed.orderId?.toLowerCase())
131
+ errors.push(BRIDGE_ERRORS.RELAY_ORDER_HASH_MISMATCH);
132
+ if (order.output.payments.length === 0 || order.output.payments.some((payment) => payment.recipient !== user)) {
133
+ errors.push(BRIDGE_ERRORS.RECEIVER_MISMATCH);
134
+ }
135
+ if (order.inputs.some((input) => input.refunds.some((refund) => refund.recipient !== user))) {
136
+ errors.push(BRIDGE_ERRORS.RELAY_REFUND_RECIPIENT_MISMATCH);
137
+ }
138
+ if (order.output.payments.length !== 1 || order.output.calls.length !== 0) {
139
+ errors.push(BRIDGE_ERRORS.UNSUPPORTED_RELAY_ORDER_SHAPE);
140
+ }
141
+ const direct = ['depositNative', 'depositErc20'].includes(parsed.method);
142
+ const token = direct ? parsed.sourceToken : parsed.bridgeToken;
143
+ const amount = direct ? parsed.sourceAmount : parsed.bridgeAmount;
144
+ const fundingRouter = relayFundingRouter(parsed.method, target.toLowerCase());
145
+ if (!token || amount === undefined || amount <= 0n ||
146
+ (direct && target.toLowerCase() !== getBridgeRouter('relay', fromChainId)) || (!direct &&
147
+ (!fundingRouter || (parsed.bridgeReceiver !== undefined && parsed.bridgeReceiver !== fundingRouter)))) {
148
+ errors.push(BRIDGE_ERRORS.UNVERIFIED_RELAY_FUNDING);
149
+ }
150
+ else if (order.inputs.length !== 1 ||
151
+ (evmChainId(fromChainId) !== undefined &&
152
+ RELAY_EVM_CHAIN_IDS[order.inputs[0].payment.chainId] !== evmChainId(fromChainId)) ||
153
+ order.inputs[0].payment.currency !== token || BigInt(order.inputs[0].payment.amount) !== amount) {
154
+ errors.push(BRIDGE_ERRORS.RELAY_SOURCE_PAYMENT_MISMATCH);
155
+ }
156
+ if (errors.length === 0) {
157
+ const output = order.output.payments[0];
158
+ Object.assign(parsed, {
159
+ destinationChainId: RELAY_EVM_CHAIN_IDS[order.output.chainId],
160
+ outputToken: output.currency, minOutputAmount: BigInt(output.minimumAmount), receiver: output.recipient,
161
+ });
162
+ }
163
+ return errors;
164
+ };
165
+ export const readRelayQuote = (raw, params, metadata) => {
166
+ const q = object(raw);
167
+ const protocol = object(object(q.protocol).v2);
168
+ const order = relayOrder(protocol.orderData, metadata);
169
+ const orderId = _TypedDataEncoder.hashStruct('Order', ORDER_TYPES, order).toLowerCase();
170
+ if (orderId !== bytes(protocol.orderId, 32))
171
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_ORDER_HASH_MISMATCH);
172
+ if (order.inputs.length !== 1 || order.output.payments.length !== 1 || order.output.calls.length !== 0)
173
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_ORDER_SHAPE);
174
+ const source = metadata.find((c) => c.evmId === chain(params.fromChainId));
175
+ const destination = metadata.find((c) => c.evmId === chain(params.toChainId));
176
+ if (!source || !destination || order.inputs[0].payment.chainId !== source.protocolId || order.output.chainId !== destination.protocolId)
177
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_ORDER_CHAIN_MISMATCH);
178
+ if (source.depository !== getBridgeRouter('relay', params.fromChainId)?.toLowerCase()) {
179
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_CANONICAL_DEPOSITORY_MISMATCH);
180
+ }
181
+ for (const refund of order.inputs[0].refunds) {
182
+ if (refund.recipient !== address(params.userAddress))
183
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_REFUND_RECIPIENT_MISMATCH);
184
+ }
185
+ const steps = array(q.steps).map(object);
186
+ const deposits = steps.filter((step) => step.id === 'deposit');
187
+ if (deposits.length !== 1 || deposits[0].kind !== 'transaction' || deposits[0].depositAddress)
188
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_DEPOSIT);
189
+ const items = array(deposits[0].items);
190
+ if (items.length !== 1)
191
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_DEPOSIT_COUNT);
192
+ const tx = transaction(object(items[0]).data, params);
193
+ const decodedResult = decodeBridgeCalldataResult('relay', tx);
194
+ if (!decodedResult.ok)
195
+ throw new BridgeError(BRIDGE_ERRORS.INVALID_CALLDATA, {}, decodedResult.errors);
196
+ const decoded = decodedResult.parsed;
197
+ if (!decoded || !['depositNative', 'depositErc20'].includes(decoded.method))
198
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_DEPOSIT_METHOD);
199
+ const payment = order.inputs[0].payment;
200
+ const paymentDetails = object(protocol.paymentDetails);
201
+ if (tx.to.toLowerCase() !== source.depository || address(paymentDetails.depository) !== source.depository ||
202
+ paymentDetails.chainId !== source.protocolId || address(paymentDetails.currency) !== payment.currency ||
203
+ uint(paymentDetails.amount) !== uint(payment.amount) ||
204
+ decoded.orderId !== orderId || decoded.depositor !== address(params.userAddress) ||
205
+ decoded.sourceToken !== payment.currency || decoded.sourceAmount !== uint(payment.amount))
206
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_SOURCE_PAYMENT_MISMATCH);
207
+ if (payment.currency !== tokenAddress(params.fromTokenId, params.fromChainId) ||
208
+ uint(tx.value) !== (payment.currency === '0x0000000000000000000000000000000000000000' ? uint(payment.amount) : 0n))
209
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_SOURCE_VALUE_MISMATCH);
210
+ // Approval steps are never executed from the response. Verify their semantics,
211
+ // then expose only the spender so the host can build its own approval.
212
+ const approvals = steps.filter((step) => step.id === 'approve');
213
+ if (approvals.length > 1)
214
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_APPROVAL_COUNT);
215
+ for (const step of steps) {
216
+ if (!['approve', 'deposit'].includes(text(step.id)))
217
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_STEP);
218
+ }
219
+ for (const step of approvals) {
220
+ const approvalsItems = array(step.items);
221
+ if (step.kind !== 'transaction' || approvalsItems.length !== 1)
222
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_RELAY_APPROVAL);
223
+ const approval = transaction(object(approvalsItems[0]).data, params);
224
+ if (!approval.data.startsWith('0x095ea7b3') || approval.to !== payment.currency || uint(approval.value) !== 0n)
225
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_APPROVAL_MISMATCH);
226
+ const args = decodeCall([{ name: 'spender', kind: 'address' }, { name: 'amount', kind: 'uint' }], approval.data);
227
+ if (asString(args.spender) !== source.depository || asBigint(args.amount) !== uint(payment.amount))
228
+ throw new BridgeError(BRIDGE_ERRORS.RELAY_APPROVAL_MISMATCH);
229
+ }
230
+ const output = order.output.payments[0];
231
+ return {
232
+ tx, orderId, requestId: bytes(deposits[0].requestId, 32), expectedAmount: uint(output.expectedAmount),
233
+ approveAddress: payment.currency === '0x0000000000000000000000000000000000000000' ? '' : source.depository,
234
+ parsed: { ...decoded, destinationChainId: destination.evmId, outputToken: output.currency,
235
+ minOutputAmount: uint(output.minimumAmount), receiver: output.recipient, unresolvedFields: [] },
236
+ };
237
+ };
@@ -0,0 +1,5 @@
1
+ import type { BridgeQuoteListParams } from '../quote.js';
2
+ import type { ProviderQuoteData } from './relay.js';
3
+ export declare const SOCKET_PROVIDER_ROUTES: readonly string[];
4
+ export declare const socketBridgeId: (value: unknown) => string;
5
+ export declare const readSocketQuote: (raw: unknown, rawRoute: unknown, params: BridgeQuoteListParams) => ProviderQuoteData;
@@ -0,0 +1,58 @@
1
+ import { sourceAmountMatches } from '../calldata/expect.js';
2
+ import { BRIDGE_ERRORS, BridgeError } from '../errors/index.js';
3
+ import { decodeBridgeCalldataResult } from '../calldata/index.js';
4
+ import { SOCKET_CCTP_MINT_RECIPIENT } from '../calldata/socket/addresses.js';
5
+ import { address, bytes, chain, object, text, tokenAddress, transaction, uint } from './data.js';
6
+ export const SOCKET_PROVIDER_ROUTES = Object.freeze(['rfq-vault-executor', 'cctp-v2', 'cctp-v2-slow']);
7
+ export const socketBridgeId = (value) => text(value).trim().toLowerCase().replace(/ /g, '-').replace(/^circle-cctp/, 'cctp');
8
+ export const readSocketQuote = (raw, rawRoute, params) => {
9
+ const result = object(raw), route = object(rawRoute);
10
+ const details = object(object(route.routeDetails).bridgeDetails);
11
+ const bridgeId = socketBridgeId(object(details.protocol).name);
12
+ if (!SOCKET_PROVIDER_ROUTES.includes(bridgeId) || route.userOp !== 'tx')
13
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_SOCKET_SERVICE_ROUTE);
14
+ const input = object(result.input), inputToken = object(input.token);
15
+ const output = object(route.output), outputToken = object(output.token);
16
+ if (uint(result.originChainId) !== chain(params.fromChainId) || uint(result.destinationChainId) !== chain(params.toChainId) ||
17
+ uint(inputToken.chainId) !== chain(params.fromChainId) || uint(outputToken.chainId) !== chain(params.toChainId) ||
18
+ address(result.userAddress) !== address(params.userAddress) || address(result.receiverAddress) !== address(params.userAddress) ||
19
+ tokenAddress(text(inputToken.address), params.fromChainId) !== tokenAddress(params.fromTokenId, params.fromChainId) ||
20
+ !sourceAmountMatches(uint(input.amount), uint(params.fromTokenRawAmount)))
21
+ throw new BridgeError(BRIDGE_ERRORS.SOCKET_QUOTE_INPUT_MISMATCH);
22
+ const txData = object(route.txData);
23
+ if (txData.kind !== 'evm_tx')
24
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_SOCKET_TRANSACTION);
25
+ const tx = transaction(txData.object, params);
26
+ const decodedResult = decodeBridgeCalldataResult('socket', tx);
27
+ if (!decodedResult.ok)
28
+ throw new BridgeError(BRIDGE_ERRORS.INVALID_CALLDATA, {}, decodedResult.errors);
29
+ const parsed = decodedResult.parsed;
30
+ const quoteId = bytes(route.quoteId, 32);
31
+ if (!parsed || parsed.quoteId !== quoteId || parsed.depositor !== address(params.userAddress) ||
32
+ parsed.sourceAmount !== uint(input.amount))
33
+ throw new BridgeError(BRIDGE_ERRORS.SOCKET_SOURCE_QUOTE_MISMATCH);
34
+ if (bridgeId === 'rfq-vault-executor') {
35
+ if (parsed.protocolMethod !== 'receiveERC20' || parsed.orderId !== quoteId)
36
+ throw new BridgeError(BRIDGE_ERRORS.SOCKET_RFQ_ORDER_MISMATCH);
37
+ }
38
+ else if (parsed.protocolMethod !== 'depositForBurn' || parsed.bridgeReceiver !== SOCKET_CCTP_MINT_RECIPIENT) {
39
+ throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_SOCKET_CCTP_RECEIVER);
40
+ }
41
+ const destinationToken = tokenAddress(text(outputToken.address), params.toChainId);
42
+ if (parsed.outputToken && tokenAddress(parsed.outputToken, params.toChainId) !== destinationToken)
43
+ throw new BridgeError(BRIDGE_ERRORS.SOCKET_BRIDGE_OUTPUT_MISMATCH);
44
+ if (parsed.destinationChainId !== undefined && parsed.destinationChainId !== chain(params.toChainId))
45
+ throw new BridgeError(BRIDGE_ERRORS.SOCKET_BRIDGE_CHAIN_MISMATCH);
46
+ const approval = route.approval ? object(route.approval) : undefined;
47
+ const native = tokenAddress(params.fromTokenId, params.fromChainId) === '0x0000000000000000000000000000000000000000';
48
+ if (!native && (!approval || address(approval.tokenAddress) !== tokenAddress(params.fromTokenId, params.fromChainId) ||
49
+ address(approval.userAddress) !== address(params.userAddress) || uint(approval.amount) !== uint(input.amount)))
50
+ throw new BridgeError(BRIDGE_ERRORS.SOCKET_APPROVAL_MISMATCH);
51
+ return {
52
+ tx, orderId: quoteId, requestId: quoteId,
53
+ approveAddress: native ? '' : address(approval.spenderAddress),
54
+ expectedAmount: uint(output.amount),
55
+ parsed: { ...parsed, destinationChainId: chain(params.toChainId), outputToken: destinationToken,
56
+ minOutputAmount: uint(output.minAmountOut), receiver: address(result.receiverAddress), unresolvedFields: [] },
57
+ };
58
+ };
package/dist/quote.d.ts CHANGED
@@ -1,6 +1,29 @@
1
+ import type { BridgeErrorReporter } from './errors/index.js';
2
+ import { BridgeError } from './errors/index.js';
1
3
  import type { OpenApiService } from '@rabby-wallet/rabby-api';
4
+ import type { BridgeCallExpectation } from './calldata/expect.js';
5
+ import type { BridgeCheck } from './calldata/types.js';
2
6
  import type { BridgeAggregatorId } from './consts.js';
3
- export interface BridgeQuoteListParams {
7
+ export interface BridgeValidationOptions {
8
+ /** Receives filtered quote failures and calldata failures. */
9
+ onError?: BridgeErrorReporter;
10
+ /**
11
+ * Test-only. Production callers must leave this unset: a missing Relay
12
+ * orderData then fails validation.
13
+ */
14
+ allowMissingRelayOrder?: boolean;
15
+ }
16
+ export type BridgeValidationResult = {
17
+ /** Whether the transaction passes all required checks. */
18
+ pass: boolean;
19
+ routerPass: boolean;
20
+ fromPass: boolean;
21
+ toPass: boolean;
22
+ receiverPass: boolean;
23
+ unverifiedFields: string[];
24
+ errors: BridgeCheck['errors'];
25
+ };
26
+ export interface BridgeQuoteListParams extends BridgeValidationOptions {
4
27
  userAddress: string;
5
28
  fromChainId: string;
6
29
  fromTokenId: string;
@@ -13,12 +36,16 @@ export interface BridgeQuoteListParams {
13
36
  export interface BridgeBuildTxParams extends BridgeQuoteListParams {
14
37
  bridgeId: string;
15
38
  quoteKey?: string | Record<string, unknown>;
39
+ /** Quoted output in raw units, before applying slippage. */
40
+ toTokenRawAmount?: string;
41
+ /** Relay `protocol.v2.orderData` from the build that produced this transaction. */
42
+ orderData?: unknown;
16
43
  }
17
44
  export type BridgeApi = Pick<OpenApiService, 'getBridgeQuoteListV2' | 'getBridgeQuoteV2' | 'buildBridgeTx' | 'getBridgeQuoteTxV2'>;
18
45
  export type BridgeQuote = Awaited<ReturnType<BridgeApi['getBridgeQuoteTxV2']>>;
19
46
  export type BridgeTx = Awaited<ReturnType<BridgeApi['buildBridgeTx']>> | NonNullable<BridgeQuote['tx']>;
20
47
  export type BridgeQuoteListItem = Awaited<ReturnType<BridgeApi['getBridgeQuoteV2']>>[number];
21
- export declare class InvalidBridgeContractError extends Error {
48
+ export declare class InvalidBridgeContractError extends BridgeError {
22
49
  readonly kind: 'spender' | 'router';
23
50
  readonly aggregatorId: BridgeAggregatorId;
24
51
  readonly chainId: string;
@@ -28,7 +55,7 @@ export declare class InvalidBridgeContractError extends Error {
28
55
  constructor(kind: 'spender' | 'router', aggregatorId: BridgeAggregatorId, chainId: string, expected: string, received: string, bridgeId?: string | undefined);
29
56
  }
30
57
  export declare const validateBridgeQuote: (aggregatorId: BridgeAggregatorId, fromChainId: string, quote: BridgeQuoteListItem) => boolean;
31
- export declare const validateBridgeTx: (aggregatorId: BridgeAggregatorId, fromChainId: string, bridgeId: string, tx: BridgeTx) => boolean;
58
+ export declare const validateBridgeTx: (aggregatorId: BridgeAggregatorId, fromChainId: string, bridgeId: string, tx: BridgeTx, expectation?: BridgeCallExpectation, options?: BridgeValidationOptions) => BridgeValidationResult;
32
59
  export declare const getQuote: (aggregatorId: string, params: BridgeQuoteListParams, api: BridgeApi) => Promise<BridgeQuoteListItem[]>;
33
60
  export declare const buildTx: (aggregatorId: string, params: BridgeBuildTxParams, api: BridgeApi) => Promise<BridgeTx>;
34
61
  export declare const getQuoteListWithTx: (aggregatorId: string, params: BridgeQuoteListParams, api: BridgeApi) => Promise<Array<BridgeQuoteListItem & {