feat: better Kyber quotes (#2683)

* feat: Kyber rework

* Get additional reserves per token
This commit is contained in:
Jacob Evans 2020-08-27 15:58:57 +10:00 committed by GitHub
parent 2f9b894d71
commit a2f0d5eedf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 444 additions and 394 deletions

View File

@ -21,6 +21,10 @@
{ {
"note": "Added `MooniswapBridge`", "note": "Added `MooniswapBridge`",
"pr": 2675 "pr": 2675
},
{
"note": "Reworked `KyberBridge`",
"pr": 2683
} }
] ]
}, },

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -45,6 +45,7 @@ contract KyberBridge is
uint256 fromTokenBalance; uint256 fromTokenBalance;
uint256 payableAmount; uint256 payableAmount;
uint256 conversionRate; uint256 conversionRate;
bytes hint;
} }
/// @dev Kyber ETH pseudo-address. /// @dev Kyber ETH pseudo-address.
@ -85,47 +86,35 @@ contract KyberBridge is
state.kyber = IKyberNetworkProxy(_getKyberNetworkProxyAddress()); state.kyber = IKyberNetworkProxy(_getKyberNetworkProxyAddress());
state.weth = IEtherToken(_getWethAddress()); state.weth = IEtherToken(_getWethAddress());
// Decode the bridge data to get the `fromTokenAddress`. // Decode the bridge data to get the `fromTokenAddress`.
(state.fromTokenAddress) = abi.decode(bridgeData, (address)); (state.fromTokenAddress, state.hint) = abi.decode(bridgeData, (address, bytes));
// Query the balance of "from" tokens. // Query the balance of "from" tokens.
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this)); state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
if (state.fromTokenBalance == 0) { if (state.fromTokenBalance == 0) {
// Return failure if no input tokens. // Return failure if no input tokens.
return BRIDGE_FAILED; return BRIDGE_FAILED;
} }
// Compute the conversion rate, expressed in 18 decimals.
// The sequential notation is to get around stack limits.
state.conversionRate = KYBER_RATE_BASE;
state.conversionRate = state.conversionRate.safeMul(amount);
state.conversionRate = state.conversionRate.safeMul(
10 ** uint256(LibERC20Token.decimals(state.fromTokenAddress))
);
state.conversionRate = state.conversionRate.safeDiv(state.fromTokenBalance);
state.conversionRate = state.conversionRate.safeDiv(
10 ** uint256(LibERC20Token.decimals(toTokenAddress))
);
if (state.fromTokenAddress == toTokenAddress) { if (state.fromTokenAddress == toTokenAddress) {
// Just transfer the tokens if they're the same. // Just transfer the tokens if they're the same.
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance); LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
return BRIDGE_SUCCESS; return BRIDGE_SUCCESS;
} else if (state.fromTokenAddress != address(state.weth)) { }
// If the input token is not WETH, grant an allowance to the exchange if (state.fromTokenAddress == address(state.weth)) {
// to spend them. // From WETH
state.fromTokenAddress = KYBER_ETH_ADDRESS;
state.payableAmount = state.fromTokenBalance;
state.weth.withdraw(state.fromTokenBalance);
} else {
LibERC20Token.approveIfBelow( LibERC20Token.approveIfBelow(
state.fromTokenAddress, state.fromTokenAddress,
address(state.kyber), address(state.kyber),
state.fromTokenBalance state.fromTokenBalance
); );
} else {
// If the input token is WETH, unwrap it and attach it to the call.
state.fromTokenAddress = KYBER_ETH_ADDRESS;
state.payableAmount = state.fromTokenBalance;
state.weth.withdraw(state.fromTokenBalance);
} }
bool isToTokenWeth = toTokenAddress == address(state.weth); bool isToTokenWeth = toTokenAddress == address(state.weth);
// Try to sell all of this contract's input token balance through // Try to sell all of this contract's input token balance through
// `KyberNetworkProxy.trade()`. // `KyberNetworkProxy.trade()`.
uint256 boughtAmount = state.kyber.trade.value(state.payableAmount)( uint256 boughtAmount = state.kyber.tradeWithHint.value(state.payableAmount)(
// Input token. // Input token.
state.fromTokenAddress, state.fromTokenAddress,
// Sell amount. // Sell amount.
@ -137,11 +126,11 @@ contract KyberBridge is
isToTokenWeth ? address(uint160(address(this))) : address(uint160(to)), isToTokenWeth ? address(uint160(address(this))) : address(uint160(to)),
// Buy as much as possible. // Buy as much as possible.
uint256(-1), uint256(-1),
// Compute the minimum conversion rate, which is expressed in units with // The minimum conversion rate
// 18 decimal places. 1,
state.conversionRate,
// No affiliate address. // No affiliate address.
address(0) address(0),
state.hint
); );
// Wrap ETH output and transfer to recipient. // Wrap ETH output and transfer to recipient.
if (isToTokenWeth) { if (isToTokenWeth) {
@ -173,4 +162,5 @@ contract KyberBridge is
{ {
return LEGACY_WALLET_MAGIC_VALUE; return LEGACY_WALLET_MAGIC_VALUE;
} }
} }

View File

@ -43,4 +43,30 @@ interface IKyberNetworkProxy {
external external
payable payable
returns (uint256 boughtAmount); returns (uint256 boughtAmount);
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens
/// using a hint for the reserve.
/// @param sellTokenAddress Token to sell.
/// @param sellAmount Amount of tokens to sell.
/// @param buyTokenAddress Token to buy.
/// @param recipientAddress Address to send bought tokens to.
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
/// @param minConversionRate The minimal conversion rate. If actual rate
/// is lower, trade is canceled.
/// @param walletId The wallet ID to send part of the fees
/// @param hint The hint for the selective inclusion (or exclusion) of reserves
/// @return boughtAmount Amount of tokens bought.
function tradeWithHint(
address sellTokenAddress,
uint256 sellAmount,
address buyTokenAddress,
address payable recipientAddress,
uint256 maxBuyTokenAmount,
uint256 minConversionRate,
address payable walletId,
bytes calldata hint
)
external
payable
returns (uint256 boughtAmount);
} }

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -28,6 +28,8 @@ contract DeploymentConstants {
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev Mainnet address of the KyberNetworkProxy contract. /// @dev Mainnet address of the KyberNetworkProxy contract.
address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x9AAb3f75489902f3a48495025729a0AF77d4b11e; address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x9AAb3f75489902f3a48495025729a0AF77d4b11e;
/// @dev Mainnet address of the KyberHintHandler contract.
address constant private KYBER_HINT_HANDLER_ADDRESS = 0xa1C0Fa73c39CFBcC11ec9Eb1Afc665aba9996E2C;
/// @dev Mainnet address of the `UniswapExchangeFactory` contract. /// @dev Mainnet address of the `UniswapExchangeFactory` contract.
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95; address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
/// @dev Mainnet address of the `UniswapV2Router01` contract. /// @dev Mainnet address of the `UniswapV2Router01` contract.
@ -155,6 +157,16 @@ contract DeploymentConstants {
return KYBER_NETWORK_PROXY_ADDRESS; return KYBER_NETWORK_PROXY_ADDRESS;
} }
/// @dev Overridable way to get the `KyberHintHandler` address.
/// @return kyberAddress The `IKyberHintHandler` address.
function _getKyberHintHandlerAddress()
internal
view
returns (address hintHandlerAddress)
{
return KYBER_HINT_HANDLER_ADDRESS;
}
/// @dev Overridable way to get the WETH address. /// @dev Overridable way to get the WETH address.
/// @return wethAddress The WETH address. /// @return wethAddress The WETH address.
function _getWethAddress() function _getWethAddress()

View File

@ -73,6 +73,10 @@
{ {
"note": "Use on-chain sampling (sometimes) for Balancer", "note": "Use on-chain sampling (sometimes) for Balancer",
"pr": 2647 "pr": 2647
},
{
"note": "Re-worked `Kyber` quotes supporting multiple reserves",
"pr": 2683
} }
] ]
}, },

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -21,9 +21,6 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "./interfaces/IKyberNetwork.sol"; import "./interfaces/IKyberNetwork.sol";
import "./interfaces/IKyberNetworkProxy.sol";
import "./interfaces/IKyberStorage.sol";
import "./interfaces/IKyberHintHandler.sol";
import "./ApproximateBuys.sol"; import "./ApproximateBuys.sol";
import "./SamplerUtils.sol"; import "./SamplerUtils.sol";
@ -34,73 +31,130 @@ contract KyberSampler is
ApproximateBuys ApproximateBuys
{ {
/// @dev Gas limit for Kyber calls. /// @dev Gas limit for Kyber calls.
uint256 constant private KYBER_CALL_GAS = 1500e3; // 1.5m uint256 constant private KYBER_CALL_GAS = 500e3; // 500k
/// @dev The Kyber Uniswap Reserve address
address constant private KYBER_UNISWAP_RESERVE = 0x31E085Afd48a1d6e51Cc193153d625e8f0514C7F;
/// @dev The Kyber Uniswap V2 Reserve address
address constant private KYBER_UNISWAPV2_RESERVE = 0x10908C875D865C66f271F5d3949848971c9595C9;
/// @dev The Kyber Eth2Dai Reserve address
address constant private KYBER_ETH2DAI_RESERVE = 0x1E158c0e93c30d24e918Ef83d1e0bE23595C3c0f;
/// @dev Sample sell quotes from Kyber. /// @dev Sample sell quotes from Kyber.
/// @param reserveId The selected kyber reserve
/// @param takerToken Address of the taker token (what to sell). /// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy). /// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample. /// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token /// @return hint The hint for the selected reserve
/// amount. /// @return makerTokenAmounts Maker amounts bought at each taker token amount.
function sampleSellsFromKyberNetwork( function sampleSellsFromKyberNetwork(
bytes32 reserveId,
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] memory takerTokenAmounts uint256[] memory takerTokenAmounts
) )
public public
view view
returns (uint256[] memory makerTokenAmounts) returns (bytes memory hint, uint256[] memory makerTokenAmounts)
{ {
_assertValidPair(makerToken, takerToken); _assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length; uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples); makerTokenAmounts = new uint256[](numSamples);
address wethAddress = _getWethAddress(); hint = this.encodeKyberHint(reserveId, takerToken, makerToken);
uint256 value;
for (uint256 i = 0; i < numSamples; i++) { for (uint256 i = 0; i < numSamples; i++) {
if (takerToken == wethAddress || makerToken == wethAddress) { uint256 value = this.sampleSellFromKyberNetwork(hint, takerToken, makerToken, takerTokenAmounts[i]);
// Direct ETH based trade // Return early if the source has no liquidity
value = _sampleSellFromKyberNetwork(takerToken, makerToken, takerTokenAmounts[i]); if (value == 0) {
} else { return (hint, makerTokenAmounts);
// Hop to ETH
value = _sampleSellFromKyberNetwork(takerToken, wethAddress, takerTokenAmounts[i]);
if (value != 0) {
value = _sampleSellFromKyberNetwork(wethAddress, makerToken, value);
}
} }
makerTokenAmounts[i] = value; makerTokenAmounts[i] = value;
} }
} }
/// @dev Sample buy quotes from Kyber. /// @dev Sample buy quotes from Kyber.
/// @param reserveId The selected kyber reserve
/// @param takerToken Address of the taker token (what to sell). /// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy). /// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample. /// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token /// @return hint The hint for the selected reserve
/// amount. /// @return takerTokenAmounts Taker amounts sold at each maker token amount.
function sampleBuysFromKyberNetwork( function sampleBuysFromKyberNetwork(
bytes32 reserveId,
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] memory makerTokenAmounts uint256[] memory makerTokenAmounts
) )
public public
view view
returns (uint256[] memory takerTokenAmounts) returns (bytes memory hint, uint256[] memory takerTokenAmounts)
{ {
_assertValidPair(makerToken, takerToken); _assertValidPair(makerToken, takerToken);
return _sampleApproximateBuys( hint = this.encodeKyberHint(reserveId, takerToken, makerToken);
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({ ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken), makerTokenData: abi.encode(makerToken, hint),
takerTokenData: abi.encode(takerToken), takerTokenData: abi.encode(takerToken, hint),
getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber
}), }),
makerTokenAmounts makerTokenAmounts
); );
return (hint, takerTokenAmounts);
}
function encodeKyberHint(
bytes32 reserveId,
address takerToken,
address makerToken
)
public
view
returns (bytes memory hint)
{
// Build a hint selecting the single reserve
IKyberHintHandler kyberHint = IKyberHintHandler(_getKyberHintHandlerAddress());
// All other reserves should be ignored with this hint
bytes32[] memory selectedReserves = new bytes32[](1);
selectedReserves[0] = reserveId;
bool didSucceed;
bytes memory resultData;
if (takerToken == _getWethAddress()) {
// ETH to Token
(didSucceed, resultData) =
address(kyberHint).staticcall.gas(KYBER_CALL_GAS)(
abi.encodeWithSelector(
IKyberHintHandler(0).buildEthToTokenHint.selector,
makerToken,
IKyberHintHandler.TradeType.MaskIn,
selectedReserves,
new uint256[](0)));
} else if (makerToken == _getWethAddress()) {
// Token to ETH
(didSucceed, resultData) =
address(kyberHint).staticcall.gas(KYBER_CALL_GAS)(
abi.encodeWithSelector(
IKyberHintHandler(0).buildTokenToEthHint.selector,
takerToken,
IKyberHintHandler.TradeType.MaskIn,
selectedReserves,
new uint256[](0)));
} else {
// Token to Token
// We use the same reserve both ways
(didSucceed, resultData) =
address(kyberHint).staticcall.gas(KYBER_CALL_GAS)(
abi.encodeWithSelector(
IKyberHintHandler(0).buildTokenToTokenHint.selector,
takerToken,
IKyberHintHandler.TradeType.MaskIn,
selectedReserves,
new uint256[](0),
makerToken,
IKyberHintHandler.TradeType.MaskIn,
selectedReserves,
new uint256[](0)
)
);
}
// If successful decode the hint
if (didSucceed) {
hint = abi.decode(resultData, (bytes));
}
return hint;
} }
function _sampleSellForApproximateBuyFromKyber( function _sampleSellForApproximateBuyFromKyber(
@ -112,82 +166,40 @@ contract KyberSampler is
view view
returns (uint256 buyAmount) returns (uint256 buyAmount)
{ {
(address makerToken, bytes memory hint) =
abi.decode(makerTokenData, (address, bytes));
(address takerToken, ) =
abi.decode(takerTokenData, (address, bytes));
(bool success, bytes memory resultData) = (bool success, bytes memory resultData) =
address(this).staticcall(abi.encodeWithSelector( address(this).staticcall(abi.encodeWithSelector(
this.sampleSellsFromKyberNetwork.selector, this.sampleSellFromKyberNetwork.selector,
abi.decode(takerTokenData, (address)), hint,
abi.decode(makerTokenData, (address)), takerToken,
_toSingleValueArray(sellAmount) makerToken,
sellAmount
)); ));
if (!success) { if (!success) {
return 0; return 0;
} }
// solhint-disable-next-line indent // solhint-disable-next-line indent
return abi.decode(resultData, (uint256[]))[0]; return abi.decode(resultData, (uint256));
} }
function _appendToList(bytes32[] memory list, bytes32 item) private view returns (bytes32[] memory appendedList) function sampleSellFromKyberNetwork(
{ bytes memory hint,
appendedList = new bytes32[](list.length + 1);
for (uint256 i = 0; i < list.length; i++) {
appendedList[i] = list[i];
}
appendedList[appendedList.length - 1] = item;
}
function _getKyberAddresses()
private
view
returns (IKyberHintHandler kyberHint, IKyberStorage kyberStorage)
{
(, , kyberHint, kyberStorage, ,) = IKyberNetwork(
IKyberNetworkProxy(_getKyberNetworkProxyAddress()).kyberNetwork()).getContracts();
return (IKyberHintHandler(kyberHint), IKyberStorage(kyberStorage));
}
function _sampleSellFromKyberNetwork(
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256 takerTokenAmount uint256 takerTokenAmount
) )
private public
view view
returns (uint256 makerTokenAmount) returns (uint256 makerTokenAmount)
{ {
(IKyberHintHandler kyberHint, IKyberStorage kyberStorage) = _getKyberAddresses(); // If there is no hint do not continue
// Ban reserves which can clash with our internal aggregation if (hint.length == 0) {
bytes32[] memory reserveIds = kyberStorage.getReserveIdsPerTokenSrc( return 0;
takerToken == _getWethAddress() ? makerToken : takerToken
);
bytes32[] memory bannedReserveIds = new bytes32[](0);
// Poor mans resize and append
for (uint256 i = 0; i < reserveIds.length; i++) {
if (
reserveIds[i] == kyberStorage.getReserveId(KYBER_UNISWAP_RESERVE) ||
reserveIds[i] == kyberStorage.getReserveId(KYBER_UNISWAPV2_RESERVE) ||
reserveIds[i] == kyberStorage.getReserveId(KYBER_ETH2DAI_RESERVE)
) {
bannedReserveIds = _appendToList(bannedReserveIds, reserveIds[i]);
}
}
// Sampler either detects X->ETH/ETH->X
// or subsamples as X->ETH-Y. So token->token here is not possible
bytes memory hint;
if (takerToken == _getWethAddress()) {
// ETH -> X
hint = kyberHint.buildEthToTokenHint(
makerToken,
IKyberHintHandler.TradeType.MaskOut,
bannedReserveIds,
new uint256[](0));
} else {
// X->ETH
hint = kyberHint.buildEthToTokenHint(
takerToken,
IKyberHintHandler.TradeType.MaskOut,
bannedReserveIds,
new uint256[](0));
} }
(bool didSucceed, bytes memory resultData) = (bool didSucceed, bytes memory resultData) =
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)( _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)(
abi.encodeWithSelector( abi.encodeWithSelector(

View File

@ -1,52 +0,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.5.9;
interface IKyberHintHandler {
function kyberStorage() external returns (address);
enum TradeType {BestOfAll, MaskIn, MaskOut, Split}
function buildTokenToEthHint(
address tokenSrc,
TradeType tokenToEthType,
bytes32[] calldata tokenToEthReserveIds,
uint256[] calldata tokenToEthSplits
) external view returns (bytes memory hint);
function buildEthToTokenHint(
address tokenDest,
TradeType ethToTokenType,
bytes32[] calldata ethToTokenReserveIds,
uint256[] calldata ethToTokenSplits
) external view returns (bytes memory hint);
function buildTokenToTokenHint(
address tokenSrc,
TradeType tokenToEthType,
bytes32[] calldata tokenToEthReserveIds,
uint256[] calldata tokenToEthSplits,
address tokenDest,
TradeType ethToTokenType,
bytes32[] calldata ethToTokenReserveIds,
uint256[] calldata ethToTokenSplits
) external view returns (bytes memory hint);
}

View File

@ -18,20 +18,62 @@
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
import "./IKyberStorage.sol"; // Keepin everything together
import "./IKyberHintHandler.sol";
interface IKyberNetwork { interface IKyberNetwork {
function getContracts()
}
interface IKyberNetworkProxy {
function getExpectedRateAfterFee(
address src,
address dest,
uint256 srcQty,
uint256 platformFeeBps,
bytes calldata hint
)
external external
view view
returns ( returns (uint256 expectedRate);
address kyberFeeHandlerAddress, }
address kyberDaoAddress,
IKyberHintHandler kyberMatchingEngineAddress, interface IKyberHintHandler {
IKyberStorage kyberStorageAddress,
address gasHelperAddress, enum TradeType {BestOfAll, MaskIn, MaskOut, Split}
address[] memory kyberProxyAddresses);
function buildTokenToEthHint(
address tokenSrc,
TradeType tokenToEthType,
bytes32[] calldata tokenToEthReserveIds,
uint256[] calldata tokenToEthSplits
)
external
view
returns (bytes memory hint);
function buildEthToTokenHint(
address tokenDest,
TradeType ethToTokenType,
bytes32[] calldata ethToTokenReserveIds,
uint256[] calldata ethToTokenSplits
)
external
view
returns (bytes memory hint);
function buildTokenToTokenHint(
address tokenSrc,
TradeType tokenToEthType,
bytes32[] calldata tokenToEthReserveIds,
uint256[] calldata tokenToEthSplits,
address tokenDest,
TradeType ethToTokenType,
bytes32[] calldata ethToTokenReserveIds,
uint256[] calldata ethToTokenSplits
)
external
view
returns (bytes memory hint);
} }

View File

@ -1,34 +0,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.5.9;
interface IKyberNetworkProxy {
function kyberNetwork() external view returns (address);
function kyberHintHandler() external view returns (address);
function getExpectedRateAfterFee(
address src,
address dest,
uint256 srcQty,
uint256 platformFeeBps,
bytes calldata hint
) external view returns (uint256 expectedRate);
}

View File

@ -1,37 +0,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.5.9;
interface IKyberStorage {
function getReserveId(
address reserve
)
external
view
returns (bytes32 reserveId);
function getReserveIdsPerTokenSrc(
address token
)
external
view
returns (bytes32[] memory reserveIds);
}

View File

@ -23,7 +23,7 @@ import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../src/ERC20BridgeSampler.sol"; import "../src/ERC20BridgeSampler.sol";
import "../src/interfaces/IEth2Dai.sol"; import "../src/interfaces/IEth2Dai.sol";
import "../src/interfaces/IKyberNetworkProxy.sol"; import "../src/interfaces/IKyberNetwork.sol";
import "../src/interfaces/IUniswapV2Router01.sol"; import "../src/interfaces/IUniswapV2Router01.sol";
@ -253,76 +253,40 @@ contract TestERC20BridgeSamplerKyberNetwork is
enum TradeType {BestOfAll, MaskIn, MaskOut, Split} enum TradeType {BestOfAll, MaskIn, MaskOut, Split}
function kyberNetwork()
external
view
returns (address)
{
return address(this);
}
// IKyberNetwork
function getContracts()
external
view
returns (
address kyberFeeHandlerAddress,
address kyberDaoAddress,
address kyberMatchingEngineAddress,
address kyberStorageAddress,
address gasHelperAddress,
address[] memory kyberProxyAddresses
)
{
return (kyberFeeHandlerAddress,
kyberDaoAddress,
address(this),
address(this),
gasHelperAddress,
kyberProxyAddresses
);
}
// IKyberStorage
function getReserveIdsPerTokenSrc(
address /* token */
)
external
view
returns (bytes32[] memory reserveIds)
{
return reserveIds;
}
function getReserveId(
address /* reserve */
)
external
view
returns (bytes32 reserveId)
{
return reserveId;
}
// IKyberHintHandler // IKyberHintHandler
function buildTokenToEthHint( function buildTokenToEthHint(
address /* tokenSrc */, address tokenSrc,
TradeType /* tokenToEthType */, TradeType /* tokenToEthType */,
bytes32[] calldata /* tokenToEthReserveIds */, bytes32[] calldata /* tokenToEthReserveIds */,
uint256[] calldata /* tokenToEthSplits */ uint256[] calldata /* tokenToEthSplits */
) external view returns (bytes memory hint) ) external view returns (bytes memory hint)
{ {
return hint; return abi.encode(tokenSrc);
} }
function buildEthToTokenHint( function buildEthToTokenHint(
address /* tokenDest */, address tokenDest,
TradeType /* ethToTokenType */, TradeType /* ethToTokenType */,
bytes32[] calldata /* ethToTokenReserveIds */, bytes32[] calldata /* ethToTokenReserveIds */,
uint256[] calldata /* ethToTokenSplits */ uint256[] calldata /* ethToTokenSplits */
) external view returns (bytes memory hint) ) external view returns (bytes memory hint)
{ {
return hint; return abi.encode(tokenDest);
}
// IKyberHintHandler
function buildTokenToTokenHint(
address tokenSrc,
TradeType /* tokenToEthType */,
bytes32[] calldata /* tokenToEthReserveIds */,
uint256[] calldata /* tokenToEthSplits */,
address /* tokenDest */,
TradeType /* EthToTokenType */,
bytes32[] calldata /* EthToTokenReserveIds */,
uint256[] calldata /* EthToTokenSplits */
) external view returns (bytes memory hint)
{
return abi.encode(tokenSrc);
} }
// Deterministic `IKyberNetworkProxy.getExpectedRateAfterFee()`. // Deterministic `IKyberNetworkProxy.getExpectedRateAfterFee()`.
@ -375,6 +339,14 @@ contract TestERC20BridgeSamplerKyberNetwork is
{ {
return address(this); return address(this);
} }
function _getKyberHintHandlerAddress()
internal
view
returns (address)
{
return address(this);
}
} }
@ -533,4 +505,13 @@ contract TestERC20BridgeSampler is
{ {
return address(kyber); return address(kyber);
} }
// Overriden to point to a custom contract.
function _getKyberHintHandlerAddress()
internal
view
returns (address kyberAddress)
{
return address(kyber);
}
} }

View File

@ -38,7 +38,7 @@
"config": { "config": {
"publicInterfaceContracts": "ERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider", "publicInterfaceContracts": "ERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalancerSampler|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberHintHandler|IKyberNetwork|IKyberNetworkProxy|IKyberStorage|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json", "abis": "./test/generated-artifacts/@(ApproximateBuys|BalancerSampler|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json",
"postpublish": { "postpublish": {
"assets": [] "assets": []
} }

View File

@ -122,8 +122,8 @@ export {
SamplerContractOperation, SamplerContractOperation,
} from './utils/market_operation_utils/sampler_contract_operation'; } from './utils/market_operation_utils/sampler_contract_operation';
export { export {
BancorFillData,
BalancerFillData, BalancerFillData,
BancorFillData,
CollapsedFill, CollapsedFill,
CurveFillData, CurveFillData,
CurveFunctionSelectors, CurveFunctionSelectors,
@ -135,6 +135,7 @@ export {
FillData, FillData,
FillFlags, FillFlags,
GetMarketOrdersRfqtOpts, GetMarketOrdersRfqtOpts,
KyberFillData,
LiquidityProviderFillData, LiquidityProviderFillData,
MarketDepth, MarketDepth,
MarketDepthSide, MarketDepthSide,

View File

@ -132,6 +132,42 @@ export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = {
}, },
}; };
export const MAINNET_KYBER_RESERVE_IDS: { [name: string]: string } = {
Reserve1: '0xff4b796265722046707200000000000000000000000000000000000000000000',
Reserve2: '0xffabcd0000000000000000000000000000000000000000000000000000000000',
Reserve3: '0xff4f6e65426974205175616e7400000000000000000000000000000000000000',
};
export const MAINNET_KYBER_TOKEN_RESERVE_IDS: { [token: string]: string } = {
// USDC
['0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48']:
'0xaa55534443303041505200000000000000000000000000000000000000000000',
// AMPL
['0xd46ba6d942050d489dbd938a2c909a5d5039a161']:
'0xaad46ba6d942050d489dbd938a2c909a5d5039a1610000000000000000000000',
// UBT
['0x8400d94a5cb0fa0d041a3788e395285d61c9ee5e']:
'0xaa55425400000000000000000000000000000000000000000000000000000000',
// ANT
['0x960b236a07cf122663c4303350609a66a7b288c0']:
'0xaa414e5400000000000000000000000000000000000000000000000000000000',
// KNC
['0xdd974d5c2e2928dea5f71b9825b8b646686bd200']:
'0xaa4b4e435f4d4547414c41444f4e000000000000000000000000000000000000',
// sUSD
['0x57ab1ec28d129707052df4df418d58a2d46d5f51']:
'0xaa73555344000000000000000000000000000000000000000000000000000000',
// SNX
['0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f']:
'0xaa534e5800000000000000000000000000000000000000000000000000000000',
// REN
['0x408e41876cccdc0f92210600ef50372656052a38']:
'0xaa72656e00000000000000000000000000000000000000000000000000000000',
// BAND
['0xba11d00c5f74255f56a5e366f4f77f5a186d7f55']:
'0xaa42414e44000000000000000000000000000000000000000000000000000000',
};
export const ERC20_PROXY_ID = '0xf47261b0'; export const ERC20_PROXY_ID = '0xf47261b0';
export const WALLET_SIGNATURE = '0x04'; export const WALLET_SIGNATURE = '0x04';
export const ONE_ETHER = new BigNumber(1e18); export const ONE_ETHER = new BigNumber(1e18);

View File

@ -0,0 +1,10 @@
import { MAINNET_KYBER_RESERVE_IDS, MAINNET_KYBER_TOKEN_RESERVE_IDS } from './constants';
// tslint:disable completed-docs
export function getKyberReserveIdsForPair(takerToken: string, makerToken: string): string[] {
return [
...Object.values(MAINNET_KYBER_RESERVE_IDS),
MAINNET_KYBER_TOKEN_RESERVE_IDS[makerToken.toLowerCase()],
MAINNET_KYBER_TOKEN_RESERVE_IDS[takerToken.toLowerCase()],
].filter(t => t);
}

View File

@ -27,6 +27,7 @@ import {
DexSample, DexSample,
ERC20BridgeSource, ERC20BridgeSource,
Fill, Fill,
KyberFillData,
LiquidityProviderFillData, LiquidityProviderFillData,
MultiBridgeFillData, MultiBridgeFillData,
MultiHopFillData, MultiHopFillData,
@ -295,6 +296,14 @@ function createBridgeOrder(
createMultiBridgeData(takerToken, makerToken), createMultiBridgeData(takerToken, makerToken),
); );
break; break;
case ERC20BridgeSource.Kyber:
const kyberFillData = (fill as CollapsedFill<KyberFillData>).fillData!; // tslint:disable-line:no-non-null-assertion
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
makerToken,
bridgeAddress,
createKyberBridgeData(takerToken, kyberFillData.hint),
);
break;
default: default:
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
makerToken, makerToken,
@ -393,6 +402,11 @@ function createBancorBridgeData(path: string[], networkAddress: string): string
return encoder.encode({ path, networkAddress }); return encoder.encode({ path, networkAddress });
} }
function createKyberBridgeData(fromTokenAddress: string, hint: string): string {
const encoder = AbiEncoder.create([{ name: 'fromTokenAddress', type: 'address' }, { name: 'hint', type: 'bytes' }]);
return encoder.encode({ fromTokenAddress, hint });
}
function createCurveBridgeData( function createCurveBridgeData(
curveAddress: string, curveAddress: string,
exchangeFunctionSelector: string, exchangeFunctionSelector: string,

View File

@ -8,6 +8,7 @@ import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote }
import { BancorService } from './bancor_service'; import { BancorService } from './bancor_service';
import { MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; import { MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants';
import { getCurveInfosForPair } from './curve_utils'; import { getCurveInfosForPair } from './curve_utils';
import { getKyberReserveIdsForPair } from './kyber_utils';
import { getMultiBridgeIntermediateToken } from './multibridge_utils'; import { getMultiBridgeIntermediateToken } from './multibridge_utils';
import { getIntermediateTokens } from './multihop_utils'; import { getIntermediateTokens } from './multihop_utils';
import { SamplerContractOperation } from './sampler_contract_operation'; import { SamplerContractOperation } from './sampler_contract_operation';
@ -20,6 +21,7 @@ import {
DexSample, DexSample,
ERC20BridgeSource, ERC20BridgeSource,
HopInfo, HopInfo,
KyberFillData,
LiquidityProviderFillData, LiquidityProviderFillData,
MultiBridgeFillData, MultiBridgeFillData,
MultiHopFillData, MultiHopFillData,
@ -71,6 +73,7 @@ export class SamplerOperations {
} }
public getKyberSellQuotes( public getKyberSellQuotes(
reserveId: string,
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
takerFillAmounts: BigNumber[], takerFillAmounts: BigNumber[],
@ -79,11 +82,21 @@ export class SamplerOperations {
source: ERC20BridgeSource.Kyber, source: ERC20BridgeSource.Kyber,
contract: this._samplerContract, contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromKyberNetwork, function: this._samplerContract.sampleSellsFromKyberNetwork,
params: [takerToken, makerToken, takerFillAmounts], params: [reserveId, takerToken, makerToken, takerFillAmounts],
callback: (callResults: string, fillData: KyberFillData): BigNumber[] => {
const [hint, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
'sampleSellsFromKyberNetwork',
callResults,
);
fillData.hint = hint;
fillData.reserveId = reserveId;
return samples;
},
}); });
} }
public getKyberBuyQuotes( public getKyberBuyQuotes(
reserveId: string,
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
makerFillAmounts: BigNumber[], makerFillAmounts: BigNumber[],
@ -92,7 +105,16 @@ export class SamplerOperations {
source: ERC20BridgeSource.Kyber, source: ERC20BridgeSource.Kyber,
contract: this._samplerContract, contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromKyberNetwork, function: this._samplerContract.sampleBuysFromKyberNetwork,
params: [takerToken, makerToken, makerFillAmounts], params: [reserveId, takerToken, makerToken, makerFillAmounts],
callback: (callResults: string, fillData: KyberFillData): BigNumber[] => {
const [hint, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
'sampleBuysFromKyberNetwork',
callResults,
);
fillData.hint = hint;
fillData.reserveId = reserveId;
return samples;
},
}); });
} }
@ -746,7 +768,9 @@ export class SamplerOperations {
} }
return ops; return ops;
case ERC20BridgeSource.Kyber: case ERC20BridgeSource.Kyber:
return this.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts); return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId =>
this.getKyberSellQuotes(reserveId, makerToken, takerToken, takerFillAmounts),
);
case ERC20BridgeSource.Curve: case ERC20BridgeSource.Curve:
return getCurveInfosForPair(takerToken, makerToken).map(curve => return getCurveInfosForPair(takerToken, makerToken).map(curve =>
this.getCurveSellQuotes( this.getCurveSellQuotes(
@ -825,7 +849,9 @@ export class SamplerOperations {
} }
return ops; return ops;
case ERC20BridgeSource.Kyber: case ERC20BridgeSource.Kyber:
return this.getKyberBuyQuotes(makerToken, takerToken, makerFillAmounts); return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId =>
this.getKyberBuyQuotes(reserveId, makerToken, takerToken, makerFillAmounts),
);
case ERC20BridgeSource.Curve: case ERC20BridgeSource.Curve:
return getCurveInfosForPair(takerToken, makerToken).map(curve => return getCurveInfosForPair(takerToken, makerToken).map(curve =>
this.getCurveBuyQuotes( this.getCurveBuyQuotes(

View File

@ -108,6 +108,11 @@ export interface BancorFillData extends FillData {
networkAddress: string; networkAddress: string;
} }
export interface KyberFillData extends FillData {
hint: string;
reserveId: string;
}
export interface Quote<TFillData = FillData> { export interface Quote<TFillData = FillData> {
amount: BigNumber; amount: BigNumber;
fillData?: TFillData; fillData?: TFillData;

View File

@ -15,10 +15,7 @@ import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json
import * as IBalancer from '../test/generated-artifacts/IBalancer.json'; import * as IBalancer from '../test/generated-artifacts/IBalancer.json';
import * as ICurve from '../test/generated-artifacts/ICurve.json'; import * as ICurve from '../test/generated-artifacts/ICurve.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json'; import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberHintHandler from '../test/generated-artifacts/IKyberHintHandler.json';
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json'; import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
import * as IKyberStorage from '../test/generated-artifacts/IKyberStorage.json';
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json'; import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquidityProviderRegistry.json'; import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquidityProviderRegistry.json';
import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json'; import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json';
@ -58,10 +55,7 @@ export const artifacts = {
IBalancer: IBalancer as ContractArtifact, IBalancer: IBalancer as ContractArtifact,
ICurve: ICurve as ContractArtifact, ICurve: ICurve as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact, IEth2Dai: IEth2Dai as ContractArtifact,
IKyberHintHandler: IKyberHintHandler as ContractArtifact,
IKyberNetwork: IKyberNetwork as ContractArtifact, IKyberNetwork: IKyberNetwork as ContractArtifact,
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
IKyberStorage: IKyberStorage as ContractArtifact,
ILiquidityProvider: ILiquidityProvider as ContractArtifact, ILiquidityProvider: ILiquidityProvider as ContractArtifact,
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact, ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
IMStable: IMStable as ContractArtifact, IMStable: IMStable as ContractArtifact,

View File

@ -81,24 +81,25 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f'; const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f';
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
const RESERVE_ID = '0xff4b796265722046707200000000000000000000000000000000000000000000';
describe('sampleSellsFromKyberNetwork()', () => { describe('sampleSellsFromKyberNetwork()', () => {
it('samples sells from Kyber DAI->WETH', async () => { it('samples sells from Kyber DAI->WETH', async () => {
const samples = await testContract const [, samples] = await testContract
.sampleSellsFromKyberNetwork(DAI, WETH, [toBaseUnitAmount(1)]) .sampleSellsFromKyberNetwork(RESERVE_ID, DAI, WETH, [toBaseUnitAmount(1)])
.callAsync({ overrides }); .callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0);
}); });
it('samples sells from Kyber WETH->DAI', async () => { it('samples sells from Kyber WETH->DAI', async () => {
const samples = await testContract const [, samples] = await testContract
.sampleSellsFromKyberNetwork(WETH, DAI, [toBaseUnitAmount(1)]) .sampleSellsFromKyberNetwork(RESERVE_ID, WETH, DAI, [toBaseUnitAmount(1)])
.callAsync({ overrides }); .callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0);
}); });
it('samples sells from Kyber DAI->USDC', async () => { it('samples sells from Kyber DAI->USDC', async () => {
const samples = await testContract const [, samples] = await testContract
.sampleSellsFromKyberNetwork(DAI, USDC, [toBaseUnitAmount(1)]) .sampleSellsFromKyberNetwork(RESERVE_ID, DAI, USDC, [toBaseUnitAmount(1)])
.callAsync({ overrides }); .callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0);
@ -109,8 +110,8 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
it('samples buys from Kyber WETH->DAI', async () => { it('samples buys from Kyber WETH->DAI', async () => {
// From ETH to DAI // From ETH to DAI
// I want to buy 1 DAI // I want to buy 1 DAI
const samples = await testContract const [, samples] = await testContract
.sampleBuysFromKyberNetwork(WETH, DAI, [toBaseUnitAmount(1)]) .sampleBuysFromKyberNetwork(RESERVE_ID, WETH, DAI, [toBaseUnitAmount(1)])
.callAsync({ overrides }); .callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0);
@ -119,8 +120,8 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
it('samples buys from Kyber DAI->WETH', async () => { it('samples buys from Kyber DAI->WETH', async () => {
// From USDC to DAI // From USDC to DAI
// I want to buy 1 WETH // I want to buy 1 WETH
const samples = await testContract const [, samples] = await testContract
.sampleBuysFromKyberNetwork(DAI, WETH, [toBaseUnitAmount(1)]) .sampleBuysFromKyberNetwork(RESERVE_ID, DAI, WETH, [toBaseUnitAmount(1)])
.callAsync({ overrides }); .callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0);

View File

@ -38,6 +38,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const MAKER_TOKEN = randomAddress(); const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress(); const TAKER_TOKEN = randomAddress();
const INTERMEDIATE_TOKEN = randomAddress(); const INTERMEDIATE_TOKEN = randomAddress();
const KYBER_RESERVE_ID = '0x';
before(async () => { before(async () => {
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync( testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
@ -330,31 +331,25 @@ blockchainTests('erc20-bridge-sampler', env => {
}); });
it('throws if tokens are the same', async () => { it('throws if tokens are the same', async () => {
const tx = testContract.sampleSellsFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); const tx = testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, MAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
}); });
it('can return no quotes', async () => { it('can return no quotes', async () => {
const quotes = await testContract.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); const [, quotes] = await testContract
expect(quotes).to.deep.eq([]); .sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, MAKER_TOKEN, [])
});
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [takerToEthQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, MAKER_TOKEN, ['Kyber'], takerToEthQuotes);
const quotes = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq([]);
}); });
it('returns zero if token -> token fails', async () => { it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync(); await enableFailTriggerAsync();
const quotes = await testContract const [, quotes] = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
@ -362,8 +357,17 @@ blockchainTests('erc20-bridge-sampler', env => {
it('can quote token -> ETH', async () => { it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
const quotes = await testContract const [, quotes] = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
const [, quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
@ -372,8 +376,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync(); await enableFailTriggerAsync();
const quotes = await testContract const [, quotes] = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
@ -381,8 +385,8 @@ blockchainTests('erc20-bridge-sampler', env => {
it('can quote ETH -> token', async () => { it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
const quotes = await testContract const [, quotes] = await testContract
.sampleSellsFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
@ -391,8 +395,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync(); await enableFailTriggerAsync();
const quotes = await testContract const [, quotes] = await testContract
.sampleSellsFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .sampleSellsFromKyberNetwork(KYBER_RESERVE_ID, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
@ -405,21 +409,24 @@ blockchainTests('erc20-bridge-sampler', env => {
}); });
it('throws if tokens are the same', async () => { it('throws if tokens are the same', async () => {
const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); const tx = testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, MAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
}); });
it('can return no quotes', async () => { it('can return no quotes', async () => {
const quotes = await testContract.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); const [, quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq([]); expect(quotes).to.deep.eq([]);
}); });
it('can quote token -> token', async () => { it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [ethToMakerQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, MAKER_TOKEN, ['Kyber'], sampleAmounts); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], ethToMakerQuotes); const [, quotes] = await testContract
const quotes = await testContract .sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
}); });
@ -428,8 +435,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync(); await enableFailTriggerAsync();
const quotes = await testContract const [, quotes] = await testContract
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
@ -437,8 +444,8 @@ blockchainTests('erc20-bridge-sampler', env => {
it('can quote token -> ETH', async () => { it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
const quotes = await testContract const [, quotes] = await testContract
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync(); .callAsync();
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
}); });
@ -447,8 +454,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync(); await enableFailTriggerAsync();
const quotes = await testContract const [, quotes] = await testContract
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
@ -456,8 +463,8 @@ blockchainTests('erc20-bridge-sampler', env => {
it('can quote ETH -> token', async () => { it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
const quotes = await testContract const [, quotes] = await testContract
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
}); });
@ -466,8 +473,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync(); await enableFailTriggerAsync();
const quotes = await testContract const [, quotes] = await testContract
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .sampleBuysFromKyberNetwork(KYBER_RESERVE_ID, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync(); .callAsync();
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });

View File

@ -135,16 +135,21 @@ describe('DexSampler tests', () => {
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const sampler = new MockSamplerContract({ const sampler = new MockSamplerContract({
sampleSellsFromKyberNetwork: (takerToken, makerToken, fillAmounts) => { sampleSellsFromKyberNetwork: (_reserveId, takerToken, makerToken, fillAmounts) => {
expect(takerToken).to.eq(expectedTakerToken); expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken); expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
return expectedMakerFillAmounts; return ['0x', expectedMakerFillAmounts];
}, },
}); });
const dexOrderSampler = new DexOrderSampler(sampler); const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync( const [fillableAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getKyberSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts), dexOrderSampler.getKyberSellQuotes(
'0x',
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
),
); );
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts); expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
}); });
@ -373,12 +378,7 @@ describe('DexSampler tests', () => {
it('getSellQuotes()', async () => { it('getSellQuotes()', async () => {
const expectedTakerToken = randomAddress(); const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress(); const expectedMakerToken = randomAddress();
const sources = [ const sources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2];
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
];
const ratesBySource: RatesBySource = { const ratesBySource: RatesBySource = {
[ERC20BridgeSource.Kyber]: getRandomFloat(0, 100), [ERC20BridgeSource.Kyber]: getRandomFloat(0, 100),
[ERC20BridgeSource.Eth2Dai]: getRandomFloat(0, 100), [ERC20BridgeSource.Eth2Dai]: getRandomFloat(0, 100),
@ -387,12 +387,6 @@ describe('DexSampler tests', () => {
}; };
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
const sampler = new MockSamplerContract({ const sampler = new MockSamplerContract({
sampleSellsFromKyberNetwork: (takerToken, makerToken, fillAmounts) => {
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Kyber]).integerValue());
},
sampleSellsFromUniswap: (takerToken, makerToken, fillAmounts) => { sampleSellsFromUniswap: (takerToken, makerToken, fillAmounts) => {
expect(takerToken).to.eq(expectedTakerToken); expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken); expect(makerToken).to.eq(expectedMakerToken);
@ -449,7 +443,8 @@ describe('DexSampler tests', () => {
})), })),
]; ];
// extra quote for Uniswap V2, which provides a direct quote (tokenA -> tokenB) AND an ETH quote (tokenA -> ETH -> tokenB) // extra quote for Uniswap V2, which provides a direct quote (tokenA -> tokenB) AND an ETH quote (tokenA -> ETH -> tokenB)
expect(quotes).to.have.lengthOf(sources.length + 1); const additionalSourceCount = 1;
expect(quotes).to.have.lengthOf(sources.length + additionalSourceCount);
expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes)); expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes));
}); });
it('getSellQuotes() uses samples from Balancer', async () => { it('getSellQuotes() uses samples from Balancer', async () => {

View File

@ -261,6 +261,7 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.UniswapV2]: { tokenAddressPath: [] }, [ERC20BridgeSource.UniswapV2]: { tokenAddressPath: [] },
[ERC20BridgeSource.Balancer]: { poolAddress: randomAddress() }, [ERC20BridgeSource.Balancer]: { poolAddress: randomAddress() },
[ERC20BridgeSource.Bancor]: { path: [], networkAddress: randomAddress() }, [ERC20BridgeSource.Bancor]: { path: [], networkAddress: randomAddress() },
[ERC20BridgeSource.Kyber]: { hint: '0x', reserveId: '0x' },
[ERC20BridgeSource.Curve]: { [ERC20BridgeSource.Curve]: {
curve: { curve: {
poolAddress: randomAddress(), poolAddress: randomAddress(),

View File

@ -23,6 +23,18 @@ export type SampleBuysHandler = (
makerToken: string, makerToken: string,
makerTokenAmounts: BigNumber[], makerTokenAmounts: BigNumber[],
) => SampleResults; ) => SampleResults;
export type SampleSellsKyberHandler = (
reserveId: string,
takerToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
) => [string, SampleResults];
export type SampleBuysKyberHandler = (
reserveId: string,
takerToken: string,
makerToken: string,
makerTokenAmounts: BigNumber[],
) => [string, SampleResults];
export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults; export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults;
export type SampleSellsLPHandler = ( export type SampleSellsLPHandler = (
registryAddress: string, registryAddress: string,
@ -48,7 +60,7 @@ const DUMMY_PROVIDER = {
interface Handlers { interface Handlers {
getOrderFillableMakerAssetAmounts: GetOrderFillableAssetAmountHandler; getOrderFillableMakerAssetAmounts: GetOrderFillableAssetAmountHandler;
getOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler; getOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler;
sampleSellsFromKyberNetwork: SampleSellsHandler; sampleSellsFromKyberNetwork: SampleSellsKyberHandler;
sampleSellsFromLiquidityProviderRegistry: SampleSellsLPHandler; sampleSellsFromLiquidityProviderRegistry: SampleSellsLPHandler;
sampleSellsFromMultiBridge: SampleSellsMBHandler; sampleSellsFromMultiBridge: SampleSellsMBHandler;
sampleSellsFromEth2Dai: SampleSellsHandler; sampleSellsFromEth2Dai: SampleSellsHandler;
@ -104,13 +116,15 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
} }
public sampleSellsFromKyberNetwork( public sampleSellsFromKyberNetwork(
reserveId: string,
takerToken: string, takerToken: string,
makerToken: string, makerToken: string,
takerAssetAmounts: BigNumber[], takerAssetAmounts: BigNumber[],
): ContractFunctionObj<BigNumber[]> { ): ContractFunctionObj<[string, BigNumber[]]> {
return this._wrapCall( return this._wrapCall(
super.sampleSellsFromKyberNetwork, super.sampleSellsFromKyberNetwork,
this._handlers.sampleSellsFromKyberNetwork, this._handlers.sampleSellsFromKyberNetwork,
reserveId,
takerToken, takerToken,
makerToken, makerToken,
takerAssetAmounts, takerAssetAmounts,

View File

@ -13,10 +13,7 @@ export * from '../test/generated-wrappers/eth2_dai_sampler';
export * from '../test/generated-wrappers/i_balancer'; export * from '../test/generated-wrappers/i_balancer';
export * from '../test/generated-wrappers/i_curve'; export * from '../test/generated-wrappers/i_curve';
export * from '../test/generated-wrappers/i_eth2_dai'; export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_hint_handler';
export * from '../test/generated-wrappers/i_kyber_network'; export * from '../test/generated-wrappers/i_kyber_network';
export * from '../test/generated-wrappers/i_kyber_network_proxy';
export * from '../test/generated-wrappers/i_kyber_storage';
export * from '../test/generated-wrappers/i_liquidity_provider'; export * from '../test/generated-wrappers/i_liquidity_provider';
export * from '../test/generated-wrappers/i_liquidity_provider_registry'; export * from '../test/generated-wrappers/i_liquidity_provider_registry';
export * from '../test/generated-wrappers/i_m_stable'; export * from '../test/generated-wrappers/i_m_stable';

View File

@ -18,10 +18,7 @@
"test/generated-artifacts/IBalancer.json", "test/generated-artifacts/IBalancer.json",
"test/generated-artifacts/ICurve.json", "test/generated-artifacts/ICurve.json",
"test/generated-artifacts/IEth2Dai.json", "test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberHintHandler.json",
"test/generated-artifacts/IKyberNetwork.json", "test/generated-artifacts/IKyberNetwork.json",
"test/generated-artifacts/IKyberNetworkProxy.json",
"test/generated-artifacts/IKyberStorage.json",
"test/generated-artifacts/ILiquidityProvider.json", "test/generated-artifacts/ILiquidityProvider.json",
"test/generated-artifacts/ILiquidityProviderRegistry.json", "test/generated-artifacts/ILiquidityProviderRegistry.json",
"test/generated-artifacts/IMStable.json", "test/generated-artifacts/IMStable.json",

View File

@ -37,6 +37,10 @@
{ {
"note": "Redeploy `MooniswapBridge` on Mainnet", "note": "Redeploy `MooniswapBridge` on Mainnet",
"pr": 2681 "pr": 2681
},
{
"note": "Redeploy `KyberBridge` on Mainnet",
"pr": 2683
} }
] ]
}, },

View File

@ -22,7 +22,7 @@
"uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad", "uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad",
"uniswapV2Bridge": "0xdcd6011f4c6b80e470d9487f5871a0cba7c93f48", "uniswapV2Bridge": "0xdcd6011f4c6b80e470d9487f5871a0cba7c93f48",
"erc20BridgeSampler": "0xd8c38704c9937ea3312de29f824b4ad3450a5e61", "erc20BridgeSampler": "0xd8c38704c9937ea3312de29f824b4ad3450a5e61",
"kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610", "kyberBridge": "0xadd97271402590564ddd8ad23cb5317b1fb0fffb",
"eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1", "eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1",
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438", "chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
"dydxBridge": "0x92af95e37afddac412e5688a9dcc1dd815d4ae53", "dydxBridge": "0x92af95e37afddac412e5688a9dcc1dd815d4ae53",