@rabby-wallet/rabby-swap 0.0.3 → 0.0.4

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/src/dexs/0xapi.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  import axios from "axios";
2
+ import { Interface } from "@ethersproject/abi";
2
3
  import { CHAINS_ENUM, CHAINS } from "@debank/common";
3
- import { QuoteParams, Tx, QuoteResult } from "../quote";
4
+ import {
5
+ QuoteParams,
6
+ Tx,
7
+ QuoteResult,
8
+ TxWithChainId,
9
+ DecodeCalldataResult,
10
+ } from "../quote";
11
+ import { isSameAddress } from "../utils";
12
+ import { ZeroXABI } from "../abi";
4
13
 
5
- const NATIVE_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
14
+ const NATIVE_TOKEN = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
6
15
 
7
16
  const API_DOMAINS = {
8
17
  [CHAINS_ENUM.ETH]: "https://api.0x.org",
@@ -74,8 +83,14 @@ export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
74
83
  });
75
84
 
76
85
  const params: SwapParams = {
77
- sellToken: options.fromToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.fromToken,
78
- buyToken: options.toToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.toToken,
86
+ sellToken:
87
+ options.fromToken === CHAINS[options.chain].nativeTokenAddress
88
+ ? NATIVE_TOKEN
89
+ : options.fromToken,
90
+ buyToken:
91
+ options.toToken === CHAINS[options.chain].nativeTokenAddress
92
+ ? NATIVE_TOKEN
93
+ : options.toToken,
79
94
  sellAmount: options.amount,
80
95
  takerAddress: options.userAddress,
81
96
  slippagePercentage: options.slippage / 100,
@@ -87,7 +102,7 @@ export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
87
102
  params.feeRecipient = options.feeAddress;
88
103
  }
89
104
 
90
- const { data } = await request.get<SwapResponse>('/swap/v1/quote', {
105
+ const { data } = await request.get<SwapResponse>("/swap/v1/quote", {
91
106
  params,
92
107
  });
93
108
 
@@ -98,10 +113,44 @@ export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
98
113
  value: data.value,
99
114
  from: data.from,
100
115
  },
101
- fromToken: data.sellTokenAddress === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.sellTokenAddress,
116
+ fromToken: isSameAddress(data.sellTokenAddress, NATIVE_TOKEN)
117
+ ? CHAINS[options.chain].nativeTokenAddress
118
+ : data.sellTokenAddress,
102
119
  spender: data.allowanceTarget,
103
120
  fromTokenAmount: data.sellAmount,
104
- toToken: data.buyTokenAddress === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.buyTokenAddress,
105
- toTokenAmount: data.buyAmount
121
+ toToken: isSameAddress(data.buyTokenAddress, NATIVE_TOKEN)
122
+ ? CHAINS[options.chain].nativeTokenAddress
123
+ : data.buyTokenAddress,
124
+ toTokenAmount: data.buyAmount,
125
+ };
126
+ };
127
+
128
+ export const decodeCalldata = (
129
+ tx: TxWithChainId
130
+ ): DecodeCalldataResult | null => {
131
+ const chain = Object.values(CHAINS).find((item) => item.id === tx.chainId);
132
+ if (!chain) return null;
133
+ const contractInterface = new Interface(ZeroXABI);
134
+ const result = contractInterface.parseTransaction({ data: tx.data });
135
+ if (result.name !== "transformERC20") {
136
+ return null;
106
137
  }
138
+
139
+ const [inputToken, outputToken, inputTokenAmount, minOutputTokenAmount] =
140
+ result.args;
141
+
142
+ if (!inputToken || !outputToken || !inputTokenAmount || !minOutputTokenAmount)
143
+ return null;
144
+
145
+ return {
146
+ fromToken: isSameAddress(inputToken, NATIVE_TOKEN)
147
+ ? chain.nativeTokenAddress
148
+ : inputToken,
149
+ fromTokenAmount: inputTokenAmount.toString(),
150
+ toToken: isSameAddress(outputToken, NATIVE_TOKEN)
151
+ ? chain.nativeTokenAddress
152
+ : outputToken,
153
+ minReceiveToTokenAmount: minOutputTokenAmount.toString(),
154
+ toTokenReceiver: tx.from,
155
+ };
107
156
  };
package/src/dexs/1inch.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  import axios from "axios";
2
2
  import { CHAINS_ENUM, CHAINS } from "@debank/common";
3
- import { QuoteParams, Tx, QuoteResult } from "../quote";
3
+ import { Interface } from "@ethersproject/abi";
4
+ import {
5
+ QuoteParams,
6
+ Tx,
7
+ QuoteResult,
8
+ TxWithChainId,
9
+ DecodeCalldataResult,
10
+ } from "../quote";
11
+ import { isSameAddress } from "../utils";
12
+ import { OneInchABI } from "../abi";
4
13
 
5
- const NATIVE_TOKEN = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
14
+ const NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
6
15
 
7
16
  const request = axios.create({
8
17
  baseURL: "https://api.1inch.io/v5.0",
@@ -76,8 +85,14 @@ export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
76
85
  }
77
86
 
78
87
  const params: SwapParams = {
79
- fromTokenAddress: options.fromToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.fromToken,
80
- toTokenAddress: options.toToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.toToken,
88
+ fromTokenAddress:
89
+ options.fromToken === CHAINS[options.chain].nativeTokenAddress
90
+ ? NATIVE_TOKEN
91
+ : options.fromToken,
92
+ toTokenAddress:
93
+ options.toToken === CHAINS[options.chain].nativeTokenAddress
94
+ ? NATIVE_TOKEN
95
+ : options.toToken,
81
96
  amount: options.amount,
82
97
  fromAddress: options.userAddress,
83
98
  slippage: options.slippage,
@@ -95,12 +110,56 @@ export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
95
110
 
96
111
  return {
97
112
  tx: data.tx,
98
- fromToken: data.fromToken.address === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.fromToken.address,
113
+ fromToken:
114
+ data.fromToken.address === NATIVE_TOKEN
115
+ ? CHAINS[options.chain].nativeTokenAddress
116
+ : data.fromToken.address,
99
117
  fromTokenAmount: data.fromTokenAmount,
100
118
  fromTokenDecimals: data.fromToken.decimals,
101
- toToken: data.toToken.address === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.toToken.address,
119
+ toToken:
120
+ data.toToken.address === NATIVE_TOKEN
121
+ ? CHAINS[options.chain].nativeTokenAddress
122
+ : data.toToken.address,
102
123
  toTokenAmount: data.toTokenAmount,
103
124
  toTokenDecimals: data.toToken.decimals,
104
125
  spender: data.tx.to,
105
126
  };
106
127
  };
128
+
129
+ export const decodeCalldata = (
130
+ tx: TxWithChainId
131
+ ): DecodeCalldataResult | null => {
132
+ const chain = Object.values(CHAINS).find((item) => item.id === tx.chainId);
133
+ if (!chain) return null;
134
+ const contractInterface = new Interface(OneInchABI);
135
+ const result = contractInterface.parseTransaction({ data: tx.data });
136
+
137
+ if (result.name !== "swap") {
138
+ return null;
139
+ }
140
+
141
+ const [executor, desc] = result.args;
142
+ const [
143
+ srcToken,
144
+ dstToken,
145
+ srcReceiver,
146
+ dstReceiver,
147
+ amount,
148
+ minReturnAmount,
149
+ ] = desc;
150
+
151
+ if (!srcToken || !dstToken || !dstReceiver || !amount || !minReturnAmount)
152
+ return null;
153
+
154
+ return {
155
+ fromToken: isSameAddress(srcToken, NATIVE_TOKEN)
156
+ ? chain.nativeTokenAddress
157
+ : srcToken,
158
+ fromTokenAmount: amount.toString(),
159
+ toToken: isSameAddress(dstToken, NATIVE_TOKEN)
160
+ ? chain.nativeTokenAddress
161
+ : dstToken,
162
+ minReceiveToTokenAmount: minReturnAmount.toString(),
163
+ toTokenReceiver: dstReceiver,
164
+ };
165
+ };
@@ -1,6 +1,15 @@
1
1
  import axios from "axios";
2
+ import { Interface } from "@ethersproject/abi";
2
3
  import { CHAINS_ENUM, CHAINS } from "@debank/common";
3
- import { QuoteParams, Tx, QuoteResult } from "../quote";
4
+ import {
5
+ QuoteParams,
6
+ Tx,
7
+ QuoteResult,
8
+ TxWithChainId,
9
+ DecodeCalldataResult,
10
+ } from "../quote";
11
+ import { isSameAddress } from "../utils";
12
+ import { OneInchABI, ParaSwapABI } from "../abi";
4
13
 
5
14
  const NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
6
15
 
@@ -159,3 +168,98 @@ export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
159
168
  spender: data.to,
160
169
  };
161
170
  };
171
+
172
+ export const decodeCalldata = (
173
+ tx: TxWithChainId
174
+ ): DecodeCalldataResult | null => {
175
+ const chain = Object.values(CHAINS).find((item) => item.id === tx.chainId);
176
+ const nullAddress = "0x0000000000000000000000000000000000000000";
177
+
178
+ if (!chain) return null;
179
+
180
+ const contractInterface = new Interface(ParaSwapABI);
181
+ const result = contractInterface.parseTransaction({
182
+ data: tx.data,
183
+ });
184
+ switch (result.name) {
185
+ case "simpleSwap": {
186
+ const [data] = result.args;
187
+ const [
188
+ fromToken,
189
+ toToken,
190
+ fromAmount,
191
+ toAmount,
192
+ expectedAmount,
193
+ callees,
194
+ exchangeData,
195
+ startIndexes,
196
+ values,
197
+ beneficiary,
198
+ ] = data;
199
+ if (!fromToken || !toToken || !fromAmount || !toAmount) return null;
200
+ return {
201
+ fromToken: isSameAddress(fromToken, NATIVE_TOKEN)
202
+ ? chain.nativeTokenAddress
203
+ : fromToken,
204
+ fromTokenAmount: fromAmount.toString(),
205
+ toToken: isSameAddress(toToken, NATIVE_TOKEN)
206
+ ? chain.nativeTokenAddress
207
+ : toToken,
208
+ minReceiveToTokenAmount: toAmount.toString(),
209
+ toTokenReceiver: beneficiary === nullAddress ? tx.from : beneficiary,
210
+ };
211
+ }
212
+ case "multiSwap": {
213
+ const [data] = result.args;
214
+ const [
215
+ fromToken,
216
+ fromAmount,
217
+ toAmount,
218
+ expectedAmount,
219
+ beneficiary,
220
+ path,
221
+ ] = data;
222
+ if (!fromToken || !fromAmount || !toAmount || !path || path.length <= 0)
223
+ return null;
224
+ const [toToken] = path[path.length - 1];
225
+ return {
226
+ fromToken: isSameAddress(fromToken, NATIVE_TOKEN)
227
+ ? chain.nativeTokenAddress
228
+ : fromToken,
229
+ fromTokenAmount: fromAmount.toString(),
230
+ toToken: isSameAddress(toToken, NATIVE_TOKEN)
231
+ ? chain.nativeTokenAddress
232
+ : toToken,
233
+ minReceiveToTokenAmount: toAmount.toString(),
234
+ toTokenReceiver: beneficiary === nullAddress ? tx.from : beneficiary,
235
+ };
236
+ }
237
+ case "megaSwap":
238
+ const [data] = result.args;
239
+ const [
240
+ fromToken,
241
+ fromAmount,
242
+ toAmount,
243
+ expectedAmount,
244
+ beneficiary,
245
+ path,
246
+ ] = data;
247
+ if (!fromToken || !fromAmount || !toAmount || !path || path.length <= 0)
248
+ return null;
249
+ const [fromAmountPercent, swapPath] = path[0];
250
+ const [toToken] = swapPath[swapPath.length - 1];
251
+ return {
252
+ fromToken: isSameAddress(fromToken, NATIVE_TOKEN)
253
+ ? chain.nativeTokenAddress
254
+ : fromToken,
255
+ fromTokenAmount: fromAmount.toString(),
256
+ toToken: isSameAddress(toToken, NATIVE_TOKEN)
257
+ ? chain.nativeTokenAddress
258
+ : toToken,
259
+ minReceiveToTokenAmount: toAmount.toString(),
260
+ toTokenReceiver: beneficiary === nullAddress ? tx.from : beneficiary,
261
+ };
262
+ default:
263
+ return null;
264
+ }
265
+ };
@@ -1,4 +1,4 @@
1
- import abiCoder, { AbiCoder } from "web3-eth-abi";
1
+ import abiCoder from "web3-eth-abi";
2
2
  import { CHAINS_ENUM, CHAINS } from "@debank/common";
3
3
  import { WrapTokenAddressMap } from "../consts";
4
4
  import { QuoteParams, Tx, QuoteResult } from "../quote";
package/src/index.ts CHANGED
@@ -1,6 +1,17 @@
1
- import { WrapTokenAddressMap, DEX_ENUM, DEX_SUPPORT_CHAINS } from './consts';
2
- import { getQuote } from './quote';
1
+ import {
2
+ WrapTokenAddressMap,
3
+ DEX_ENUM,
4
+ DEX_SUPPORT_CHAINS,
5
+ DEX_ROUTER_WHITELIST,
6
+ DEX_SPENDER_WHITELIST,
7
+ } from "./consts";
8
+ import { getQuote } from "./quote";
3
9
 
4
10
  export {
5
- WrapTokenAddressMap, DEX_ENUM, DEX_SUPPORT_CHAINS, getQuote
6
- }
11
+ WrapTokenAddressMap,
12
+ DEX_ENUM,
13
+ DEX_SUPPORT_CHAINS,
14
+ getQuote,
15
+ DEX_ROUTER_WHITELIST,
16
+ DEX_SPENDER_WHITELIST,
17
+ };
package/src/quote.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { CHAINS_ENUM } from '@debank/common';
2
2
  import { DEX_ENUM } from './consts';
3
3
  import { getQuote as wrapTokenGetQuote } from './dexs/wrapToken';
4
- import { getQuote as oneInchGetQuote } from './dexs/1inch';
5
- import { getQuote as zeroXGetQuote } from './dexs/0xapi';
6
- import { getQuote as paraSwapGetQuote } from './dexs/paraswap';
4
+ import { getQuote as oneInchGetQuote, decodeCalldata as oneInchDecodeCalldata } from './dexs/1inch';
5
+ import { getQuote as zeroXGetQuote, decodeCalldata as zeroXDecodeCalldata } from './dexs/0xapi';
6
+ import { getQuote as paraSwapGetQuote, decodeCalldata as paraSwapDecodeCalldata } from './dexs/paraswap';
7
7
 
8
8
  export interface QuoteParams {
9
9
  fromToken: string;
@@ -27,6 +27,10 @@ export interface Tx {
27
27
  gasPrice?: string;
28
28
  }
29
29
 
30
+ export interface TxWithChainId extends Tx {
31
+ chainId: number;
32
+ }
33
+
30
34
  export interface QuoteResult {
31
35
  tx: Tx;
32
36
  fromToken: string;
@@ -51,4 +55,25 @@ export const getQuote = async (id: DEX_ENUM, params: QuoteParams) => {
51
55
  default:
52
56
  throw new Error(`${id} is not supported!`);
53
57
  }
54
- }
58
+ }
59
+
60
+ export interface DecodeCalldataResult {
61
+ fromToken: string;
62
+ fromTokenAmount: string;
63
+ toToken: string;
64
+ minReceiveToTokenAmount: string;
65
+ toTokenReceiver: string;
66
+ }
67
+
68
+ export const decodeCalldata = (id: DEX_ENUM, tx: TxWithChainId): DecodeCalldataResult | null => {
69
+ switch(id) {
70
+ case DEX_ENUM.ONEINCH:
71
+ return oneInchDecodeCalldata(tx);
72
+ case DEX_ENUM.PARASWAP:
73
+ return paraSwapDecodeCalldata(tx);
74
+ case DEX_ENUM.ZEROXAPI:
75
+ return zeroXDecodeCalldata(tx);
76
+ default:
77
+ throw new Error(`${id} is not supported!`);
78
+ }
79
+ };
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ process() {
3
+ return {
4
+ code: 'module.exports = {};'
5
+ };
6
+ },
7
+ getCacheKey() {
8
+ // The output is always the same.
9
+ return 'svgTransform';
10
+ }
11
+ };