@rabby-wallet/rabby-bridge 0.0.13-alpha.0 → 0.0.13
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 +1 -131
- package/dist/chains.d.ts +2 -1
- package/dist/chains.js +2 -0
- package/dist/data.d.ts +3 -0
- package/dist/data.js +3 -0
- package/dist/index.d.ts +1 -7
- package/dist/index.js +0 -3
- package/dist/quote.d.ts +4 -18
- package/dist/quote.js +65 -282
- package/package.json +3 -13
- package/dist/calldata/abi.d.ts +0 -29
- package/dist/calldata/abi.js +0 -160
- package/dist/calldata/across/deposit.d.ts +0 -2
- package/dist/calldata/across/deposit.js +0 -36
- package/dist/calldata/across/depositNative.d.ts +0 -2
- package/dist/calldata/across/depositNative.js +0 -45
- package/dist/calldata/across/depositNow.d.ts +0 -2
- package/dist/calldata/across/depositNow.js +0 -35
- package/dist/calldata/across/depositV3.d.ts +0 -2
- package/dist/calldata/across/depositV3.js +0 -35
- package/dist/calldata/across/depositV3Now.d.ts +0 -2
- package/dist/calldata/across/depositV3Now.js +0 -34
- package/dist/calldata/across/index.d.ts +0 -4
- package/dist/calldata/across/index.js +0 -31
- package/dist/calldata/across/swapAndBridge.d.ts +0 -2
- package/dist/calldata/across/swapAndBridge.js +0 -70
- package/dist/calldata/across/validate.d.ts +0 -3
- package/dist/calldata/across/validate.js +0 -30
- package/dist/calldata/chains.d.ts +0 -2
- package/dist/calldata/chains.js +0 -35
- package/dist/calldata/destination.d.ts +0 -6
- package/dist/calldata/destination.js +0 -148
- package/dist/calldata/diagnostics.d.ts +0 -4
- package/dist/calldata/diagnostics.js +0 -18
- package/dist/calldata/expect.d.ts +0 -25
- package/dist/calldata/expect.js +0 -129
- package/dist/calldata/hex.d.ts +0 -6
- package/dist/calldata/hex.js +0 -46
- package/dist/calldata/index.d.ts +0 -20
- package/dist/calldata/index.js +0 -85
- package/dist/calldata/lifi/across/acrossV4Erc20Min.d.ts +0 -2
- package/dist/calldata/lifi/across/acrossV4Erc20Min.js +0 -26
- package/dist/calldata/lifi/across/acrossV4NativeMin.d.ts +0 -2
- package/dist/calldata/lifi/across/acrossV4NativeMin.js +0 -22
- package/dist/calldata/lifi/across/index.d.ts +0 -6
- package/dist/calldata/lifi/across/index.js +0 -19
- package/dist/calldata/lifi/allbridge/index.d.ts +0 -3
- package/dist/calldata/lifi/allbridge/index.js +0 -26
- package/dist/calldata/lifi/celer/index.d.ts +0 -4
- package/dist/calldata/lifi/celer/index.js +0 -10
- package/dist/calldata/lifi/centrifuge/index.d.ts +0 -3
- package/dist/calldata/lifi/centrifuge/index.js +0 -22
- package/dist/calldata/lifi/frax/index.d.ts +0 -4
- package/dist/calldata/lifi/frax/index.js +0 -29
- package/dist/calldata/lifi/garden/index.d.ts +0 -3
- package/dist/calldata/lifi/garden/index.js +0 -22
- package/dist/calldata/lifi/gaszip/index.d.ts +0 -6
- package/dist/calldata/lifi/gaszip/index.js +0 -15
- package/dist/calldata/lifi/index.d.ts +0 -4
- package/dist/calldata/lifi/index.js +0 -60
- package/dist/calldata/lifi/intents/index.d.ts +0 -4
- package/dist/calldata/lifi/intents/index.js +0 -10
- package/dist/calldata/lifi/layerswap/index.d.ts +0 -4
- package/dist/calldata/lifi/layerswap/index.js +0 -10
- package/dist/calldata/lifi/m0/index.d.ts +0 -3
- package/dist/calldata/lifi/m0/index.js +0 -33
- package/dist/calldata/lifi/mayan/index.d.ts +0 -4
- package/dist/calldata/lifi/mayan/index.js +0 -10
- package/dist/calldata/lifi/method.d.ts +0 -6
- package/dist/calldata/lifi/method.js +0 -16
- package/dist/calldata/lifi/native/index.d.ts +0 -4
- package/dist/calldata/lifi/native/index.js +0 -80
- package/dist/calldata/lifi/params.d.ts +0 -30
- package/dist/calldata/lifi/params.js +0 -370
- package/dist/calldata/lifi/paxos/index.d.ts +0 -4
- package/dist/calldata/lifi/paxos/index.js +0 -10
- package/dist/calldata/lifi/polymer/index.d.ts +0 -4
- package/dist/calldata/lifi/polymer/index.js +0 -10
- package/dist/calldata/lifi/relay/index.d.ts +0 -6
- package/dist/calldata/lifi/relay/index.js +0 -15
- package/dist/calldata/lifi/shape.d.ts +0 -4
- package/dist/calldata/lifi/shape.js +0 -31
- package/dist/calldata/lifi/squid/index.d.ts +0 -6
- package/dist/calldata/lifi/squid/index.js +0 -15
- package/dist/calldata/lifi/stargate/index.d.ts +0 -5
- package/dist/calldata/lifi/stargate/index.js +0 -15
- package/dist/calldata/lifi/swap/index.d.ts +0 -5
- package/dist/calldata/lifi/swap/index.js +0 -18
- package/dist/calldata/lifi/swap/swapMethod.d.ts +0 -2
- package/dist/calldata/lifi/swap/swapMethod.js +0 -40
- package/dist/calldata/match.d.ts +0 -3
- package/dist/calldata/match.js +0 -21
- package/dist/calldata/mayan/destination.d.ts +0 -2
- package/dist/calldata/mayan/destination.js +0 -13
- package/dist/calldata/mayan/fastMctpBridge.d.ts +0 -3
- package/dist/calldata/mayan/fastMctpBridge.js +0 -43
- package/dist/calldata/mayan/fastMctpCreateOrder.d.ts +0 -3
- package/dist/calldata/mayan/fastMctpCreateOrder.js +0 -39
- package/dist/calldata/mayan/forward.d.ts +0 -8
- package/dist/calldata/mayan/forward.js +0 -35
- package/dist/calldata/mayan/forwardERC20.d.ts +0 -2
- package/dist/calldata/mayan/forwardERC20.js +0 -19
- package/dist/calldata/mayan/forwardEth.d.ts +0 -2
- package/dist/calldata/mayan/forwardEth.js +0 -16
- package/dist/calldata/mayan/index.d.ts +0 -4
- package/dist/calldata/mayan/index.js +0 -21
- package/dist/calldata/mayan/mctp.d.ts +0 -5
- package/dist/calldata/mayan/mctp.js +0 -41
- package/dist/calldata/mayan/readProtocol.d.ts +0 -2
- package/dist/calldata/mayan/readProtocol.js +0 -29
- package/dist/calldata/mayan/swapAndForwardERC20.d.ts +0 -2
- package/dist/calldata/mayan/swapAndForwardERC20.js +0 -24
- package/dist/calldata/mayan/swapAndForwardEth.d.ts +0 -2
- package/dist/calldata/mayan/swapAndForwardEth.js +0 -23
- package/dist/calldata/mayan/swiftCreateOrder.d.ts +0 -3
- package/dist/calldata/mayan/swiftCreateOrder.js +0 -49
- package/dist/calldata/mayan/validate.d.ts +0 -3
- package/dist/calldata/mayan/validate.js +0 -20
- package/dist/calldata/protocols.d.ts +0 -9
- package/dist/calldata/protocols.js +0 -56
- package/dist/calldata/relay/calls.d.ts +0 -13
- package/dist/calldata/relay/calls.js +0 -54
- package/dist/calldata/relay/depositErc20.d.ts +0 -2
- package/dist/calldata/relay/depositErc20.js +0 -22
- package/dist/calldata/relay/depositErc20Allowance.d.ts +0 -2
- package/dist/calldata/relay/depositErc20Allowance.js +0 -20
- package/dist/calldata/relay/depositNative.d.ts +0 -2
- package/dist/calldata/relay/depositNative.js +0 -20
- package/dist/calldata/relay/index.d.ts +0 -4
- package/dist/calldata/relay/index.js +0 -40
- package/dist/calldata/relay/multicall.d.ts +0 -2
- package/dist/calldata/relay/multicall.js +0 -15
- package/dist/calldata/relay/permit2TransferAndMulticall.d.ts +0 -2
- package/dist/calldata/relay/permit2TransferAndMulticall.js +0 -42
- package/dist/calldata/relay/permit3009TransferAndMulticall.d.ts +0 -2
- package/dist/calldata/relay/permit3009TransferAndMulticall.js +0 -41
- package/dist/calldata/relay/permit3009TransferAndMulticallSigned.d.ts +0 -2
- package/dist/calldata/relay/permit3009TransferAndMulticallSigned.js +0 -40
- package/dist/calldata/relay/permitTransferAndMulticall.d.ts +0 -2
- package/dist/calldata/relay/permitTransferAndMulticall.js +0 -34
- package/dist/calldata/relay/permitTransferAndMulticallLegacy.d.ts +0 -2
- package/dist/calldata/relay/permitTransferAndMulticallLegacy.js +0 -35
- package/dist/calldata/relay/transferAndMulticall.d.ts +0 -2
- package/dist/calldata/relay/transferAndMulticall.js +0 -26
- package/dist/calldata/relay/validate.d.ts +0 -3
- package/dist/calldata/relay/validate.js +0 -18
- package/dist/calldata/resolve.d.ts +0 -2
- package/dist/calldata/resolve.js +0 -28
- package/dist/calldata/socket/bridge.d.ts +0 -2
- package/dist/calldata/socket/bridge.js +0 -28
- package/dist/calldata/socket/exec.d.ts +0 -2
- package/dist/calldata/socket/exec.js +0 -71
- package/dist/calldata/socket/index.d.ts +0 -4
- package/dist/calldata/socket/index.js +0 -13
- package/dist/calldata/socket/params.d.ts +0 -5
- package/dist/calldata/socket/params.js +0 -14
- package/dist/calldata/socket/protocols/ccip.d.ts +0 -2
- package/dist/calldata/socket/protocols/ccip.js +0 -32
- package/dist/calldata/socket/protocols/cctp.d.ts +0 -2
- package/dist/calldata/socket/protocols/cctp.js +0 -24
- package/dist/calldata/socket/protocols/eco.d.ts +0 -2
- package/dist/calldata/socket/protocols/eco.js +0 -20
- package/dist/calldata/socket/protocols/index.d.ts +0 -3
- package/dist/calldata/socket/protocols/index.js +0 -81
- package/dist/calldata/socket/protocols/native.d.ts +0 -2
- package/dist/calldata/socket/protocols/native.js +0 -70
- package/dist/calldata/socket/protocols/oft.d.ts +0 -2
- package/dist/calldata/socket/protocols/oft.js +0 -31
- package/dist/calldata/socket/protocols/swapsXYZ.d.ts +0 -2
- package/dist/calldata/socket/protocols/swapsXYZ.js +0 -62
- package/dist/calldata/socket/swapAndBridge.d.ts +0 -2
- package/dist/calldata/socket/swapAndBridge.js +0 -37
- package/dist/calldata/socket/validate.d.ts +0 -3
- package/dist/calldata/socket/validate.js +0 -21
- package/dist/calldata/types.d.ts +0 -82
- package/dist/calldata/types.js +0 -20
- package/dist/errors/constants.d.ts +0 -876
- package/dist/errors/constants.js +0 -883
- package/dist/errors/index.d.ts +0 -61
- package/dist/errors/index.js +0 -53
- package/dist/errors/sanitize.d.ts +0 -3
- package/dist/errors/sanitize.js +0 -75
- package/dist/providers/data.d.ts +0 -23
- package/dist/providers/data.js +0 -116
- package/dist/providers/errors.d.ts +0 -7
- package/dist/providers/errors.js +0 -10
- package/dist/providers/index.d.ts +0 -7
- package/dist/providers/index.js +0 -340
- package/dist/providers/layerswap.d.ts +0 -9
- package/dist/providers/layerswap.js +0 -95
- package/dist/providers/relay.d.ts +0 -59
- package/dist/providers/relay.js +0 -185
- package/dist/providers/request.d.ts +0 -23
- package/dist/providers/request.js +0 -69
- package/dist/providers/socket.d.ts +0 -5
- package/dist/providers/socket.js +0 -57
- package/dist/providers/types.d.ts +0 -36
- package/dist/providers/types.js +0 -1
package/dist/quote.js
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
import { BRIDGE_ERRORS, BridgeError, asBridgeError, reportBridgeError } from './errors/index.js';
|
|
2
|
-
import { CALLDATA_AGGREGATORS, InvalidBridgeCalldataError, validateBridgeCalldata, } from './calldata/index.js';
|
|
3
|
-
import { calldataDiagnostics } from './calldata/diagnostics.js';
|
|
4
|
-
import { InvalidBridgeProviderError } from './providers/errors.js';
|
|
5
|
-
import { SOCKET_PROVIDER_ROUTES } from './providers/socket.js';
|
|
6
1
|
import { BRIDGE_ENUM } from './consts.js';
|
|
7
2
|
import { getBridgeAltContracts, getBridgeRouter, getBridgeSpender, isSupportedBridgeAggregator, isSupportedBridgeChain, } from './list.js';
|
|
8
|
-
export class InvalidBridgeContractError extends
|
|
3
|
+
export class InvalidBridgeContractError extends Error {
|
|
9
4
|
constructor(kind, aggregatorId, chainId, expected, received, bridgeId) {
|
|
10
|
-
super(kind
|
|
5
|
+
super(`Invalid bridge ${kind} for ${aggregatorId} on ${chainId}: expected ${expected}, got ${received}`);
|
|
11
6
|
this.kind = kind;
|
|
12
7
|
this.aggregatorId = aggregatorId;
|
|
13
8
|
this.chainId = chainId;
|
|
14
9
|
this.expected = expected;
|
|
15
10
|
this.received = received;
|
|
16
11
|
this.bridgeId = bridgeId;
|
|
17
|
-
this.message =
|
|
18
|
-
`Invalid bridge ${kind} for ${aggregatorId} on ${chainId}: expected ${expected}, got ${received}`;
|
|
19
12
|
this.name = 'InvalidBridgeContractError';
|
|
20
13
|
}
|
|
21
14
|
}
|
|
@@ -27,19 +20,15 @@ const isSameAddress = (left, right) => {
|
|
|
27
20
|
};
|
|
28
21
|
function assertSupported(aggregatorId, fromChainId) {
|
|
29
22
|
if (!isSupportedBridgeAggregator(aggregatorId)) {
|
|
30
|
-
|
|
31
|
-
error.message = `${aggregatorId} is not supported`;
|
|
32
|
-
throw error;
|
|
23
|
+
throw new Error(`${aggregatorId} is not supported`);
|
|
33
24
|
}
|
|
34
25
|
if (!isSupportedBridgeChain(aggregatorId, fromChainId)) {
|
|
35
|
-
|
|
36
|
-
error.message = `${aggregatorId} does not support ${fromChainId}`;
|
|
37
|
-
throw error;
|
|
26
|
+
throw new Error(`${aggregatorId} does not support ${fromChainId}`);
|
|
38
27
|
}
|
|
39
28
|
}
|
|
40
29
|
const requireSlippage = (slippage) => {
|
|
41
30
|
if (!slippage) {
|
|
42
|
-
throw new
|
|
31
|
+
throw new Error('slippage is required for this API call');
|
|
43
32
|
}
|
|
44
33
|
return slippage;
|
|
45
34
|
};
|
|
@@ -50,22 +39,13 @@ const stringifyQuoteKey = (quoteKey) => {
|
|
|
50
39
|
return JSON.stringify(quoteKey || {});
|
|
51
40
|
};
|
|
52
41
|
const shouldValidateBridgeContracts = (aggregatorId) => aggregatorId !== BRIDGE_ENUM.NEAR_INTENTS;
|
|
53
|
-
const
|
|
54
|
-
if (!options.lite)
|
|
55
|
-
throw error;
|
|
56
|
-
reportBridgeError(options.onError, asBridgeError(error), { ...context, lite: true, blocking: false });
|
|
57
|
-
};
|
|
58
|
-
export const validateBridgeQuote = (aggregatorId, fromChainId, quote, options = {}) => {
|
|
42
|
+
export const validateBridgeQuote = (aggregatorId, fromChainId, quote) => {
|
|
59
43
|
if (!shouldValidateBridgeContracts(aggregatorId)) {
|
|
60
44
|
return true;
|
|
61
45
|
}
|
|
62
46
|
const expectedSpender = getBridgeSpender(aggregatorId, fromChainId);
|
|
63
47
|
const receivedSpender = quote.approve_contract_id;
|
|
64
|
-
if (!receivedSpender) {
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
if (!expectedSpender) {
|
|
68
|
-
handleNewValidationError(new InvalidBridgeContractError('spender', aggregatorId, fromChainId, 'configured spender', receivedSpender, quote.bridge_id), options, { aggregatorId, chainId: fromChainId, bridgeId: quote.bridge_id, operation: 'getQuote' });
|
|
48
|
+
if (!expectedSpender || !receivedSpender) {
|
|
69
49
|
return true;
|
|
70
50
|
}
|
|
71
51
|
const allowedSpenders = [
|
|
@@ -77,273 +57,81 @@ export const validateBridgeQuote = (aggregatorId, fromChainId, quote, options =
|
|
|
77
57
|
}
|
|
78
58
|
return true;
|
|
79
59
|
};
|
|
80
|
-
const
|
|
81
|
-
let key = quoteKey;
|
|
82
|
-
if (typeof key === 'string') {
|
|
83
|
-
try {
|
|
84
|
-
key = JSON.parse(key);
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (!key || typeof key !== 'object' || Array.isArray(key))
|
|
91
|
-
return undefined;
|
|
92
|
-
const amount = key.to_token_raw_amount;
|
|
93
|
-
if (typeof amount === 'string' || (typeof amount === 'number' && Number.isSafeInteger(amount))) {
|
|
94
|
-
return String(amount);
|
|
95
|
-
}
|
|
96
|
-
return undefined;
|
|
97
|
-
};
|
|
98
|
-
const validateBridgeRouter = (aggregatorId, fromChainId, bridgeId, tx, legacy = false) => {
|
|
60
|
+
export const validateBridgeTx = (aggregatorId, fromChainId, bridgeId, tx) => {
|
|
99
61
|
if (!shouldValidateBridgeContracts(aggregatorId)) {
|
|
100
|
-
return
|
|
62
|
+
return true;
|
|
101
63
|
}
|
|
102
64
|
const expectedRouter = getBridgeRouter(aggregatorId, fromChainId);
|
|
103
|
-
if (
|
|
104
|
-
return;
|
|
65
|
+
if (!expectedRouter) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
105
68
|
const allowedRouters = [
|
|
106
|
-
|
|
69
|
+
expectedRouter,
|
|
107
70
|
...getBridgeAltContracts(aggregatorId),
|
|
108
71
|
];
|
|
109
72
|
if (!allowedRouters.some((addr) => isSameAddress(addr, tx.to))) {
|
|
110
|
-
throw
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
const checkBridgeTx = (aggregatorId, fromChainId, bridgeId, tx, expectation, options = {}, operation = 'verifyTx') => {
|
|
114
|
-
const context = { aggregatorId, chainId: fromChainId, toChainId: expectation?.toChainId, bridgeId, operation,
|
|
115
|
-
...calldataDiagnostics(tx.data) };
|
|
116
|
-
// A configured-router mismatch was already blocking on main; never suppress it.
|
|
117
|
-
if (options.lite)
|
|
118
|
-
validateBridgeRouter(aggregatorId, fromChainId, bridgeId, tx, true);
|
|
119
|
-
try {
|
|
120
|
-
validateBridgeRouter(aggregatorId, fromChainId, bridgeId, tx);
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
handleNewValidationError(error, options, context);
|
|
73
|
+
throw new InvalidBridgeContractError('router', aggregatorId, fromChainId, expectedRouter, tx.to, bridgeId);
|
|
124
74
|
}
|
|
125
|
-
if (CALLDATA_AGGREGATORS.includes(aggregatorId) || options.lite) {
|
|
126
|
-
let check;
|
|
127
|
-
try {
|
|
128
|
-
check = validateBridgeCalldata(aggregatorId, fromChainId, tx, expectation);
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
handleNewValidationError(error, options, context);
|
|
132
|
-
return undefined;
|
|
133
|
-
}
|
|
134
|
-
if (!check.ok) {
|
|
135
|
-
handleNewValidationError(new InvalidBridgeCalldataError(aggregatorId, fromChainId, check.method, check.reason || BRIDGE_ERRORS.INVALID_CALLDATA.message, check.errors.map((error) => ({ ...error, bridgeId }))), options, context);
|
|
136
|
-
}
|
|
137
|
-
return check;
|
|
138
|
-
}
|
|
139
|
-
return undefined;
|
|
140
|
-
};
|
|
141
|
-
export const validateBridgeTx = (aggregatorId, fromChainId, bridgeId, tx, expectation, options = {}) => {
|
|
142
|
-
checkBridgeTx(aggregatorId, fromChainId, bridgeId, tx, expectation, options);
|
|
143
75
|
return true;
|
|
144
76
|
};
|
|
145
77
|
export const getQuote = async (aggregatorId, params, api) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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');
|
|
95
|
+
}
|
|
96
|
+
return quotes.filter((quote) => {
|
|
150
97
|
try {
|
|
151
|
-
|
|
98
|
+
validateBridgeQuote(aggregatorId, params.fromChainId, quote);
|
|
99
|
+
return true;
|
|
152
100
|
}
|
|
153
101
|
catch (error) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (aggregatorId === BRIDGE_ENUM.RELAY && !params.lite) {
|
|
157
|
-
if (!params.providerClient)
|
|
158
|
-
throw new InvalidBridgeProviderError('relay', BRIDGE_ERRORS.DIRECT_PROVIDER_CLIENT_IS_REQUIRED);
|
|
159
|
-
const quotes = await params.providerClient.getQuotes('relay', params);
|
|
160
|
-
try {
|
|
161
|
-
assertContext?.();
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
handleNewValidationError(error, params, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'getQuote' });
|
|
102
|
+
if (error instanceof InvalidBridgeContractError) {
|
|
103
|
+
return false;
|
|
165
104
|
}
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
const baseParams = {
|
|
169
|
-
aggregator_id: aggregatorId,
|
|
170
|
-
user_addr: params.userAddress,
|
|
171
|
-
from_chain_id: params.fromChainId,
|
|
172
|
-
from_token_id: params.fromTokenId,
|
|
173
|
-
from_token_raw_amount: params.fromTokenRawAmount,
|
|
174
|
-
to_chain_id: params.toChainId,
|
|
175
|
-
to_token_id: params.toTokenId,
|
|
176
|
-
fee_rate: params.feeRate,
|
|
177
|
-
slippage: requireSlippage(params.slippage),
|
|
178
|
-
};
|
|
179
|
-
const [backendQuotes, providerQuotes] = await Promise.all([
|
|
180
|
-
api.getBridgeQuoteListV2
|
|
181
|
-
? api.getBridgeQuoteListV2(baseParams).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_QUOTE_REQUEST_FAILED, { requestKind: 'backend_quote' }); })
|
|
182
|
-
: api.getBridgeQuoteV2?.(baseParams).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_QUOTE_REQUEST_FAILED, { requestKind: 'backend_quote' }); }),
|
|
183
|
-
aggregatorId === BRIDGE_ENUM.SOCKET && params.providerClient && !params.lite
|
|
184
|
-
? params.providerClient.getQuotes('socket', params).catch((error) => {
|
|
185
|
-
if (error instanceof InvalidBridgeProviderError) {
|
|
186
|
-
reportBridgeError(params.onError, error, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'getQuote' });
|
|
187
|
-
return [];
|
|
188
|
-
}
|
|
189
|
-
throw error;
|
|
190
|
-
})
|
|
191
|
-
: Promise.resolve([]),
|
|
192
|
-
]);
|
|
193
|
-
try {
|
|
194
|
-
assertContext?.();
|
|
195
|
-
}
|
|
196
|
-
catch (error) {
|
|
197
|
-
handleNewValidationError(error, params, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'getQuote' });
|
|
198
|
-
}
|
|
199
|
-
if (!backendQuotes) {
|
|
200
|
-
throw new BridgeError(BRIDGE_ERRORS.MISSING_QUOTE_API);
|
|
105
|
+
throw error;
|
|
201
106
|
}
|
|
202
|
-
|
|
203
|
-
...backendQuotes.filter((quote) => !(aggregatorId === BRIDGE_ENUM.SOCKET && params.providerClient && !params.lite &&
|
|
204
|
-
SOCKET_PROVIDER_ROUTES.includes(quote.bridge_id))),
|
|
205
|
-
...providerQuotes,
|
|
206
|
-
];
|
|
207
|
-
return quotes.filter((quote) => {
|
|
208
|
-
try {
|
|
209
|
-
validateBridgeQuote(aggregatorId, params.fromChainId, quote, params);
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
if (error instanceof InvalidBridgeContractError) {
|
|
214
|
-
reportBridgeError(params.onError, error, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, bridgeId: quote.bridge_id, operation: 'getQuote', ...(params.lite ? { lite: true, blocking: true } : {}) });
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
throw error;
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
catch (error) {
|
|
222
|
-
throw asBridgeError(error, BRIDGE_ERRORS.UNKNOWN, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'getQuote', ...(params.lite ? { lite: true, blocking: true } : {}) });
|
|
223
|
-
}
|
|
107
|
+
});
|
|
224
108
|
};
|
|
225
109
|
export const buildTx = async (aggregatorId, params, api) => {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
aggregator_id: aggregatorId,
|
|
252
|
-
bridge_id: params.bridgeId,
|
|
253
|
-
user_addr: params.userAddress,
|
|
254
|
-
from_chain_id: params.fromChainId,
|
|
255
|
-
from_token_id: params.fromTokenId,
|
|
256
|
-
from_token_raw_amount: params.fromTokenRawAmount,
|
|
257
|
-
to_chain_id: params.toChainId,
|
|
258
|
-
to_token_id: params.toTokenId,
|
|
259
|
-
slippage: requireSlippage(params.slippage),
|
|
260
|
-
};
|
|
261
|
-
const tx = api.buildBridgeTx
|
|
262
|
-
? await api.buildBridgeTx({
|
|
263
|
-
...buildParams,
|
|
264
|
-
quote_key: stringifyQuoteKey(params.quoteKey),
|
|
265
|
-
}).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_BUILD_REQUEST_FAILED, { requestKind: 'backend_build' }); })
|
|
266
|
-
: (await api.getBridgeQuoteTxV2?.({
|
|
267
|
-
...buildParams,
|
|
268
|
-
}).catch((error) => { throw asBridgeError(error, BRIDGE_ERRORS.BACKEND_BUILD_REQUEST_FAILED, { requestKind: 'backend_build' }); }))?.tx;
|
|
269
|
-
if (!tx) {
|
|
270
|
-
throw new BridgeError(BRIDGE_ERRORS.MISSING_BUILD_API);
|
|
271
|
-
}
|
|
272
|
-
try {
|
|
273
|
-
assertContext?.();
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
handleNewValidationError(error, params, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'buildTx' });
|
|
277
|
-
}
|
|
278
|
-
const expectation = {
|
|
279
|
-
userAddress: params.userAddress,
|
|
280
|
-
fromTokenId: params.fromTokenId,
|
|
281
|
-
fromTokenRawAmount: params.fromTokenRawAmount,
|
|
282
|
-
toChainId: params.toChainId,
|
|
283
|
-
toTokenId: params.toTokenId,
|
|
284
|
-
toTokenRawAmount: params.toTokenRawAmount ?? quotedToAmount(params.quoteKey),
|
|
285
|
-
slippage: params.slippage,
|
|
286
|
-
};
|
|
287
|
-
const check = checkBridgeTx(aggregatorId, params.fromChainId, params.bridgeId, tx, expectation, params, 'buildTx');
|
|
288
|
-
// A backend may mislabel bridge_id. Decide from calldata as well, so renaming
|
|
289
|
-
// an RFQ/CCTP/LayerSwap route cannot bypass the provider requirement.
|
|
290
|
-
const parsed = check?.parsed;
|
|
291
|
-
if (params.lite && (aggregatorId === BRIDGE_ENUM.RELAY ||
|
|
292
|
-
(aggregatorId === BRIDGE_ENUM.SOCKET && SOCKET_PROVIDER_ROUTES.includes(params.bridgeId)))) {
|
|
293
|
-
try {
|
|
294
|
-
if (!params.providerClient)
|
|
295
|
-
throw new InvalidBridgeProviderError(aggregatorId, BRIDGE_ERRORS.DIRECT_PROVIDER_CLIENT_IS_REQUIRED);
|
|
296
|
-
params.providerClient.verifyTx(aggregatorId, params, tx);
|
|
297
|
-
}
|
|
298
|
-
catch (error) {
|
|
299
|
-
handleNewValidationError(error, params, { aggregatorId, chainId: params.fromChainId,
|
|
300
|
-
toChainId: params.toChainId, bridgeId: params.bridgeId, operation: 'buildTx' });
|
|
301
|
-
}
|
|
302
|
-
return tx;
|
|
303
|
-
}
|
|
304
|
-
try {
|
|
305
|
-
if (aggregatorId === BRIDGE_ENUM.LIFI && parsed?.method.endsWith('ViaLayerSwap')) {
|
|
306
|
-
if (!params.providerClient)
|
|
307
|
-
throw new InvalidBridgeProviderError('layerswap', BRIDGE_ERRORS.DIRECT_PROVIDER_CLIENT_IS_REQUIRED);
|
|
308
|
-
const verification = params.providerClient.verifyLayerSwapTx({
|
|
309
|
-
...params, toTokenRawAmount: expectation.toTokenRawAmount,
|
|
310
|
-
}, params.lite ? { ...tx } : tx);
|
|
311
|
-
if (params.lite) {
|
|
312
|
-
void verification.then(() => assertContext?.()).catch((error) => {
|
|
313
|
-
handleNewValidationError(error, params, { aggregatorId, chainId: params.fromChainId,
|
|
314
|
-
toChainId: params.toChainId, bridgeId: params.bridgeId, operation: 'buildTx' });
|
|
315
|
-
});
|
|
316
|
-
return tx;
|
|
317
|
-
}
|
|
318
|
-
await verification;
|
|
319
|
-
try {
|
|
320
|
-
assertContext?.();
|
|
321
|
-
}
|
|
322
|
-
catch (error) {
|
|
323
|
-
handleNewValidationError(error, params, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'buildTx' });
|
|
324
|
-
}
|
|
325
|
-
return tx;
|
|
326
|
-
}
|
|
327
|
-
if (aggregatorId === BRIDGE_ENUM.SOCKET && parsed?.unresolvedFields?.length &&
|
|
328
|
-
['receiveERC20', 'depositForBurn'].includes(parsed.protocolMethod ?? '')) {
|
|
329
|
-
throw new InvalidBridgeProviderError(aggregatorId, BRIDGE_ERRORS.PROVIDER_DESTINATION_UNBOUND);
|
|
330
|
-
}
|
|
331
|
-
// shortcut: chain-state output checks are deferred until the follow-up RPC PR.
|
|
332
|
-
const unverifiedFields = check?.unverifiedFields?.filter((field) => !(field === 'outputToken' && parsed?.outputToken === undefined && parsed?.outputTokenReference) &&
|
|
333
|
-
!(field === 'minOutputAmount' && parsed?.minOutputAmount === undefined && parsed?.outputAmountReference));
|
|
334
|
-
if (unverifiedFields?.length) {
|
|
335
|
-
throw new InvalidBridgeCalldataError(aggregatorId, params.fromChainId, check?.method, `unverified bridge fields: ${unverifiedFields.join(',')}`, [{ ...BRIDGE_ERRORS.UNVERIFIED_BRIDGE_FIELDS, bridgeId: params.bridgeId, unverifiedFields }]);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
catch (error) {
|
|
339
|
-
handleNewValidationError(error, params, { aggregatorId, chainId: params.fromChainId,
|
|
340
|
-
toChainId: params.toChainId, bridgeId: params.bridgeId, operation: 'buildTx' });
|
|
341
|
-
}
|
|
342
|
-
return tx;
|
|
343
|
-
}
|
|
344
|
-
catch (error) {
|
|
345
|
-
throw asBridgeError(error, BRIDGE_ERRORS.UNKNOWN, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, operation: 'buildTx', bridgeId: params.bridgeId, ...(params.lite ? { lite: true, blocking: true } : {}) });
|
|
346
|
-
}
|
|
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;
|
|
347
135
|
};
|
|
348
136
|
export const getQuoteListWithTx = async (aggregatorId, params, api) => {
|
|
349
137
|
const quotes = await getQuote(aggregatorId, params, api);
|
|
@@ -353,8 +141,6 @@ export const getQuoteListWithTx = async (aggregatorId, params, api) => {
|
|
|
353
141
|
...params,
|
|
354
142
|
bridgeId: quote.bridge_id,
|
|
355
143
|
quoteKey: quote.quote_key,
|
|
356
|
-
toTokenRawAmount: quote.to_token_raw_amount_hex_str ??
|
|
357
|
-
(Number.isSafeInteger(quote.to_token_raw_amount) ? String(quote.to_token_raw_amount) : undefined),
|
|
358
144
|
}, api);
|
|
359
145
|
return {
|
|
360
146
|
...quote,
|
|
@@ -362,10 +148,7 @@ export const getQuoteListWithTx = async (aggregatorId, params, api) => {
|
|
|
362
148
|
};
|
|
363
149
|
}
|
|
364
150
|
catch (error) {
|
|
365
|
-
if (error instanceof InvalidBridgeContractError
|
|
366
|
-
error instanceof InvalidBridgeCalldataError ||
|
|
367
|
-
error instanceof InvalidBridgeProviderError) {
|
|
368
|
-
reportBridgeError(params.onError, error, { aggregatorId, chainId: params.fromChainId, toChainId: params.toChainId, bridgeId: quote.bridge_id, operation: 'buildTx', ...(params.lite ? { lite: true, blocking: true } : {}) });
|
|
151
|
+
if (error instanceof InvalidBridgeContractError) {
|
|
369
152
|
return null;
|
|
370
153
|
}
|
|
371
154
|
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.13",
|
|
4
4
|
"description": "Bridge quote helpers and contract allowlists for Rabby",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,13 +22,7 @@
|
|
|
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"
|
|
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\\]'"
|
|
25
|
+
"test": "yarn build && NODE_OPTIONS=--experimental-vm-modules jest --runInBand --watchman=false"
|
|
32
26
|
},
|
|
33
27
|
"devDependencies": {
|
|
34
28
|
"@debank/common": "^0.3.60",
|
|
@@ -38,9 +32,5 @@
|
|
|
38
32
|
"rimraf": "^3.0.2",
|
|
39
33
|
"typescript": "^5.8.3"
|
|
40
34
|
},
|
|
41
|
-
"packageManager": "yarn@4.12.0"
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@ethersproject/hash": "5.8.0",
|
|
44
|
-
"@ethersproject/strings": "5.8.0"
|
|
45
|
-
}
|
|
35
|
+
"packageManager": "yarn@4.12.0"
|
|
46
36
|
}
|
package/dist/calldata/abi.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ParsedBridgeCall } from './types.js';
|
|
2
|
-
export type AbiParam = {
|
|
3
|
-
name: string;
|
|
4
|
-
kind: 'address' | 'uint' | 'bool' | 'bytes' | 'string' | 'array' | 'tuple';
|
|
5
|
-
size?: number;
|
|
6
|
-
length?: number;
|
|
7
|
-
item?: AbiParam;
|
|
8
|
-
components?: AbiParam[];
|
|
9
|
-
};
|
|
10
|
-
export type AbiValue = string | bigint | boolean | AbiValue[] | {
|
|
11
|
-
[key: string]: AbiValue;
|
|
12
|
-
};
|
|
13
|
-
export declare const decodeCall: (params: AbiParam[], calldata: string) => {
|
|
14
|
-
[key: string]: AbiValue;
|
|
15
|
-
};
|
|
16
|
-
export declare const decodeParameters: (params: AbiParam[], data: string) => {
|
|
17
|
-
[key: string]: AbiValue;
|
|
18
|
-
};
|
|
19
|
-
export declare const asRecord: (value: AbiValue) => {
|
|
20
|
-
[key: string]: AbiValue;
|
|
21
|
-
};
|
|
22
|
-
export declare const asArray: (value: AbiValue) => AbiValue[];
|
|
23
|
-
export declare const asBigint: (value: AbiValue) => bigint;
|
|
24
|
-
export declare const asString: (value: AbiValue) => string;
|
|
25
|
-
export type CalldataMethod = {
|
|
26
|
-
name: string;
|
|
27
|
-
selector: string;
|
|
28
|
-
decode: (data: string, value: bigint) => ParsedBridgeCall;
|
|
29
|
-
};
|
package/dist/calldata/abi.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { BRIDGE_ERRORS, BridgeError } from '../errors/index.js';
|
|
2
|
-
import { toUtf8String, Utf8ErrorFuncs } from '@ethersproject/strings';
|
|
3
|
-
import { bytesToHex, hexToBytes } from './hex.js';
|
|
4
|
-
const isDynamic = (param) => {
|
|
5
|
-
if (param.kind === 'string') {
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
if (param.kind === 'bytes') {
|
|
9
|
-
return param.size === undefined;
|
|
10
|
-
}
|
|
11
|
-
if (param.kind === 'array') {
|
|
12
|
-
return param.length === undefined || isDynamic(param.item);
|
|
13
|
-
}
|
|
14
|
-
if (param.kind === 'tuple') {
|
|
15
|
-
return (param.components ?? []).some(isDynamic);
|
|
16
|
-
}
|
|
17
|
-
return false;
|
|
18
|
-
};
|
|
19
|
-
const staticWordCount = (param) => {
|
|
20
|
-
if (isDynamic(param)) {
|
|
21
|
-
return 1;
|
|
22
|
-
}
|
|
23
|
-
if (param.kind === 'tuple') {
|
|
24
|
-
return (param.components ?? []).reduce((sum, item) => sum + staticWordCount(item), 0);
|
|
25
|
-
}
|
|
26
|
-
if (param.kind === 'array') {
|
|
27
|
-
return (param.length ?? 0) * staticWordCount(param.item);
|
|
28
|
-
}
|
|
29
|
-
return 1;
|
|
30
|
-
};
|
|
31
|
-
const readWord = (buf, offset) => {
|
|
32
|
-
if (offset < 0 || offset + 32 > buf.length) {
|
|
33
|
-
throw new BridgeError(BRIDGE_ERRORS.ABI_WORD_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
|
|
34
|
-
}
|
|
35
|
-
let value = 0n;
|
|
36
|
-
for (let index = 0; index < 32; index += 1) {
|
|
37
|
-
value = (value << 8n) + BigInt(buf[offset + index]);
|
|
38
|
-
}
|
|
39
|
-
return value;
|
|
40
|
-
};
|
|
41
|
-
const toAddress = (word) => {
|
|
42
|
-
if (word >= (1n << 160n))
|
|
43
|
-
throw new BridgeError(BRIDGE_ERRORS.INVALID_ABI_ADDRESS);
|
|
44
|
-
return `0x${word.toString(16).padStart(40, '0')}`;
|
|
45
|
-
};
|
|
46
|
-
const readByteLength = (buf, offset) => {
|
|
47
|
-
const length = readWord(buf, offset);
|
|
48
|
-
if (length > BigInt(buf.length)) {
|
|
49
|
-
throw new BridgeError(BRIDGE_ERRORS.ABI_BYTES_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
|
|
50
|
-
}
|
|
51
|
-
return Number(length);
|
|
52
|
-
};
|
|
53
|
-
const readDynamicBytes = (buf, offset) => {
|
|
54
|
-
const length = readByteLength(buf, offset);
|
|
55
|
-
const start = offset + 32;
|
|
56
|
-
if (start + length > buf.length) {
|
|
57
|
-
throw new BridgeError(BRIDGE_ERRORS.ABI_BYTES_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
|
|
58
|
-
}
|
|
59
|
-
return bytesToHex(buf.subarray(start, start + length));
|
|
60
|
-
};
|
|
61
|
-
const decodeSequence = (params, buf, base) => {
|
|
62
|
-
let cursor = base;
|
|
63
|
-
const locations = [];
|
|
64
|
-
for (const param of params) {
|
|
65
|
-
if (isDynamic(param)) {
|
|
66
|
-
const relative = readWord(buf, cursor);
|
|
67
|
-
if (relative > BigInt(buf.length)) {
|
|
68
|
-
throw new BridgeError(BRIDGE_ERRORS.ABI_OFFSET_OUT_OF_BOUNDS, { abiOffset: cursor, abiBufferBytes: buf.length });
|
|
69
|
-
}
|
|
70
|
-
locations.push(base + Number(relative));
|
|
71
|
-
cursor += 32;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
locations.push(cursor);
|
|
75
|
-
cursor += staticWordCount(param) * 32;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return params.map((param, index) => decodeAt(param, buf, locations[index]));
|
|
79
|
-
};
|
|
80
|
-
const decodeAt = (param, buf, offset) => {
|
|
81
|
-
switch (param.kind) {
|
|
82
|
-
case 'address':
|
|
83
|
-
return toAddress(readWord(buf, offset));
|
|
84
|
-
case 'uint':
|
|
85
|
-
return readWord(buf, offset);
|
|
86
|
-
case 'bool': {
|
|
87
|
-
const word = readWord(buf, offset);
|
|
88
|
-
if (word !== 0n && word !== 1n) {
|
|
89
|
-
throw new BridgeError(BRIDGE_ERRORS.INVALID_BOOL);
|
|
90
|
-
}
|
|
91
|
-
return word === 1n;
|
|
92
|
-
}
|
|
93
|
-
case 'bytes': {
|
|
94
|
-
if (param.size === undefined) {
|
|
95
|
-
return readDynamicBytes(buf, offset);
|
|
96
|
-
}
|
|
97
|
-
if (offset + 32 > buf.length) {
|
|
98
|
-
throw new BridgeError(BRIDGE_ERRORS.ABI_WORD_OUT_OF_BOUNDS, { abiOffset: offset, abiBufferBytes: buf.length });
|
|
99
|
-
}
|
|
100
|
-
return bytesToHex(buf.subarray(offset, offset + param.size));
|
|
101
|
-
}
|
|
102
|
-
case 'string':
|
|
103
|
-
return toUtf8String(readDynamicBytes(buf, offset), Utf8ErrorFuncs.replace).replace(/^\uFEFF/, '');
|
|
104
|
-
case 'tuple':
|
|
105
|
-
return decodeTuple(param.components ?? [], buf, offset);
|
|
106
|
-
case 'array': {
|
|
107
|
-
const item = param.item;
|
|
108
|
-
if (param.length === undefined) {
|
|
109
|
-
const length = readByteLength(buf, offset);
|
|
110
|
-
if (length > 2048) {
|
|
111
|
-
throw new BridgeError(BRIDGE_ERRORS.ABI_ARRAY_TOO_LONG);
|
|
112
|
-
}
|
|
113
|
-
return decodeSequence(Array.from({ length }, () => item), buf, offset + 32);
|
|
114
|
-
}
|
|
115
|
-
return decodeSequence(Array.from({ length: param.length }, () => item), buf, offset);
|
|
116
|
-
}
|
|
117
|
-
default:
|
|
118
|
-
throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_ABI_TYPE);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
const decodeTuple = (components, buf, offset) => {
|
|
122
|
-
const values = decodeSequence(components, buf, offset);
|
|
123
|
-
return Object.fromEntries(components.map((component, index) => [component.name, values[index]]));
|
|
124
|
-
};
|
|
125
|
-
export const decodeCall = (params, calldata) => {
|
|
126
|
-
const bytes = hexToBytes(calldata);
|
|
127
|
-
if (bytes.length < 4) {
|
|
128
|
-
throw new BridgeError(BRIDGE_ERRORS.MISSING_SELECTOR);
|
|
129
|
-
}
|
|
130
|
-
const values = decodeSequence(params, bytes.subarray(4), 0);
|
|
131
|
-
return Object.fromEntries(params.map((param, index) => [param.name, values[index]]));
|
|
132
|
-
};
|
|
133
|
-
export const decodeParameters = (params, data) => {
|
|
134
|
-
const values = decodeSequence(params, hexToBytes(data), 0);
|
|
135
|
-
return Object.fromEntries(params.map((param, index) => [param.name, values[index]]));
|
|
136
|
-
};
|
|
137
|
-
export const asRecord = (value) => {
|
|
138
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
139
|
-
throw new BridgeError(BRIDGE_ERRORS.EXPECTED_TUPLE);
|
|
140
|
-
}
|
|
141
|
-
return value;
|
|
142
|
-
};
|
|
143
|
-
export const asArray = (value) => {
|
|
144
|
-
if (!Array.isArray(value)) {
|
|
145
|
-
throw new BridgeError(BRIDGE_ERRORS.EXPECTED_ARRAY);
|
|
146
|
-
}
|
|
147
|
-
return value;
|
|
148
|
-
};
|
|
149
|
-
export const asBigint = (value) => {
|
|
150
|
-
if (typeof value !== 'bigint') {
|
|
151
|
-
throw new BridgeError(BRIDGE_ERRORS.EXPECTED_UINT);
|
|
152
|
-
}
|
|
153
|
-
return value;
|
|
154
|
-
};
|
|
155
|
-
export const asString = (value) => {
|
|
156
|
-
if (typeof value !== 'string') {
|
|
157
|
-
throw new BridgeError(BRIDGE_ERRORS.EXPECTED_STRING);
|
|
158
|
-
}
|
|
159
|
-
return value;
|
|
160
|
-
};
|