@paraswap/dex-lib 2.14.7 → 2.15.2-curve-v1-issue
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/.idea/aws.xml +17 -0
- package/.idea/codeStyles/Project.xml +19 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/modules.xml +1 -1
- package/.idea/paraswap-dex-lib.iml +9 -0
- package/.idea/runConfigurations.xml +10 -0
- package/build/dex/aave-v3/tokens/arbitrum.json +20 -20
- package/build/dex/aave-v3/tokens/avalanche.json +44 -20
- package/build/dex/aave-v3/tokens/fantom.json +30 -30
- package/build/dex/aave-v3/tokens/optimism.json +18 -18
- package/build/dex/aave-v3/tokens/polygon.json +60 -42
- package/build/dex/curve-v1/curve-v1.js +1 -1
- package/build/dex/curve-v1/curve-v1.js.map +1 -1
- package/build/dex/curve-v1/pools/curve-pool.js +3 -2
- package/build/dex/curve-v1/pools/curve-pool.js.map +1 -1
- package/build/dex/generic-rfq/fetch-mm-api.js +11 -10
- package/build/dex/generic-rfq/fetch-mm-api.js.map +1 -1
- package/build/dex/generic-rfq/rate-fetcher.js +12 -11
- package/build/dex/generic-rfq/rate-fetcher.js.map +1 -1
- package/build/dex/generic-rfq/validators.d.ts +0 -6
- package/build/dex/generic-rfq/validators.js +1 -24
- package/build/dex/generic-rfq/validators.js.map +1 -1
- package/build/dex/hashflow/constants.d.ts +6 -1
- package/build/dex/hashflow/constants.js +7 -2
- package/build/dex/hashflow/constants.js.map +1 -1
- package/build/dex/hashflow/hashflow.d.ts +9 -2
- package/build/dex/hashflow/hashflow.js +65 -21
- package/build/dex/hashflow/hashflow.js.map +1 -1
- package/build/dex/hashflow/rate-fetcher.d.ts +21 -0
- package/build/dex/hashflow/rate-fetcher.js +67 -0
- package/build/dex/hashflow/rate-fetcher.js.map +1 -0
- package/build/dex/hashflow/types.d.ts +36 -0
- package/build/dex/hashflow/types.js.map +1 -1
- package/build/dex/hashflow/validators.d.ts +4 -0
- package/build/dex/hashflow/validators.js +31 -0
- package/build/dex/hashflow/validators.js.map +1 -0
- package/build/lib/fetcher/fetcher.d.ts +7 -2
- package/build/lib/fetcher/fetcher.js +37 -13
- package/build/lib/fetcher/fetcher.js.map +1 -1
- package/build/lib/validators.d.ts +7 -0
- package/build/lib/validators.js +27 -0
- package/build/lib/validators.js.map +1 -0
- package/package.json +1 -1
- package/src/dex/curve-v1/curve-v1.ts +1 -1
- package/src/dex/curve-v1/pools/curve-pool.ts +2 -2
- package/src/dex/generic-rfq/fetch-mm-api.ts +2 -2
- package/src/dex/generic-rfq/generic-rfq-e2e.test.ts +10 -26
- package/src/dex/generic-rfq/rate-fetcher.ts +4 -5
- package/src/dex/generic-rfq/validators.test.ts +1 -1
- package/src/dex/generic-rfq/validators.ts +0 -33
- package/src/dex/hashflow/constants.ts +12 -2
- package/src/dex/hashflow/hashflow.ts +117 -60
- package/src/dex/hashflow/rate-fetcher.ts +120 -0
- package/src/dex/hashflow/types.ts +40 -0
- package/src/dex/hashflow/validators.ts +27 -0
- package/src/lib/fetcher/fetcher.ts +41 -12
- package/src/lib/validators.ts +34 -0
- package/tests/utils-e2e.ts +13 -13
- package/.vscode/launch.json +0 -21
- package/build/dex/aave-v3/tokens/mainnet.json +0 -44
- package/build/dex/balancer-v2/VirtualBoostedPool.d.ts +0 -79
- package/build/dex/balancer-v2/VirtualBoostedPool.js +0 -382
- package/build/dex/balancer-v2/VirtualBoostedPool.js.map +0 -1
- package/build/dex/test-aave-v3/config.d.ts +0 -4
- package/build/dex/test-aave-v3/config.js +0 -15
- package/build/dex/test-aave-v3/config.js.map +0 -1
- package/build/dex/test-aave-v3/test-aave-v3-pool.d.ts +0 -39
- package/build/dex/test-aave-v3/test-aave-v3-pool.js +0 -63
- package/build/dex/test-aave-v3/test-aave-v3-pool.js.map +0 -1
- package/build/dex/test-aave-v3/test-aave-v3.d.ts +0 -37
- package/build/dex/test-aave-v3/test-aave-v3.js +0 -126
- package/build/dex/test-aave-v3/test-aave-v3.js.map +0 -1
- package/build/dex/test-aave-v3/types.d.ts +0 -6
- package/build/dex/test-aave-v3/types.js +0 -3
- package/build/dex/test-aave-v3/types.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../../../src/lib/fetcher/fetcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../../../src/lib/fetcher/fetcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import joi from 'joi';
|
|
2
|
+
export declare class ValidationError extends Error {
|
|
3
|
+
readonly rawMessage: string;
|
|
4
|
+
readonly key?: string;
|
|
5
|
+
constructor(message: string, key?: string);
|
|
6
|
+
}
|
|
7
|
+
export declare const validateAndCast: <T>(value: unknown, schema: joi.Schema, name?: string | undefined) => T;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateAndCast = exports.ValidationError = void 0;
|
|
4
|
+
class ValidationError extends Error {
|
|
5
|
+
constructor(message, key) {
|
|
6
|
+
super(key !== undefined ? `'${key}': ${message}` : message);
|
|
7
|
+
this.rawMessage = message;
|
|
8
|
+
this.key = key;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.ValidationError = ValidationError;
|
|
12
|
+
const validateAndCast = (value, schema, name) => {
|
|
13
|
+
const { error, value: parsed } = schema.validate(value);
|
|
14
|
+
if (error !== undefined) {
|
|
15
|
+
let message;
|
|
16
|
+
if (name) {
|
|
17
|
+
message = `"${name}" ${error.message.slice(error.message.indexOf('must'))}`;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
message = error.message;
|
|
21
|
+
}
|
|
22
|
+
throw new ValidationError(message);
|
|
23
|
+
}
|
|
24
|
+
return parsed;
|
|
25
|
+
};
|
|
26
|
+
exports.validateAndCast = validateAndCast;
|
|
27
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/lib/validators.ts"],"names":[],"mappings":";;;AAEA,MAAa,eAAgB,SAAQ,KAAK;IAKxC,YAAY,OAAe,EAAE,GAAY;QACvC,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC5D,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF;AAVD,0CAUC;AAEM,MAAM,eAAe,GAAG,CAC7B,KAAc,EACd,MAAkB,EAClB,IAAa,EACV,EAAE;IACL,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAExD,IAAI,KAAK,KAAK,SAAS,EAAE;QACvB,IAAI,OAAO,CAAC;QACZ,IAAI,IAAI,EAAE;YACR,OAAO,GAAG,IAAI,IAAI,KAAK,KAAK,CAAC,OAAO,CAAC,KAAK,CACxC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAC9B,EAAE,CAAC;SACL;aAAM;YACL,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;SACzB;QACD,MAAM,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;KACpC;IACD,OAAO,MAAW,CAAC;AACrB,CAAC,CAAC;AAnBW,QAAA,eAAe,mBAmB1B"}
|
package/package.json
CHANGED
|
@@ -503,7 +503,7 @@ export class CurveV1 extends SimpleExchange implements IDex<CurveV1Data> {
|
|
|
503
503
|
|
|
504
504
|
let fromIndex = curveIndexes[0];
|
|
505
505
|
let toIndex = curveIndexes[1];
|
|
506
|
-
let isTokenSwap = curveIndexes[2]
|
|
506
|
+
let isTokenSwap = curveIndexes[2] === 1;
|
|
507
507
|
|
|
508
508
|
let pool = this.getPoolByAddress(exchange);
|
|
509
509
|
if (!pool) {
|
|
@@ -323,7 +323,7 @@ export abstract class CurvePool extends StatefulEventSubscriber<PoolState> {
|
|
|
323
323
|
const xp = this._xp(rates, balances);
|
|
324
324
|
const x = xp[i].plus(dx.times(this.PRECISION_MUL[i]));
|
|
325
325
|
const y = this.get_y(i, j, x, xp, A);
|
|
326
|
-
const dy = xp[j].minus(y).idiv(this.PRECISION_MUL[j]);
|
|
326
|
+
const dy = xp[j].minus(y).minus(new BigNumber(1)).idiv(this.PRECISION_MUL[j]);
|
|
327
327
|
let _fee = fee.times(dy).idiv(this.FEE_DENOMINATOR);
|
|
328
328
|
if (!usefee) _fee = BN_0;
|
|
329
329
|
return dy.minus(_fee);
|
|
@@ -360,7 +360,7 @@ export abstract class CurvePool extends StatefulEventSubscriber<PoolState> {
|
|
|
360
360
|
const xp = this._xp(rates, balances);
|
|
361
361
|
const x = xp[i].plus(dx.times(rates[i]).idiv(this.PRECISION));
|
|
362
362
|
const y = this.get_y(i, j, x, xp, A);
|
|
363
|
-
const dy = xp[j].minus(y).times(this.PRECISION).idiv(rates[j]);
|
|
363
|
+
const dy = xp[j].minus(y).minus(new BigNumber(1)).times(this.PRECISION).idiv(rates[j]);
|
|
364
364
|
let _fee = fee.times(dy).idiv(this.FEE_DENOMINATOR);
|
|
365
365
|
if (!usefee) _fee = BN_0;
|
|
366
366
|
return dy.minus(_fee);
|
|
@@ -5,7 +5,7 @@ import Web3 from 'web3';
|
|
|
5
5
|
import { generateConfig } from '../../config';
|
|
6
6
|
import { DummyRequestWrapper } from '../../dex-helper/dummy-dex-helper';
|
|
7
7
|
import { RequestConfig } from '../../dex-helper/irequest-wrapper';
|
|
8
|
-
import Fetcher from '../../lib/fetcher/fetcher';
|
|
8
|
+
import { Fetcher } from '../../lib/fetcher/fetcher';
|
|
9
9
|
import { getLogger } from '../../lib/log4js';
|
|
10
10
|
import { MultiWrapper } from '../../lib/multi-wrapper';
|
|
11
11
|
import { genericRFQAuthHttp } from './security';
|
|
@@ -23,11 +23,11 @@ import {
|
|
|
23
23
|
pairsResponseValidator,
|
|
24
24
|
pricesResponse,
|
|
25
25
|
tokensResponseValidator,
|
|
26
|
-
validateAndCast,
|
|
27
26
|
} from './validators';
|
|
28
27
|
import MultiV2Abi from '../../abi/multi-v2.json';
|
|
29
28
|
import { AbiItem } from 'web3-utils';
|
|
30
29
|
import { checkOrder } from './utils';
|
|
30
|
+
import { validateAndCast } from '../../lib/validators';
|
|
31
31
|
|
|
32
32
|
const network = 1;
|
|
33
33
|
|
|
@@ -5,15 +5,9 @@ import { ethers } from 'ethers';
|
|
|
5
5
|
import { Network, ContractMethod, SwapSide, MAX_UINT } from '../../constants';
|
|
6
6
|
import { generateConfig } from '../../config';
|
|
7
7
|
import { newTestE2E, getEnv } from '../../../tests/utils-e2e';
|
|
8
|
-
import {
|
|
9
|
-
SmartTokens,
|
|
10
|
-
GENERIC_ADDR1,
|
|
11
|
-
Tokens,
|
|
12
|
-
} from '../../../tests/constants-e2e';
|
|
8
|
+
import { SmartTokens, GENERIC_ADDR1 } from '../../../tests/constants-e2e';
|
|
13
9
|
import { RFQConfig } from './types';
|
|
14
10
|
import { testConfig } from './e2e-test-config';
|
|
15
|
-
import { SmartToken } from '../../../tests/smart-tokens';
|
|
16
|
-
import { Token } from '../../types';
|
|
17
11
|
|
|
18
12
|
const PK_KEY = process.env.TEST_PK_KEY;
|
|
19
13
|
|
|
@@ -91,32 +85,22 @@ const dexKey = 'YOUR_NAME';
|
|
|
91
85
|
describe(`GenericRFQ ${dexKey} E2E`, () => {
|
|
92
86
|
for (const [_network, testCases] of Object.entries(testConfig)) {
|
|
93
87
|
const network = parseInt(_network, 10);
|
|
94
|
-
const tokens = Tokens[network];
|
|
95
88
|
const smartTokens = SmartTokens[network];
|
|
96
89
|
const config = generateConfig(network);
|
|
97
90
|
|
|
98
91
|
config.rfqConfigs[dexKey] = buildConfigForGenericRFQ();
|
|
99
92
|
describe(`${Network[network]}`, () => {
|
|
100
93
|
for (const testCase of testCases) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
`Please add "addBalance" and "addAllowance" functions for ${testCase.srcToken} on ${Network[network]} (in constants-e2e.ts).`,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
if (!smartTokens.hasOwnProperty(testCase.destToken)) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`Please add "addBalance" and "addAllowance" functions for ${testCase.destToken} on ${Network[network]} (in constants-e2e.ts).`,
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
srcToken = smartTokens[testCase.srcToken];
|
|
118
|
-
destToken = smartTokens[testCase.destToken];
|
|
94
|
+
if (!smartTokens.hasOwnProperty(testCase.srcToken)) {
|
|
95
|
+
throw new Error(`Please add "addBalance" and "addAllowance" functions for ${testCase.srcToken} on ${Network[network]} (in constants-e2e.ts).`);
|
|
96
|
+
}
|
|
97
|
+
if (!smartTokens.hasOwnProperty(testCase.destToken)){
|
|
98
|
+
throw new Error(`Please add "addBalance" and "addAllowance" functions for ${testCase.destToken} on ${Network[network]} (in constants-e2e.ts).`);
|
|
99
|
+
}
|
|
100
|
+
const srcToken = smartTokens[testCase.srcToken];
|
|
101
|
+
const destToken = smartTokens[testCase.destToken];
|
|
119
102
|
|
|
103
|
+
if (!SKIP_TENDERLY) {
|
|
120
104
|
srcToken.addBalance(testAccount.address, MAX_UINT);
|
|
121
105
|
srcToken.addAllowance(
|
|
122
106
|
testAccount.address,
|
|
@@ -3,7 +3,7 @@ import { SwapSide } from '@paraswap/core';
|
|
|
3
3
|
import { BN_1 } from '../../bignumber-constants';
|
|
4
4
|
import { IDexHelper } from '../../dex-helper';
|
|
5
5
|
import { RequestConfig } from '../../dex-helper/irequest-wrapper';
|
|
6
|
-
import Fetcher from '../../lib/fetcher/fetcher';
|
|
6
|
+
import { Fetcher } from '../../lib/fetcher/fetcher';
|
|
7
7
|
|
|
8
8
|
import { Logger, Address, Token } from '../../types';
|
|
9
9
|
import { OrderInfo } from '../paraswap-limit-orders/types';
|
|
@@ -28,17 +28,16 @@ import {
|
|
|
28
28
|
pairsResponseValidator,
|
|
29
29
|
pricesResponse,
|
|
30
30
|
tokensResponseValidator,
|
|
31
|
-
validateAndCast,
|
|
32
31
|
} from './validators';
|
|
33
32
|
import { genericRFQAuthHttp } from './security';
|
|
34
|
-
|
|
35
|
-
const GET_FIRM_RATE_TIMEOUT_MS = 2000;
|
|
33
|
+
import { validateAndCast } from '../../lib/validators';
|
|
36
34
|
import {
|
|
37
35
|
createERC1271Contract,
|
|
38
36
|
ERC1271Contract,
|
|
39
37
|
} from '../../lib/erc1271-utils';
|
|
40
38
|
import { isContractAddress } from '../../utils';
|
|
41
39
|
|
|
40
|
+
const GET_FIRM_RATE_TIMEOUT_MS = 2000;
|
|
42
41
|
export const reversePrice = (price: PriceAndAmountBigNumber) =>
|
|
43
42
|
[
|
|
44
43
|
BN_1.dividedBy(price[0]),
|
|
@@ -60,7 +59,7 @@ export class RateFetcher {
|
|
|
60
59
|
public blackListCacheKey: string;
|
|
61
60
|
|
|
62
61
|
private authHttp: (
|
|
63
|
-
|
|
62
|
+
secret: RFQSecret,
|
|
64
63
|
) => (options: RequestConfig) => RequestConfig;
|
|
65
64
|
|
|
66
65
|
private verifierContract?: ERC1271Contract;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
validateAndCast,
|
|
3
2
|
tokensResponseValidator,
|
|
4
3
|
pairsResponseValidator,
|
|
5
4
|
pricesResponse,
|
|
6
5
|
blacklistResponseValidator,
|
|
7
6
|
firmRateResponseValidator,
|
|
8
7
|
} from './validators';
|
|
8
|
+
import { validateAndCast } from '../../lib/validators';
|
|
9
9
|
|
|
10
10
|
describe('GenericRFQ Validator test', () => {
|
|
11
11
|
describe('GenericRFQ Tokens Test', () => {
|
|
@@ -4,18 +4,6 @@ import * as ethers from 'ethers';
|
|
|
4
4
|
|
|
5
5
|
const tokensType = ['ERC20'];
|
|
6
6
|
|
|
7
|
-
export class ValidationError extends Error {
|
|
8
|
-
readonly rawMessage: string;
|
|
9
|
-
|
|
10
|
-
readonly key?: string;
|
|
11
|
-
|
|
12
|
-
constructor(message: string, key?: string) {
|
|
13
|
-
super(key !== undefined ? `'${key}': ${message}` : message);
|
|
14
|
-
this.rawMessage = message;
|
|
15
|
-
this.key = key;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
export const tokenValidator = joi.object({
|
|
20
8
|
symbol: joi.string().min(1).required(),
|
|
21
9
|
name: joi.string().min(1).required(),
|
|
@@ -120,27 +108,6 @@ export const pricesResponse = joi.object({
|
|
|
120
108
|
),
|
|
121
109
|
});
|
|
122
110
|
|
|
123
|
-
export const validateAndCast = <T>(
|
|
124
|
-
value: unknown,
|
|
125
|
-
schema: joi.Schema,
|
|
126
|
-
name?: string,
|
|
127
|
-
): T => {
|
|
128
|
-
const { error, value: parsed } = schema.validate(value);
|
|
129
|
-
|
|
130
|
-
if (error !== undefined) {
|
|
131
|
-
let message;
|
|
132
|
-
if (name) {
|
|
133
|
-
message = `"${name}" ${error.message.slice(
|
|
134
|
-
error.message.indexOf('must'),
|
|
135
|
-
)}`;
|
|
136
|
-
} else {
|
|
137
|
-
message = error.message;
|
|
138
|
-
}
|
|
139
|
-
throw new ValidationError(message);
|
|
140
|
-
}
|
|
141
|
-
return parsed as T;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
111
|
export const addressSchema = joi.string().custom((value, helpers) => {
|
|
145
112
|
if (ethers.utils.isAddress(value)) {
|
|
146
113
|
return value.toLowerCase();
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
export const PRICE_LEVELS_TTL_SECONDS = 1;
|
|
2
|
-
|
|
3
1
|
// 7 days
|
|
4
2
|
export const HASHFLOW_BLACKLIST_TTL_S = 60 * 60 * 24 * 7;
|
|
5
3
|
|
|
6
4
|
export const HASHFLOW_MM_RESTRICT_TTL_S = 60 * 60;
|
|
5
|
+
|
|
6
|
+
export const HASHFLOW_PRICES_CACHES_TTL_S = 3;
|
|
7
|
+
|
|
8
|
+
export const HASHFLOW_MARKET_MAKERS_CACHES_TTL_S = 30;
|
|
9
|
+
|
|
10
|
+
export const HASHFLOW_API_PRICES_POLLING_INTERVAL_MS = 0;
|
|
11
|
+
|
|
12
|
+
export const HASHFLOW_API_MARKET_MAKERS_POLLING_INTERVAL_MS = 28 * 1000; // 28 secs
|
|
13
|
+
|
|
14
|
+
export const HASHFLOW_API_URL = 'https://api.hashflow.com';
|
|
15
|
+
|
|
16
|
+
export const HASHFLOW_API_CLIENT_NAME = 'paraswap';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {
|
|
3
2
|
Token,
|
|
4
3
|
Address,
|
|
@@ -19,7 +18,7 @@ import {
|
|
|
19
18
|
CACHE_PREFIX,
|
|
20
19
|
} from '../../constants';
|
|
21
20
|
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
22
|
-
import { getDexKeysWithNetwork
|
|
21
|
+
import { getDexKeysWithNetwork } from '../../utils';
|
|
23
22
|
import { IDex } from '../../dex/idex';
|
|
24
23
|
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
25
24
|
import {
|
|
@@ -32,12 +31,14 @@ import {
|
|
|
32
31
|
import { SimpleExchange } from '../simple-exchange';
|
|
33
32
|
import { Adapters, HashflowConfig } from './config';
|
|
34
33
|
import { HashflowApi } from '@hashflow/taker-js';
|
|
34
|
+
import { RateFetcher } from './rate-fetcher';
|
|
35
35
|
import routerAbi from '../../abi/hashflow/HashflowRouter.abi.json';
|
|
36
36
|
import BigNumber from 'bignumber.js';
|
|
37
37
|
import { BN_0, BN_1, getBigNumberPow } from '../../bignumber-constants';
|
|
38
38
|
import { Interface } from 'ethers/lib/utils';
|
|
39
39
|
import { ChainId, ZERO_ADDRESS } from '@hashflow/sdk';
|
|
40
40
|
import {
|
|
41
|
+
MarketMakersResponse,
|
|
41
42
|
PriceLevelsResponse,
|
|
42
43
|
RfqResponse,
|
|
43
44
|
} from '@hashflow/taker-js/dist/types/rest';
|
|
@@ -45,22 +46,30 @@ import { assert } from 'ts-essentials';
|
|
|
45
46
|
import {
|
|
46
47
|
HASHFLOW_BLACKLIST_TTL_S,
|
|
47
48
|
HASHFLOW_MM_RESTRICT_TTL_S,
|
|
48
|
-
|
|
49
|
+
HASHFLOW_API_CLIENT_NAME,
|
|
50
|
+
HASHFLOW_API_URL,
|
|
51
|
+
HASHFLOW_API_PRICES_POLLING_INTERVAL_MS,
|
|
52
|
+
HASHFLOW_API_MARKET_MAKERS_POLLING_INTERVAL_MS,
|
|
53
|
+
HASHFLOW_PRICES_CACHES_TTL_S,
|
|
54
|
+
HASHFLOW_MARKET_MAKERS_CACHES_TTL_S,
|
|
49
55
|
} from './constants';
|
|
50
56
|
import { BI_MAX_UINT256 } from '../../bigint-constants';
|
|
51
57
|
|
|
52
|
-
const HASHFLOW_ASYNC_CALL_TIMEOUT = 150;
|
|
53
|
-
|
|
54
58
|
export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
59
|
+
readonly isStatePollingDex = true;
|
|
55
60
|
readonly hasConstantPriceLargeAmounts = false;
|
|
56
61
|
readonly needWrapNative = false;
|
|
57
62
|
readonly isFeeOnTransferSupported = false;
|
|
58
63
|
private api: HashflowApi;
|
|
64
|
+
private rateFetcher: RateFetcher;
|
|
59
65
|
|
|
60
66
|
private hashFlowAuthToken: string;
|
|
61
67
|
private disabledMMs: Set<string>;
|
|
62
68
|
private runtimeMMsRestrictHashMapKey: string;
|
|
63
69
|
|
|
70
|
+
private pricesCacheKey: string;
|
|
71
|
+
private marketMakersCacheKey: string;
|
|
72
|
+
|
|
64
73
|
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
65
74
|
getDexKeysWithNetwork(HashflowConfig);
|
|
66
75
|
|
|
@@ -82,11 +91,65 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
82
91
|
token !== undefined,
|
|
83
92
|
'Hashflow auth token is not specified with env variable',
|
|
84
93
|
);
|
|
94
|
+
|
|
85
95
|
this.hashFlowAuthToken = token;
|
|
86
|
-
this.api = new HashflowApi(
|
|
96
|
+
this.api = new HashflowApi(
|
|
97
|
+
'taker',
|
|
98
|
+
HASHFLOW_API_CLIENT_NAME,
|
|
99
|
+
this.hashFlowAuthToken,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
this.pricesCacheKey = `${CACHE_PREFIX}_${this.dexHelper.config.data.network}_${this.dexKey}_prices`;
|
|
103
|
+
this.marketMakersCacheKey = `${CACHE_PREFIX}_${this.dexHelper.config.data.network}_${this.dexKey}_mms`;
|
|
104
|
+
|
|
87
105
|
this.disabledMMs = new Set(dexHelper.config.data.hashFlowDisabledMMs);
|
|
88
106
|
this.runtimeMMsRestrictHashMapKey =
|
|
89
107
|
`${CACHE_PREFIX}_${this.dexKey}_${this.network}_restricted_mms`.toLowerCase();
|
|
108
|
+
|
|
109
|
+
this.rateFetcher = new RateFetcher(
|
|
110
|
+
this.dexHelper,
|
|
111
|
+
this.dexKey,
|
|
112
|
+
this.network,
|
|
113
|
+
this.logger,
|
|
114
|
+
{
|
|
115
|
+
rateConfig: {
|
|
116
|
+
pricesIntervalMs: HASHFLOW_API_PRICES_POLLING_INTERVAL_MS,
|
|
117
|
+
markerMakersIntervalMs:
|
|
118
|
+
HASHFLOW_API_MARKET_MAKERS_POLLING_INTERVAL_MS,
|
|
119
|
+
marketMakersReqParams: {
|
|
120
|
+
url: `${HASHFLOW_API_URL}/taker/v1/marketMakers`,
|
|
121
|
+
params: {
|
|
122
|
+
networkId: this.network,
|
|
123
|
+
source: HASHFLOW_API_CLIENT_NAME,
|
|
124
|
+
},
|
|
125
|
+
headers: { Authorization: this.hashFlowAuthToken },
|
|
126
|
+
},
|
|
127
|
+
pricesReqParams: {
|
|
128
|
+
url: `${HASHFLOW_API_URL}/taker/v2/price-levels`,
|
|
129
|
+
params: {
|
|
130
|
+
networkId: this.network,
|
|
131
|
+
source: HASHFLOW_API_CLIENT_NAME,
|
|
132
|
+
marketMakers: [],
|
|
133
|
+
},
|
|
134
|
+
headers: { Authorization: this.hashFlowAuthToken },
|
|
135
|
+
},
|
|
136
|
+
getCachedMarketMakers: this.getCachedMarketMakers.bind(this),
|
|
137
|
+
filterMarketMakers: this.getFilteredMarketMakers.bind(this),
|
|
138
|
+
pricesCacheKey: this.pricesCacheKey,
|
|
139
|
+
pricesCacheTTLSecs: HASHFLOW_PRICES_CACHES_TTL_S,
|
|
140
|
+
marketMakersCacheKey: this.marketMakersCacheKey,
|
|
141
|
+
marketMakersCacheTTLSecs: HASHFLOW_MARKET_MAKERS_CACHES_TTL_S,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async initializePricing(blockNumber: number): Promise<void> {
|
|
148
|
+
if (!this.dexHelper.config.isSlave) {
|
|
149
|
+
this.rateFetcher.start();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return;
|
|
90
153
|
}
|
|
91
154
|
|
|
92
155
|
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
@@ -123,7 +186,7 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
123
186
|
return [];
|
|
124
187
|
}
|
|
125
188
|
|
|
126
|
-
const levels = await this.
|
|
189
|
+
const levels = (await this.getCachedLevels()) || {};
|
|
127
190
|
const makers = Object.keys(levels);
|
|
128
191
|
|
|
129
192
|
return makers
|
|
@@ -144,11 +207,10 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
144
207
|
);
|
|
145
208
|
}
|
|
146
209
|
|
|
147
|
-
private async getFilteredMarketMakers(
|
|
148
|
-
const
|
|
149
|
-
this.
|
|
150
|
-
|
|
151
|
-
]);
|
|
210
|
+
private async getFilteredMarketMakers(makers: string[]): Promise<string[]> {
|
|
211
|
+
const cachedRestrictionUnparsed = await this.dexHelper.cache.hgetAll(
|
|
212
|
+
this.runtimeMMsRestrictHashMapKey,
|
|
213
|
+
);
|
|
152
214
|
|
|
153
215
|
const runtimeRestrictedMMs = this.parseCacheRestrictionAndExpiryIfNeeded(
|
|
154
216
|
cachedRestrictionUnparsed,
|
|
@@ -312,36 +374,30 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
312
374
|
return undefined;
|
|
313
375
|
}
|
|
314
376
|
|
|
315
|
-
async
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
377
|
+
async getCachedMarketMakers(): Promise<
|
|
378
|
+
MarketMakersResponse['marketMakers'] | null
|
|
379
|
+
> {
|
|
380
|
+
const cachedMarketMakers = await this.dexHelper.cache.rawget(
|
|
381
|
+
this.marketMakersCacheKey,
|
|
320
382
|
);
|
|
321
|
-
|
|
322
|
-
|
|
383
|
+
|
|
384
|
+
if (cachedMarketMakers) {
|
|
385
|
+
return JSON.parse(
|
|
386
|
+
cachedMarketMakers,
|
|
387
|
+
) as MarketMakersResponse['marketMakers'];
|
|
323
388
|
}
|
|
324
389
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
HASHFLOW_ASYNC_CALL_TIMEOUT,
|
|
328
|
-
'Hashflow: getFilteredMarketMakers timeout',
|
|
329
|
-
);
|
|
330
|
-
const levels = await Utils.timeoutPromise(
|
|
331
|
-
this.api.getPriceLevels(this.network as ChainId, makers),
|
|
332
|
-
HASHFLOW_ASYNC_CALL_TIMEOUT,
|
|
333
|
-
'Hashflow: getPriceLevels timeout',
|
|
334
|
-
);
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
335
392
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
this.network as ChainId,
|
|
339
|
-
`levels`,
|
|
340
|
-
PRICE_LEVELS_TTL_SECONDS,
|
|
341
|
-
JSON.stringify(levels),
|
|
342
|
-
);
|
|
393
|
+
async getCachedLevels(): Promise<PriceLevelsResponse['levels'] | null> {
|
|
394
|
+
const cachedLevels = await this.dexHelper.cache.rawget(this.pricesCacheKey);
|
|
343
395
|
|
|
344
|
-
|
|
396
|
+
if (cachedLevels) {
|
|
397
|
+
return JSON.parse(cachedLevels) as PriceLevelsResponse['levels'];
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return null;
|
|
345
401
|
}
|
|
346
402
|
|
|
347
403
|
// Hashflow protocol for native token expects 0x00000... instead of 0xeeeee...
|
|
@@ -381,7 +437,7 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
381
437
|
|
|
382
438
|
const marketMakersToUse = pools.map(p => p.split(`${prefix}_`).pop());
|
|
383
439
|
|
|
384
|
-
const levelsMap = await this.
|
|
440
|
+
const levelsMap = (await this.getCachedLevels()) || {};
|
|
385
441
|
|
|
386
442
|
Object.keys(levelsMap).forEach(mmKey => {
|
|
387
443
|
if (!marketMakersToUse.includes(mmKey)) {
|
|
@@ -486,23 +542,19 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
486
542
|
|
|
487
543
|
let rfq: RfqResponse;
|
|
488
544
|
try {
|
|
489
|
-
rfq = await
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}),
|
|
503
|
-
HASHFLOW_ASYNC_CALL_TIMEOUT,
|
|
504
|
-
'Hashflow: requestQuote timeout',
|
|
505
|
-
);
|
|
545
|
+
rfq = await this.api.requestQuote({
|
|
546
|
+
chainId,
|
|
547
|
+
baseToken: normalizedSrcToken.address,
|
|
548
|
+
quoteToken: normalizedDestToken.address,
|
|
549
|
+
...(side === SwapSide.SELL
|
|
550
|
+
? {
|
|
551
|
+
baseTokenAmount: optimalSwapExchange.srcAmount,
|
|
552
|
+
}
|
|
553
|
+
: { quoteTokenAmount: optimalSwapExchange.destAmount }),
|
|
554
|
+
wallet: this.augustusAddress.toLowerCase(),
|
|
555
|
+
effectiveTrader: options.txOrigin.toLowerCase(),
|
|
556
|
+
marketMakers: [mm],
|
|
557
|
+
});
|
|
506
558
|
|
|
507
559
|
if (rfq.status !== 'success') {
|
|
508
560
|
const message = `${this.dexKey}-${
|
|
@@ -816,16 +868,15 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
816
868
|
): Promise<PoolLiquidity[]> {
|
|
817
869
|
const _tokenAddress = tokenAddress.toLowerCase();
|
|
818
870
|
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
const
|
|
822
|
-
const pLevels = await this.api.getPriceLevels(chainId, makers);
|
|
871
|
+
const makers = (await this.getCachedMarketMakers()) || [];
|
|
872
|
+
const filteredMakers = await this.getFilteredMarketMakers(makers);
|
|
873
|
+
const pLevels = (await this.getCachedLevels()) || {};
|
|
823
874
|
|
|
824
875
|
let baseToken: Token | undefined = undefined;
|
|
825
876
|
// TODO: Improve efficiency of this part. Quite inefficient way to determine
|
|
826
877
|
// Token address and decimals. But since it is not called frequently, not worth
|
|
827
878
|
// optimizing now
|
|
828
|
-
for (const maker of
|
|
879
|
+
for (const maker of filteredMakers) {
|
|
829
880
|
const baseTokenEntry = pLevels[maker]?.find(
|
|
830
881
|
entry => entry.pair.baseToken.toLowerCase() === _tokenAddress,
|
|
831
882
|
);
|
|
@@ -873,4 +924,10 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
873
924
|
.sort((a, b) => b.liquidityUSD - a.liquidityUSD)
|
|
874
925
|
.slice(0, limit);
|
|
875
926
|
}
|
|
927
|
+
|
|
928
|
+
releaseResources(): void {
|
|
929
|
+
if (this.rateFetcher) {
|
|
930
|
+
this.rateFetcher.stop();
|
|
931
|
+
}
|
|
932
|
+
}
|
|
876
933
|
}
|