Skip to content
This repository was archived by the owner on Oct 15, 2024. It is now read-only.

Commit 3415d63

Browse files
xiannydekzBMillman19moodlezoup
authored
feat: support ExchangeProxy for swap quotes (Swap V1) (#262)
* add support for exchange proxy in Swap V1 * skip failing test * add logging * fix v0/v1 logic after review * fix: remove undefined extension opts (#268) * fix: remove undefined extension opts * github workflow * fix: revert package version change (#269) * update published packages * add labels to test * check ETH buys AND sells * fix: Upgrade to @0x/utils 5.5.0 * fix: change validation test to WETH * fix: Bump ganache snapshot version * fix: gitpkg contract-addresses * fix: update rfqt test swap path * cleanup * fix tests * add log line * chore: Clean up and handle Insufficient ETH funds * fix: revert back to actions/cache@v1 * fix: increase wait timeouts for mesh * fix: move rfqt v0 tests to rfqt test runner * fix: to is exchangeproxy * fix: takerAddress is flashwallet in rfqt tests * fix: rfqt v1 allowance tests * fix: custom mock order for EP * add allowanceTarget to swap/price response * fix log line * fix: add allowanceTarget field to /meta_transaction/v0/quote and /meta_transaction/v0/price * rebase off master * Combine rfqt tests * versioned gas schedules * chore: update to latest contract addresses * Adjust value fields in v1 * cache certain values for a time (decimals, GST balance, flash wallet address) * Add Balancer as a source and make changes to reflect updates ERC20BridgeSource and gas/fee schedules * update asset-swapper with balancer changes * try adding @balancer-labs/sor as direct dep * Use faster AssetSwapper version 🤞 * Use new AssetSwapper gitpkg * Try bumping down runLimit to 2 ** 13 * Update to latest packages * for ganache do not override as its unsupported * use flash wallet address in contract-addresses * chore: upgrade to typescript 3.9.6 (#282) Co-authored-by: Jacob Evans <jacob@dekz.net> Co-authored-by: Brandon Millman <brandon.millman@gmail.com> Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com>
1 parent 8032d96 commit 3415d63

26 files changed

Lines changed: 3086 additions & 1361 deletions

.github/workflows/ci-build.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,20 @@ jobs:
1717
steps:
1818
- name: Checkout repository
1919
uses: actions/checkout@v2
20-
20+
2121
- name: Use Node.js ${{ matrix.node-version }}
2222
uses: actions/setup-node@v1
2323
with:
2424
node-version: ${{ matrix.node-version }}
25-
25+
2626
- name: Cache node modules
27-
uses: actions/cache@v1
27+
uses: actions/cache@v2
2828
env:
2929
cache-name: cached-node-modules
3030
with:
3131
path: ~/work/0x-api/0x-api/node_modules
3232
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('yarn.lock') }}
33-
33+
3434
- run: yarn install --frozen-lockfile
3535
- run: yarn build
3636
# Pull the docker images so that they don't need to be pulled in the tests.

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
ports:
66
- '8545:8545'
77
environment:
8-
VERSION: 6.2.4-110e1afa8
8+
VERSION: 6.4.0
99
postgres:
1010
image: postgres:9.6
1111
environment:

package.json

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"integration-test": "mocha --require source-map-support/register --require make-promises-safe lib/integration-test/**/*_test.js --timeout 200000 --exit",
1515
"test": "yarn test:rest && yarn test:rfqt",
1616
"test:rest": "env-cmd -f ./test/test_env mocha --exclude lib/test/rfqt_test.js --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --timeout 200000 --exit",
17-
"test:rfqt": "env-cmd -f ./test/test_env mocha --require source-map-support/register --require make-promises-safe lib/test/rfqt_test.js --timeout 200000 --exit",
17+
"test:rfqt": "env-cmd -f ./test/test_env mocha --require source-map-support/register --require make-promises-safe lib/test/rfqt_**.js --timeout 200000 --exit",
1818
"dev": "nodemon -r dotenv/config src/index.ts | pino-pretty",
1919
"dev:service:http": "nodemon -r dotenv/config src/runners/http_service_runner.ts | pino-pretty",
2020
"dev:service:sra_http": "nodemon -r dotenv/config src/runners/http_sra_service_runner.ts | pino-pretty",
@@ -24,6 +24,7 @@
2424
"dev:service:order_watcher": "nodemon -r dotenv/config src/runners/order_watcher_service_runner.ts | pino-pretty",
2525
"dev:service:transaction_watcher": "nodemon -r dotenv/config src/runners/transaction_watcher_signer_service_runner.ts | pino-pretty",
2626
"watch": "tsc -w",
27+
"fix": "tslint --project . --format stylish --fix && yarn prettier",
2728
"prettier": "prettier --write ${npm_package_config_prettier_target} --config .prettierrc",
2829
"prettier:ci": "prettier --list-different ${npm_package_config_prettier_target} --config .prettierrc",
2930
"start": "node -r dotenv/config lib/src/index.js",
@@ -41,11 +42,6 @@
4142
"config": {
4243
"prettier_target": "{.,test/**,src/**}/*.{ts,tsx,json,md}"
4344
},
44-
"resolutions": {
45-
"@0x/contract-addresses": "0xProject/gitpkg-registry#0x-contract-addresses-v4.9.0-f14b6f2ba",
46-
"@0x/contract-wrappers": "0xProject/gitpkg-registry#0x-contract-wrappers-v13.6.3-f14b6f2ba",
47-
"@0x/contracts-zero-ex": "0xProject/gitpkg-registry#0x-contracts-zero-ex-v0.1.0-f14b6f2ba"
48-
},
4945
"release": {
5046
"plugins": [
5147
"@semantic-release/commit-analyzer",
@@ -96,6 +92,7 @@
9692
"@0x/tslint-config": "^4.0.0",
9793
"@0x/types": "^3.1.1",
9894
"@0x/typescript-typings": "^5.0.1",
95+
"@balancer-labs/sor": "^0.3.0",
9996
"@semantic-release/changelog": "^5.0.1",
10097
"@semantic-release/exec": "^5.0.0",
10198
"@semantic-release/git": "^9.0.0",
@@ -122,22 +119,21 @@
122119
"supertest": "^4.0.2",
123120
"ts-node": "^8.4.1",
124121
"tslint": "^5.19.0",
125-
"typescript": "3.0.1"
122+
"typescript": "^3.9.6"
126123
},
127124
"dependencies": {
128125
"@0x/assert": "^3.0.4",
129-
"@0x/asset-swapper": "0xProject/gitpkg-registry#0x-asset-swapper-v4.5.0-0eb0d4d45",
126+
"@0x/asset-swapper": "0xProject/gitpkg-registry#0x-asset-swapper-v4.6.0-916f6785b",
130127
"@0x/connect": "^6.0.4",
131-
"@0x/contract-addresses": "0xProject/gitpkg-registry#0x-contract-addresses-v4.9.0-f14b6f2ba",
132-
"@0x/contract-wrappers": "0xProject/gitpkg-registry#0x-contract-wrappers-v13.6.3-f14b6f2ba",
133-
"@0x/contracts-dev-utils": "0xProject/gitpkg-registry#0x-contracts-dev-utils-v1.3.3-110e1afa8",
134-
"@0x/contracts-zero-ex": "0xProject/gitpkg-registry#0x-contracts-zero-ex-v0.1.0-f14b6f2ba",
128+
"@0x/contract-addresses": "^4.11.0",
129+
"@0x/contract-wrappers": "^13.7.0",
130+
"@0x/contracts-dev-utils": "^1.3.4",
135131
"@0x/json-schemas": "^5.0.4",
136132
"@0x/mesh-rpc-client": "^9.3.0",
137-
"@0x/order-utils": "^10.1.1",
133+
"@0x/order-utils": "^10.3.0",
138134
"@0x/subproviders": "^6.0.4",
139135
"@0x/types": "^3.1.1",
140-
"@0x/utils": "^5.4.0",
136+
"@0x/utils": "^5.5.0",
141137
"@0x/web3-wrapper": "^7.0.7",
142138
"@types/uuid": "^7.0.2",
143139
"@types/uuid-validate": "^0.0.1",
@@ -146,7 +142,7 @@
146142
"cors": "^2.8.5",
147143
"debug": "^4.1.1",
148144
"dotenv": "^8.1.0",
149-
"ethereum-types": "0xProject/gitpkg-registry#ethereum-types-v3.1.0-110e1afa8",
145+
"ethereum-types": "^3.2.0",
150146
"express": "^4.17.1",
151147
"express-async-handler": "^1.1.4",
152148
"http-status-codes": "^1.3.2",

src/app.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,9 @@ function createMetaTransactionRateLimiterFromConfig(
168168
}
169169
});
170170
})
171-
.reduce((prev, cur, []) => {
171+
.reduce((prev, cur) => {
172172
return prev.concat(...cur);
173-
});
174-
173+
}, []);
175174
return new MetaTransactionComposableLimiter(configuredRateLimiters);
176175
}
177176

src/config.ts

Lines changed: 105 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1-
// tslint:disable:custom-no-magic-numbers
1+
// tslint:disable:custom-no-magic-numbers max-file-line-count
22
import { assert } from '@0x/assert';
3-
import { ERC20BridgeSource, RfqtMakerAssetOfferings, SwapQuoteRequestOpts } from '@0x/asset-swapper';
3+
import {
4+
BlockParamLiteral,
5+
CurveFillData,
6+
ERC20BridgeSource,
7+
FeeSchedule,
8+
RfqtMakerAssetOfferings,
9+
SamplerOverrides,
10+
SwapQuoteRequestOpts,
11+
SwapQuoterOpts,
12+
UniswapV2FillData,
13+
} from '@0x/asset-swapper';
14+
import { OrderPrunerPermittedFeeTypes } from '@0x/asset-swapper/lib/src/types';
415
import { BigNumber } from '@0x/utils';
516
import * as _ from 'lodash';
617
import * as validateUUID from 'uuid-validate';
@@ -15,6 +26,7 @@ import {
1526
DEFAULT_RFQT_SKIP_BUY_REQUESTS,
1627
NULL_ADDRESS,
1728
NULL_BYTES,
29+
QUOTE_ORDER_EXPIRATION_BUFFER_MS,
1830
} from './constants';
1931
import { TokenMetadatasForChains } from './token_metadatas_for_networks';
2032
import { ChainId, HttpServiceConfig, MetaTransactionRateLimitConfig } from './types';
@@ -179,6 +191,8 @@ export const RFQT_SKIP_BUY_REQUESTS: boolean = _.isEmpty(process.env.RFQT_SKIP_B
179191
? DEFAULT_RFQT_SKIP_BUY_REQUESTS
180192
: assertEnvVarType('RFQT_SKIP_BUY_REQUESTS', process.env.RFQT_SKIP_BUY_REQUESTS, EnvVarType.Boolean);
181193

194+
export const RFQT_REQUEST_MAX_RESPONSE_MS = 600;
195+
182196
// Whitelisted 0x API keys that can use the meta-txn /submit endpoint
183197
export const META_TXN_SUBMIT_WHITELISTED_API_KEYS: string[] =
184198
process.env.META_TXN_SUBMIT_WHITELISTED_API_KEYS === undefined
@@ -254,45 +268,112 @@ const EXCLUDED_SOURCES = (() => {
254268
ERC20BridgeSource.Kyber,
255269
ERC20BridgeSource.Uniswap,
256270
ERC20BridgeSource.UniswapV2,
257-
ERC20BridgeSource.UniswapV2Eth,
258271
ERC20BridgeSource.MultiBridge,
272+
ERC20BridgeSource.Balancer,
259273
];
260274
}
261275
})();
262276

263-
export const GAS_SCHEDULE: { [key in ERC20BridgeSource]: number } = {
264-
[ERC20BridgeSource.Native]: 1.5e5,
265-
[ERC20BridgeSource.Uniswap]: 3e5,
266-
[ERC20BridgeSource.UniswapV2]: 3.5e5,
267-
[ERC20BridgeSource.UniswapV2Eth]: 4e5,
268-
[ERC20BridgeSource.LiquidityProvider]: 3e5,
269-
[ERC20BridgeSource.MultiBridge]: 6.5e5,
270-
[ERC20BridgeSource.Eth2Dai]: 5.5e5,
271-
[ERC20BridgeSource.Kyber]: 8e5,
272-
[ERC20BridgeSource.CurveUsdcDai]: 9e5,
273-
[ERC20BridgeSource.CurveUsdcDaiUsdt]: 9e5,
274-
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: 10e5,
275-
[ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: 10e5,
276-
[ERC20BridgeSource.CurveUsdcDaiUsdtSusd]: 6e5,
277+
export const GAS_SCHEDULE_V0: FeeSchedule = {
278+
[ERC20BridgeSource.Native]: () => 1.5e5,
279+
[ERC20BridgeSource.Uniswap]: () => 3e5,
280+
[ERC20BridgeSource.LiquidityProvider]: () => 3e5,
281+
[ERC20BridgeSource.Eth2Dai]: () => 5.5e5,
282+
[ERC20BridgeSource.Kyber]: () => 8e5,
283+
[ERC20BridgeSource.Curve]: fillData => {
284+
switch ((fillData as CurveFillData).poolAddress.toLowerCase()) {
285+
case '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56':
286+
case '0x52ea46506b9cc5ef470c5bf89f17dc28bb35d85c':
287+
return 9e5;
288+
case '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51':
289+
case '0x79a8c46dea5ada233abaffd40f3a0a2b1e5a4f27':
290+
return 10e5;
291+
case '0xa5407eae9ba41422680e2e00537571bcc53efbfd':
292+
return 6e5;
293+
default:
294+
throw new Error('Unrecognized Curve address');
295+
}
296+
},
297+
[ERC20BridgeSource.MultiBridge]: () => 6.5e5,
298+
[ERC20BridgeSource.UniswapV2]: fillData => {
299+
let gas = 3e5;
300+
if ((fillData as UniswapV2FillData).tokenAddressPath.length > 2) {
301+
gas += 5e4;
302+
}
303+
return gas;
304+
},
305+
[ERC20BridgeSource.Balancer]: () => 4.5e5,
277306
};
278307

279-
const feeSchedule: { [key in ERC20BridgeSource]: BigNumber } = Object.assign(
308+
const FEE_SCHEDULE_V0: FeeSchedule = Object.assign(
280309
{},
281-
...(Object.keys(GAS_SCHEDULE) as ERC20BridgeSource[]).map(k => ({
282-
[k]: new BigNumber(GAS_SCHEDULE[k] + 1.5e5),
310+
...(Object.keys(GAS_SCHEDULE_V0) as ERC20BridgeSource[]).map(k => ({
311+
[k]: fillData => new BigNumber(1.5e5).plus(GAS_SCHEDULE_V0[k](fillData)),
283312
})),
284313
);
285314

286-
export const RFQT_REQUEST_MAX_RESPONSE_MS = 600;
315+
export const ASSET_SWAPPER_MARKET_ORDERS_V0_OPTS: Partial<SwapQuoteRequestOpts> = {
316+
excludedSources: EXCLUDED_SOURCES,
317+
bridgeSlippage: DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE,
318+
maxFallbackSlippage: DEFAULT_FALLBACK_SLIPPAGE_PERCENTAGE,
319+
numSamples: 13,
320+
sampleDistributionBase: 1.05,
321+
feeSchedule: FEE_SCHEDULE_V0,
322+
gasSchedule: GAS_SCHEDULE_V0,
323+
shouldBatchBridgeOrders: true,
324+
runLimit: 2 ** 13,
325+
};
326+
327+
export const GAS_SCHEDULE_V1: FeeSchedule = {
328+
...GAS_SCHEDULE_V0,
329+
};
287330

288-
export const ASSET_SWAPPER_MARKET_ORDERS_OPTS: Partial<SwapQuoteRequestOpts> = {
331+
const FEE_SCHEDULE_V1: FeeSchedule = Object.assign(
332+
{},
333+
...(Object.keys(GAS_SCHEDULE_V0) as ERC20BridgeSource[]).map(k => ({
334+
[k]:
335+
k === ERC20BridgeSource.Native
336+
? fillData => new BigNumber(1.5e5).plus(GAS_SCHEDULE_V1[k](fillData))
337+
: fillData => GAS_SCHEDULE_V1[k](fillData),
338+
})),
339+
);
340+
341+
export const ASSET_SWAPPER_MARKET_ORDERS_V1_OPTS: Partial<SwapQuoteRequestOpts> = {
289342
excludedSources: EXCLUDED_SOURCES,
290343
bridgeSlippage: DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE,
291344
maxFallbackSlippage: DEFAULT_FALLBACK_SLIPPAGE_PERCENTAGE,
292345
numSamples: 13,
293346
sampleDistributionBase: 1.05,
294-
feeSchedule,
295-
gasSchedule: GAS_SCHEDULE,
347+
feeSchedule: FEE_SCHEDULE_V1,
348+
gasSchedule: GAS_SCHEDULE_V1,
349+
shouldBatchBridgeOrders: false,
350+
runLimit: 2 ** 13,
351+
};
352+
353+
export const SAMPLER_OVERRIDES: SamplerOverrides | undefined = (() => {
354+
switch (CHAIN_ID) {
355+
case ChainId.Ganache:
356+
case ChainId.Kovan:
357+
return { overrides: {}, block: BlockParamLiteral.Latest };
358+
default:
359+
return undefined;
360+
}
361+
})();
362+
363+
export const SWAP_QUOTER_OPTS: Partial<SwapQuoterOpts> = {
364+
chainId: CHAIN_ID,
365+
expiryBufferMs: QUOTE_ORDER_EXPIRATION_BUFFER_MS,
366+
liquidityProviderRegistryAddress: LIQUIDITY_POOL_REGISTRY_ADDRESS,
367+
rfqt: {
368+
takerApiKeyWhitelist: RFQT_API_KEY_WHITELIST,
369+
makerAssetOfferings: RFQT_MAKER_ASSET_OFFERINGS,
370+
skipBuyRequests: RFQT_SKIP_BUY_REQUESTS,
371+
// warningLogger: logger.warn.bind(logger),
372+
// infoLogger: logger.info.bind(logger),
373+
},
374+
ethGasStationUrl: ETH_GAS_STATION_API_URL,
375+
permittedOrderFeeTypes: new Set([OrderPrunerPermittedFeeTypes.NoFees]),
376+
samplerOverrides: SAMPLER_OVERRIDES,
296377
};
297378

298379
export const defaultHttpServiceConfig: HttpServiceConfig = {

src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const DEFAULT_RFQT_SKIP_BUY_REQUESTS = false;
4545
// API namespaces
4646
export const SRA_PATH = '/sra/v3';
4747
export const STAKING_PATH = '/staking';
48-
export const SWAP_PATH = '/swap/v0';
48+
export const SWAP_PATH = '/swap';
4949
export const META_TRANSACTION_PATH = '/meta_transaction/v0';
5050
export const METRICS_PATH = '/metrics';
5151
export const API_KEY_HEADER = '0x-api-key';

src/errors.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ export class RevertAPIError extends BadRequestError {
8989
}
9090
}
9191

92+
export class InsufficientFundsError extends BadRequestError {
93+
public statusCode = HttpStatus.BAD_REQUEST;
94+
public generalErrorCode = GeneralErrorCodes.InsufficientFundsError;
95+
}
96+
9297
export enum GeneralErrorCodes {
9398
ValidationError = 100,
9499
MalformedJson = 101,
@@ -99,6 +104,7 @@ export enum GeneralErrorCodes {
99104
UnableToSubmitOnBehalfOfTaker = 106,
100105
InvalidAPIKey = 107,
101106
ServiceDisabled = 108,
107+
InsufficientFundsError = 109,
102108
}
103109

104110
export const generalErrorCodeToReason: { [key in GeneralErrorCodes]: string } = {
@@ -111,6 +117,7 @@ export const generalErrorCodeToReason: { [key in GeneralErrorCodes]: string } =
111117
[GeneralErrorCodes.UnableToSubmitOnBehalfOfTaker]: 'Unable to submit transaction on behalf of taker',
112118
[GeneralErrorCodes.InvalidAPIKey]: 'Invalid API key',
113119
[GeneralErrorCodes.ServiceDisabled]: 'Service disabled',
120+
[GeneralErrorCodes.InsufficientFundsError]: 'Insufficient funds for transaction',
114121
};
115122

116123
export enum ValidationErrorCodes {

src/handlers/meta_transaction_handlers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ export class MetaTransactionHandlers {
169169
protocolFee: metaTransactionPrice.protocolFee,
170170
minimumProtocolFee: metaTransactionPrice.minimumProtocolFee,
171171
estimatedGasTokenRefund: ZERO,
172+
allowanceTarget: metaTransactionPrice.allowanceTarget,
172173
};
173174
res.status(HttpStatus.OK).send(metaTransactionPriceResponse);
174175
} catch (e) {

0 commit comments

Comments
 (0)