@rabby-wallet/rabby-swap 0.0.88-alpha.0 → 0.0.88-alpha.2
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/dist/abi.d.ts +2 -26
- package/dist/abi.js +2 -50
- package/dist/calldata/chain.d.ts +1 -0
- package/dist/calldata/chain.js +9 -0
- package/dist/calldata/magpie/abi.d.ts +1 -0
- package/dist/calldata/magpie/abi.js +3 -0
- package/dist/calldata/magpie/dexAggregator.d.ts +3 -0
- package/dist/calldata/magpie/dexAggregator.js +30 -0
- package/dist/calldata/magpie/index.d.ts +3 -0
- package/dist/calldata/magpie/index.js +65 -0
- package/dist/calldata/magpie/packed.d.ts +16 -0
- package/dist/calldata/magpie/packed.js +30 -0
- package/dist/calldata/types.d.ts +22 -0
- package/dist/calldata/types.js +1 -0
- package/dist/calldata/uni/abi.d.ts +3 -0
- package/dist/calldata/uni/abi.js +6 -0
- package/dist/calldata/uni/assets.d.ts +5 -0
- package/dist/calldata/uni/assets.js +65 -0
- package/dist/calldata/uni/index.d.ts +2 -0
- package/dist/calldata/uni/index.js +40 -0
- package/dist/calldata/uni/shared.d.ts +45 -0
- package/dist/calldata/uni/shared.js +222 -0
- package/dist/calldata/uni/universalRouter/commands.d.ts +3 -0
- package/dist/calldata/uni/universalRouter/commands.js +92 -0
- package/dist/calldata/uni/universalRouter/profiles.d.ts +6 -0
- package/dist/calldata/uni/universalRouter/profiles.js +27 -0
- package/dist/calldata/uni/universalRouter/sdk.d.ts +11 -0
- package/dist/calldata/uni/universalRouter/sdk.js +21 -0
- package/dist/calldata/uni/universalRouter/v2.d.ts +1 -0
- package/dist/calldata/uni/universalRouter/v2.js +18 -0
- package/dist/calldata/uni/universalRouter/v4/index.d.ts +3 -0
- package/dist/calldata/uni/universalRouter/v4/index.js +139 -0
- package/dist/consts.d.ts +2 -1
- package/dist/consts.js +1 -0
- package/dist/dexs/1inch.js +5 -0
- package/dist/dexs/magpie.d.ts +1 -2
- package/dist/dexs/magpie.js +3 -57
- package/dist/dexs/pancake.d.ts +5 -0
- package/dist/dexs/pancake.js +17 -0
- package/dist/dexs/uni.d.ts +1 -2
- package/dist/dexs/uni.js +3 -301
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/list.d.ts +15 -0
- package/dist/list.js +24 -0
- package/dist/quote.d.ts +3 -20
- package/dist/quote.js +8 -3
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/dist/verify/calldata.d.ts +9 -13
- package/dist/verify/calldata.js +44 -22
- package/dist/verify/sdk.d.ts +2 -1
- package/dist/verify/sdk.js +4 -1
- package/package.json +7 -2
package/dist/abi.d.ts
CHANGED
|
@@ -335,29 +335,5 @@ export declare const SushiABI: ({
|
|
|
335
335
|
stateMutability: string;
|
|
336
336
|
type: string;
|
|
337
337
|
})[];
|
|
338
|
-
export
|
|
339
|
-
|
|
340
|
-
internalType: string;
|
|
341
|
-
name: string;
|
|
342
|
-
type: string;
|
|
343
|
-
}[];
|
|
344
|
-
name: string;
|
|
345
|
-
outputs: never[];
|
|
346
|
-
stateMutability: string;
|
|
347
|
-
type: string;
|
|
348
|
-
}[];
|
|
349
|
-
export declare const MagpieABI: {
|
|
350
|
-
inputs: {
|
|
351
|
-
internalType: string;
|
|
352
|
-
name: string;
|
|
353
|
-
type: string;
|
|
354
|
-
}[];
|
|
355
|
-
name: string;
|
|
356
|
-
outputs: {
|
|
357
|
-
internalType: string;
|
|
358
|
-
name: string;
|
|
359
|
-
type: string;
|
|
360
|
-
}[];
|
|
361
|
-
stateMutability: string;
|
|
362
|
-
type: string;
|
|
363
|
-
}[];
|
|
338
|
+
export { UniABI } from "./calldata/uni/abi";
|
|
339
|
+
export { MagpieABI } from "./calldata/magpie/abi";
|
package/dist/abi.js
CHANGED
|
@@ -1652,53 +1652,5 @@ export const ZeroXABIV2 = [
|
|
|
1652
1652
|
{ stateMutability: "payable", type: "fallback" },
|
|
1653
1653
|
];
|
|
1654
1654
|
export const SushiABI = [{ "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [{ "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint256", "name": "amountOut", "type": "uint256" }], "name": "MinimalOutputBalanceViolation", "type": "error" }, { "inputs": [], "name": "safeExecutor", "outputs": [{ "internalType": "contract SafeExecutor", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "contract IERC20", "name": "tokenIn", "type": "address" }, { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "contract IERC20", "name": "tokenOut", "type": "address" }, { "internalType": "uint256", "name": "amountOutMin", "type": "uint256" }, { "internalType": "address", "name": "executor", "type": "address" }, { "internalType": "bytes", "name": "executorData", "type": "bytes" }], "name": "snwap", "outputs": [{ "internalType": "uint256", "name": "amountOut", "type": "uint256" }], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "contract IERC20", "name": "token", "type": "address" }, { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, { "internalType": "address", "name": "transferTo", "type": "address" }], "internalType": "struct InputToken[]", "name": "inputTokens", "type": "tuple[]" }, { "components": [{ "internalType": "contract IERC20", "name": "token", "type": "address" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amountOutMin", "type": "uint256" }], "internalType": "struct OutputToken[]", "name": "outputTokens", "type": "tuple[]" }, { "components": [{ "internalType": "address", "name": "executor", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bytes", "name": "data", "type": "bytes" }], "internalType": "struct Executor[]", "name": "executors", "type": "tuple[]" }], "name": "snwapMultiple", "outputs": [{ "internalType": "uint256[]", "name": "amountOut", "type": "uint256[]" }], "stateMutability": "payable", "type": "function" }];
|
|
1655
|
-
export
|
|
1656
|
-
|
|
1657
|
-
inputs: [
|
|
1658
|
-
{ internalType: "bytes", name: "commands", type: "bytes" },
|
|
1659
|
-
{ internalType: "bytes[]", name: "inputs", type: "bytes[]" },
|
|
1660
|
-
],
|
|
1661
|
-
name: "execute",
|
|
1662
|
-
outputs: [],
|
|
1663
|
-
stateMutability: "payable",
|
|
1664
|
-
type: "function",
|
|
1665
|
-
},
|
|
1666
|
-
{
|
|
1667
|
-
inputs: [
|
|
1668
|
-
{ internalType: "bytes", name: "commands", type: "bytes" },
|
|
1669
|
-
{ internalType: "bytes[]", name: "inputs", type: "bytes[]" },
|
|
1670
|
-
{ internalType: "uint256", name: "deadline", type: "uint256" },
|
|
1671
|
-
],
|
|
1672
|
-
name: "execute",
|
|
1673
|
-
outputs: [],
|
|
1674
|
-
stateMutability: "payable",
|
|
1675
|
-
type: "function",
|
|
1676
|
-
},
|
|
1677
|
-
{
|
|
1678
|
-
inputs: [
|
|
1679
|
-
{
|
|
1680
|
-
internalType: "contract IUniversalRouter",
|
|
1681
|
-
name: "router",
|
|
1682
|
-
type: "address",
|
|
1683
|
-
},
|
|
1684
|
-
{ internalType: "address", name: "token", type: "address" },
|
|
1685
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
1686
|
-
{ internalType: "bytes", name: "commands", type: "bytes" },
|
|
1687
|
-
{ internalType: "bytes[]", name: "inputs", type: "bytes[]" },
|
|
1688
|
-
{ internalType: "uint256", name: "deadline", type: "uint256" },
|
|
1689
|
-
],
|
|
1690
|
-
name: "execute",
|
|
1691
|
-
outputs: [],
|
|
1692
|
-
stateMutability: "nonpayable",
|
|
1693
|
-
type: "function",
|
|
1694
|
-
},
|
|
1695
|
-
];
|
|
1696
|
-
export const MagpieABI = [
|
|
1697
|
-
{
|
|
1698
|
-
inputs: [{ internalType: "bytes", name: "swapData", type: "bytes" }],
|
|
1699
|
-
name: "swapWithMagpieSignature",
|
|
1700
|
-
outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }],
|
|
1701
|
-
stateMutability: "payable",
|
|
1702
|
-
type: "function",
|
|
1703
|
-
},
|
|
1704
|
-
];
|
|
1655
|
+
export { UniABI } from "./calldata/uni/abi";
|
|
1656
|
+
export { MagpieABI } from "./calldata/magpie/abi";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getNativeTokenAddress: (chainId: number, nativeTokenAddress?: string) => string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CHAINS } from "@debank/common";
|
|
2
|
+
export const getNativeTokenAddress = (chainId, nativeTokenAddress) => {
|
|
3
|
+
var _a;
|
|
4
|
+
const native = nativeTokenAddress !== null && nativeTokenAddress !== void 0 ? nativeTokenAddress : (_a = Object.values(CHAINS).find((chain) => chain.id === chainId)) === null || _a === void 0 ? void 0 : _a.nativeTokenAddress;
|
|
5
|
+
if (!native) {
|
|
6
|
+
throw new Error(`Native token is not configured for chain ${chainId}`);
|
|
7
|
+
}
|
|
8
|
+
return native;
|
|
9
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MagpieABI: string[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
// DexAggregator LibRouter.getData(), verified Arbitrum source:
|
|
3
|
+
// https://sourcify.dev/server/v2/contract/42161/0x20F6ee51340aDEed01A59B0e65cB3703f3dc860c?fields=sources
|
|
4
|
+
export const decodeDexAggregator = (data) => ({
|
|
5
|
+
receiver: data.address(72),
|
|
6
|
+
fromAsset: data.address(92),
|
|
7
|
+
toAsset: data.address(112),
|
|
8
|
+
amountIn: data.uint(132),
|
|
9
|
+
amountOutMin: data.compact(167, 168),
|
|
10
|
+
});
|
|
11
|
+
// LibRouter.getData(): byte 274 holds the fee count, followed by three
|
|
12
|
+
// arrays of compact descriptors (asset, receiver, amount), 3 bytes per entry.
|
|
13
|
+
// transferFees() transfers these separately from the swap's amountIn.
|
|
14
|
+
// Only fees paid in the input asset belong in its total debit.
|
|
15
|
+
// https://sourcify.dev/server/v2/contract/42161/0x20F6ee51340aDEed01A59B0e65cB3703f3dc860c?fields=sources
|
|
16
|
+
// https://docs.fly.trade/developers/api-reference/evm-swap-integration-guide#step-1-get-a-quote
|
|
17
|
+
export const decodeDexAggregatorInputFee = (data) => {
|
|
18
|
+
const count = Number(data.uint(274, 1));
|
|
19
|
+
const inputAsset = new BigNumber(data.address(92).slice(2), 16);
|
|
20
|
+
let total = new BigNumber(0);
|
|
21
|
+
for (let index = 0; index < count; index++) {
|
|
22
|
+
const assetOffset = 275 + index * 3;
|
|
23
|
+
const amountOffset = 275 + (count * 2 + index) * 3;
|
|
24
|
+
const asset = data.compact(assetOffset, assetOffset + 1);
|
|
25
|
+
const amount = data.compact(amountOffset, amountOffset + 1);
|
|
26
|
+
if (inputAsset.eq(asset))
|
|
27
|
+
total = total.plus(amount);
|
|
28
|
+
}
|
|
29
|
+
return total.toFixed(0);
|
|
30
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DecodeCalldataOptions, DecodeCalldataResult, TxWithChainId } from "../types";
|
|
2
|
+
export declare const decodeInputFee: (tx: TxWithChainId, decoded?: DecodeCalldataResult) => string;
|
|
3
|
+
export declare const decodeCalldata: (tx: TxWithChainId, options?: DecodeCalldataOptions) => DecodeCalldataResult;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { utils } from "ethers";
|
|
2
|
+
import BigNumber from "bignumber.js";
|
|
3
|
+
import { getNativeTokenAddress } from "../chain";
|
|
4
|
+
import { isSameAddress, NULL_ADDRESS } from "../../utils";
|
|
5
|
+
import { MagpieABI } from "./abi";
|
|
6
|
+
import { decodeDexAggregator, decodeDexAggregatorInputFee, } from "./dexAggregator";
|
|
7
|
+
import { PackedReader } from "./packed";
|
|
8
|
+
const magpieInterface = new utils.Interface(MagpieABI);
|
|
9
|
+
const dexAggregatorSelector = magpieInterface.getSighash("swapWithBackendSignature");
|
|
10
|
+
// Keep parsing details private and tied to the decoded result's lifetime.
|
|
11
|
+
const decodedPayloads = new WeakMap();
|
|
12
|
+
const readPayload = (tx) => {
|
|
13
|
+
const selector = tx.data.slice(0, 10).toLowerCase();
|
|
14
|
+
if (selector !== dexAggregatorSelector) {
|
|
15
|
+
throw new Error(`Unsupported Magpie calldata selector: ${selector}`);
|
|
16
|
+
}
|
|
17
|
+
const transaction = magpieInterface.parseTransaction({ data: tx.data });
|
|
18
|
+
const payloadLength = utils.arrayify(transaction.args[0]).length;
|
|
19
|
+
return new PackedReader(tx.data, payloadLength);
|
|
20
|
+
};
|
|
21
|
+
export const decodeInputFee = (tx, decoded) => {
|
|
22
|
+
var _a;
|
|
23
|
+
const cached = decoded && decodedPayloads.get(decoded);
|
|
24
|
+
if (cached && cached.data === tx.data) {
|
|
25
|
+
return ((_a = cached.inputFee) !== null && _a !== void 0 ? _a : (cached.inputFee = decodeDexAggregatorInputFee(cached.payload)));
|
|
26
|
+
}
|
|
27
|
+
return decodeDexAggregatorInputFee(readPayload(tx));
|
|
28
|
+
};
|
|
29
|
+
export const decodeCalldata = (tx, options = {}) => {
|
|
30
|
+
const native = getNativeTokenAddress(tx.chainId, options.nativeTokenAddress);
|
|
31
|
+
const payload = readPayload(tx);
|
|
32
|
+
const swap = decodeDexAggregator(payload);
|
|
33
|
+
const nativeInput = isSameAddress(swap.fromAsset, NULL_ADDRESS);
|
|
34
|
+
let amountIn = new BigNumber(swap.amountIn);
|
|
35
|
+
// DexAggregator.adjustAmountIn(): zero means automatic input, not a zero
|
|
36
|
+
// debit. Native input is known from value minus encoded native fees;
|
|
37
|
+
// ERC20 input depends on min(balance, allowance), unavailable offline.
|
|
38
|
+
// https://sourcify.dev/server/v2/contract/42161/0x20F6ee51340aDEed01A59B0e65cB3703f3dc860c?fields=sources
|
|
39
|
+
const inputFee = nativeInput ? decodeDexAggregatorInputFee(payload) : "0";
|
|
40
|
+
if (amountIn.isZero()) {
|
|
41
|
+
if (!nativeInput)
|
|
42
|
+
throw new Error("Automatic ERC20 input depends on onchain balance and allowance; it cannot be determined from calldata alone");
|
|
43
|
+
amountIn = new BigNumber(tx.value).minus(inputFee);
|
|
44
|
+
}
|
|
45
|
+
if (!amountIn.gt(0))
|
|
46
|
+
throw new Error("Original input amount is zero or unresolved");
|
|
47
|
+
if (nativeInput && !amountIn.plus(inputFee).eq(tx.value))
|
|
48
|
+
throw new Error("Native transaction value does not equal input amount plus encoded native fees");
|
|
49
|
+
const decoded = {
|
|
50
|
+
fromToken: isSameAddress(swap.fromAsset, NULL_ADDRESS)
|
|
51
|
+
? native
|
|
52
|
+
: swap.fromAsset,
|
|
53
|
+
fromTokenAmount: amountIn.toFixed(0),
|
|
54
|
+
toToken: isSameAddress(swap.toAsset, NULL_ADDRESS) ? native : swap.toAsset,
|
|
55
|
+
minReceiveToTokenAmount: swap.amountOutMin,
|
|
56
|
+
// LibRouter reads toAddress literally; zero is not a msg.sender sentinel.
|
|
57
|
+
toTokenReceiver: swap.receiver,
|
|
58
|
+
};
|
|
59
|
+
decodedPayloads.set(decoded, {
|
|
60
|
+
data: tx.data,
|
|
61
|
+
payload,
|
|
62
|
+
inputFee: nativeInput ? inputFee : undefined,
|
|
63
|
+
});
|
|
64
|
+
return decoded;
|
|
65
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface PackedSwap {
|
|
2
|
+
receiver: string;
|
|
3
|
+
fromAsset: string;
|
|
4
|
+
toAsset: string;
|
|
5
|
+
amountIn: string;
|
|
6
|
+
amountOutMin: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class PackedReader {
|
|
9
|
+
private readonly payloadLength;
|
|
10
|
+
private readonly hex;
|
|
11
|
+
constructor(data: string, payloadLength: number);
|
|
12
|
+
private read;
|
|
13
|
+
address(offset: number): string;
|
|
14
|
+
uint(offset: number, length?: number): string;
|
|
15
|
+
compact(shiftOffset: number, pointerOffset: number): string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
// Offsets include the selector. Reads must stay inside the declared bytes payload.
|
|
3
|
+
export class PackedReader {
|
|
4
|
+
constructor(data, payloadLength) {
|
|
5
|
+
this.payloadLength = payloadLength;
|
|
6
|
+
this.hex = data.slice(2);
|
|
7
|
+
}
|
|
8
|
+
read(offset, length) {
|
|
9
|
+
if (offset < 68 || offset + length > 68 + this.payloadLength) {
|
|
10
|
+
throw new Error(`Packed field points outside the Magpie bytes payload (offset ${offset}, length ${length})`);
|
|
11
|
+
}
|
|
12
|
+
return this.hex.slice(offset * 2, (offset + length) * 2);
|
|
13
|
+
}
|
|
14
|
+
address(offset) {
|
|
15
|
+
return `0x${this.read(offset, 20)}`.toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
uint(offset, length = 32) {
|
|
18
|
+
return new BigNumber(this.read(offset, length), 16).toFixed(0);
|
|
19
|
+
}
|
|
20
|
+
compact(shiftOffset, pointerOffset) {
|
|
21
|
+
const shift = Number(this.uint(shiftOffset, 1));
|
|
22
|
+
const pointer = Number(this.uint(pointerOffset, 2));
|
|
23
|
+
// Only the high (256 - shift) bits survive Solidity's shr.
|
|
24
|
+
const significantBits = 256 - shift;
|
|
25
|
+
const length = Math.ceil(significantBits / 8);
|
|
26
|
+
return new BigNumber(this.uint(pointer, length))
|
|
27
|
+
.idiv(new BigNumber(2).pow(length * 8 - significantBits))
|
|
28
|
+
.toFixed(0);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface Tx {
|
|
2
|
+
from: string;
|
|
3
|
+
to: string;
|
|
4
|
+
data: string;
|
|
5
|
+
value: string;
|
|
6
|
+
chainId?: number;
|
|
7
|
+
gas?: number;
|
|
8
|
+
gasPrice?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TxWithChainId extends Tx {
|
|
11
|
+
chainId: number;
|
|
12
|
+
}
|
|
13
|
+
export interface DecodeCalldataOptions {
|
|
14
|
+
nativeTokenAddress?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface DecodeCalldataResult {
|
|
17
|
+
fromToken: string;
|
|
18
|
+
fromTokenAmount: string;
|
|
19
|
+
toToken: string;
|
|
20
|
+
minReceiveToTokenAmount: string;
|
|
21
|
+
toTokenReceiver: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SwapRouter } from "@uniswap/universal-router-sdk";
|
|
2
|
+
// The current quote flow uses execute directly or through SwapProxy.
|
|
3
|
+
// Keep the official ABI as the source of truth, excluding signed execution.
|
|
4
|
+
export const UniversalRouterABI = Object.values(SwapRouter.INTERFACE.functions).filter((fragment) => fragment.name === "execute");
|
|
5
|
+
export const SwapProxyABI = Object.values(SwapRouter.PROXY_INTERFACE.functions);
|
|
6
|
+
export const UniABI = [...UniversalRouterABI, ...SwapProxyABI];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DecodeCalldataResult } from "../types";
|
|
2
|
+
export declare const getArcNativeInputAmount: (token: string, chainId: number, txValue: string) => string | undefined;
|
|
3
|
+
export declare const isSameUniAsset: (a: string, b: string, chainId: number, native: string) => boolean;
|
|
4
|
+
export declare const normalizeUniAssets: (result: DecodeCalldataResult, chainId: number, native: string) => DecodeCalldataResult;
|
|
5
|
+
export declare const getComparableUniInputAmount: (requestedAmount: string, decoded: DecodeCalldataResult, chainId: number, native: string) => string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { isSameAddress } from "../../utils";
|
|
3
|
+
import { ARC_ERC20_USDC } from "../../consts";
|
|
4
|
+
const ARC_CHAIN_ID = 5042;
|
|
5
|
+
const ARC_NATIVE_SCALE = "1000000000000";
|
|
6
|
+
// Arc funds its ERC20 USDC route through msg.value without a WETH-style wrap.
|
|
7
|
+
// Fix the complete input before reading individual router-funded split legs,
|
|
8
|
+
// just as SwapProxy ingress / WRAP_ETH does on other routes. Keep ERC20 units
|
|
9
|
+
// until normalizeUniAssets converts the final result to native units.
|
|
10
|
+
// https://docs.arc.io/arc/concepts/stablecoin-native-model#two-interfaces-one-balance
|
|
11
|
+
export const getArcNativeInputAmount = (token, chainId, txValue) => chainId === ARC_CHAIN_ID &&
|
|
12
|
+
isSameAddress(token, ARC_ERC20_USDC) &&
|
|
13
|
+
new BigNumber(txValue).gt(0)
|
|
14
|
+
? new BigNumber(txValue).idiv(ARC_NATIVE_SCALE).toFixed(0)
|
|
15
|
+
: undefined;
|
|
16
|
+
// Compare asset identity for refunds only. Do not merge raw native (18) and
|
|
17
|
+
// ERC20 (6) amounts or balances: they share an asset, but use different units.
|
|
18
|
+
// https://docs.arc.io/arc/concepts/stablecoin-native-model#two-interfaces-one-balance
|
|
19
|
+
export const isSameUniAsset = (a, b, chainId, native) => {
|
|
20
|
+
const identity = (token) => chainId === ARC_CHAIN_ID && isSameAddress(token, ARC_ERC20_USDC)
|
|
21
|
+
? native
|
|
22
|
+
: token;
|
|
23
|
+
return isSameAddress(identity(a), identity(b));
|
|
24
|
+
};
|
|
25
|
+
// Arc exposes one USDC balance as native (18 decimals) and ERC20 (6 decimals).
|
|
26
|
+
// Normalize both the asset and its amount together, after route aggregation.
|
|
27
|
+
// https://docs.arc.io/arc/concepts/stablecoin-native-model#two-interfaces-one-balance
|
|
28
|
+
// Tempo pathUSD is a real 6-decimal TIP20; it must retain its contract address.
|
|
29
|
+
export const normalizeUniAssets = (result, chainId, native) => {
|
|
30
|
+
if (chainId !== ARC_CHAIN_ID || isSameAddress(native, ARC_ERC20_USDC))
|
|
31
|
+
return result;
|
|
32
|
+
const nativeIn = isSameAddress(result.fromToken, ARC_ERC20_USDC);
|
|
33
|
+
const nativeOut = isSameAddress(result.toToken, ARC_ERC20_USDC);
|
|
34
|
+
return Object.assign(Object.assign(Object.assign({}, result), (nativeIn
|
|
35
|
+
? {
|
|
36
|
+
fromToken: native,
|
|
37
|
+
fromTokenAmount: new BigNumber(result.fromTokenAmount)
|
|
38
|
+
.times(ARC_NATIVE_SCALE)
|
|
39
|
+
.toFixed(),
|
|
40
|
+
}
|
|
41
|
+
: {})), (nativeOut
|
|
42
|
+
? {
|
|
43
|
+
toToken: native,
|
|
44
|
+
minReceiveToTokenAmount: new BigNumber(result.minReceiveToTokenAmount)
|
|
45
|
+
.times(ARC_NATIVE_SCALE)
|
|
46
|
+
.toFixed(),
|
|
47
|
+
}
|
|
48
|
+
: {}));
|
|
49
|
+
};
|
|
50
|
+
export const getComparableUniInputAmount = (requestedAmount, decoded, chainId, native) => {
|
|
51
|
+
// Keep exact native-value inputs intact. Only Arc's native USDC can also
|
|
52
|
+
// travel through its 6-decimal ERC20 interface, which truncates 12 digits.
|
|
53
|
+
// Compare against that exact integer conversion, without a general tolerance.
|
|
54
|
+
// https://www.arc.io/blog/arc-compatibility-guide-for-existing-evm-apps
|
|
55
|
+
if (decoded.fromTokenAmount !== requestedAmount &&
|
|
56
|
+
chainId === ARC_CHAIN_ID &&
|
|
57
|
+
isSameAddress(decoded.fromToken, native) &&
|
|
58
|
+
!isSameAddress(native, ARC_ERC20_USDC)) {
|
|
59
|
+
return new BigNumber(requestedAmount)
|
|
60
|
+
.idiv(ARC_NATIVE_SCALE)
|
|
61
|
+
.times(ARC_NATIVE_SCALE)
|
|
62
|
+
.toFixed(0);
|
|
63
|
+
}
|
|
64
|
+
return requestedAmount;
|
|
65
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { utils } from "ethers";
|
|
2
|
+
import BigNumber from "bignumber.js";
|
|
3
|
+
import { getNativeTokenAddress } from "../chain";
|
|
4
|
+
import { UniABI } from "./abi";
|
|
5
|
+
import { chainToken, finishSwap } from "./shared";
|
|
6
|
+
import { walkCommands } from "./universalRouter/commands";
|
|
7
|
+
import { getRouterProfile } from "./universalRouter/profiles";
|
|
8
|
+
import { normalizeUniAssets } from "./assets";
|
|
9
|
+
const uniInterface = new utils.Interface(UniABI);
|
|
10
|
+
export const decodeCalldata = (tx, options = {}) => {
|
|
11
|
+
var _a;
|
|
12
|
+
const native = getNativeTokenAddress(tx.chainId, options.nativeTokenAddress);
|
|
13
|
+
const transaction = uniInterface.parseTransaction({ data: tx.data });
|
|
14
|
+
const ctx = {
|
|
15
|
+
chainId: tx.chainId,
|
|
16
|
+
native,
|
|
17
|
+
sender: tx.from,
|
|
18
|
+
// SwapProxy calls execute normally, so the Router's msgSender() is the
|
|
19
|
+
// proxy, while tx.from remains the user whose requested payout we verify.
|
|
20
|
+
// https://github.com/Uniswap/universal-router/blob/main/contracts/SwapProxy.sol
|
|
21
|
+
routerCaller: transaction.args.router ? tx.to : tx.from,
|
|
22
|
+
// SwapProxy passes the actual Universal Router as an explicit argument.
|
|
23
|
+
router: transaction.args.router || tx.to,
|
|
24
|
+
proxyToken: transaction.args.token,
|
|
25
|
+
proxyAmount: (_a = transaction.args.amount) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
26
|
+
txValue: tx.value,
|
|
27
|
+
};
|
|
28
|
+
const profile = getRouterProfile(ctx.chainId, ctx.router);
|
|
29
|
+
const swap = {};
|
|
30
|
+
if (new BigNumber(ctx.txValue).gt(0))
|
|
31
|
+
swap.pendingMinimums = { [ctx.native.toLowerCase()]: ctx.txValue };
|
|
32
|
+
if (ctx.proxyToken && ctx.proxyAmount && ctx.proxyAmount !== "0") {
|
|
33
|
+
swap.fromToken = ctx.proxyToken;
|
|
34
|
+
swap.fromTokenAmount = ctx.proxyAmount;
|
|
35
|
+
swap.inputAmountLocked = true;
|
|
36
|
+
(swap.pendingMinimums || (swap.pendingMinimums = {}))[chainToken(ctx.proxyToken, ctx.native).toLowerCase()] = ctx.proxyAmount;
|
|
37
|
+
}
|
|
38
|
+
walkCommands(transaction.args.commands, transaction.args.inputs, swap, ctx, profile);
|
|
39
|
+
return normalizeUniAssets(finishSwap(swap), tx.chainId, native);
|
|
40
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import type { DecodeCalldataResult } from "../types";
|
|
3
|
+
export declare const MSG_SENDER = "0x0000000000000000000000000000000000000001";
|
|
4
|
+
export declare const ROUTER = "0x0000000000000000000000000000000000000002";
|
|
5
|
+
export declare const CONTRACT_BALANCE: BigNumber;
|
|
6
|
+
export type Minimums = Record<string, string>;
|
|
7
|
+
export interface Swap {
|
|
8
|
+
fromToken?: string;
|
|
9
|
+
fromTokenAmount?: string;
|
|
10
|
+
toToken?: string;
|
|
11
|
+
minReceive?: string;
|
|
12
|
+
receiver?: string;
|
|
13
|
+
inputAmountLocked?: boolean;
|
|
14
|
+
hasSwap?: boolean;
|
|
15
|
+
pendingMinimums?: Minimums;
|
|
16
|
+
payouts?: Record<string, {
|
|
17
|
+
token: string;
|
|
18
|
+
minimum: string;
|
|
19
|
+
receiver: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export interface Context {
|
|
23
|
+
chainId: number;
|
|
24
|
+
native: string;
|
|
25
|
+
sender: string;
|
|
26
|
+
routerCaller: string;
|
|
27
|
+
router: string;
|
|
28
|
+
txValue: string;
|
|
29
|
+
proxyToken?: string;
|
|
30
|
+
proxyAmount?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare const chainToken: (token: string, native: string) => string;
|
|
33
|
+
export declare const realAmount: (amount: string, fallback?: string) => string;
|
|
34
|
+
export declare const assertInputAsset: (swap: Swap, ctx: Context, token: string) => void;
|
|
35
|
+
export declare const setSwap: (swap: Swap, ctx: Context, fromToken: string, toToken: string, amountA: string, amountB: string, payment?: {
|
|
36
|
+
recipient?: string;
|
|
37
|
+
payerIsUser?: boolean;
|
|
38
|
+
inputPair?: string;
|
|
39
|
+
minimums?: Minimums;
|
|
40
|
+
}) => void;
|
|
41
|
+
export declare const setOutput: (swap: Swap, ctx: Context, token: string, recipient: string, minimum?: string, exactAmount?: boolean, poolMinimums?: Minimums) => void;
|
|
42
|
+
export declare const payPortion: (swap: Swap, ctx: Context, token: string, recipient: string, portion: string, denominator: string, poolMinimums?: Minimums) => void;
|
|
43
|
+
export declare const wrapNative: (swap: Swap, ctx: Context, amount: string, recipient: string) => void;
|
|
44
|
+
export declare const unwrapNative: (swap: Swap, ctx: Context, recipient: string, minimum: string) => void;
|
|
45
|
+
export declare const finishSwap: (swap: Swap) => DecodeCalldataResult;
|