Compare commits

..

14 Commits

Author SHA1 Message Date
Github Actions
9ce73931f7 Publish
- @0x/contracts-integrations@2.7.34
 - @0x/asset-swapper@6.5.3
2021-04-14 20:56:21 +00:00
Github Actions
97020df178 Updated CHANGELOGS & MD docs 2021-04-14 20:56:14 +00:00
Lawrence Forman
dfb7b3de8f Apply slippage to non-native orders [TKR-39] (#198)
* `@0x/asset-swapper`: Apply slippage to FQT bridge orders

* review comments

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-04-13 19:24:50 -04:00
Github Actions
9e152912fe Publish
- @0x/contracts-integrations@2.7.33
 - @0x/asset-swapper@6.5.2
2021-04-13 11:51:04 +00:00
Github Actions
b2c2f1e1aa Updated CHANGELOGS & MD docs 2021-04-13 11:50:57 +00:00
Jacob Evans
629c7d8e92 Fix asset-swapper test (#199) 2021-04-13 21:01:26 +10:00
Jacob Evans
62f24d4356 [asset-swapper] add Native fee token on all chains (#191) 2021-04-13 08:21:26 +10:00
Github Actions
ae281c33ca Publish
- @0x/contracts-integrations@2.7.32
 - @0x/contracts-treasury@1.1.2
 - @0x/contracts-zero-ex@0.21.1
 - @0x/asset-swapper@6.5.1
 - @0x/contract-wrappers-test@12.2.40
 - @0x/migrations@8.0.1
 - @0x/protocol-utils@1.4.1
2021-04-12 20:37:59 +00:00
Github Actions
5d034dd106 Updated CHANGELOGS & MD docs 2021-04-12 20:37:55 +00:00
Romain Butteaud
c1f8df0eca chore: Component.finance, Smoothy.finance, Saddle.finance, Curve open pools, Gas schedule [TKR-1] (#182)
* chore: Component.finance, Smoothy.finance (mainnet + BSC), Saddle.finance, Curve open pools, adjusting gas schedule, fixing Shell buys

* chore: adding a Sampler for Smoothy.finance to only use whats in the contracts reserve

* fix: Smoothy sampler, only use approx. for buys, removing y and BUSD curve pools

* add CHANGELOGs

* fix: prettier

* add: FRAX Curve open pool

* fix: prettier

* chore: adjusting gas schedule for BSC Smoothy
2021-04-08 18:07:12 -07:00
Github Actions
76dda9eeda Publish
- @0x/contracts-integrations@2.7.31
 - @0x/asset-swapper@6.5.0
2021-04-08 20:27:04 +00:00
Github Actions
a9b84a92ac Updated CHANGELOGS & MD docs 2021-04-08 20:26:57 +00:00
mzhu25
0f7e881899 Add default liquidity provider registry and allow gas costs to be a function of tokens (#196) 2021-04-08 11:09:32 -07:00
mzhu25
6045f777ab Add Kyber DMM as a liquidity source (#194)
* Add Kyber DMM as a liquidity source

* update changelog
2021-04-07 10:08:19 -07:00
38 changed files with 1245 additions and 463 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.7.30",
"version": "2.7.34",
"private": true,
"engines": {
"node": ">=6.12"
@@ -65,9 +65,9 @@
"@0x/contracts-utils": "^4.7.6",
"@0x/coordinator-server": "^1.0.5",
"@0x/dev-utils": "^4.2.1",
"@0x/migrations": "^8.0.0",
"@0x/migrations": "^8.0.1",
"@0x/order-utils": "^10.4.19",
"@0x/protocol-utils": "^1.4.0",
"@0x/protocol-utils": "^1.4.1",
"@0x/sol-compiler": "^4.6.1",
"@0x/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.4.1",
@@ -93,7 +93,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/asset-swapper": "^6.4.0",
"@0x/asset-swapper": "^6.5.3",
"@0x/base-contract": "^6.2.18",
"@0x/contracts-asset-proxy": "^3.7.9",
"@0x/contracts-erc1155": "^2.1.27",
@@ -103,7 +103,7 @@
"@0x/contracts-multisig": "^4.1.28",
"@0x/contracts-staking": "^2.0.35",
"@0x/contracts-test-utils": "^5.3.24",
"@0x/contracts-zero-ex": "^0.21.0",
"@0x/contracts-zero-ex": "^0.21.1",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1618259868,
"version": "1.1.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1617311315,
"version": "1.1.1",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.2 - _April 12, 2021_
* Dependencies updated
## v1.1.1 - _April 1, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.1.1",
"version": "1.1.2",
"engines": {
"node": ">=6.12"
},
@@ -73,7 +73,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/protocol-utils": "^1.4.0",
"@0x/protocol-utils": "^1.4.1",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1618259868,
"version": "0.21.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.21.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.21.1 - _April 12, 2021_
* Dependencies updated
## v0.21.0 - _April 1, 2021_
* Encoding protocol ID and source name in bridge source ID (#162)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.21.0",
"version": "0.21.1",
"engines": {
"node": ">=6.12"
},
@@ -83,7 +83,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/protocol-utils": "^1.4.0",
"@0x/protocol-utils": "^1.4.1",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,7 +1,53 @@
[
{
"version": "6.5.3",
"changes": [
{
"note": "Apply slippage to bridge orders in consumer",
"pr": 198
}
],
"timestamp": 1618433771
},
{
"timestamp": 1618314654,
"version": "6.5.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1618259868,
"version": "6.5.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "6.5.0",
"changes": [
{
"note": "Add Kyber DMM to Ethereum mainnet",
"pr": 194
},
{
"note": "Add default LiquidityProvider registry and allow LiquidityProvider gasCost to be a function of tokens",
"pr": 196
}
],
"timestamp": 1617913615
},
{
"version": "6.4.0",
"changes": [
{
"note": "Added Component, Smoothy, Saddle, Curve open pools, tweeks gas schedule, adding SushiSwap as a fee quote source",
"pr": 182
},
{
"note": "Use SOURCE_FLAGS.rfqOrder in comparisonPrice",
"pr": 177

View File

@@ -5,8 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.5.3 - _April 14, 2021_
* Apply slippage to bridge orders in consumer (#198)
## v6.5.2 - _April 13, 2021_
* Dependencies updated
## v6.5.1 - _April 12, 2021_
* Dependencies updated
## v6.5.0 - _April 8, 2021_
* Add Kyber DMM to Ethereum mainnet (#194)
* Add default LiquidityProvider registry and allow LiquidityProvider gasCost to be a function of tokens (#196)
## v6.4.0 - _April 1, 2021_
* Added Component, Smoothy, Saddle, Curve open pools, tweeks gas schedule, adding SushiSwap as a fee quote source (#182)
* Use SOURCE_FLAGS.rfqOrder in comparisonPrice (#177)
* Add a cancel token to ensure timeouts are respected (#176)
* Rename {Rfqt=>Rfq} for many types in Asset Swapper (#179)

View File

@@ -34,6 +34,7 @@ import "./MStableSampler.sol";
import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol";
import "./ShellSampler.sol";
import "./SmoothySampler.sol";
import "./TwoHopSampler.sol";
import "./UniswapSampler.sol";
import "./UniswapV2Sampler.sol";
@@ -55,6 +56,7 @@ contract ERC20BridgeSampler is
MultiBridgeSampler,
NativeOrderSampler,
ShellSampler,
SmoothySampler,
TwoHopSampler,
UniswapSampler,
UniswapV2Sampler,

View File

@@ -20,10 +20,20 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./ApproximateBuys.sol";
import "./interfaces/IShell.sol";
import "./SamplerUtils.sol";
contract ShellSampler
contract ShellSampler is
SamplerUtils,
ApproximateBuys
{
struct ShellInfo {
address poolAddress;
}
/// @dev Default gas limit for Shell calls.
uint256 constant private DEFAULT_CALL_GAS = 300e3; // 300k
@@ -56,10 +66,6 @@ contract ShellSampler
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
@@ -84,26 +90,37 @@ contract ShellSampler
view
returns (uint256[] memory takerTokenAmounts)
{
// Initialize array of maker token amounts.
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, pool),
takerTokenData: abi.encode(takerToken, pool),
getSellQuoteCallback: _sampleSellForApproximateBuyFromShell
}),
makerTokenAmounts
);
}
for (uint256 i = 0; i < numSamples; i++) {
try
IShell(pool).viewTargetSwap
{gas: DEFAULT_CALL_GAS}
(takerToken, makerToken, makerTokenAmounts[i])
returns (uint256 amount)
{
takerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
function _sampleSellForApproximateBuyFromShell(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(address takerToken, address pool) = abi.decode(takerTokenData, (address, address));
(address makerToken) = abi.decode(makerTokenData, (address));
try
this.sampleSellsFromShell
(pool, takerToken, makerToken, _toSingleValueArray(sellAmount))
returns (uint256[] memory amounts)
{
return amounts[0];
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
}

View File

@@ -0,0 +1,156 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
// import "./interfaces/ISmoothy.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
import "./interfaces/ISmoothy.sol";
contract SmoothySampler is
SamplerUtils,
ApproximateBuys
{
/// @dev Information for sampling from smoothy sources.
struct SmoothyInfo {
address poolAddress;
bytes4 sellQuoteFunctionSelector;
bytes4 buyQuoteFunctionSelector;
}
/// @dev Base gas limit for Smoothy calls.
uint256 constant private SMOOTHY_CALL_GAS = 600e3;
/// @dev Sample sell quotes from Smoothy.
/// @param smoothyInfo Smoothy information specific to this token pair.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromSmoothy(
SmoothyInfo memory smoothyInfo,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
// Basically a Curve fork
// Smoothy only keep a percentage of its tokens available in reserve
uint256 poolReserveMakerAmount = ISmoothy(smoothyInfo.poolAddress).getBalance(uint256(toTokenIdx)) -
ISmoothy(smoothyInfo.poolAddress)._yBalances(uint256(toTokenIdx));
(, , , uint256 decimals) = ISmoothy(smoothyInfo.poolAddress).getTokenStats(uint256(toTokenIdx));
poolReserveMakerAmount = poolReserveMakerAmount/(10**(18-decimals));
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
smoothyInfo.poolAddress.staticcall.gas(SMOOTHY_CALL_GAS)(
abi.encodeWithSelector(
smoothyInfo.sellQuoteFunctionSelector,
fromTokenIdx,
toTokenIdx,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
}
// Make sure the quoted buyAmount is available in the pool reserve
if (buyAmount >= poolReserveMakerAmount) {
// Assign pool reserve amount for all higher samples to break early
for (uint256 j = i; j < numSamples; j++) {
makerTokenAmounts[j] = poolReserveMakerAmount;
}
break;
} else {
makerTokenAmounts[i] = buyAmount;
}
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from Smoothy.
/// @param smoothyInfo Smoothy information specific to this token pair.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromSmoothy(
SmoothyInfo memory smoothyInfo,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
// Buys not supported so approximate it.
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(toTokenIdx, smoothyInfo),
takerTokenData: abi.encode(fromTokenIdx, smoothyInfo),
getSellQuoteCallback: _sampleSellForApproximateBuyFromSmoothy
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromSmoothy(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(int128 takerTokenIdx, SmoothyInfo memory smoothyInfo) =
abi.decode(takerTokenData, (int128, SmoothyInfo));
(int128 makerTokenIdx) =
abi.decode(makerTokenData, (int128));
(bool success, bytes memory resultData) =
address(this).staticcall(abi.encodeWithSelector(
this.sampleSellsFromSmoothy.selector,
smoothyInfo,
takerTokenIdx,
makerTokenIdx,
_toSingleValueArray(sellAmount)
));
if (!success) {
return 0;
}
// solhint-disable-next-line indent
return abi.decode(resultData, (uint256[]))[0];
}
}

View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
interface ISmoothy {
function getBalance (
uint256 tid
)
external
view
returns (uint256 balance);
function _yBalances (
uint256 tid
)
external
view
returns (uint256 balance);
function getTokenStats (
uint256 tid
)
external
view
returns (uint256 softWeight, uint256 hardWeight, uint256 balance, uint256 decimals);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "6.4.0",
"version": "6.5.3",
"engines": {
"node": ">=6.12"
},
@@ -38,7 +38,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json",
"postpublish": {
"assets": []
}
@@ -62,10 +62,10 @@
"@0x/contract-addresses": "^6.0.0",
"@0x/contract-wrappers": "^13.15.0",
"@0x/contracts-erc20": "^3.3.6",
"@0x/contracts-zero-ex": "^0.21.0",
"@0x/contracts-zero-ex": "^0.21.1",
"@0x/dev-utils": "^4.2.1",
"@0x/json-schemas": "^5.4.1",
"@0x/protocol-utils": "^1.4.0",
"@0x/protocol-utils": "^1.4.1",
"@0x/quote-server": "^5.0.0",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
@@ -96,7 +96,7 @@
"@0x/contracts-test-utils": "^5.3.24",
"@0x/contracts-utils": "^4.7.6",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^8.0.0",
"@0x/migrations": "^8.0.1",
"@0x/sol-compiler": "^4.6.1",
"@0x/subproviders": "^6.4.1",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -23,6 +23,7 @@ import {
CalldataInfo,
ExchangeProxyContractOpts,
MarketBuySwapQuote,
MarketOperation,
MarketSellSwapQuote,
SwapQuote,
SwapQuoteConsumerBase,
@@ -43,6 +44,7 @@ import {
LiquidityProviderFillData,
MooniswapFillData,
OptimizedMarketBridgeOrder,
OptimizedMarketOrder,
UniswapV2FillData,
} from '../utils/market_operation_utils/types';
@@ -136,7 +138,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
const buyToken = quote.makerToken;
// Take the bounds from the worst case
const sellAmount = quote.worstCaseQuoteInfo.totalTakerAmount;
const sellAmount = BigNumber.max(
quote.bestCaseQuoteInfo.totalTakerAmount,
quote.worstCaseQuoteInfo.totalTakerAmount,
);
let minBuyAmount = quote.worstCaseQuoteInfo.makerAmount;
let ethAmount = quote.worstCaseQuoteInfo.protocolFeeInWeiAmount;
@@ -144,13 +149,15 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
ethAmount = ethAmount.plus(sellAmount);
}
const slippedOrders = slipNonNativeOrders(quote);
// VIP routes.
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap])
) {
const source = quote.orders[0].source;
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
const source = slippedOrders[0].source;
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
return {
calldataHexString: this._exchangeProxy
.sellToUniswap(
@@ -183,8 +190,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
ERC20BridgeSource.SushiSwap,
])
) {
const source = quote.orders[0].source;
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
const source = slippedOrders[0].source;
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
return {
calldataHexString: this._exchangeProxy
.sellToPancakeSwap(
@@ -213,7 +220,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])
) {
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
const target = fillData.poolAddress;
return {
calldataHexString: this._exchangeProxy
@@ -238,7 +245,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])
) {
const fillData = quote.orders[0].fills[0].fillData as CurveFillData;
const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
@@ -267,7 +274,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
) {
const fillData = quote.orders[0].fills[0].fillData as MooniswapFillData;
const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
@@ -289,7 +296,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexBatchFillCalldata(quote),
calldataHexString: this._encodeMultiplexBatchFillCalldata({ ...quote, orders: slippedOrders }),
ethAmount,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
@@ -298,7 +305,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}
if (this.chainId === ChainId.Mainnet && isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexMultiHopFillCalldata(quote, optsWithDefaults),
calldataHexString: this._encodeMultiplexMultiHopFillCalldata(
{ ...quote, orders: slippedOrders },
optsWithDefaults,
),
ethAmount,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
@@ -321,10 +331,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
// If it's two hop we have an intermediate token this is needed to encode the individual FQT
// and we also want to ensure no dust amount is left in the flash wallet
const intermediateToken = quote.isTwoHop ? quote.orders[0].makerToken : NULL_ADDRESS;
const intermediateToken = quote.isTwoHop ? slippedOrders[0].makerToken : NULL_ADDRESS;
// This transformer will fill the quote.
if (quote.isTwoHop) {
const [firstHopOrder, secondHopOrder] = quote.orders;
const [firstHopOrder, secondHopOrder] = slippedOrders;
transforms.push({
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
data: encodeFillQuoteTransformerData({
@@ -349,14 +359,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
});
} else {
const fillAmount = isBuyQuote(quote) ? quote.makerTokenFillAmount : quote.takerTokenFillAmount;
transforms.push({
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
data: encodeFillQuoteTransformerData({
side: isBuyQuote(quote) ? FillQuoteTransformerSide.Buy : FillQuoteTransformerSide.Sell,
sellToken,
buyToken,
...getFQTTransformerDataFromOptimizedOrders(quote.orders),
...getFQTTransformerDataFromOptimizedOrders(slippedOrders),
refundReceiver: refundReceiver || NULL_ADDRESS,
fillAmount: !isBuyQuote(quote) && shouldSellEntireBalance ? MAX_UINT256 : fillAmount,
}),
@@ -598,3 +607,38 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
.getABIEncodedTransactionData();
}
}
function slipNonNativeOrders(quote: MarketSellSwapQuote | MarketBuySwapQuote): OptimizedMarketOrder[] {
const slippage = getMaxQuoteSlippageRate(quote);
if (!slippage) {
return quote.orders;
}
return quote.orders.map(o => {
if (o.source === ERC20BridgeSource.Native) {
return o;
}
return {
...o,
...(quote.type === MarketOperation.Sell
? { makerAmount: o.makerAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN) }
: { takerAmount: o.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP) }),
};
});
}
function getMaxQuoteSlippageRate(quote: MarketBuySwapQuote | MarketSellSwapQuote): number {
if (quote.type === MarketOperation.Buy) {
// (worstCaseTaker - bestCaseTaker) / bestCaseTaker
// where worstCaseTaker >= bestCaseTaker
return quote.worstCaseQuoteInfo.takerAmount
.minus(quote.bestCaseQuoteInfo.takerAmount)
.div(quote.bestCaseQuoteInfo.takerAmount)
.toNumber();
}
// (bestCaseMaker - worstCaseMaker) / bestCaseMaker
// where bestCaseMaker >= worstCaseMaker
return quote.bestCaseQuoteInfo.makerAmount
.minus(quote.worstCaseQuoteInfo.makerAmount)
.div(quote.bestCaseQuoteInfo.makerAmount)
.toNumber();
}

View File

@@ -591,9 +591,10 @@ function calculateTwoHopQuoteInfo(
: secondHopOrder.makerAmount,
takerAmount: MarketOperation.Sell
? firstHopOrder.takerAmount
: // tslint:disable-next-line: binary-expression-operand-order
firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
totalTakerAmount: firstHopOrder.takerAmount,
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
totalTakerAmount: MarketOperation.Sell
? firstHopOrder.takerAmount
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
feeTakerTokenAmount: constants.ZERO_AMOUNT,
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
gas,

View File

@@ -3,11 +3,11 @@ import { SDK } from '@bancor/sdk';
import { Ethereum } from '@bancor/sdk/dist/blockchains/ethereum';
import { BlockchainType } from '@bancor/sdk/dist/types';
import { TOKENS } from './constants';
import { MAINNET_TOKENS } from './constants';
const findToken = (tokenAddress: string, graph: object): string =>
// If we're looking for WETH it is stored by Bancor as the 0xeee address
tokenAddress.toLowerCase() === TOKENS.WETH.toLowerCase()
tokenAddress.toLowerCase() === MAINNET_TOKENS.WETH.toLowerCase()
? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
: Object.keys(graph).filter(k => k.toLowerCase() === tokenAddress.toLowerCase())[0];

View File

@@ -4,19 +4,24 @@ import { BigNumber, NULL_BYTES } from '@0x/utils';
import {
BAKERYSWAP_ROUTER_BY_CHAIN_ID,
BELT_BSC_INFOS,
COMPONENT_POOLS_BY_CHAIN_ID,
CRYPTO_COM_ROUTER_BY_CHAIN_ID,
CURVE_MAINNET_INFOS,
ELLIPSIS_BSC_INFOS,
KYBER_BRIDGED_LIQUIDITY_PREFIX,
MAINNET_CURVE_INFOS,
MAINNET_SNOWSWAP_INFOS,
MAINNET_SWERVE_INFOS,
KYBER_DMM_ROUTER_BY_CHAIN_ID,
MAX_DODOV2_POOLS_QUERIED,
MAX_KYBER_RESERVES_QUERIED,
NERVE_BSC_INFOS,
NULL_ADDRESS,
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
SADDLE_MAINNET_INFOS,
SHELL_POOLS_BY_CHAIN_ID,
SMOOTHY_BSC_INFOS,
SMOOTHY_MAINNET_INFOS,
SNOWSWAP_MAINNET_INFOS,
SUSHISWAP_ROUTER_BY_CHAIN_ID,
SWERVE_MAINNET_INFOS,
UNISWAPV2_ROUTER_BY_CHAIN_ID,
} from './constants';
import { CurveInfo, ERC20BridgeSource } from './types';
@@ -60,12 +65,22 @@ export function getShellsForPair(chainId: ChainId, takerToken: string, makerToke
.map(i => i.poolAddress);
}
// tslint:disable completed-docs
export function getComponentForPair(chainId: ChainId, takerToken: string, makerToken: string): string[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(COMPONENT_POOLS_BY_CHAIN_ID[chainId])
.filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t)))
.map(i => i.poolAddress);
}
// tslint:disable completed-docs
export function getCurveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(MAINNET_CURVE_INFOS).filter(c =>
return Object.values(CURVE_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
@@ -78,7 +93,7 @@ export function getSwerveInfosForPair(chainId: ChainId, takerToken: string, make
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(MAINNET_SWERVE_INFOS).filter(c =>
return Object.values(SWERVE_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
@@ -91,7 +106,7 @@ export function getSnowSwapInfosForPair(chainId: ChainId, takerToken: string, ma
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(MAINNET_SNOWSWAP_INFOS).filter(c =>
return Object.values(SNOWSWAP_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
@@ -139,6 +154,61 @@ export function getEllipsisInfosForPair(chainId: ChainId, takerToken: string, ma
);
}
export function getSmoothyInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId === ChainId.BSC) {
return Object.values(SMOOTHY_BSC_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
(c.tokens.includes(t) &&
c.metaToken !== undefined &&
[makerToken, takerToken].includes(c.metaToken)),
),
);
} else if (chainId === ChainId.Mainnet) {
return Object.values(SMOOTHY_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
(c.tokens.includes(t) &&
c.metaToken !== undefined &&
[makerToken, takerToken].includes(c.metaToken)),
),
);
} else {
return [];
}
}
export function getSaddleInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(SADDLE_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
(c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)),
),
);
}
export function getShellLikeInfosForPair(
chainId: ChainId,
takerToken: string,
makerToken: string,
source: ERC20BridgeSource.Shell | ERC20BridgeSource.Component,
): string[] {
switch (source) {
case ERC20BridgeSource.Shell:
return getShellsForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Component:
return getComponentForPair(chainId, takerToken, makerToken);
default:
throw new Error(`Unknown Shell like source ${source}`);
}
}
export function getCurveLikeInfosForPair(
chainId: ChainId,
takerToken: string,
@@ -149,7 +219,9 @@ export function getCurveLikeInfosForPair(
| ERC20BridgeSource.SnowSwap
| ERC20BridgeSource.Nerve
| ERC20BridgeSource.Belt
| ERC20BridgeSource.Ellipsis,
| ERC20BridgeSource.Ellipsis
| ERC20BridgeSource.Smoothy
| ERC20BridgeSource.Saddle,
): CurveInfo[] {
switch (source) {
case ERC20BridgeSource.Curve:
@@ -164,6 +236,10 @@ export function getCurveLikeInfosForPair(
return getBeltInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Ellipsis:
return getEllipsisInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Smoothy:
return getSmoothyInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Saddle:
return getSaddleInfosForPair(chainId, takerToken, makerToken);
default:
throw new Error(`Unknown Curve like source ${source}`);
}
@@ -176,7 +252,8 @@ export function uniswapV2LikeRouterAddress(
| ERC20BridgeSource.SushiSwap
| ERC20BridgeSource.CryptoCom
| ERC20BridgeSource.PancakeSwap
| ERC20BridgeSource.BakerySwap,
| ERC20BridgeSource.BakerySwap
| ERC20BridgeSource.KyberDmm,
): string {
switch (source) {
case ERC20BridgeSource.UniswapV2:
@@ -189,6 +266,8 @@ export function uniswapV2LikeRouterAddress(
return PANCAKESWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.BakerySwap:
return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.KyberDmm:
return KYBER_DMM_ROUTER_BY_CHAIN_ID[chainId];
default:
throw new Error(`Unknown UniswapV2 like source ${source}`);
}

View File

@@ -5,8 +5,22 @@ export function getLiquidityProvidersForPair(
registry: LiquidityProviderRegistry,
takerToken: string,
makerToken: string,
): string[] {
): Array<{ providerAddress: string; gasCost: number }> {
return Object.entries(registry)
.filter(([, plp]) => [makerToken, takerToken].every(t => plp.tokens.includes(t)))
.map(([providerAddress]) => providerAddress);
.map(([providerAddress]) => {
let gasCost: number;
if (typeof registry[providerAddress].gasCost === 'number') {
gasCost = registry[providerAddress].gasCost as number;
} else {
gasCost = (registry[providerAddress].gasCost as (takerToken: string, makerToken: string) => number)(
takerToken,
makerToken,
);
}
return {
providerAddress,
gasCost,
};
});
}

View File

@@ -89,6 +89,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo');
case ERC20BridgeSource.Kyber:
return encodeBridgeSourceId(BridgeProtocol.Kyber, 'Kyber');
case ERC20BridgeSource.KyberDmm:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'KyberDmm');
case ERC20BridgeSource.LiquidityProvider:
// "LiquidityProvider" is too long to encode (17 characters).
return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP');
@@ -126,6 +128,12 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Belt');
case ERC20BridgeSource.Ellipsis:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Ellipsis');
case ERC20BridgeSource.Component:
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Component');
case ERC20BridgeSource.Smoothy:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Smoothy');
case ERC20BridgeSource.Saddle:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Saddle');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -153,6 +161,8 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Smoothy:
case ERC20BridgeSource.Saddle:
const curveFillData = (order as OptimizedMarketBridgeOrder<CurveFillData>).fillData;
bridgeData = encoder.encode([
curveFillData.pool.poolAddress,
@@ -176,6 +186,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.Linkswap:
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.BakerySwap:
case ERC20BridgeSource.KyberDmm:
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
break;
@@ -200,6 +211,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
bridgeData = encoder.encode([dodoV2FillData.poolAddress, dodoV2FillData.isSellBase]);
break;
case ERC20BridgeSource.Shell:
case ERC20BridgeSource.Component:
const shellFillData = (order as OptimizedMarketBridgeOrder<ShellFillData>).fillData;
bridgeData = encoder.encode([shellFillData.poolAddress]);
break;
@@ -299,14 +311,18 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Nerve]: curveEncoder,
[ERC20BridgeSource.Belt]: curveEncoder,
[ERC20BridgeSource.Ellipsis]: curveEncoder,
[ERC20BridgeSource.Smoothy]: curveEncoder,
[ERC20BridgeSource.Saddle]: curveEncoder,
// UniswapV2 like, (router, address[])
[ERC20BridgeSource.Bancor]: routerAddressPathEncoder,
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
[ERC20BridgeSource.SushiSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder,
[ERC20BridgeSource.Linkswap]: routerAddressPathEncoder,
[ERC20BridgeSource.KyberDmm]: routerAddressPathEncoder,
// Generic pools
[ERC20BridgeSource.Shell]: poolEncoder,
[ERC20BridgeSource.Component]: poolEncoder,
[ERC20BridgeSource.Mooniswap]: poolEncoder,
[ERC20BridgeSource.Eth2Dai]: poolEncoder,
[ERC20BridgeSource.MStable]: poolEncoder,

View File

@@ -12,7 +12,7 @@ import {
getCurveLikeInfosForPair,
getDodoV2Offsets,
getKyberOffsets,
getShellsForPair,
getShellLikeInfosForPair,
isAllowedKyberReserveId,
isBadTokenForSource,
isValidAddress,
@@ -24,7 +24,8 @@ import {
DODOV2_FACTORIES_BY_CHAIN_ID,
KYBER_CONFIG_BY_CHAIN_ID,
LINKSWAP_ROUTER_BY_CHAIN_ID,
LIQUIDITY_PROVIDER_REGISTRY,
LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID,
MAINNET_TOKENS,
MAKER_PSM_INFO_BY_CHAIN_ID,
MAX_UINT256,
MOONISWAP_REGISTRIES_BY_CHAIN_ID,
@@ -34,7 +35,6 @@ import {
NULL_BYTES,
OASIS_ROUTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
TOKENS,
UNISWAPV1_ROUTER_BY_CHAIN_ID,
ZERO_AMOUNT,
} from './constants';
@@ -87,6 +87,7 @@ export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSour
* for use with `DexOrderSampler.executeAsync()`.
*/
export class SamplerOperations {
public readonly liquidityProviderRegistry: LiquidityProviderRegistry;
protected _bancorService?: BancorService;
public static constant<T>(result: T): BatchedOperation<T> {
return {
@@ -102,9 +103,13 @@ export class SamplerOperations {
public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(),
public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(),
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
public readonly liquidityProviderRegistry: LiquidityProviderRegistry = LIQUIDITY_PROVIDER_REGISTRY,
liquidityProviderRegistry: LiquidityProviderRegistry = {},
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
) {
this.liquidityProviderRegistry = {
...LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID[chainId],
...liquidityProviderRegistry,
};
// Initialize the Bancor service, fetching paths in the background
bancorServiceFn()
.then(service => (this._bancorService = service))
@@ -277,12 +282,13 @@ export class SamplerOperations {
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
gasCost: number,
): SourceQuoteOperation<LiquidityProviderFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.LiquidityProvider,
fillData: {
poolAddress: providerAddress,
gasCost: this.liquidityProviderRegistry[providerAddress].gasCost,
gasCost,
},
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromLiquidityProvider,
@@ -295,12 +301,13 @@ export class SamplerOperations {
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
gasCost: number,
): SourceQuoteOperation<LiquidityProviderFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.LiquidityProvider,
fillData: {
poolAddress: providerAddress,
gasCost: this.liquidityProviderRegistry[providerAddress].gasCost,
gasCost,
},
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromLiquidityProvider,
@@ -396,6 +403,62 @@ export class SamplerOperations {
});
}
public getSmoothySellQuotes(
pool: CurveInfo,
fromTokenIdx: number,
toTokenIdx: number,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<CurveFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Smoothy,
fillData: {
pool,
fromTokenIdx,
toTokenIdx,
},
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromSmoothy,
params: [
{
poolAddress: pool.poolAddress,
sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector,
buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector,
},
new BigNumber(fromTokenIdx),
new BigNumber(toTokenIdx),
takerFillAmounts,
],
});
}
public getSmoothyBuyQuotes(
pool: CurveInfo,
fromTokenIdx: number,
toTokenIdx: number,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<CurveFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Smoothy,
fillData: {
pool,
fromTokenIdx,
toTokenIdx,
},
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromSmoothy,
params: [
{
poolAddress: pool.poolAddress,
sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector,
buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector,
},
new BigNumber(fromTokenIdx),
new BigNumber(toTokenIdx),
makerFillAmounts,
],
});
}
public getBalancerSellQuotes(
poolAddress: string,
makerToken: string,
@@ -743,9 +806,10 @@ export class SamplerOperations {
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
source: ERC20BridgeSource = ERC20BridgeSource.Shell,
): SourceQuoteOperation<ShellFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Shell,
source,
fillData: { poolAddress },
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromShell,
@@ -758,9 +822,10 @@ export class SamplerOperations {
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
source: ERC20BridgeSource = ERC20BridgeSource.Shell,
): SourceQuoteOperation {
return new SamplerContractOperation({
source: ERC20BridgeSource.Shell,
source,
fillData: { poolAddress },
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromShell,
@@ -1022,6 +1087,7 @@ export class SamplerOperations {
case ERC20BridgeSource.CryptoCom:
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.BakerySwap:
case ERC20BridgeSource.KyberDmm:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];
@@ -1046,6 +1112,7 @@ export class SamplerOperations {
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Saddle:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveSellQuotes(
pool,
@@ -1055,13 +1122,33 @@ export class SamplerOperations {
source,
),
);
case ERC20BridgeSource.Smoothy:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getSmoothySellQuotes(
pool,
pool.tokens.indexOf(takerToken),
pool.tokens.indexOf(makerToken),
takerFillAmounts,
),
);
case ERC20BridgeSource.Shell:
case ERC20BridgeSource.Component:
return getShellLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getShellSellQuotes(pool, makerToken, takerToken, takerFillAmounts, source),
);
case ERC20BridgeSource.LiquidityProvider:
return getLiquidityProvidersForPair(
this.liquidityProviderRegistry,
takerToken,
makerToken,
).map(pool =>
this.getLiquidityProviderSellQuotes(pool, makerToken, takerToken, takerFillAmounts),
).map(({ providerAddress, gasCost }) =>
this.getLiquidityProviderSellQuotes(
providerAddress,
makerToken,
takerToken,
takerFillAmounts,
gasCost,
),
);
case ERC20BridgeSource.MStable:
return isValidAddress(MSTABLE_ROUTER_BY_CHAIN_ID[this.chainId])
@@ -1104,10 +1191,6 @@ export class SamplerOperations {
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Shell:
return getShellsForPair(this.chainId, takerToken, makerToken).map(pool =>
this.getShellSellQuotes(pool, makerToken, takerToken, takerFillAmounts),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODO_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];
@@ -1151,7 +1234,7 @@ export class SamplerOperations {
return [
[takerToken, makerToken],
...getIntermediateTokens(makerToken, takerToken, {
default: [TOKENS.LINK, TOKENS.WETH],
default: [MAINNET_TOKENS.LINK, MAINNET_TOKENS.WETH],
}).map(t => [takerToken, t, makerToken]),
].map(path =>
this.getUniswapV2SellQuotes(
@@ -1213,6 +1296,7 @@ export class SamplerOperations {
case ERC20BridgeSource.CryptoCom:
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.BakerySwap:
case ERC20BridgeSource.KyberDmm:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];
@@ -1237,6 +1321,7 @@ export class SamplerOperations {
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Saddle:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveBuyQuotes(
pool,
@@ -1246,13 +1331,33 @@ export class SamplerOperations {
source,
),
);
case ERC20BridgeSource.Smoothy:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getSmoothyBuyQuotes(
pool,
pool.tokens.indexOf(takerToken),
pool.tokens.indexOf(makerToken),
makerFillAmounts,
),
);
case ERC20BridgeSource.Shell:
case ERC20BridgeSource.Component:
return getShellLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getShellBuyQuotes(pool, makerToken, takerToken, makerFillAmounts, source),
);
case ERC20BridgeSource.LiquidityProvider:
return getLiquidityProvidersForPair(
this.liquidityProviderRegistry,
takerToken,
makerToken,
).map(pool =>
this.getLiquidityProviderBuyQuotes(pool, makerToken, takerToken, makerFillAmounts),
).map(({ providerAddress, gasCost }) =>
this.getLiquidityProviderBuyQuotes(
providerAddress,
makerToken,
takerToken,
makerFillAmounts,
gasCost,
),
);
case ERC20BridgeSource.MStable:
return isValidAddress(MSTABLE_ROUTER_BY_CHAIN_ID[this.chainId])
@@ -1295,10 +1400,6 @@ export class SamplerOperations {
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Shell:
return getShellsForPair(this.chainId, takerToken, makerToken).map(pool =>
this.getShellBuyQuotes(pool, makerToken, takerToken, makerFillAmounts),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODO_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];
@@ -1337,7 +1438,7 @@ export class SamplerOperations {
[takerToken, makerToken],
// LINK is the base asset in many of the pools on Linkswap
...getIntermediateTokens(makerToken, takerToken, {
default: [TOKENS.LINK, TOKENS.WETH],
default: [MAINNET_TOKENS.LINK, MAINNET_TOKENS.WETH],
}).map(t => [takerToken, t, makerToken]),
].map(path =>
this.getUniswapV2BuyQuotes(

View File

@@ -59,7 +59,11 @@ export enum ERC20BridgeSource {
DodoV2 = 'DODO_V2',
CryptoCom = 'CryptoCom',
Linkswap = 'Linkswap',
// Other
KyberDmm = 'KyberDMM',
Smoothy = 'Smoothy',
Component = 'Component',
Saddle = 'Saddle',
// BSC only
PancakeSwap = 'PancakeSwap',
BakerySwap = 'BakerySwap',
Nerve = 'Nerve',
@@ -79,9 +83,12 @@ export enum CurveFunctionSelectors {
get_dx_underlying = '0x0e71d1b9',
get_dy = '0x5e0d443f',
get_dx = '0x67df02ca',
// Nerve BSC
swap = '0x91695586',
calculateSwap = '0xa95b089f',
// Smoothy
swap_uint256 = '0x5673b02d', // swap(uint256,uint256,uint256,uint256)
get_swap_amount = '0x45cf2ef6', // getSwapAmount(uint256,uint256,uint256)
// Nerve BSC, Saddle Mainnet
swap = '0x91695586', // swap(uint8,uint8,uint256,uint256,uint256)
calculateSwap = '0xa95b089f', // calculateSwap(uint8,uint8,uint256)
}
// tslint:enable: enum-naming
@@ -95,6 +102,7 @@ export interface CurveInfo {
poolAddress: string;
tokens: string[];
metaToken: string | undefined;
gasSchedule: number;
}
/**
@@ -439,7 +447,7 @@ export interface TokenAdjacencyGraph {
export interface LiquidityProviderRegistry {
[address: string]: {
tokens: string[];
gasCost: number;
gasCost: number | ((takerToken: string, makerToken: string) => number);
};
}

View File

@@ -129,11 +129,10 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult
};
// Adjust the output by 1-slippage for the worst case if it is a sell
// Adjust the output by 1+slippage for the worst case if it is a buy
const outputMultiplier =
result.output =
quoteInfo.side === MarketOperation.Sell
? new BigNumber(1).minus(opts.slippage)
: new BigNumber(1).plus(opts.slippage);
result.output = result.output.times(outputMultiplier).integerValue();
? result.output.times(1 - opts.slippage).integerValue(BigNumber.ROUND_DOWN)
: result.output.times(1 + opts.slippage).integerValue(BigNumber.ROUND_UP);
return fromIntermediateQuoteFillResult(result, quoteInfo);
}

View File

@@ -25,6 +25,7 @@ import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json';
import * as IMStable from '../test/generated-artifacts/IMStable.json';
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
import * as IShell from '../test/generated-artifacts/IShell.json';
import * as ISmoothy from '../test/generated-artifacts/ISmoothy.json';
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
import * as KyberSampler from '../test/generated-artifacts/KyberSampler.json';
@@ -36,6 +37,7 @@ import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSamp
import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json';
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
import * as SmoothySampler from '../test/generated-artifacts/SmoothySampler.json';
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
@@ -62,6 +64,7 @@ export const artifacts = {
NativeOrderSampler: NativeOrderSampler as ContractArtifact,
SamplerUtils: SamplerUtils as ContractArtifact,
ShellSampler: ShellSampler as ContractArtifact,
SmoothySampler: SmoothySampler as ContractArtifact,
TwoHopSampler: TwoHopSampler as ContractArtifact,
UniswapSampler: UniswapSampler as ContractArtifact,
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
@@ -75,6 +78,7 @@ export const artifacts = {
IMooniswap: IMooniswap as ContractArtifact,
IMultiBridge: IMultiBridge as ContractArtifact,
IShell: IShell as ContractArtifact,
ISmoothy: ISmoothy as ContractArtifact,
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,

View File

@@ -3,7 +3,7 @@ import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine
import { RPCSubprovider } from '@0x/subproviders';
import { BigNumber, NULL_BYTES, providerUtils } from '@0x/utils';
import { KYBER_CONFIG_BY_CHAIN_ID, TOKENS } from '../../src/utils/market_operation_utils/constants';
import { KYBER_CONFIG_BY_CHAIN_ID, MAINNET_TOKENS } from '../../src/utils/market_operation_utils/constants';
import { artifacts } from '../artifacts';
import { ERC20BridgeSamplerContract } from '../wrappers';
@@ -80,9 +80,9 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
});
});
describe('Kyber', () => {
const WETH = TOKENS.WETH;
const DAI = TOKENS.DAI;
const USDC = TOKENS.USDC;
const WETH = MAINNET_TOKENS.WETH;
const DAI = MAINNET_TOKENS.DAI;
const USDC = MAINNET_TOKENS.USDC;
const RESERVE_OFFSET = new BigNumber(0);
const KYBER_OPTS = {
...KYBER_CONFIG_BY_CHAIN_ID[ChainId.Mainnet],

View File

@@ -68,6 +68,10 @@ const DEFAULT_EXCLUDED = [
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.MakerPsm,
ERC20BridgeSource.KyberDmm,
ERC20BridgeSource.Smoothy,
ERC20BridgeSource.Component,
ERC20BridgeSource.Saddle,
];
const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
@@ -282,32 +286,12 @@ describe('MarketOperationUtils tests', () => {
[source: string]: Numberish[];
}
const ZERO_RATES: RatesBySource = {
[ERC20BridgeSource.Native]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Eth2Dai]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Uniswap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Kyber]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.UniswapV2]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Balancer]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Bancor]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Curve]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.MStable]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Mooniswap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Swerve]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.SnowSwap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.SushiSwap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.MultiHop]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Shell]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Cream]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Dodo]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.DodoV2]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.CryptoCom]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Linkswap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.PancakeSwap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.BakerySwap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.MakerPsm]: _.times(NUM_SAMPLES, () => 0),
};
const ZERO_RATES: RatesBySource = Object.assign(
{},
...Object.values(ERC20BridgeSource).map(source => ({
[source]: _.times(NUM_SAMPLES, () => 0),
})),
);
const DEFAULT_RATES: RatesBySource = {
...ZERO_RATES,
@@ -358,12 +342,35 @@ describe('MarketOperationUtils tests', () => {
fromTokenIdx: 0,
toTokenIdx: 1,
},
[ERC20BridgeSource.Smoothy]: {
pool: {
poolAddress: randomAddress(),
tokens: [TAKER_TOKEN, MAKER_TOKEN],
exchangeFunctionSelector: hexUtils.random(4),
sellQuoteFunctionSelector: hexUtils.random(4),
buyQuoteFunctionSelector: hexUtils.random(4),
},
fromTokenIdx: 0,
toTokenIdx: 1,
},
[ERC20BridgeSource.Saddle]: {
pool: {
poolAddress: randomAddress(),
tokens: [TAKER_TOKEN, MAKER_TOKEN],
exchangeFunctionSelector: hexUtils.random(4),
sellQuoteFunctionSelector: hexUtils.random(4),
buyQuoteFunctionSelector: hexUtils.random(4),
},
fromTokenIdx: 0,
toTokenIdx: 1,
},
[ERC20BridgeSource.LiquidityProvider]: { poolAddress: randomAddress() },
[ERC20BridgeSource.SushiSwap]: { tokenAddressPath: [] },
[ERC20BridgeSource.Mooniswap]: { poolAddress: randomAddress() },
[ERC20BridgeSource.Native]: { order: new LimitOrder() },
[ERC20BridgeSource.MultiHop]: {},
[ERC20BridgeSource.Shell]: { poolAddress: randomAddress() },
[ERC20BridgeSource.Component]: { poolAddress: randomAddress() },
[ERC20BridgeSource.Cream]: { poolAddress: randomAddress() },
[ERC20BridgeSource.Dodo]: {},
[ERC20BridgeSource.DodoV2]: {},
@@ -372,6 +379,7 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.Uniswap]: { router: randomAddress() },
[ERC20BridgeSource.Eth2Dai]: { router: randomAddress() },
[ERC20BridgeSource.MakerPsm]: {},
[ERC20BridgeSource.KyberDmm]: { tokenAddressPath: [] },
};
const DEFAULT_OPS = {
@@ -907,6 +915,7 @@ describe('MarketOperationUtils tests', () => {
intentOnFilling: true,
quoteRequestor: {
requestRfqtIndicativeQuotesAsync: requestor.object.requestRfqtIndicativeQuotesAsync,
getMakerUriForSignature: requestor.object.getMakerUriForSignature,
} as any,
},
},

View File

@@ -23,6 +23,7 @@ export * from '../test/generated-wrappers/i_m_stable';
export * from '../test/generated-wrappers/i_mooniswap';
export * from '../test/generated-wrappers/i_multi_bridge';
export * from '../test/generated-wrappers/i_shell';
export * from '../test/generated-wrappers/i_smoothy';
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
export * from '../test/generated-wrappers/kyber_sampler';
@@ -34,6 +35,7 @@ export * from '../test/generated-wrappers/multi_bridge_sampler';
export * from '../test/generated-wrappers/native_order_sampler';
export * from '../test/generated-wrappers/sampler_utils';
export * from '../test/generated-wrappers/shell_sampler';
export * from '../test/generated-wrappers/smoothy_sampler';
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
export * from '../test/generated-wrappers/test_native_order_sampler';
export * from '../test/generated-wrappers/two_hop_sampler';

View File

@@ -26,6 +26,7 @@
"test/generated-artifacts/IMooniswap.json",
"test/generated-artifacts/IMultiBridge.json",
"test/generated-artifacts/IShell.json",
"test/generated-artifacts/ISmoothy.json",
"test/generated-artifacts/IUniswapExchangeQuotes.json",
"test/generated-artifacts/IUniswapV2Router01.json",
"test/generated-artifacts/KyberSampler.json",
@@ -37,6 +38,7 @@
"test/generated-artifacts/NativeOrderSampler.json",
"test/generated-artifacts/SamplerUtils.json",
"test/generated-artifacts/ShellSampler.json",
"test/generated-artifacts/SmoothySampler.json",
"test/generated-artifacts/TestERC20BridgeSampler.json",
"test/generated-artifacts/TestNativeOrderSampler.json",
"test/generated-artifacts/TwoHopSampler.json",

View File

@@ -1,7 +1,8 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"max-file-line-count": false
"max-file-line-count": false,
"binary-expression-operand-order": false
},
"linterOptions": {
"exclude": ["src/artifacts.ts", "test/artifacts.ts"]

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-wrappers-test",
"version": "12.2.39",
"version": "12.2.40",
"engines": {
"node": ">=6.12"
},
@@ -34,7 +34,7 @@
"@0x/contract-wrappers": "^13.15.0",
"@0x/contracts-test-utils": "^5.3.24",
"@0x/dev-utils": "^4.2.1",
"@0x/migrations": "^8.0.0",
"@0x/migrations": "^8.0.1",
"@0x/order-utils": "^10.4.19",
"@0x/subproviders": "^6.4.1",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1618259868,
"version": "8.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "8.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v8.0.1 - _April 12, 2021_
* Dependencies updated
## v8.0.0 - _April 1, 2021_
* Remove exchangeProxyAllowanceTarget

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/migrations",
"version": "8.0.0",
"version": "8.0.1",
"engines": {
"node": ">=6.12"
},
@@ -81,7 +81,7 @@
"@0x/contracts-multisig": "^4.1.28",
"@0x/contracts-staking": "^2.0.35",
"@0x/contracts-utils": "^4.7.6",
"@0x/contracts-zero-ex": "^0.21.0",
"@0x/contracts-zero-ex": "^0.21.1",
"@0x/sol-compiler": "^4.6.1",
"@0x/subproviders": "^6.4.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,7 +1,20 @@
[
{
"timestamp": 1618259868,
"version": "1.4.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.4.0",
"changes": [
{
"note": "Added Smoothy, Component, Saddle",
"pr": 182
},
{
"note": "Added Nerve",
"pr": 181

View File

@@ -5,8 +5,13 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.4.1 - _April 12, 2021_
* Dependencies updated
## v1.4.0 - _April 1, 2021_
* Added Smoothy, Component, Saddle (#182)
* Added Nerve (#181)
## v1.3.1 - _March 17, 2021_

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/protocol-utils",
"version": "1.4.0",
"version": "1.4.1",
"engines": {
"node": ">=6.12"
},

View File

@@ -126,6 +126,9 @@ export enum BridgeProtocol {
CoFiX,
Nerve,
MakerPsm,
Smoothy,
Component,
Saddle,
}
// tslint:enable: enum-naming