@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.
- package/README.md +163 -1
- package/dist/calldata/abi.d.ts +29 -0
- package/dist/calldata/abi.js +160 -0
- package/dist/calldata/across/deposit.d.ts +2 -0
- package/dist/calldata/across/deposit.js +36 -0
- package/dist/calldata/across/depositNative.d.ts +2 -0
- package/dist/calldata/across/depositNative.js +45 -0
- package/dist/calldata/across/depositNow.d.ts +2 -0
- package/dist/calldata/across/depositNow.js +35 -0
- package/dist/calldata/across/depositV3.d.ts +2 -0
- package/dist/calldata/across/depositV3.js +35 -0
- package/dist/calldata/across/depositV3Now.d.ts +2 -0
- package/dist/calldata/across/depositV3Now.js +34 -0
- package/dist/calldata/across/index.d.ts +4 -0
- package/dist/calldata/across/index.js +31 -0
- package/dist/calldata/across/swapAndBridge.d.ts +2 -0
- package/dist/calldata/across/swapAndBridge.js +70 -0
- package/dist/calldata/across/validate.d.ts +3 -0
- package/dist/calldata/across/validate.js +34 -0
- package/dist/calldata/chains.d.ts +2 -0
- package/dist/calldata/chains.js +36 -0
- package/dist/calldata/destination.d.ts +6 -0
- package/dist/calldata/destination.js +227 -0
- package/dist/calldata/destinationSwap.d.ts +14 -0
- package/dist/calldata/destinationSwap.js +83 -0
- package/dist/calldata/destinationUniswap.d.ts +7 -0
- package/dist/calldata/destinationUniswap.js +57 -0
- package/dist/calldata/diagnostics.d.ts +4 -0
- package/dist/calldata/diagnostics.js +18 -0
- package/dist/calldata/expect.d.ts +29 -0
- package/dist/calldata/expect.js +157 -0
- package/dist/calldata/hex.d.ts +6 -0
- package/dist/calldata/hex.js +46 -0
- package/dist/calldata/index.d.ts +20 -0
- package/dist/calldata/index.js +88 -0
- package/dist/calldata/lifi/across/acrossV4Erc20Min.d.ts +2 -0
- package/dist/calldata/lifi/across/acrossV4Erc20Min.js +27 -0
- package/dist/calldata/lifi/across/acrossV4NativeMin.d.ts +2 -0
- package/dist/calldata/lifi/across/acrossV4NativeMin.js +23 -0
- package/dist/calldata/lifi/across/index.d.ts +6 -0
- package/dist/calldata/lifi/across/index.js +19 -0
- package/dist/calldata/lifi/allbridge/index.d.ts +3 -0
- package/dist/calldata/lifi/allbridge/index.js +26 -0
- package/dist/calldata/lifi/celer/index.d.ts +4 -0
- package/dist/calldata/lifi/celer/index.js +10 -0
- package/dist/calldata/lifi/centrifuge/index.d.ts +3 -0
- package/dist/calldata/lifi/centrifuge/index.js +22 -0
- package/dist/calldata/lifi/destination.d.ts +5 -0
- package/dist/calldata/lifi/destination.js +40 -0
- package/dist/calldata/lifi/frax/index.d.ts +4 -0
- package/dist/calldata/lifi/frax/index.js +29 -0
- package/dist/calldata/lifi/garden/index.d.ts +3 -0
- package/dist/calldata/lifi/garden/index.js +22 -0
- package/dist/calldata/lifi/gaszip/index.d.ts +6 -0
- package/dist/calldata/lifi/gaszip/index.js +15 -0
- package/dist/calldata/lifi/index.d.ts +4 -0
- package/dist/calldata/lifi/index.js +62 -0
- package/dist/calldata/lifi/intents/index.d.ts +4 -0
- package/dist/calldata/lifi/intents/index.js +10 -0
- package/dist/calldata/lifi/layerswap/index.d.ts +4 -0
- package/dist/calldata/lifi/layerswap/index.js +10 -0
- package/dist/calldata/lifi/m0/index.d.ts +3 -0
- package/dist/calldata/lifi/m0/index.js +33 -0
- package/dist/calldata/lifi/mayan/index.d.ts +4 -0
- package/dist/calldata/lifi/mayan/index.js +10 -0
- package/dist/calldata/lifi/method.d.ts +6 -0
- package/dist/calldata/lifi/method.js +16 -0
- package/dist/calldata/lifi/native/index.d.ts +4 -0
- package/dist/calldata/lifi/native/index.js +80 -0
- package/dist/calldata/lifi/params.d.ts +28 -0
- package/dist/calldata/lifi/params.js +349 -0
- package/dist/calldata/lifi/paxos/index.d.ts +4 -0
- package/dist/calldata/lifi/paxos/index.js +10 -0
- package/dist/calldata/lifi/polymer/index.d.ts +4 -0
- package/dist/calldata/lifi/polymer/index.js +10 -0
- package/dist/calldata/lifi/relay/index.d.ts +6 -0
- package/dist/calldata/lifi/relay/index.js +15 -0
- package/dist/calldata/lifi/shape.d.ts +4 -0
- package/dist/calldata/lifi/shape.js +31 -0
- package/dist/calldata/lifi/squid/index.d.ts +6 -0
- package/dist/calldata/lifi/squid/index.js +15 -0
- package/dist/calldata/lifi/stargate/index.d.ts +5 -0
- package/dist/calldata/lifi/stargate/index.js +15 -0
- package/dist/calldata/lifi/swap/index.d.ts +5 -0
- package/dist/calldata/lifi/swap/index.js +18 -0
- package/dist/calldata/lifi/swap/swapMethod.d.ts +2 -0
- package/dist/calldata/lifi/swap/swapMethod.js +40 -0
- package/dist/calldata/lifi/swapData.d.ts +5 -0
- package/dist/calldata/lifi/swapData.js +33 -0
- package/dist/calldata/match.d.ts +3 -0
- package/dist/calldata/match.js +21 -0
- package/dist/calldata/mayan/destination.d.ts +2 -0
- package/dist/calldata/mayan/destination.js +13 -0
- package/dist/calldata/mayan/fastMctpBridge.d.ts +3 -0
- package/dist/calldata/mayan/fastMctpBridge.js +48 -0
- package/dist/calldata/mayan/fastMctpCreateOrder.d.ts +3 -0
- package/dist/calldata/mayan/fastMctpCreateOrder.js +39 -0
- package/dist/calldata/mayan/forward.d.ts +8 -0
- package/dist/calldata/mayan/forward.js +35 -0
- package/dist/calldata/mayan/forwardERC20.d.ts +2 -0
- package/dist/calldata/mayan/forwardERC20.js +19 -0
- package/dist/calldata/mayan/forwardEth.d.ts +2 -0
- package/dist/calldata/mayan/forwardEth.js +16 -0
- package/dist/calldata/mayan/index.d.ts +4 -0
- package/dist/calldata/mayan/index.js +21 -0
- package/dist/calldata/mayan/mctp.d.ts +5 -0
- package/dist/calldata/mayan/mctp.js +41 -0
- package/dist/calldata/mayan/readProtocol.d.ts +2 -0
- package/dist/calldata/mayan/readProtocol.js +29 -0
- package/dist/calldata/mayan/swapAndForwardERC20.d.ts +2 -0
- package/dist/calldata/mayan/swapAndForwardERC20.js +24 -0
- package/dist/calldata/mayan/swapAndForwardEth.d.ts +2 -0
- package/dist/calldata/mayan/swapAndForwardEth.js +23 -0
- package/dist/calldata/mayan/swiftCreateOrder.d.ts +3 -0
- package/dist/calldata/mayan/swiftCreateOrder.js +49 -0
- package/dist/calldata/mayan/validate.d.ts +3 -0
- package/dist/calldata/mayan/validate.js +20 -0
- package/dist/calldata/protocols.d.ts +9 -0
- package/dist/calldata/protocols.js +58 -0
- package/dist/calldata/relay/calls.d.ts +13 -0
- package/dist/calldata/relay/calls.js +58 -0
- package/dist/calldata/relay/depositErc20.d.ts +2 -0
- package/dist/calldata/relay/depositErc20.js +22 -0
- package/dist/calldata/relay/depositErc20Allowance.d.ts +2 -0
- package/dist/calldata/relay/depositErc20Allowance.js +20 -0
- package/dist/calldata/relay/depositNative.d.ts +2 -0
- package/dist/calldata/relay/depositNative.js +20 -0
- package/dist/calldata/relay/index.d.ts +4 -0
- package/dist/calldata/relay/index.js +45 -0
- package/dist/calldata/relay/multicall.d.ts +2 -0
- package/dist/calldata/relay/multicall.js +15 -0
- package/dist/calldata/relay/nestedDeposit.d.ts +8 -0
- package/dist/calldata/relay/nestedDeposit.js +107 -0
- package/dist/calldata/relay/permit2TransferAndMulticall.d.ts +2 -0
- package/dist/calldata/relay/permit2TransferAndMulticall.js +42 -0
- package/dist/calldata/relay/permit3009TransferAndMulticall.d.ts +2 -0
- package/dist/calldata/relay/permit3009TransferAndMulticall.js +41 -0
- package/dist/calldata/relay/permit3009TransferAndMulticallSigned.d.ts +2 -0
- package/dist/calldata/relay/permit3009TransferAndMulticallSigned.js +40 -0
- package/dist/calldata/relay/permitTransferAndMulticall.d.ts +2 -0
- package/dist/calldata/relay/permitTransferAndMulticall.js +34 -0
- package/dist/calldata/relay/permitTransferAndMulticallLegacy.d.ts +2 -0
- package/dist/calldata/relay/permitTransferAndMulticallLegacy.js +35 -0
- package/dist/calldata/relay/transferAndMulticall.d.ts +2 -0
- package/dist/calldata/relay/transferAndMulticall.js +26 -0
- package/dist/calldata/relay/validate.d.ts +3 -0
- package/dist/calldata/relay/validate.js +18 -0
- package/dist/calldata/resolve.d.ts +2 -0
- package/dist/calldata/resolve.js +28 -0
- package/dist/calldata/socket/addresses.d.ts +9 -0
- package/dist/calldata/socket/addresses.js +9 -0
- package/dist/calldata/socket/bridge.d.ts +2 -0
- package/dist/calldata/socket/bridge.js +28 -0
- package/dist/calldata/socket/exec.d.ts +2 -0
- package/dist/calldata/socket/exec.js +71 -0
- package/dist/calldata/socket/index.d.ts +4 -0
- package/dist/calldata/socket/index.js +13 -0
- package/dist/calldata/socket/params.d.ts +5 -0
- package/dist/calldata/socket/params.js +14 -0
- package/dist/calldata/socket/protocols/ccip.d.ts +2 -0
- package/dist/calldata/socket/protocols/ccip.js +32 -0
- package/dist/calldata/socket/protocols/cctp.d.ts +2 -0
- package/dist/calldata/socket/protocols/cctp.js +25 -0
- package/dist/calldata/socket/protocols/eco.d.ts +2 -0
- package/dist/calldata/socket/protocols/eco.js +49 -0
- package/dist/calldata/socket/protocols/index.d.ts +3 -0
- package/dist/calldata/socket/protocols/index.js +87 -0
- package/dist/calldata/socket/protocols/native.d.ts +2 -0
- package/dist/calldata/socket/protocols/native.js +71 -0
- package/dist/calldata/socket/protocols/oft.d.ts +2 -0
- package/dist/calldata/socket/protocols/oft.js +33 -0
- package/dist/calldata/socket/protocols/swapsXYZ.d.ts +2 -0
- package/dist/calldata/socket/protocols/swapsXYZ.js +62 -0
- package/dist/calldata/socket/swapAndBridge.d.ts +2 -0
- package/dist/calldata/socket/swapAndBridge.js +41 -0
- package/dist/calldata/socket/validate.d.ts +3 -0
- package/dist/calldata/socket/validate.js +24 -0
- package/dist/calldata/types.d.ts +86 -0
- package/dist/calldata/types.js +20 -0
- package/dist/errors/constants.d.ts +906 -0
- package/dist/errors/constants.js +913 -0
- package/dist/errors/index.d.ts +65 -0
- package/dist/errors/index.js +53 -0
- package/dist/errors/sanitize.d.ts +3 -0
- package/dist/errors/sanitize.js +74 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +3 -0
- package/dist/providers/data.d.ts +23 -0
- package/dist/providers/data.js +116 -0
- package/dist/providers/errors.d.ts +7 -0
- package/dist/providers/errors.js +10 -0
- package/dist/providers/layerswap.d.ts +9 -0
- package/dist/providers/layerswap.js +95 -0
- package/dist/providers/relay.d.ts +62 -0
- package/dist/providers/relay.js +237 -0
- package/dist/providers/socket.d.ts +5 -0
- package/dist/providers/socket.js +58 -0
- package/dist/quote.d.ts +30 -3
- package/dist/quote.js +236 -66
- 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
|
-
|
|
8
|
+
import { verifyRelayOrder } from './providers/relay.js';
|
|
9
|
+
export class InvalidBridgeContractError extends BridgeError {
|
|
4
10
|
constructor(kind, aggregatorId, chainId, expected, received, bridgeId) {
|
|
5
|
-
super(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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.
|
|
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
|
}
|