@rabby-wallet/rabby-swap 0.0.1
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 +0 -0
- package/babel.config.json +3 -0
- package/jest.config.ts +10 -0
- package/package.json +39 -0
- package/src/consts.ts +32 -0
- package/src/dexs/0xapi.ts +107 -0
- package/src/dexs/1inch.ts +106 -0
- package/src/dexs/paraswap.ts +144 -0
- package/src/dexs/wrapToken.ts +74 -0
- package/src/index.ts +6 -0
- package/src/quote.ts +54 -0
- package/src/utils.ts +4 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
File without changes
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
transformIgnorePatterns: ['<rootDir>/node_modules/(?!@debank|@rabby-wallet)'],
|
|
4
|
+
resetMocks: false,
|
|
5
|
+
transform: {
|
|
6
|
+
'^.+\\.js$': 'babel-jest',
|
|
7
|
+
'^.+\\.ts$': 'ts-jest',
|
|
8
|
+
'^.+\\.(svg|png|jpg)$': '<rootDir>/svgTransform.js'
|
|
9
|
+
}
|
|
10
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rabby-wallet/rabby-swap",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"build": "rimraf dist && tsc",
|
|
9
|
+
"preversion": "npm run build"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/RabbyHub/rabby-swap.git"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@debank/common": "^0.2.20",
|
|
17
|
+
"axios": "^0.27.2",
|
|
18
|
+
"web3-eth-abi": "^1.8.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@babel/preset-env": "^7.19.1",
|
|
22
|
+
"@types/jest": "^29.0.3",
|
|
23
|
+
"@types/node": "^18.7.18",
|
|
24
|
+
"axios-mock-adapter": "^1.21.2",
|
|
25
|
+
"babel-jest": "^29.0.3",
|
|
26
|
+
"jest": "^29.0.3",
|
|
27
|
+
"jest-mock-axios": "^4.7.0-beta",
|
|
28
|
+
"rimraf": "^3.0.2",
|
|
29
|
+
"ts-jest": "^29.0.1",
|
|
30
|
+
"ts-node": "^10.9.1",
|
|
31
|
+
"typescript": "^4.8.3"
|
|
32
|
+
},
|
|
33
|
+
"author": "vvvvvv1vvvvvv",
|
|
34
|
+
"license": "ISC",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/RabbyHub/rabby-swap/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/RabbyHub/rabby-swap#readme"
|
|
39
|
+
}
|
package/src/consts.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CHAINS_ENUM } from '@debank/common';
|
|
2
|
+
import { SUPPORT_CHAINS as wrapTokenSupportChains } from './dexs/wrapToken';
|
|
3
|
+
import { SUPPORT_CHAINS as zeroXSupportChains } from './dexs/0xapi';
|
|
4
|
+
import { SUPPORT_CHAINS as oneInchSupportChains } from './dexs/1inch';
|
|
5
|
+
import { SUPPORT_CHAINS as paraswapSupportChains } from './dexs/paraswap';
|
|
6
|
+
|
|
7
|
+
export enum DEX_ENUM {
|
|
8
|
+
ONEINCH = '1inch',
|
|
9
|
+
PARASWAP = 'ParaSwap',
|
|
10
|
+
ZEROXAPI = '0xAPI',
|
|
11
|
+
WRAPTOKEN = 'WrapToken'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const WrapTokenAddressMap = {
|
|
15
|
+
[CHAINS_ENUM.ETH]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
|
16
|
+
[CHAINS_ENUM.BSC]: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
|
|
17
|
+
[CHAINS_ENUM.POLYGON]: '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270',
|
|
18
|
+
[CHAINS_ENUM.OP]: '0x4200000000000000000000000000000000000006',
|
|
19
|
+
[CHAINS_ENUM.FTM]: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83',
|
|
20
|
+
[CHAINS_ENUM.AVAX]: '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7',
|
|
21
|
+
[CHAINS_ENUM.ARBITRUM]: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
|
|
22
|
+
[CHAINS_ENUM.KLAY]: '0xe4f05a66ec68b54a58b17c22107b02e0232cc817',
|
|
23
|
+
[CHAINS_ENUM.GNOSIS]: '0xe91d153e0b41518a2ce8dd3d7944fa863463a97d',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DEX_SUPPORT_CHAINS = {
|
|
27
|
+
[DEX_ENUM.WRAPTOKEN]: wrapTokenSupportChains,
|
|
28
|
+
[DEX_ENUM.ONEINCH]: oneInchSupportChains,
|
|
29
|
+
[DEX_ENUM.PARASWAP]: paraswapSupportChains,
|
|
30
|
+
[DEX_ENUM.ZEROXAPI]: zeroXSupportChains,
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { CHAINS_ENUM, CHAINS } from "@debank/common";
|
|
3
|
+
import { QuoteParams, Tx, QuoteResult } from "../quote";
|
|
4
|
+
|
|
5
|
+
const NATIVE_TOKEN = 'ETH';
|
|
6
|
+
|
|
7
|
+
const API_DOMAINS = {
|
|
8
|
+
[CHAINS_ENUM.ETH]: "https://api.0x.org",
|
|
9
|
+
[CHAINS_ENUM.POLYGON]: "https://polygon.api.0x.org",
|
|
10
|
+
[CHAINS_ENUM.BSC]: "https://bsc.api.0x.org",
|
|
11
|
+
[CHAINS_ENUM.OP]: "https://optimism.api.0x.org",
|
|
12
|
+
[CHAINS_ENUM.FTM]: "https://fantom.api.0x.org",
|
|
13
|
+
[CHAINS_ENUM.CELO]: "https://celo.api.0x.org",
|
|
14
|
+
[CHAINS_ENUM.AVAX]: "https://avalanche.api.0x.org",
|
|
15
|
+
[CHAINS_ENUM.ARBITRUM]: "https://arbitrum.api.0x.org",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const SUPPORT_CHAINS = [
|
|
19
|
+
CHAINS_ENUM.ETH,
|
|
20
|
+
CHAINS_ENUM.POLYGON,
|
|
21
|
+
CHAINS_ENUM.BSC,
|
|
22
|
+
CHAINS_ENUM.OP,
|
|
23
|
+
CHAINS_ENUM.FTM,
|
|
24
|
+
CHAINS_ENUM.AVAX,
|
|
25
|
+
CHAINS_ENUM.ARBITRUM,
|
|
26
|
+
CHAINS_ENUM.CELO,
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
interface SwapParams {
|
|
30
|
+
sellToken: string;
|
|
31
|
+
buyToken: string;
|
|
32
|
+
sellAmount: string;
|
|
33
|
+
takerAddress: string;
|
|
34
|
+
slippagePercentage: number;
|
|
35
|
+
skipValidation: boolean;
|
|
36
|
+
buyTokenPercentageFee?: number;
|
|
37
|
+
feeRecipient?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface SwapResponse extends Tx {
|
|
41
|
+
allowanceTarget: string;
|
|
42
|
+
buyAmount: string;
|
|
43
|
+
sellAmount: string;
|
|
44
|
+
buyTokenAddress: string;
|
|
45
|
+
sellTokenAddress: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
|
|
49
|
+
if (!SUPPORT_CHAINS.includes(options.chain)) {
|
|
50
|
+
throw new Error(`${CHAINS[options.chain]} is not support on 1inch`);
|
|
51
|
+
}
|
|
52
|
+
const baseURL = API_DOMAINS[options.chain as keyof typeof API_DOMAINS];
|
|
53
|
+
const request = axios.create({
|
|
54
|
+
baseURL,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
request.interceptors.response.use((response) => {
|
|
58
|
+
const code = response.data?.code;
|
|
59
|
+
const msg = response.data?.reason;
|
|
60
|
+
|
|
61
|
+
if (code && code !== 200) {
|
|
62
|
+
if (msg) {
|
|
63
|
+
let err;
|
|
64
|
+
try {
|
|
65
|
+
err = new Error(JSON.parse(msg));
|
|
66
|
+
} catch (e) {
|
|
67
|
+
err = new Error(msg);
|
|
68
|
+
}
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
throw new Error(response.data);
|
|
72
|
+
}
|
|
73
|
+
return response;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
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,
|
|
79
|
+
sellAmount: options.amount,
|
|
80
|
+
takerAddress: options.userAddress,
|
|
81
|
+
slippagePercentage: options.slippage / 100,
|
|
82
|
+
skipValidation: true,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (options.feeRate) {
|
|
86
|
+
params.buyTokenPercentageFee = options.feeRate;
|
|
87
|
+
params.feeRecipient = options.feeAddress;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const { data } = await request.get<SwapResponse>('/swap/v1/quote', {
|
|
91
|
+
params,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
tx: {
|
|
96
|
+
data: data.data,
|
|
97
|
+
to: data.to,
|
|
98
|
+
value: data.value,
|
|
99
|
+
from: data.from,
|
|
100
|
+
},
|
|
101
|
+
fromToken: data.sellTokenAddress === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.sellTokenAddress,
|
|
102
|
+
spender: data.allowanceTarget,
|
|
103
|
+
fromTokenAmount: data.sellAmount,
|
|
104
|
+
toToken: data.buyTokenAddress === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.buyTokenAddress,
|
|
105
|
+
toTokenAmount: data.buyAmount
|
|
106
|
+
}
|
|
107
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { CHAINS_ENUM, CHAINS } from "@debank/common";
|
|
3
|
+
import { QuoteParams, Tx, QuoteResult } from "../quote";
|
|
4
|
+
|
|
5
|
+
const NATIVE_TOKEN = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
|
6
|
+
|
|
7
|
+
const request = axios.create({
|
|
8
|
+
baseURL: "https://api.1inch.io/v5.0",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
request.interceptors.response.use((response) => {
|
|
12
|
+
const code = response.data?.statusCode;
|
|
13
|
+
const msg = response.data?.description || response.data?.error;
|
|
14
|
+
|
|
15
|
+
if (code && code !== 200) {
|
|
16
|
+
if (msg) {
|
|
17
|
+
let err;
|
|
18
|
+
try {
|
|
19
|
+
err = new Error(JSON.parse(msg));
|
|
20
|
+
} catch (e) {
|
|
21
|
+
err = new Error(msg);
|
|
22
|
+
}
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
throw new Error(response.data);
|
|
26
|
+
}
|
|
27
|
+
return response;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const SUPPORT_CHAINS = [
|
|
31
|
+
CHAINS_ENUM.ETH,
|
|
32
|
+
CHAINS_ENUM.POLYGON,
|
|
33
|
+
CHAINS_ENUM.BSC,
|
|
34
|
+
CHAINS_ENUM.OP,
|
|
35
|
+
CHAINS_ENUM.FTM,
|
|
36
|
+
CHAINS_ENUM.AVAX,
|
|
37
|
+
CHAINS_ENUM.ARBITRUM,
|
|
38
|
+
CHAINS_ENUM.KLAY,
|
|
39
|
+
CHAINS_ENUM.GNOSIS,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
interface SwapParams {
|
|
43
|
+
fromTokenAddress: string;
|
|
44
|
+
toTokenAddress: string;
|
|
45
|
+
amount: string;
|
|
46
|
+
fromAddress: string;
|
|
47
|
+
slippage: number;
|
|
48
|
+
disableEstimate: boolean;
|
|
49
|
+
fee?: number;
|
|
50
|
+
referrerAddress?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type IToken = {
|
|
54
|
+
symbol: string;
|
|
55
|
+
name: string;
|
|
56
|
+
decimals: number;
|
|
57
|
+
address: string;
|
|
58
|
+
logoURI: string;
|
|
59
|
+
tags: string[];
|
|
60
|
+
eip2612?: boolean;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
interface SwapResponse {
|
|
64
|
+
fromToken: IToken;
|
|
65
|
+
toToken: IToken;
|
|
66
|
+
toTokenAmount: string;
|
|
67
|
+
fromTokenAmount: string;
|
|
68
|
+
tx: Tx;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
|
|
72
|
+
const chainId = CHAINS[options.chain].id;
|
|
73
|
+
|
|
74
|
+
if (!SUPPORT_CHAINS.includes(options.chain)) {
|
|
75
|
+
throw new Error(`${CHAINS[options.chain]} is not support on 1inch`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
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,
|
|
81
|
+
amount: options.amount,
|
|
82
|
+
fromAddress: options.userAddress,
|
|
83
|
+
slippage: options.slippage,
|
|
84
|
+
disableEstimate: true,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (options.feeRate) {
|
|
88
|
+
params.fee = options.feeRate;
|
|
89
|
+
params.referrerAddress = options.feeAddress;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const { data } = await request.get<SwapResponse>(`/${chainId}/swap`, {
|
|
93
|
+
params,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
tx: data.tx,
|
|
98
|
+
fromToken: data.fromToken.address === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.fromToken.address,
|
|
99
|
+
fromTokenAmount: data.fromTokenAmount,
|
|
100
|
+
fromTokenDecimals: data.fromToken.decimals,
|
|
101
|
+
toToken: data.toToken.address === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : data.toToken.address,
|
|
102
|
+
toTokenAmount: data.toTokenAmount,
|
|
103
|
+
toTokenDecimals: data.toToken.decimals,
|
|
104
|
+
spender: data.tx.to,
|
|
105
|
+
};
|
|
106
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { CHAINS_ENUM, CHAINS } from "@debank/common";
|
|
3
|
+
import { QuoteParams, Tx, QuoteResult } from "../quote";
|
|
4
|
+
|
|
5
|
+
const NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
6
|
+
|
|
7
|
+
const request = axios.create({
|
|
8
|
+
baseURL: "https://apiv5.paraswap.io",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
request.interceptors.response.use((response) => {
|
|
12
|
+
const code = response.data?.statusCode;
|
|
13
|
+
const msg = response.data?.description || response.data?.error;
|
|
14
|
+
|
|
15
|
+
if (code && code !== 200) {
|
|
16
|
+
if (msg) {
|
|
17
|
+
let err;
|
|
18
|
+
try {
|
|
19
|
+
err = new Error(JSON.parse(msg));
|
|
20
|
+
} catch (e) {
|
|
21
|
+
err = new Error(msg);
|
|
22
|
+
}
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
throw new Error(response.data);
|
|
26
|
+
}
|
|
27
|
+
return response;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const SUPPORT_CHAINS = [
|
|
31
|
+
CHAINS_ENUM.ETH,
|
|
32
|
+
CHAINS_ENUM.POLYGON,
|
|
33
|
+
CHAINS_ENUM.BSC,
|
|
34
|
+
CHAINS_ENUM.AVAX,
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
interface PriceParams {
|
|
38
|
+
srcToken: string;
|
|
39
|
+
srcDecimals: number;
|
|
40
|
+
destToken: string;
|
|
41
|
+
amount: string;
|
|
42
|
+
userAddress: string;
|
|
43
|
+
network: number;
|
|
44
|
+
includeContractMethods: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface PriceResponse {
|
|
48
|
+
priceRoute: PriceRoute;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface PriceRoute {
|
|
52
|
+
blockNumber: number;
|
|
53
|
+
network: number;
|
|
54
|
+
srcToken: string;
|
|
55
|
+
srcDecimals: number;
|
|
56
|
+
srcAmount: string;
|
|
57
|
+
destToken: string;
|
|
58
|
+
destDecimals: number;
|
|
59
|
+
destAmount: string;
|
|
60
|
+
bestRoute: any[];
|
|
61
|
+
gasCostUSD: string;
|
|
62
|
+
gasCost: string;
|
|
63
|
+
side: string;
|
|
64
|
+
tokenTransferProxy: string;
|
|
65
|
+
contractAddress: string;
|
|
66
|
+
contractMethod: string;
|
|
67
|
+
partnerFee: number;
|
|
68
|
+
srcUSD: string;
|
|
69
|
+
destUSD: string;
|
|
70
|
+
partner: string;
|
|
71
|
+
maxImpactReached: boolean;
|
|
72
|
+
hmac: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface SwapParams {
|
|
76
|
+
srcToken: string;
|
|
77
|
+
srcDecimals: number;
|
|
78
|
+
destToken: string;
|
|
79
|
+
slippage: number;
|
|
80
|
+
partnerAddress?: string;
|
|
81
|
+
partnerFeeBps?: string;
|
|
82
|
+
userAddress: string;
|
|
83
|
+
srcAmount: string;
|
|
84
|
+
priceRoute: PriceRoute;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const getPriceRoute = async (params: PriceParams): Promise<PriceResponse> => {
|
|
88
|
+
const { data } = await request.get("/prices", {
|
|
89
|
+
params,
|
|
90
|
+
});
|
|
91
|
+
return data;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
|
|
95
|
+
const chainId = CHAINS[options.chain].id;
|
|
96
|
+
|
|
97
|
+
if (!SUPPORT_CHAINS.includes(options.chain)) {
|
|
98
|
+
throw new Error(`${CHAINS[options.chain]} is not support on 1inch`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { priceRoute } = await getPriceRoute({
|
|
102
|
+
srcDecimals: options.fromTokenDecimals,
|
|
103
|
+
srcToken: options.fromToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.fromToken,
|
|
104
|
+
destToken: options.toToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.toToken,
|
|
105
|
+
amount: options.amount,
|
|
106
|
+
userAddress: options.userAddress,
|
|
107
|
+
network: chainId,
|
|
108
|
+
includeContractMethods: 'simpleSwap,multiSwap,megaSwap',
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const params: SwapParams = {
|
|
112
|
+
srcToken: options.fromToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.fromToken,
|
|
113
|
+
srcDecimals: options.fromTokenDecimals,
|
|
114
|
+
destToken: options.toToken === CHAINS[options.chain].nativeTokenAddress ? NATIVE_TOKEN : options.toToken,
|
|
115
|
+
srcAmount: options.amount,
|
|
116
|
+
userAddress: options.userAddress,
|
|
117
|
+
slippage: Math.floor(options.slippage * 100),
|
|
118
|
+
priceRoute: priceRoute,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (options.feeRate) {
|
|
122
|
+
params.partnerFeeBps = (options.feeRate * 100).toString();
|
|
123
|
+
params.partnerAddress = options.feeAddress;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const { data } = await request.get<Tx>(`/transactions/${chainId}`, {
|
|
127
|
+
params: {
|
|
128
|
+
ignoreChecks: true,
|
|
129
|
+
ignoreGasEstimate: true,
|
|
130
|
+
},
|
|
131
|
+
data: params,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
tx: data,
|
|
136
|
+
fromToken: priceRoute.srcToken === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : priceRoute.srcToken,
|
|
137
|
+
fromTokenAmount: priceRoute.srcAmount,
|
|
138
|
+
fromTokenDecimals: priceRoute.srcDecimals,
|
|
139
|
+
toToken: priceRoute.destToken === NATIVE_TOKEN ? CHAINS[options.chain].nativeTokenAddress : priceRoute.destToken,
|
|
140
|
+
toTokenAmount: priceRoute.destAmount,
|
|
141
|
+
toTokenDecimals: priceRoute.destDecimals,
|
|
142
|
+
spender: data.to,
|
|
143
|
+
};
|
|
144
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import abiCoder, { AbiCoder } from "web3-eth-abi";
|
|
2
|
+
import { CHAINS_ENUM, CHAINS } from "@debank/common";
|
|
3
|
+
import { WrapTokenAddressMap } from "../consts";
|
|
4
|
+
import { QuoteParams, Tx, QuoteResult } from "../quote";
|
|
5
|
+
import { isSameAddress } from "../utils";
|
|
6
|
+
|
|
7
|
+
export const SUPPORT_CHAINS = [
|
|
8
|
+
CHAINS_ENUM.ETH,
|
|
9
|
+
CHAINS_ENUM.POLYGON,
|
|
10
|
+
CHAINS_ENUM.BSC,
|
|
11
|
+
CHAINS_ENUM.OP,
|
|
12
|
+
CHAINS_ENUM.FTM,
|
|
13
|
+
CHAINS_ENUM.AVAX,
|
|
14
|
+
CHAINS_ENUM.ARBITRUM,
|
|
15
|
+
CHAINS_ENUM.KLAY,
|
|
16
|
+
CHAINS_ENUM.GNOSIS,
|
|
17
|
+
CHAINS_ENUM.CELO,
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export const getQuote = async (options: QuoteParams): Promise<QuoteResult> => {
|
|
21
|
+
const chain = CHAINS[options.chain];
|
|
22
|
+
|
|
23
|
+
if (!SUPPORT_CHAINS.includes(options.chain)) {
|
|
24
|
+
throw new Error(`${CHAINS[options.chain]} is not support on 1inch`);
|
|
25
|
+
}
|
|
26
|
+
const wrapTokenAddress = WrapTokenAddressMap[options.chain as keyof typeof WrapTokenAddressMap];
|
|
27
|
+
let calldata = "";
|
|
28
|
+
let value = "0";
|
|
29
|
+
if (
|
|
30
|
+
options.fromToken === chain.nativeTokenAddress &&
|
|
31
|
+
isSameAddress(
|
|
32
|
+
options.toToken,
|
|
33
|
+
wrapTokenAddress
|
|
34
|
+
)
|
|
35
|
+
) {
|
|
36
|
+
calldata = abiCoder.encodeFunctionCall(
|
|
37
|
+
{
|
|
38
|
+
constant: false,
|
|
39
|
+
inputs: [],
|
|
40
|
+
name: "deposit",
|
|
41
|
+
outputs: [],
|
|
42
|
+
payable: true,
|
|
43
|
+
stateMutability: "payable",
|
|
44
|
+
type: "function",
|
|
45
|
+
},
|
|
46
|
+
[]
|
|
47
|
+
);
|
|
48
|
+
value = options.amount;
|
|
49
|
+
} else {
|
|
50
|
+
calldata = abiCoder.encodeFunctionCall({
|
|
51
|
+
constant: false,
|
|
52
|
+
inputs: [{ name: "wad", type: "uint256" }],
|
|
53
|
+
name: "withdraw",
|
|
54
|
+
outputs: [],
|
|
55
|
+
payable: false,
|
|
56
|
+
stateMutability: "nonpayable",
|
|
57
|
+
type: "function",
|
|
58
|
+
}, [options.amount]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
tx: {
|
|
63
|
+
from: options.userAddress,
|
|
64
|
+
to: wrapTokenAddress,
|
|
65
|
+
value,
|
|
66
|
+
data: calldata,
|
|
67
|
+
},
|
|
68
|
+
fromToken: options.fromToken,
|
|
69
|
+
fromTokenAmount: options.amount,
|
|
70
|
+
toToken: options.toToken,
|
|
71
|
+
toTokenAmount: options.amount,
|
|
72
|
+
spender: wrapTokenAddress,
|
|
73
|
+
}
|
|
74
|
+
};
|
package/src/index.ts
ADDED
package/src/quote.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { CHAINS_ENUM } from '@debank/common';
|
|
2
|
+
import { DEX_ENUM } from './consts';
|
|
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';
|
|
7
|
+
|
|
8
|
+
export interface QuoteParams {
|
|
9
|
+
fromToken: string;
|
|
10
|
+
fromTokenDecimals: number;
|
|
11
|
+
toToken: string;
|
|
12
|
+
amount: string;
|
|
13
|
+
userAddress: string;
|
|
14
|
+
slippage: number;
|
|
15
|
+
feeRate?: number;
|
|
16
|
+
feeAddress?: string;
|
|
17
|
+
chain: CHAINS_ENUM;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Tx {
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
data: string;
|
|
24
|
+
value: string;
|
|
25
|
+
chainId?: number;
|
|
26
|
+
gas?: number;
|
|
27
|
+
gasPrice?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface QuoteResult {
|
|
31
|
+
tx: Tx;
|
|
32
|
+
fromToken: string;
|
|
33
|
+
fromTokenDecimals?: number;
|
|
34
|
+
fromTokenAmount: string;
|
|
35
|
+
toToken: string;
|
|
36
|
+
toTokenDecimals?: number;
|
|
37
|
+
toTokenAmount: string;
|
|
38
|
+
spender: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const getQuote = async (id: DEX_ENUM, params: QuoteParams) => {
|
|
42
|
+
switch(id) {
|
|
43
|
+
case DEX_ENUM.WRAPTOKEN:
|
|
44
|
+
return await wrapTokenGetQuote(params);
|
|
45
|
+
case DEX_ENUM.ONEINCH:
|
|
46
|
+
return await oneInchGetQuote(params);
|
|
47
|
+
case DEX_ENUM.PARASWAP:
|
|
48
|
+
return await paraSwapGetQuote(params);
|
|
49
|
+
case DEX_ENUM.ZEROXAPI:
|
|
50
|
+
return await zeroXGetQuote(params);
|
|
51
|
+
default:
|
|
52
|
+
throw new Error(`${id} is not supported!`);
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/utils.ts
ADDED
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2016",
|
|
4
|
+
"moduleResolution": "node",
|
|
5
|
+
"esModuleInterop": true,
|
|
6
|
+
"forceConsistentCasingInFileNames": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noImplicitAny": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|