@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
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { BRIDGE_ERRORS, bridgeFailure } from '../errors/index.js';
|
|
2
|
+
import { evmChainId } from './chains.js';
|
|
3
|
+
const NATIVE_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
|
4
|
+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
5
|
+
const RATIO_SCALE = 1000000n;
|
|
6
|
+
export const knownCompare = (parsed) => ({
|
|
7
|
+
fromToken: parsed.sourceToken !== undefined,
|
|
8
|
+
fromAmount: parsed.sourceAmount !== undefined,
|
|
9
|
+
toToken: parsed.outputToken !== undefined,
|
|
10
|
+
toAmount: parsed.minOutputAmount !== undefined,
|
|
11
|
+
receiver: parsed.receiver !== undefined,
|
|
12
|
+
});
|
|
13
|
+
const isHexAddress = (value) => /^0x[0-9a-f]{40}$/i.test(value);
|
|
14
|
+
const isNativeToken = (value, chainId) => {
|
|
15
|
+
const normalized = value.toLowerCase();
|
|
16
|
+
const network = evmChainId(normalized);
|
|
17
|
+
return (normalized === NATIVE_ADDRESS ||
|
|
18
|
+
normalized === ZERO_ADDRESS ||
|
|
19
|
+
normalized === chainId.toLowerCase() ||
|
|
20
|
+
(network !== undefined && network === evmChainId(chainId)));
|
|
21
|
+
};
|
|
22
|
+
const sameToken = (actual, expected, chainId) => {
|
|
23
|
+
if (!actual) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (isNativeToken(actual, chainId) && isNativeToken(expected, chainId)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (!isHexAddress(actual) || !isHexAddress(expected)) {
|
|
30
|
+
return actual.toLowerCase() === expected.toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
return actual.toLowerCase() === expected.toLowerCase();
|
|
33
|
+
};
|
|
34
|
+
const parseAmount = (amount) => {
|
|
35
|
+
try {
|
|
36
|
+
return BigInt(amount);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const selectedQuoteIdentifier = (quoteKey) => {
|
|
43
|
+
let key = quoteKey;
|
|
44
|
+
if (typeof key === 'string') {
|
|
45
|
+
try {
|
|
46
|
+
key = JSON.parse(key);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!key || typeof key !== 'object' || Array.isArray(key) || typeof key.quote_id !== 'string')
|
|
53
|
+
return undefined;
|
|
54
|
+
// Socket/Relay cache keys preserve the provider ID before a cache UUID suffix.
|
|
55
|
+
const match = /^(0x[0-9a-f]{64})(?::[^:]+)?$/i.exec(key.quote_id);
|
|
56
|
+
return match?.[1].toLowerCase();
|
|
57
|
+
};
|
|
58
|
+
/** Source payment is capped at 100.1% of local intent; smaller payments are allowed. */
|
|
59
|
+
export const sourceAmountMatches = (actual, expected) => actual !== undefined && actual >= 0n && expected >= 0n && actual * 1000n <= expected * 1001n;
|
|
60
|
+
/** Allow up to 5% below the slippage-adjusted minimum; higher guarantees always pass. */
|
|
61
|
+
const minReceiveMatches = (minReceive, quoted, slippage) => {
|
|
62
|
+
if (minReceive <= 0n || quoted <= 0n || slippage < 0 || slippage >= 1) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const ratioScaled = BigInt(Math.round((1 - slippage) * Number(RATIO_SCALE)));
|
|
66
|
+
const minimum = (quoted * ratioScaled) / RATIO_SCALE;
|
|
67
|
+
if (minimum <= 0n) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return minReceive * 20n >= minimum * 19n;
|
|
71
|
+
};
|
|
72
|
+
export const expectationFailures = (parsed, expectation, chainId, mask) => {
|
|
73
|
+
const reasons = [];
|
|
74
|
+
const quoteIdentifier = selectedQuoteIdentifier(expectation.quoteKey);
|
|
75
|
+
if (quoteIdentifier && parsed.aggregatorId === 'socket' && parsed.quoteId !== quoteIdentifier) {
|
|
76
|
+
reasons.push(BRIDGE_ERRORS.QUOTE_ID_MISMATCH);
|
|
77
|
+
}
|
|
78
|
+
// Relay direct deposits sign an order hash, distinct from the cache key's requestId.
|
|
79
|
+
if (quoteIdentifier && parsed.aggregatorId === 'relay' &&
|
|
80
|
+
!['depositNative', 'depositErc20', 'depositErc20Allowance'].includes(parsed.method) &&
|
|
81
|
+
parsed.requestId !== quoteIdentifier) {
|
|
82
|
+
reasons.push(BRIDGE_ERRORS.REQUEST_ID_MISMATCH);
|
|
83
|
+
}
|
|
84
|
+
if (expectation.toChainId && parsed.destinationChainRef && parsed.destinationChainId === undefined) {
|
|
85
|
+
reasons.push(BRIDGE_ERRORS.TO_CHAIN_MISMATCH);
|
|
86
|
+
}
|
|
87
|
+
if (expectation.toChainId && parsed.destinationChainId !== undefined) {
|
|
88
|
+
const expectedChain = evmChainId(expectation.toChainId);
|
|
89
|
+
if (expectedChain !== undefined && parsed.destinationChainId !== expectedChain)
|
|
90
|
+
reasons.push(BRIDGE_ERRORS.TO_CHAIN_MISMATCH);
|
|
91
|
+
}
|
|
92
|
+
if (mask.fromToken && !sameToken(parsed.sourceToken, expectation.fromTokenId, chainId)) {
|
|
93
|
+
reasons.push(BRIDGE_ERRORS.FROM_TOKEN_MISMATCH);
|
|
94
|
+
}
|
|
95
|
+
if (mask.fromAmount) {
|
|
96
|
+
const expectedAmount = parseAmount(expectation.fromTokenRawAmount);
|
|
97
|
+
if (expectedAmount === undefined || !sourceAmountMatches(parsed.sourceAmount, expectedAmount)) {
|
|
98
|
+
reasons.push(BRIDGE_ERRORS.FROM_AMOUNT_MISMATCH);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (mask.toToken &&
|
|
102
|
+
!sameToken(parsed.outputToken, expectation.toTokenId, expectation.toChainId || chainId)) {
|
|
103
|
+
reasons.push(BRIDGE_ERRORS.TO_TOKEN_MISMATCH);
|
|
104
|
+
}
|
|
105
|
+
if (mask.toAmount && expectation.toTokenRawAmount !== undefined) {
|
|
106
|
+
const quoted = parseAmount(expectation.toTokenRawAmount);
|
|
107
|
+
const slippage = Number(expectation.slippage);
|
|
108
|
+
if (quoted === undefined ||
|
|
109
|
+
parsed.minOutputAmount === undefined ||
|
|
110
|
+
!Number.isFinite(slippage) ||
|
|
111
|
+
!minReceiveMatches(parsed.minOutputAmount, quoted, slippage)) {
|
|
112
|
+
reasons.push(BRIDGE_ERRORS.MIN_RECEIVE_MISMATCH);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (mask.receiver && !parsed.fixedReceiver && !sameToken(parsed.receiver, expectation.userAddress, chainId)) {
|
|
116
|
+
reasons.push(BRIDGE_ERRORS.RECEIVER_MISMATCH);
|
|
117
|
+
}
|
|
118
|
+
if (parsed.refundRecipients?.some((recipient) => recipient.toLowerCase() !== expectation.userAddress.toLowerCase())) {
|
|
119
|
+
reasons.push(BRIDGE_ERRORS.REFUND_RECIPIENT_MISMATCH);
|
|
120
|
+
}
|
|
121
|
+
return reasons;
|
|
122
|
+
};
|
|
123
|
+
export const finishCheck = (aggregatorId, parsed, structural, expectation, chainId, mask) => {
|
|
124
|
+
mask = {
|
|
125
|
+
...mask,
|
|
126
|
+
fromToken: mask.fromToken && parsed.sourceToken !== undefined,
|
|
127
|
+
fromAmount: mask.fromAmount && parsed.sourceAmount !== undefined,
|
|
128
|
+
toToken: mask.toToken && parsed.outputToken !== undefined,
|
|
129
|
+
toAmount: mask.toAmount && parsed.minOutputAmount !== undefined,
|
|
130
|
+
receiver: mask.receiver && parsed.receiver !== undefined,
|
|
131
|
+
};
|
|
132
|
+
const expected = expectation
|
|
133
|
+
? expectationFailures(parsed, expectation, chainId, mask)
|
|
134
|
+
: [];
|
|
135
|
+
// A locally proven receiver is still useful when destination assets/amounts
|
|
136
|
+
// cannot be reduced to one outcome. Unknown execution/receivers remain check failures.
|
|
137
|
+
const unsupportedCall = (parsed.hasDestinationCall || parsed.destinationCallData !== undefined) &&
|
|
138
|
+
parsed.receiver === undefined;
|
|
139
|
+
const errors = [
|
|
140
|
+
...(structural ? [structural] : []),
|
|
141
|
+
...(unsupportedCall ? [BRIDGE_ERRORS.UNSUPPORTED_DESTINATION_CALL] : []),
|
|
142
|
+
...expected,
|
|
143
|
+
].map((error) => ({ ...error, ...parsed.diagnostics, aggregatorId, chainId, method: parsed.method,
|
|
144
|
+
hasDestinationCall: Boolean(parsed.hasDestinationCall || parsed.destinationCallData !== undefined) }));
|
|
145
|
+
const unverifiedFields = Object.entries({
|
|
146
|
+
sourceToken: mask.fromToken, sourceAmount: mask.fromAmount, outputToken: mask.toToken,
|
|
147
|
+
minOutputAmount: mask.toAmount && expectation?.toTokenRawAmount !== undefined,
|
|
148
|
+
receiver: mask.receiver,
|
|
149
|
+
}).filter(([, checked]) => !expectation || !checked).map(([field]) => field);
|
|
150
|
+
if (expectation?.toChainId && parsed.destinationChainId === undefined) {
|
|
151
|
+
unverifiedFields.push('destinationChainId');
|
|
152
|
+
}
|
|
153
|
+
if (errors.length) {
|
|
154
|
+
return { ...bridgeFailure(errors), aggregatorId, method: parsed.method, parsed, unverifiedFields };
|
|
155
|
+
}
|
|
156
|
+
return { ok: true, aggregatorId, method: parsed.method, parsed, unverifiedFields, errors: [] };
|
|
157
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const normalizeHex: (value: string) => string;
|
|
2
|
+
export declare const hexToBytes: (value: string) => Uint8Array;
|
|
3
|
+
export declare const bytesToHex: (bytes: Uint8Array) => string;
|
|
4
|
+
export declare const readSelector: (data: string) => string | null;
|
|
5
|
+
export declare const isZeroWord: (value?: string) => boolean;
|
|
6
|
+
export declare const accountFromBytes32: (word: string) => string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { BRIDGE_ERRORS, BridgeError } from '../errors/index.js';
|
|
2
|
+
const HEX = '0123456789abcdef';
|
|
3
|
+
export const normalizeHex = (value) => {
|
|
4
|
+
const hex = value.startsWith('0x') || value.startsWith('0X') ? value.slice(2) : value;
|
|
5
|
+
if (hex.length % 2 !== 0 || /[^0-9a-fA-F]/.test(hex)) {
|
|
6
|
+
throw new BridgeError(BRIDGE_ERRORS.INVALID_HEX);
|
|
7
|
+
}
|
|
8
|
+
return hex.toLowerCase();
|
|
9
|
+
};
|
|
10
|
+
export const hexToBytes = (value) => {
|
|
11
|
+
const hex = normalizeHex(value);
|
|
12
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
13
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
14
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
15
|
+
}
|
|
16
|
+
return bytes;
|
|
17
|
+
};
|
|
18
|
+
export const bytesToHex = (bytes) => {
|
|
19
|
+
let hex = '0x';
|
|
20
|
+
for (const byte of bytes) {
|
|
21
|
+
hex += HEX[byte >> 4] + HEX[byte & 15];
|
|
22
|
+
}
|
|
23
|
+
return hex;
|
|
24
|
+
};
|
|
25
|
+
export const readSelector = (data) => {
|
|
26
|
+
try {
|
|
27
|
+
const hex = normalizeHex(data || '0x');
|
|
28
|
+
if (hex.length < 8 || /^0+$/.test(hex.slice(0, 8))) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return `0x${hex.slice(0, 8)}`;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
export const isZeroWord = (value) => {
|
|
38
|
+
return !value || /^0x0*$/i.test(value);
|
|
39
|
+
};
|
|
40
|
+
export const accountFromBytes32 = (word) => {
|
|
41
|
+
const body = word.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
|
42
|
+
if (body.slice(0, 24) === '0'.repeat(24)) {
|
|
43
|
+
return `0x${body.slice(24)}`;
|
|
44
|
+
}
|
|
45
|
+
return `0x${body}`;
|
|
46
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BridgeError } from '../errors/index.js';
|
|
2
|
+
import type { BridgeErrorDetail } from '../errors/index.js';
|
|
3
|
+
import type { BridgeAggregatorId } from '../consts.js';
|
|
4
|
+
import type { BridgeCallExpectation } from './expect.js';
|
|
5
|
+
import type { BridgeCalldataTx, BridgeCheck, DecodeHit, ParsedBridgeCall } from './types.js';
|
|
6
|
+
export declare const CALLDATA_AGGREGATORS: BridgeAggregatorId[];
|
|
7
|
+
export declare class InvalidBridgeCalldataError extends BridgeError {
|
|
8
|
+
readonly aggregatorId: string;
|
|
9
|
+
readonly chainId: string;
|
|
10
|
+
readonly method: string | undefined;
|
|
11
|
+
readonly reason: string;
|
|
12
|
+
constructor(aggregatorId: string, chainId: string, method: string | undefined, reason: string, errors?: BridgeErrorDetail[]);
|
|
13
|
+
}
|
|
14
|
+
export declare const hasBridgeCalldata: (tx: BridgeCalldataTx) => boolean;
|
|
15
|
+
/** Detailed decoding for callers that need failures instead of a nullable result. */
|
|
16
|
+
export declare const decodeBridgeCalldataResult: (aggregatorId: string, tx: BridgeCalldataTx) => DecodeHit;
|
|
17
|
+
export declare const decodeBridgeCalldata: (aggregatorId: string, tx: BridgeCalldataTx) => ParsedBridgeCall | null;
|
|
18
|
+
export declare const validateBridgeCalldata: (aggregatorId: string, chainId: string, tx: BridgeCalldataTx, expectation?: BridgeCallExpectation) => BridgeCheck;
|
|
19
|
+
export type { BridgeCallExpectation } from './expect.js';
|
|
20
|
+
export type { BridgeCalldataTx, BridgeCheck, BridgeTokenReference, BridgeAmountReference, ParsedBridgeCall, DecodeHit } from './types.js';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { BRIDGE_ERRORS, BridgeError, asBridgeError, bridgeFailure } from '../errors/index.js';
|
|
2
|
+
import { decodeAcross, validateAcross } from './across/index.js';
|
|
3
|
+
import { readSelector } from './hex.js';
|
|
4
|
+
import { decodeLifi, validateLifi } from './lifi/index.js';
|
|
5
|
+
import { decodeMayan, validateMayan } from './mayan/index.js';
|
|
6
|
+
import { decodeRelay, validateRelay } from './relay/index.js';
|
|
7
|
+
import { decodeSocket, validateSocket } from './socket/index.js';
|
|
8
|
+
import { txCalldata, txValue } from './types.js';
|
|
9
|
+
import { sourceChainMismatch } from './chains.js';
|
|
10
|
+
import { calldataDiagnostics } from './diagnostics.js';
|
|
11
|
+
import { sanitizeBridgeError } from '../errors/sanitize.js';
|
|
12
|
+
const DECODERS = {
|
|
13
|
+
across: decodeAcross,
|
|
14
|
+
lifi: decodeLifi,
|
|
15
|
+
mayan: decodeMayan,
|
|
16
|
+
relay: decodeRelay,
|
|
17
|
+
socket: decodeSocket,
|
|
18
|
+
};
|
|
19
|
+
const VALIDATORS = {
|
|
20
|
+
across: validateAcross,
|
|
21
|
+
lifi: validateLifi,
|
|
22
|
+
mayan: validateMayan,
|
|
23
|
+
relay: validateRelay,
|
|
24
|
+
socket: validateSocket,
|
|
25
|
+
};
|
|
26
|
+
export const CALLDATA_AGGREGATORS = Object.keys(VALIDATORS);
|
|
27
|
+
export class InvalidBridgeCalldataError extends BridgeError {
|
|
28
|
+
constructor(aggregatorId, chainId, method, reason, errors) {
|
|
29
|
+
super(BRIDGE_ERRORS.INVALID_CALLDATA, { aggregatorId, chainId, method }, errors);
|
|
30
|
+
this.aggregatorId = aggregatorId;
|
|
31
|
+
this.chainId = chainId;
|
|
32
|
+
this.method = method;
|
|
33
|
+
this.reason = reason;
|
|
34
|
+
this.message = `Invalid bridge calldata for ${aggregatorId} on ${chainId}: ${reason}`;
|
|
35
|
+
this.name = 'InvalidBridgeCalldataError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export const hasBridgeCalldata = (tx) => {
|
|
39
|
+
return readSelector(txCalldata(tx)) !== null;
|
|
40
|
+
};
|
|
41
|
+
/** Detailed decoding for callers that need failures instead of a nullable result. */
|
|
42
|
+
export const decodeBridgeCalldataResult = (aggregatorId, tx) => {
|
|
43
|
+
const context = { aggregatorId, ...calldataDiagnostics(tx.data || tx.input) };
|
|
44
|
+
const decode = DECODERS[aggregatorId];
|
|
45
|
+
if (!decode)
|
|
46
|
+
return bridgeFailure([{ ...BRIDGE_ERRORS.UNSUPPORTED_CALLDATA_AGGREGATOR, ...context }]);
|
|
47
|
+
let value;
|
|
48
|
+
try {
|
|
49
|
+
value = txValue(tx);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return bridgeFailure(asBridgeError(error, BRIDGE_ERRORS.INVALID_TRANSACTION_VALUE, context).errors);
|
|
53
|
+
}
|
|
54
|
+
const hit = decode(txCalldata(tx), value);
|
|
55
|
+
return hit.ok ? hit : { ...hit, errors: hit.errors.map((error) => sanitizeBridgeError({ ...error, ...context })) };
|
|
56
|
+
};
|
|
57
|
+
export const decodeBridgeCalldata = (aggregatorId, tx) => {
|
|
58
|
+
const hit = decodeBridgeCalldataResult(aggregatorId, tx);
|
|
59
|
+
return hit.ok ? hit.parsed : null;
|
|
60
|
+
};
|
|
61
|
+
export const validateBridgeCalldata = (aggregatorId, chainId, tx, expectation) => {
|
|
62
|
+
const context = { aggregatorId, chainId, toChainId: expectation?.toChainId, ...calldataDiagnostics(tx.data || tx.input) };
|
|
63
|
+
const inputErrors = [];
|
|
64
|
+
if (sourceChainMismatch(chainId, tx.chainId))
|
|
65
|
+
inputErrors.push(BRIDGE_ERRORS.FROM_CHAIN_MISMATCH);
|
|
66
|
+
if (expectation && tx.from !== undefined &&
|
|
67
|
+
(typeof tx.from !== 'string' || tx.from.toLowerCase() !== expectation.userAddress.toLowerCase())) {
|
|
68
|
+
inputErrors.push(BRIDGE_ERRORS.FROM_ADDRESS_MISMATCH);
|
|
69
|
+
}
|
|
70
|
+
const validate = VALIDATORS[aggregatorId];
|
|
71
|
+
if (!validate) {
|
|
72
|
+
return {
|
|
73
|
+
...bridgeFailure([...inputErrors, BRIDGE_ERRORS.UNSUPPORTED_CALLDATA_AGGREGATOR].map((error) => ({ ...error, ...context }))),
|
|
74
|
+
aggregatorId,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
let value;
|
|
78
|
+
try {
|
|
79
|
+
value = txValue(tx);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return { ...bridgeFailure([...inputErrors, ...asBridgeError(error, BRIDGE_ERRORS.INVALID_TRANSACTION_VALUE).errors]
|
|
83
|
+
.map((detail) => ({ ...detail, ...context }))), aggregatorId };
|
|
84
|
+
}
|
|
85
|
+
const check = validate(txCalldata(tx), value, expectation, chainId);
|
|
86
|
+
const errors = [...inputErrors, ...check.errors].map((error) => sanitizeBridgeError({ ...error, ...context }));
|
|
87
|
+
return { ...check, ...(errors.length ? bridgeFailure(errors) : {}), errors };
|
|
88
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { asBigint, asRecord, asString, decodeCall } from '../../abi.js';
|
|
2
|
+
import { accountFromBytes32 } from '../../hex.js';
|
|
3
|
+
import { acrossPackedParam } from '../params.js';
|
|
4
|
+
export const startBridgeTokensViaAcrossV4ERC20Min = {
|
|
5
|
+
name: 'startBridgeTokensViaAcrossV4ERC20Min',
|
|
6
|
+
selector: '0x7260352d',
|
|
7
|
+
decode(data) {
|
|
8
|
+
const args = decodeCall([
|
|
9
|
+
acrossPackedParam,
|
|
10
|
+
{ name: 'sendingAssetId', kind: 'bytes', size: 32 },
|
|
11
|
+
{ name: 'inputAmount', kind: 'uint' },
|
|
12
|
+
], data);
|
|
13
|
+
const parameters = asRecord(args.parameters);
|
|
14
|
+
return {
|
|
15
|
+
aggregatorId: 'lifi',
|
|
16
|
+
method: 'startBridgeTokensViaAcrossV4ERC20Min',
|
|
17
|
+
kind: 'bridge',
|
|
18
|
+
sourceToken: accountFromBytes32(asString(args.sendingAssetId)),
|
|
19
|
+
sourceAmount: asBigint(args.inputAmount),
|
|
20
|
+
destinationChainId: asBigint(parameters.destinationChainId),
|
|
21
|
+
receiver: accountFromBytes32(asString(parameters.receiver)),
|
|
22
|
+
refundRecipients: [accountFromBytes32(asString(parameters.depositor))],
|
|
23
|
+
outputToken: accountFromBytes32(asString(parameters.receivingAssetId)),
|
|
24
|
+
minOutputAmount: asBigint(parameters.outputAmount),
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { asBigint, asRecord, asString, decodeCall } from '../../abi.js';
|
|
2
|
+
import { accountFromBytes32 } from '../../hex.js';
|
|
3
|
+
import { acrossPackedParam } from '../params.js';
|
|
4
|
+
export const startBridgeTokensViaAcrossV4NativeMin = {
|
|
5
|
+
name: 'startBridgeTokensViaAcrossV4NativeMin',
|
|
6
|
+
selector: '0x72dd147e',
|
|
7
|
+
decode(data, value) {
|
|
8
|
+
const args = decodeCall([acrossPackedParam], data);
|
|
9
|
+
const parameters = asRecord(args.parameters);
|
|
10
|
+
return {
|
|
11
|
+
aggregatorId: 'lifi',
|
|
12
|
+
method: 'startBridgeTokensViaAcrossV4NativeMin',
|
|
13
|
+
kind: 'bridge',
|
|
14
|
+
sourceToken: '0x0000000000000000000000000000000000000000',
|
|
15
|
+
sourceAmount: value,
|
|
16
|
+
destinationChainId: asBigint(parameters.destinationChainId),
|
|
17
|
+
receiver: accountFromBytes32(asString(parameters.receiver)),
|
|
18
|
+
refundRecipients: [accountFromBytes32(asString(parameters.depositor))],
|
|
19
|
+
outputToken: accountFromBytes32(asString(parameters.receivingAssetId)),
|
|
20
|
+
minOutputAmount: asBigint(parameters.outputAmount),
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CalldataMethod } from '../../abi.js';
|
|
2
|
+
import type { CompareMask } from '../../expect.js';
|
|
3
|
+
import type { ParsedBridgeCall } from '../../types.js';
|
|
4
|
+
export declare const methods: CalldataMethod[];
|
|
5
|
+
export declare const compare: (parsed: ParsedBridgeCall) => CompareMask;
|
|
6
|
+
export declare const check: (parsed: ParsedBridgeCall) => import("../../../index.js").BridgeErrorDefinition | undefined;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { bridgeShape } from '../shape.js';
|
|
2
|
+
import { startBridgeTokensViaAcrossV4ERC20Min } from './acrossV4Erc20Min.js';
|
|
3
|
+
import { startBridgeTokensViaAcrossV4NativeMin } from './acrossV4NativeMin.js';
|
|
4
|
+
import { acrossDataParam, applyAcrossData } from '../params.js';
|
|
5
|
+
import { lifiBridgeMethod } from '../method.js';
|
|
6
|
+
export const methods = [
|
|
7
|
+
lifiBridgeMethod('startBridgeTokensViaAcrossV4', '0xa1f1ce43', [acrossDataParam], false, (parsed, args) => applyAcrossData(parsed, args.acrossData)),
|
|
8
|
+
lifiBridgeMethod('swapAndStartBridgeTokensViaAcrossV4', '0x1794958f', [acrossDataParam], true, (parsed, args) => applyAcrossData(parsed, args.acrossData)),
|
|
9
|
+
startBridgeTokensViaAcrossV4ERC20Min,
|
|
10
|
+
startBridgeTokensViaAcrossV4NativeMin,
|
|
11
|
+
];
|
|
12
|
+
export const compare = (parsed) => ({
|
|
13
|
+
fromToken: parsed.sourceToken !== undefined,
|
|
14
|
+
fromAmount: parsed.sourceAmount !== undefined,
|
|
15
|
+
toToken: true,
|
|
16
|
+
toAmount: true,
|
|
17
|
+
receiver: true,
|
|
18
|
+
});
|
|
19
|
+
export const check = bridgeShape;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const methods: import("../../abi.js").CalldataMethod[];
|
|
2
|
+
export declare const compare: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../expect.js").CompareMask;
|
|
3
|
+
export declare const check: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../../errors/constants.js").BridgeErrorDefinition | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BRIDGE_ERRORS, BridgeError } from '../../../errors/index.js';
|
|
2
|
+
import { asRecord, asString } from '../../abi.js';
|
|
3
|
+
import { accountFromBytes32 } from '../../hex.js';
|
|
4
|
+
import { knownCompare } from '../../expect.js';
|
|
5
|
+
import { lifiBridgeMethod } from '../method.js';
|
|
6
|
+
import { bridgeShape } from '../shape.js';
|
|
7
|
+
const allBridgeData = { name: 'allBridgeData', kind: 'tuple', components: [
|
|
8
|
+
{ name: 'recipient', kind: 'bytes', size: 32 }, { name: 'fees', kind: 'uint' },
|
|
9
|
+
{ name: 'receiveToken', kind: 'bytes', size: 32 }, { name: 'nonce', kind: 'uint' },
|
|
10
|
+
{ name: 'messenger', kind: 'uint' }, { name: 'payFeeWithSendingAsset', kind: 'bool' },
|
|
11
|
+
] };
|
|
12
|
+
const fill = (parsed, args) => {
|
|
13
|
+
const data = asRecord(args.allBridgeData);
|
|
14
|
+
const receiver = accountFromBytes32(asString(data.recipient));
|
|
15
|
+
if (receiver !== parsed.receiver)
|
|
16
|
+
throw new BridgeError(BRIDGE_ERRORS.ALLBRIDGE_RECEIVER_MISMATCH);
|
|
17
|
+
parsed.receiver = receiver;
|
|
18
|
+
parsed.outputToken = accountFromBytes32(asString(data.receiveToken));
|
|
19
|
+
// The destination pool prices vUSD when redeeming; there is no signed output minimum.
|
|
20
|
+
};
|
|
21
|
+
export const methods = [
|
|
22
|
+
lifiBridgeMethod('startBridgeTokensViaAllBridge', '0x6a51e9a9', [allBridgeData], false, fill),
|
|
23
|
+
lifiBridgeMethod('swapAndStartBridgeTokensViaAllBridge', '0x63267469', [allBridgeData], true, fill),
|
|
24
|
+
];
|
|
25
|
+
export const compare = knownCompare;
|
|
26
|
+
export const check = bridgeShape;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CalldataMethod } from '../../abi.js';
|
|
2
|
+
export declare const methods: CalldataMethod[];
|
|
3
|
+
export declare const compare: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../expect.js").CompareMask;
|
|
4
|
+
export declare const check: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../../index.js").BridgeErrorDefinition | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { knownCompare } from '../../expect.js';
|
|
2
|
+
import { bridgeShape } from '../shape.js';
|
|
3
|
+
import { lifiBridgeMethod } from '../method.js';
|
|
4
|
+
import { applyCelerCircleData, celerCircleDataParam } from '../params.js';
|
|
5
|
+
export const methods = [
|
|
6
|
+
lifiBridgeMethod('startBridgeTokensViaCelerCircleBridge', '0xe0a4201c', [celerCircleDataParam], false, (parsed, args) => applyCelerCircleData(parsed, args.celerCircleData)),
|
|
7
|
+
lifiBridgeMethod('swapAndStartBridgeTokensViaCelerCircleBridge', '0x4f3b0759', [celerCircleDataParam], true, (parsed, args) => applyCelerCircleData(parsed, args.celerCircleData)),
|
|
8
|
+
];
|
|
9
|
+
export const compare = knownCompare;
|
|
10
|
+
export const check = bridgeShape;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const methods: import("../../abi.js").CalldataMethod[];
|
|
2
|
+
export declare const compare: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../expect.js").CompareMask;
|
|
3
|
+
export declare const check: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../../errors/constants.js").BridgeErrorDefinition | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BRIDGE_ERRORS, BridgeError } from '../../../errors/index.js';
|
|
2
|
+
import { asRecord, asString } from '../../abi.js';
|
|
3
|
+
import { knownCompare } from '../../expect.js';
|
|
4
|
+
import { lifiBridgeMethod } from '../method.js';
|
|
5
|
+
import { bridgeShape } from '../shape.js';
|
|
6
|
+
const centrifugeData = { name: 'centrifugeData', kind: 'tuple', components: [
|
|
7
|
+
{ name: 'nativeFee', kind: 'uint' }, { name: 'refundRecipient', kind: 'address' },
|
|
8
|
+
] };
|
|
9
|
+
const fill = (parsed, args) => {
|
|
10
|
+
if (asString(asRecord(args.centrifugeData).refundRecipient) !== parsed.receiver) {
|
|
11
|
+
throw new BridgeError(BRIDGE_ERRORS.CENTRIFUGE_REFUND_RECIPIENT_MISMATCH);
|
|
12
|
+
}
|
|
13
|
+
parsed.minOutputAmount = parsed.bridgeAmount;
|
|
14
|
+
parsed.outputTokenReference = { protocol: 'centrifuge', token: parsed.bridgeToken,
|
|
15
|
+
destinationChainId: parsed.destinationChainId };
|
|
16
|
+
};
|
|
17
|
+
export const methods = [
|
|
18
|
+
lifiBridgeMethod('startBridgeTokensViaCentrifuge', '0x0315138f', [centrifugeData], false, fill),
|
|
19
|
+
lifiBridgeMethod('swapAndStartBridgeTokensViaCentrifuge', '0x8fd4d4dd', [centrifugeData], true, fill),
|
|
20
|
+
];
|
|
21
|
+
export const compare = knownCompare;
|
|
22
|
+
export const check = bridgeShape;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { asArray, asBigint, asRecord, asString, decodeCall } from '../abi.js';
|
|
2
|
+
import { accountFromBytes32, readSelector } from '../hex.js';
|
|
3
|
+
import { ALLOWANCE_HOLDER, readSettlerSwap } from '../destinationSwap.js';
|
|
4
|
+
import { isNative } from '../protocols.js';
|
|
5
|
+
// Base ReceiverOIF constructor binds these two contracts. No RPC or mutable
|
|
6
|
+
// caller-supplied identity is used; see evidence/base-lifi-oif-verification.json.
|
|
7
|
+
const BASE_OIF = '0xe6ee2a573047598b2452a879f2188b31d0442dbf';
|
|
8
|
+
const BASE_EXECUTOR = '0x4dac9d1769b9b304cb04741dcdeb2fc14abdf110';
|
|
9
|
+
const BASE_OUTPUT_SETTLER = '0x75220b7600c300005038432a0000f308e0000068';
|
|
10
|
+
export const readLifiDestination = (parsed, data) => {
|
|
11
|
+
const receiver = accountFromBytes32(asString(data.recipient));
|
|
12
|
+
if (parsed.destinationChainId !== 8453n ||
|
|
13
|
+
accountFromBytes32(asString(data.dstCallReceiver)) !== BASE_OIF ||
|
|
14
|
+
accountFromBytes32(asString(data.outputSettler)) !== BASE_OUTPUT_SETTLER ||
|
|
15
|
+
asString(data.depositAndRefundAddress) !== receiver)
|
|
16
|
+
return undefined;
|
|
17
|
+
const swaps = asArray(data.dstCallSwapData);
|
|
18
|
+
// No generic call is trusted merely because ReceiverOIF/Executor will forward it.
|
|
19
|
+
if (swaps.length !== 1 || !parsed.outputToken || parsed.minOutputAmount === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
const swap = asRecord(swaps[0]);
|
|
22
|
+
const input = asString(swap.sendingAssetId), amount = asBigint(swap.fromAmount);
|
|
23
|
+
const output = asString(swap.receivingAssetId), callTo = asString(swap.callTo), approval = asString(swap.approveTo);
|
|
24
|
+
if (input !== parsed.outputToken || isNative(input) || amount <= 0n || amount > parsed.minOutputAmount)
|
|
25
|
+
return undefined;
|
|
26
|
+
const callData = asString(swap.callData);
|
|
27
|
+
if (callTo === input && approval === BASE_EXECUTOR && output === input && readSelector(callData) === '0xa9059cbb') {
|
|
28
|
+
const transfer = decodeCall([{ name: 'to', kind: 'address' }, { name: 'amount', kind: 'uint' }], callData);
|
|
29
|
+
if (asString(transfer.to) !== receiver || asBigint(transfer.amount) !== amount)
|
|
30
|
+
return undefined;
|
|
31
|
+
// Executor sends any remaining input to the same receiver after this transfer.
|
|
32
|
+
return { receiver, outputToken: input, minOutputAmount: parsed.minOutputAmount };
|
|
33
|
+
}
|
|
34
|
+
if (callTo !== ALLOWANCE_HOLDER || approval !== ALLOWANCE_HOLDER)
|
|
35
|
+
return undefined;
|
|
36
|
+
const result = readSettlerSwap(callData, parsed.destinationChainId, BASE_EXECUTOR);
|
|
37
|
+
if (!result || result.inputToken !== input || result.inputAmount !== amount || result.outputToken !== output)
|
|
38
|
+
return undefined;
|
|
39
|
+
return { receiver, outputToken: output, minOutputAmount: result.minOutputAmount };
|
|
40
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ParsedBridgeCall } from '../../types.js';
|
|
2
|
+
export declare const methods: import("../../abi.js").CalldataMethod[];
|
|
3
|
+
export declare const compare: (parsed: ParsedBridgeCall) => import("../../expect.js").CompareMask;
|
|
4
|
+
export declare const check: (parsed: ParsedBridgeCall) => import("../../../errors/constants.js").BridgeErrorDefinition | undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BRIDGE_ERRORS, BridgeError } from '../../../errors/index.js';
|
|
2
|
+
import { asBigint, asRecord, asString } from '../../abi.js';
|
|
3
|
+
import { knownCompare } from '../../expect.js';
|
|
4
|
+
import { LAYERZERO_CHAINS } from '../../protocols.js';
|
|
5
|
+
import { lifiBridgeMethod } from '../method.js';
|
|
6
|
+
import { bridgeShape } from '../shape.js';
|
|
7
|
+
const fraxData = { name: 'fraxData', kind: 'tuple', components: [
|
|
8
|
+
{ name: 'oft', kind: 'address' }, { name: 'dstEid', kind: 'uint' },
|
|
9
|
+
{ name: 'nativeFee', kind: 'uint' }, { name: 'refundRecipient', kind: 'address' },
|
|
10
|
+
{ name: 'nonEVMReceiver', kind: 'bytes', size: 32 },
|
|
11
|
+
] };
|
|
12
|
+
const fill = (parsed, args) => {
|
|
13
|
+
const data = asRecord(args.fraxData);
|
|
14
|
+
const eid = asBigint(data.dstEid);
|
|
15
|
+
parsed.destinationChainRef = `layerzero:${eid}`;
|
|
16
|
+
parsed.destinationChainId = LAYERZERO_CHAINS[eid.toString()];
|
|
17
|
+
if (!/^0x0*$/.test(asString(data.nonEVMReceiver)))
|
|
18
|
+
throw new BridgeError(BRIDGE_ERRORS.UNSUPPORTED_NON_EVM_FRAX_RECEIVER);
|
|
19
|
+
const token = { protocol: 'frax', contract: asString(data.oft), token: parsed.bridgeToken,
|
|
20
|
+
destinationChainId: parsed.destinationChainId, destinationChainRef: parsed.destinationChainRef };
|
|
21
|
+
parsed.outputTokenReference = token;
|
|
22
|
+
parsed.outputAmountReference = { kind: 'dust', amount: parsed.bridgeAmount, token };
|
|
23
|
+
};
|
|
24
|
+
export const methods = [
|
|
25
|
+
lifiBridgeMethod('startBridgeTokensViaFrax', '0x678786ea', [fraxData], false, fill),
|
|
26
|
+
lifiBridgeMethod('swapAndStartBridgeTokensViaFrax', '0x07b8e4c7', [fraxData], true, fill),
|
|
27
|
+
];
|
|
28
|
+
export const compare = knownCompare;
|
|
29
|
+
export const check = bridgeShape;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const methods: import("../../abi.js").CalldataMethod[];
|
|
2
|
+
export declare const compare: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../expect.js").CompareMask;
|
|
3
|
+
export declare const check: (parsed: import("../../types.js").ParsedBridgeCall) => import("../../../index.js").BridgeErrorDefinition | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { asRecord, asString } from '../../abi.js';
|
|
2
|
+
import { knownCompare } from '../../expect.js';
|
|
3
|
+
import { lifiBridgeMethod } from '../method.js';
|
|
4
|
+
import { bridgeShape } from '../shape.js';
|
|
5
|
+
const gardenData = { name: 'gardenData', kind: 'tuple', components: [
|
|
6
|
+
{ name: 'redeemer', kind: 'address' }, { name: 'refundAddress', kind: 'address' },
|
|
7
|
+
{ name: 'timelock', kind: 'uint' }, { name: 'secretHash', kind: 'bytes', size: 32 },
|
|
8
|
+
{ name: 'nonEvmReceiver', kind: 'bytes', size: 32 },
|
|
9
|
+
] };
|
|
10
|
+
const fill = (parsed, args) => {
|
|
11
|
+
parsed.requestId = asString(asRecord(args.gardenData).secretHash);
|
|
12
|
+
// Garden locks only the source HTLC; BridgeData's destination fields are event metadata.
|
|
13
|
+
delete parsed.receiver;
|
|
14
|
+
delete parsed.destinationChainId;
|
|
15
|
+
parsed.destinationChainRef = 'garden:offchain';
|
|
16
|
+
};
|
|
17
|
+
export const methods = [
|
|
18
|
+
lifiBridgeMethod('startBridgeTokensViaGarden', '0x5f9af35d', [gardenData], false, fill),
|
|
19
|
+
lifiBridgeMethod('swapAndStartBridgeTokensViaGarden', '0x76ad76fe', [gardenData], true, fill),
|
|
20
|
+
];
|
|
21
|
+
export const compare = knownCompare;
|
|
22
|
+
export const check = bridgeShape;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CalldataMethod } from '../../abi.js';
|
|
2
|
+
import type { CompareMask } from '../../expect.js';
|
|
3
|
+
import type { ParsedBridgeCall } from '../../types.js';
|
|
4
|
+
export declare const methods: CalldataMethod[];
|
|
5
|
+
export declare const compare: (parsed: ParsedBridgeCall) => CompareMask;
|
|
6
|
+
export declare const check: (parsed: ParsedBridgeCall) => import("../../../index.js").BridgeErrorDefinition | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { bridgeShape } from '../shape.js';
|
|
2
|
+
import { applyGasZipData, gasZipDataParam } from '../params.js';
|
|
3
|
+
import { lifiBridgeMethod } from '../method.js';
|
|
4
|
+
export const methods = [
|
|
5
|
+
lifiBridgeMethod('startBridgeTokensViaGasZip', '0xfc5f1003', [gasZipDataParam], false, (parsed, args) => applyGasZipData(parsed, args.gasZipData)),
|
|
6
|
+
lifiBridgeMethod('swapAndStartBridgeTokensViaGasZip', '0x606326ff', [gasZipDataParam], true, (parsed, args) => applyGasZipData(parsed, args.gasZipData)),
|
|
7
|
+
];
|
|
8
|
+
export const compare = (parsed) => ({
|
|
9
|
+
fromToken: parsed.sourceToken !== undefined,
|
|
10
|
+
fromAmount: parsed.sourceAmount !== undefined,
|
|
11
|
+
toToken: false,
|
|
12
|
+
toAmount: false,
|
|
13
|
+
receiver: true,
|
|
14
|
+
});
|
|
15
|
+
export const check = bridgeShape;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BridgeCallExpectation } from '../expect.js';
|
|
2
|
+
import type { BridgeCheck, DecodeHit } from '../types.js';
|
|
3
|
+
export declare const decodeLifi: (data: string, value: bigint) => DecodeHit;
|
|
4
|
+
export declare const validateLifi: (data: string, value: bigint, expectation?: BridgeCallExpectation, chainId?: string) => BridgeCheck;
|