@0x/contracts-asset-proxy
: Update CurveBridge
to support more varied curves.
`@0x/contracts-erc20-bridge-sampler`: Refactor. `@0x/contracts-erc20-bridge-sampler`: Add support for more varied curves. `@0x/contracts-integrations`: Update curve bridge tests.
This commit is contained in:
parent
74d9df2fb0
commit
4dac620156
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "3.5.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Update `CurveBridge` to support more varied curves",
|
||||||
|
"pr": 2633
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "3.4.0",
|
"version": "3.4.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -26,7 +26,6 @@ import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
|||||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
import "../interfaces/ICurve.sol";
|
import "../interfaces/ICurve.sol";
|
||||||
import "./MixinGasToken.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable not-rely-on-time
|
// solhint-disable not-rely-on-time
|
||||||
@ -34,14 +33,14 @@ import "./MixinGasToken.sol";
|
|||||||
contract CurveBridge is
|
contract CurveBridge is
|
||||||
IERC20Bridge,
|
IERC20Bridge,
|
||||||
IWallet,
|
IWallet,
|
||||||
DeploymentConstants,
|
DeploymentConstants
|
||||||
MixinGasToken
|
|
||||||
{
|
{
|
||||||
struct CurveBridgeData {
|
struct CurveBridgeData {
|
||||||
address curveAddress;
|
address curveAddress;
|
||||||
|
bytes4 exchangeFunctionSelector;
|
||||||
|
address fromTokenAddress;
|
||||||
int128 fromCoinIdx;
|
int128 fromCoinIdx;
|
||||||
int128 toCoinIdx;
|
int128 toCoinIdx;
|
||||||
int128 version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Callback for `ICurve`. Tries to buy `amount` of
|
/// @dev Callback for `ICurve`. Tries to buy `amount` of
|
||||||
@ -62,39 +61,31 @@ contract CurveBridge is
|
|||||||
bytes calldata bridgeData
|
bytes calldata bridgeData
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
freesGasTokensFromCollector
|
|
||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
{
|
{
|
||||||
// Decode the bridge data to get the Curve metadata.
|
// Decode the bridge data to get the Curve metadata.
|
||||||
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
||||||
|
|
||||||
address fromTokenAddress = ICurve(data.curveAddress).underlying_coins(data.fromCoinIdx);
|
require(toTokenAddress != data.fromTokenAddress, "CurveBridge/INVALID_PAIR");
|
||||||
require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
|
uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this));
|
||||||
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||||
LibERC20Token.approveIfBelow(fromTokenAddress, data.curveAddress, fromTokenBalance);
|
LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance);
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||||
if (data.version == 0) {
|
{
|
||||||
ICurve(data.curveAddress).exchange_underlying(
|
(bool didSucceed, bytes memory resultData) =
|
||||||
data.fromCoinIdx,
|
data.curveAddress.call(abi.encodeWithSelector(
|
||||||
data.toCoinIdx,
|
data.exchangeFunctionSelector,
|
||||||
// dx
|
data.fromCoinIdx,
|
||||||
fromTokenBalance,
|
data.toCoinIdx,
|
||||||
// min dy
|
// dx
|
||||||
amount,
|
fromTokenBalance,
|
||||||
// expires
|
// min dy
|
||||||
block.timestamp + 1
|
amount
|
||||||
);
|
));
|
||||||
} else {
|
if (!didSucceed) {
|
||||||
ICurve(data.curveAddress).exchange_underlying(
|
assembly { revert(add(resultData, 32), mload(resultData)) }
|
||||||
data.fromCoinIdx,
|
}
|
||||||
data.toCoinIdx,
|
|
||||||
// dx
|
|
||||||
fromTokenBalance,
|
|
||||||
// min dy
|
|
||||||
amount
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
||||||
@ -102,7 +93,7 @@ contract CurveBridge is
|
|||||||
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
emit ERC20BridgeTransfer(
|
||||||
fromTokenAddress,
|
data.fromTokenAddress,
|
||||||
toTokenAddress,
|
toTokenAddress,
|
||||||
fromTokenBalance,
|
fromTokenBalance,
|
||||||
toTokenBalance,
|
toTokenBalance,
|
||||||
|
@ -22,22 +22,6 @@ pragma solidity ^0.5.9;
|
|||||||
// solhint-disable func-name-mixedcase
|
// solhint-disable func-name-mixedcase
|
||||||
interface ICurve {
|
interface ICurve {
|
||||||
|
|
||||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
|
||||||
/// This function exists on early versions of Curve (USDC/DAI)
|
|
||||||
/// @param i The token index being sold.
|
|
||||||
/// @param j The token index being bought.
|
|
||||||
/// @param sellAmount The amount of token being bought.
|
|
||||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
|
||||||
/// @param deadline The time in seconds when this operation should expire.
|
|
||||||
function exchange_underlying(
|
|
||||||
int128 i,
|
|
||||||
int128 j,
|
|
||||||
uint256 sellAmount,
|
|
||||||
uint256 minBuyAmount,
|
|
||||||
uint256 deadline
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||||
/// @param i The token index being sold.
|
/// @param i The token index being sold.
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "1.8.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Refactor and support more varied curves",
|
||||||
|
"pr": 2633
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
123
contracts/erc20-bridge-sampler/contracts/src/ApproximateBuys.sol
Normal file
123
contracts/erc20-bridge-sampler/contracts/src/ApproximateBuys.sol
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract ApproximateBuys {
|
||||||
|
|
||||||
|
/// @dev Information computing buy quotes for sources that do not have native
|
||||||
|
/// buy quote support.
|
||||||
|
struct ApproximateBuyQuoteOpts {
|
||||||
|
// Arbitrary maker token data to pass to `getSellQuoteCallback`.
|
||||||
|
bytes makerTokenData;
|
||||||
|
// Arbitrary taker token data to pass to `getSellQuoteCallback`.
|
||||||
|
bytes takerTokenData;
|
||||||
|
// Callback to retrieve a sell quote.
|
||||||
|
function (bytes memory, bytes memory, uint256)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint256) getSellQuoteCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 private constant ONE_HUNDED_PERCENT_BPS = 1e4;
|
||||||
|
/// @dev Maximum approximate (positive) error rate when approximating a buy quote.
|
||||||
|
uint256 private constant APPROXIMATE_BUY_TARGET_EPSILON_BPS = 0.0005e4;
|
||||||
|
/// @dev Maximum iterations to perform when approximating a buy quote.
|
||||||
|
uint256 private constant APPROXIMATE_BUY_MAX_ITERATIONS = 5;
|
||||||
|
|
||||||
|
function _sampleApproximateBuys(
|
||||||
|
ApproximateBuyQuoteOpts memory opts,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
|
||||||
|
if (makerTokenAmounts.length == 0) {
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 sellAmount = opts.getSellQuoteCallback(
|
||||||
|
opts.makerTokenData,
|
||||||
|
opts.takerTokenData,
|
||||||
|
makerTokenAmounts[0]
|
||||||
|
);
|
||||||
|
if (sellAmount == 0) {
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 buyAmount = opts.getSellQuoteCallback(
|
||||||
|
opts.takerTokenData,
|
||||||
|
opts.makerTokenData,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
if (buyAmount == 0) {
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
|
||||||
|
for (uint256 iter = 0; iter < APPROXIMATE_BUY_MAX_ITERATIONS; iter++) {
|
||||||
|
// adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
|
||||||
|
sellAmount = LibMath.getPartialAmountCeil(
|
||||||
|
makerTokenAmounts[i],
|
||||||
|
buyAmount,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
sellAmount = LibMath.getPartialAmountCeil(
|
||||||
|
(ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS),
|
||||||
|
ONE_HUNDED_PERCENT_BPS,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
uint256 _buyAmount = opts.getSellQuoteCallback(
|
||||||
|
opts.takerTokenData,
|
||||||
|
opts.makerTokenData,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
if (_buyAmount == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// We re-use buyAmount next iteration, only assign if it is
|
||||||
|
// non zero
|
||||||
|
buyAmount = _buyAmount;
|
||||||
|
// If we've reached our goal, exit early
|
||||||
|
if (buyAmount >= makerTokenAmounts[i]) {
|
||||||
|
uint256 eps =
|
||||||
|
(buyAmount - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS /
|
||||||
|
makerTokenAmounts[i];
|
||||||
|
if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We do our best to close in on the requested amount, but we can either over buy or under buy and exit
|
||||||
|
// if we hit a max iteration limit
|
||||||
|
// We scale the sell amount to get the approximate target
|
||||||
|
takerTokenAmounts[i] = LibMath.getPartialAmountCeil(
|
||||||
|
makerTokenAmounts[i],
|
||||||
|
buyAmount,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
156
contracts/erc20-bridge-sampler/contracts/src/CurveSampler.sol
Normal file
156
contracts/erc20-bridge-sampler/contracts/src/CurveSampler.sol
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./ICurve.sol";
|
||||||
|
import "./ApproximateBuys.sol";
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract CurveSampler is
|
||||||
|
SamplerUtils,
|
||||||
|
ApproximateBuys
|
||||||
|
{
|
||||||
|
/// @dev Information for sampling from curve sources.
|
||||||
|
struct CurveInfo {
|
||||||
|
address poolAddress;
|
||||||
|
bytes4 sellQuoteFunctionSelector;
|
||||||
|
bytes4 buyQuoteFunctionSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
|
||||||
|
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
|
||||||
|
uint256 constant private CURVE_CALL_GAS = 600e3; // 600k
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Curve.
|
||||||
|
/// @param curveInfo Curve 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 sampleSellsFromCurve(
|
||||||
|
CurveInfo memory curveInfo,
|
||||||
|
int128 fromTokenIdx,
|
||||||
|
int128 toTokenIdx,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
curveInfo.sellQuoteFunctionSelector,
|
||||||
|
fromTokenIdx,
|
||||||
|
toTokenIdx,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 buyAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
makerTokenAmounts[i] = buyAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Curve.
|
||||||
|
/// @param curveInfo Curve 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 sampleBuysFromCurve(
|
||||||
|
CurveInfo memory curveInfo,
|
||||||
|
int128 fromTokenIdx,
|
||||||
|
int128 toTokenIdx,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
if (curveInfo.buyQuoteFunctionSelector == bytes4(0)) {
|
||||||
|
// Buys not supported on this curve, so approximate it.
|
||||||
|
return _sampleApproximateBuys(
|
||||||
|
ApproximateBuyQuoteOpts({
|
||||||
|
makerTokenData: abi.encode(toTokenIdx, curveInfo),
|
||||||
|
takerTokenData: abi.encode(fromTokenIdx, curveInfo),
|
||||||
|
getSellQuoteCallback: _sampleSellForApproximateBuyFromCurve
|
||||||
|
}),
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
curveInfo.buyQuoteFunctionSelector,
|
||||||
|
fromTokenIdx,
|
||||||
|
toTokenIdx,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 sellAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
sellAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
takerTokenAmounts[i] = sellAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sampleSellForApproximateBuyFromCurve(
|
||||||
|
bytes memory takerTokenData,
|
||||||
|
bytes memory makerTokenData,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 buyAmount)
|
||||||
|
{
|
||||||
|
(int128 takerTokenIdx, CurveInfo memory curveInfo) =
|
||||||
|
abi.decode(takerTokenData, (int128, CurveInfo));
|
||||||
|
(int128 makerTokenIdx) =
|
||||||
|
abi.decode(makerTokenData, (int128));
|
||||||
|
(bool success, bytes memory resultData) =
|
||||||
|
address(this).staticcall(abi.encodeWithSelector(
|
||||||
|
this.sampleSellsFromCurve.selector,
|
||||||
|
curveInfo,
|
||||||
|
takerTokenIdx,
|
||||||
|
makerTokenIdx,
|
||||||
|
_toSingleValueArray(sellAmount)
|
||||||
|
));
|
||||||
|
if (!success) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
return abi.decode(resultData, (uint256[]))[0];
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
140
contracts/erc20-bridge-sampler/contracts/src/Eth2DaiSampler.sol
Normal file
140
contracts/erc20-bridge-sampler/contracts/src/Eth2DaiSampler.sol
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "./IEth2Dai.sol";
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract Eth2DaiSampler is
|
||||||
|
DeploymentConstants,
|
||||||
|
SamplerUtils
|
||||||
|
{
|
||||||
|
/// @dev Base gas limit for Eth2Dai calls.
|
||||||
|
uint256 constant private ETH2DAI_CALL_GAS = 1000e3; // 1m
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address 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 sampleSellsFromEth2Dai(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
_getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
IEth2Dai(0).getBuyAmount.selector,
|
||||||
|
makerToken,
|
||||||
|
takerToken,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 buyAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
makerTokenAmounts[i] = buyAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Eth2Dai/Oasis using a hop to an intermediate token.
|
||||||
|
/// I.e WBTC/DAI via ETH or WBTC/ETH via DAI
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param intermediateToken Address of the token to hop to.
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromEth2DaiHop(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
address intermediateToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
if (makerToken == intermediateToken || takerToken == intermediateToken) {
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
|
uint256[] memory intermediateAmounts = sampleSellsFromEth2Dai(
|
||||||
|
takerToken,
|
||||||
|
intermediateToken,
|
||||||
|
takerTokenAmounts
|
||||||
|
);
|
||||||
|
makerTokenAmounts = sampleSellsFromEth2Dai(
|
||||||
|
intermediateToken,
|
||||||
|
makerToken,
|
||||||
|
intermediateAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Eth2Dai/Oasis.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Maker token sell amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromEth2Dai(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
_getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
IEth2Dai(0).getPayAmount.selector,
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 sellAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
sellAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
takerTokenAmounts[i] = sellAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,22 +22,6 @@ pragma solidity ^0.5.9;
|
|||||||
// solhint-disable func-name-mixedcase
|
// solhint-disable func-name-mixedcase
|
||||||
interface ICurve {
|
interface ICurve {
|
||||||
|
|
||||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
|
||||||
/// This function exists on early versions of Curve (USDC/DAI)
|
|
||||||
/// @param i The token index being sold.
|
|
||||||
/// @param j The token index being bought.
|
|
||||||
/// @param sellAmount The amount of token being bought.
|
|
||||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
|
||||||
/// @param deadline The time in seconds when this operation should expire.
|
|
||||||
function exchange_underlying(
|
|
||||||
int128 i,
|
|
||||||
int128 j,
|
|
||||||
uint256 sellAmount,
|
|
||||||
uint256 minBuyAmount,
|
|
||||||
uint256 deadline
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||||
/// @param i The token index being sold.
|
/// @param i The token index being sold.
|
||||||
|
@ -1,293 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|
||||||
|
|
||||||
|
|
||||||
interface IERC20BridgeSampler {
|
|
||||||
|
|
||||||
struct FakeBuyOptions {
|
|
||||||
uint256 targetSlippageBps;
|
|
||||||
uint256 maxIterations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Call multiple public functions on this contract in a single transaction.
|
|
||||||
/// @param callDatas ABI-encoded call data for each function call.
|
|
||||||
/// @return callResults ABI-encoded results data for each call.
|
|
||||||
function batchCall(bytes[] calldata callDatas)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (bytes[] memory callResults);
|
|
||||||
|
|
||||||
/// @dev Queries the fillable taker asset amounts of native orders.
|
|
||||||
/// @param orders Native orders to query.
|
|
||||||
/// @param orderSignatures Signatures for each respective order in `orders`.
|
|
||||||
/// @param devUtilsAddress Address to the DevUtils contract.
|
|
||||||
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
|
||||||
/// by each order in `orders`.
|
|
||||||
function getOrderFillableTakerAssetAmounts(
|
|
||||||
LibOrder.Order[] calldata orders,
|
|
||||||
bytes[] calldata orderSignatures,
|
|
||||||
address devUtilsAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory orderFillableTakerAssetAmounts);
|
|
||||||
|
|
||||||
/// @dev Queries the fillable maker asset amounts of native orders.
|
|
||||||
/// @param orders Native orders to query.
|
|
||||||
/// @param orderSignatures Signatures for each respective order in `orders`.
|
|
||||||
/// @param devUtilsAddress Address to the DevUtils contract.
|
|
||||||
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
|
||||||
/// by each order in `orders`.
|
|
||||||
function getOrderFillableMakerAssetAmounts(
|
|
||||||
LibOrder.Order[] calldata orders,
|
|
||||||
bytes[] calldata orderSignatures,
|
|
||||||
address devUtilsAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory orderFillableMakerAssetAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Kyber.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address 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 sampleSellsFromKyberNetwork(
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Kyber.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
|
||||||
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
|
||||||
/// amount.
|
|
||||||
function sampleBuysFromKyberNetwork(
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata makerTokenAmounts,
|
|
||||||
FakeBuyOptions calldata opts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address 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 sampleSellsFromEth2Dai(
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Uniswap.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address 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 sampleSellsFromUniswap(
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Uniswap.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address 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 sampleBuysFromUniswap(
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata makerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Eth2Dai/Oasis.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address 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 sampleBuysFromEth2Dai(
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata makerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Curve.
|
|
||||||
/// @param curveAddress Address of the Curve contract.
|
|
||||||
/// @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 sampleSellsFromCurve(
|
|
||||||
address curveAddress,
|
|
||||||
int128 fromTokenIdx,
|
|
||||||
int128 toTokenIdx,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Curve.
|
|
||||||
/// @param curveAddress Address of the Curve contract.
|
|
||||||
/// @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 sampleBuysFromCurve(
|
|
||||||
address curveAddress,
|
|
||||||
int128 fromTokenIdx,
|
|
||||||
int128 toTokenIdx,
|
|
||||||
uint256[] calldata makerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
|
||||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address 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 sampleSellsFromLiquidityProviderRegistry(
|
|
||||||
address registryAddress,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from MultiBridge.
|
|
||||||
/// @param multibridge Address of the MultiBridge contract.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param intermediateToken The address of the intermediate token to
|
|
||||||
/// use in an indirect route.
|
|
||||||
/// @param makerToken Address 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 sampleSellsFromMultiBridge(
|
|
||||||
address multibridge,
|
|
||||||
address takerToken,
|
|
||||||
address intermediateToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
|
|
||||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
|
||||||
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
|
||||||
/// amount.
|
|
||||||
function sampleBuysFromLiquidityProviderRegistry(
|
|
||||||
address registryAddress,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] calldata makerTokenAmounts,
|
|
||||||
FakeBuyOptions calldata opts
|
|
||||||
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Returns the address of a liquidity provider for the given market
|
|
||||||
/// (takerToken, makerToken), from a registry of liquidity providers.
|
|
||||||
/// Returns address(0) if no such provider exists in the registry.
|
|
||||||
/// @param takerToken Taker asset managed by liquidity provider.
|
|
||||||
/// @param makerToken Maker asset managed by liquidity provider.
|
|
||||||
/// @return providerAddress Address of the liquidity provider.
|
|
||||||
function getLiquidityProviderFromRegistry(
|
|
||||||
address registryAddress,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address providerAddress);
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from UniswapV2.
|
|
||||||
/// @param path Token route.
|
|
||||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
|
||||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
|
||||||
/// amount.
|
|
||||||
function sampleSellsFromUniswapV2(
|
|
||||||
address[] calldata path,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from UniswapV2.
|
|
||||||
/// @param path Token route.
|
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
|
||||||
/// amount.
|
|
||||||
function sampleBuysFromUniswapV2(
|
|
||||||
address[] calldata path,
|
|
||||||
uint256[] calldata makerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts);
|
|
||||||
}
|
|
218
contracts/erc20-bridge-sampler/contracts/src/KyberSampler.sol
Normal file
218
contracts/erc20-bridge-sampler/contracts/src/KyberSampler.sol
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "./IKyberNetwork.sol";
|
||||||
|
import "./IKyberNetworkProxy.sol";
|
||||||
|
import "./IKyberStorage.sol";
|
||||||
|
import "./IKyberHintHandler.sol";
|
||||||
|
import "./ApproximateBuys.sol";
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract KyberSampler is
|
||||||
|
DeploymentConstants,
|
||||||
|
SamplerUtils,
|
||||||
|
ApproximateBuys
|
||||||
|
{
|
||||||
|
/// @dev Gas limit for Kyber calls.
|
||||||
|
uint256 constant private KYBER_CALL_GAS = 1500e3; // 1.5m
|
||||||
|
/// @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.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address 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 sampleSellsFromKyberNetwork(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
address wethAddress = _getWethAddress();
|
||||||
|
uint256 value;
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
if (takerToken == wethAddress || makerToken == wethAddress) {
|
||||||
|
// Direct ETH based trade
|
||||||
|
value = _sampleSellFromKyberNetwork(takerToken, makerToken, takerTokenAmounts[i]);
|
||||||
|
} else {
|
||||||
|
// Hop to ETH
|
||||||
|
value = _sampleSellFromKyberNetwork(takerToken, wethAddress, takerTokenAmounts[i]);
|
||||||
|
if (value != 0) {
|
||||||
|
value = _sampleSellFromKyberNetwork(wethAddress, makerToken, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
makerTokenAmounts[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Kyber.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address 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 sampleBuysFromKyberNetwork(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
return _sampleApproximateBuys(
|
||||||
|
ApproximateBuyQuoteOpts({
|
||||||
|
makerTokenData: abi.encode(makerToken),
|
||||||
|
takerTokenData: abi.encode(takerToken),
|
||||||
|
getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber
|
||||||
|
}),
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sampleSellForApproximateBuyFromKyber(
|
||||||
|
bytes memory takerTokenData,
|
||||||
|
bytes memory makerTokenData,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 buyAmount)
|
||||||
|
{
|
||||||
|
(bool success, bytes memory resultData) =
|
||||||
|
address(this).staticcall(abi.encodeWithSelector(
|
||||||
|
this.sampleSellsFromKyberNetwork.selector,
|
||||||
|
abi.decode(takerTokenData, (address)),
|
||||||
|
abi.decode(makerTokenData, (address)),
|
||||||
|
_toSingleValueArray(sellAmount)
|
||||||
|
));
|
||||||
|
if (!success) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
return abi.decode(resultData, (uint256[]))[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _appendToList(bytes32[] memory list, bytes32 item) private view returns (bytes32[] memory appendedList)
|
||||||
|
{
|
||||||
|
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 makerToken,
|
||||||
|
uint256 takerTokenAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 makerTokenAmount)
|
||||||
|
{
|
||||||
|
(IKyberHintHandler kyberHint, IKyberStorage kyberStorage) = _getKyberAddresses();
|
||||||
|
// Ban reserves which can clash with our internal aggregation
|
||||||
|
bytes32[] memory reserveIds = kyberStorage.getReserveIdsPerTokenSrc(
|
||||||
|
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) =
|
||||||
|
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
IKyberNetworkProxy(0).getExpectedRateAfterFee.selector,
|
||||||
|
takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken,
|
||||||
|
makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken,
|
||||||
|
takerTokenAmount,
|
||||||
|
0, // fee
|
||||||
|
hint
|
||||||
|
));
|
||||||
|
uint256 rate = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
(rate) = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
|
||||||
|
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
|
||||||
|
makerTokenAmount =
|
||||||
|
rate *
|
||||||
|
takerTokenAmount *
|
||||||
|
10 ** makerTokenDecimals /
|
||||||
|
10 ** takerTokenDecimals /
|
||||||
|
10 ** 18;
|
||||||
|
return makerTokenAmount;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "./ILiquidityProvider.sol";
|
||||||
|
import "./ILiquidityProviderRegistry.sol";
|
||||||
|
import "./ApproximateBuys.sol";
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract LiquidityProviderSampler is
|
||||||
|
SamplerUtils,
|
||||||
|
ApproximateBuys
|
||||||
|
{
|
||||||
|
/// @dev Default gas limit for liquidity provider calls.
|
||||||
|
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
||||||
|
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address 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 sampleSellsFromLiquidityProviderRegistry(
|
||||||
|
address registryAddress,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
// Initialize array of maker token amounts.
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
// Query registry for provider address.
|
||||||
|
address providerAddress = getLiquidityProviderFromRegistry(
|
||||||
|
registryAddress,
|
||||||
|
takerToken,
|
||||||
|
makerToken
|
||||||
|
);
|
||||||
|
// If provider doesn't exist, return all zeros.
|
||||||
|
if (providerAddress == address(0)) {
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
providerAddress.staticcall.gas(DEFAULT_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
ILiquidityProvider(0).getSellQuote.selector,
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 buyAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
// Exit early if the amount is too high for the liquidity provider to serve
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
makerTokenAmounts[i] = buyAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||||
|
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address 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 sampleBuysFromLiquidityProviderRegistry(
|
||||||
|
address registryAddress,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
return _sampleApproximateBuys(
|
||||||
|
ApproximateBuyQuoteOpts({
|
||||||
|
makerTokenData: abi.encode(makerToken, registryAddress),
|
||||||
|
takerTokenData: abi.encode(takerToken, registryAddress),
|
||||||
|
getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProviderRegistry
|
||||||
|
}),
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns the address of a liquidity provider for the given market
|
||||||
|
/// (takerToken, makerToken), from a registry of liquidity providers.
|
||||||
|
/// Returns address(0) if no such provider exists in the registry.
|
||||||
|
/// @param takerToken Taker asset managed by liquidity provider.
|
||||||
|
/// @param makerToken Maker asset managed by liquidity provider.
|
||||||
|
/// @return providerAddress Address of the liquidity provider.
|
||||||
|
function getLiquidityProviderFromRegistry(
|
||||||
|
address registryAddress,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (address providerAddress)
|
||||||
|
{
|
||||||
|
bytes memory callData = abi.encodeWithSelector(
|
||||||
|
ILiquidityProviderRegistry(0).getLiquidityProviderForMarket.selector,
|
||||||
|
takerToken,
|
||||||
|
makerToken
|
||||||
|
);
|
||||||
|
(bool didSucceed, bytes memory returnData) = registryAddress.staticcall(callData);
|
||||||
|
if (didSucceed && returnData.length == 32) {
|
||||||
|
return LibBytes.readAddress(returnData, 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sampleSellForApproximateBuyFromLiquidityProviderRegistry(
|
||||||
|
bytes memory takerTokenData,
|
||||||
|
bytes memory makerTokenData,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 buyAmount)
|
||||||
|
{
|
||||||
|
(address takerToken, address plpRegistryAddress) =
|
||||||
|
abi.decode(takerTokenData, (address, address));
|
||||||
|
(address makerToken) =
|
||||||
|
abi.decode(makerTokenData, (address));
|
||||||
|
(bool success, bytes memory resultData) =
|
||||||
|
address(this).staticcall(abi.encodeWithSelector(
|
||||||
|
this.sampleSellsFromLiquidityProviderRegistry.selector,
|
||||||
|
plpRegistryAddress,
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
_toSingleValueArray(sellAmount)
|
||||||
|
));
|
||||||
|
if (!success) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
return abi.decode(resultData, (uint256[]))[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./IMultiBridge.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract MultiBridgeSampler {
|
||||||
|
|
||||||
|
/// @dev Default gas limit for multibridge calls.
|
||||||
|
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from MultiBridge.
|
||||||
|
/// @param multibridge Address of the MultiBridge contract.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param intermediateToken The address of the intermediate token to
|
||||||
|
/// use in an indirect route.
|
||||||
|
/// @param makerToken Address 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 sampleSellsFromMultiBridge(
|
||||||
|
address multibridge,
|
||||||
|
address takerToken,
|
||||||
|
address intermediateToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
// Initialize array of maker token amounts.
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
// If no address provided, return all zeros.
|
||||||
|
if (multibridge == address(0)) {
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
IMultiBridge(0).getSellQuote.selector,
|
||||||
|
takerToken,
|
||||||
|
intermediateToken,
|
||||||
|
makerToken,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 buyAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
// Exit early if the amount is too high for the liquidity provider to serve
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
makerTokenAmounts[i] = buyAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
import "./IDevUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract NativeOrderSampler {
|
||||||
|
|
||||||
|
/// @dev Gas limit for DevUtils calls.
|
||||||
|
uint256 constant internal DEV_UTILS_CALL_GAS = 500e3; // 500k
|
||||||
|
|
||||||
|
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||||
|
/// Effectively ignores orders that have empty signatures or
|
||||||
|
/// maker/taker asset amounts (returning 0).
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
|
/// @param devUtilsAddress Address to the DevUtils contract.
|
||||||
|
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
|
function getOrderFillableTakerAssetAmounts(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory orderSignatures,
|
||||||
|
address devUtilsAddress
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory orderFillableTakerAssetAmounts)
|
||||||
|
{
|
||||||
|
orderFillableTakerAssetAmounts = new uint256[](orders.length);
|
||||||
|
for (uint256 i = 0; i != orders.length; i++) {
|
||||||
|
// Ignore orders with no signature or empty maker/taker amounts.
|
||||||
|
if (orderSignatures[i].length == 0 ||
|
||||||
|
orders[i].makerAssetAmount == 0 ||
|
||||||
|
orders[i].takerAssetAmount == 0) {
|
||||||
|
orderFillableTakerAssetAmounts[i] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// solhint-disable indent
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
devUtilsAddress
|
||||||
|
.staticcall
|
||||||
|
.gas(DEV_UTILS_CALL_GAS)
|
||||||
|
(abi.encodeWithSelector(
|
||||||
|
IDevUtils(devUtilsAddress).getOrderRelevantState.selector,
|
||||||
|
orders[i],
|
||||||
|
orderSignatures[i]
|
||||||
|
));
|
||||||
|
// solhint-enable indent
|
||||||
|
if (!didSucceed) {
|
||||||
|
orderFillableTakerAssetAmounts[i] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(
|
||||||
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
|
uint256 fillableTakerAssetAmount,
|
||||||
|
bool isValidSignature
|
||||||
|
) = abi.decode(
|
||||||
|
resultData,
|
||||||
|
(LibOrder.OrderInfo, uint256, bool)
|
||||||
|
);
|
||||||
|
// The fillable amount is zero if the order is not fillable or if the
|
||||||
|
// signature is invalid.
|
||||||
|
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE ||
|
||||||
|
!isValidSignature) {
|
||||||
|
orderFillableTakerAssetAmounts[i] = 0;
|
||||||
|
} else {
|
||||||
|
orderFillableTakerAssetAmounts[i] = fillableTakerAssetAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||||
|
/// Effectively ignores orders that have empty signatures or
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
|
/// @param devUtilsAddress Address to the DevUtils contract.
|
||||||
|
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
|
function getOrderFillableMakerAssetAmounts(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory orderSignatures,
|
||||||
|
address devUtilsAddress
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory orderFillableMakerAssetAmounts)
|
||||||
|
{
|
||||||
|
orderFillableMakerAssetAmounts = getOrderFillableTakerAssetAmounts(
|
||||||
|
orders,
|
||||||
|
orderSignatures,
|
||||||
|
devUtilsAddress
|
||||||
|
);
|
||||||
|
// `orderFillableMakerAssetAmounts` now holds taker asset amounts, so
|
||||||
|
// convert them to maker asset amounts.
|
||||||
|
for (uint256 i = 0; i < orders.length; ++i) {
|
||||||
|
if (orderFillableMakerAssetAmounts[i] != 0) {
|
||||||
|
orderFillableMakerAssetAmounts[i] = LibMath.getPartialAmountCeil(
|
||||||
|
orderFillableMakerAssetAmounts[i],
|
||||||
|
orders[i].takerAssetAmount,
|
||||||
|
orders[i].makerAssetAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract SamplerUtils {
|
||||||
|
|
||||||
|
/// @dev Overridable way to get token decimals.
|
||||||
|
/// @param tokenAddress Address of the token.
|
||||||
|
/// @return decimals The decimal places for the token.
|
||||||
|
function _getTokenDecimals(address tokenAddress)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint8 decimals)
|
||||||
|
{
|
||||||
|
return LibERC20Token.decimals(tokenAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _toSingleValueArray(uint256 v)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256[] memory arr)
|
||||||
|
{
|
||||||
|
arr = new uint256[](1);
|
||||||
|
arr[0] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Assert that the tokens in a trade pair are valid.
|
||||||
|
/// @param makerToken Address of the maker token.
|
||||||
|
/// @param takerToken Address of the taker token.
|
||||||
|
function _assertValidPair(address makerToken, address takerToken)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
|
||||||
|
}
|
||||||
|
}
|
203
contracts/erc20-bridge-sampler/contracts/src/UniswapSampler.sol
Normal file
203
contracts/erc20-bridge-sampler/contracts/src/UniswapSampler.sol
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "./IUniswapExchangeQuotes.sol";
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract UniswapSampler is
|
||||||
|
DeploymentConstants,
|
||||||
|
SamplerUtils
|
||||||
|
{
|
||||||
|
/// @dev Gas limit for Uniswap calls.
|
||||||
|
uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k
|
||||||
|
/// @dev Gas limit for UniswapV2 calls.
|
||||||
|
uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Uniswap.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address 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 sampleSellsFromUniswap(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
||||||
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
|
if (makerToken == _getWethAddress()) {
|
||||||
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else if (takerToken == _getWethAddress()) {
|
||||||
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
uint256 ethBought;
|
||||||
|
(ethBought, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
if (ethBought != 0) {
|
||||||
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
|
ethBought
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
makerTokenAmounts[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Uniswap.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromUniswap(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
||||||
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
|
if (makerToken == _getWethAddress()) {
|
||||||
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else if (takerToken == _getWethAddress()) {
|
||||||
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
uint256 ethSold;
|
||||||
|
(ethSold, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
if (ethSold != 0) {
|
||||||
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
|
ethSold
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
takerTokenAmounts[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Gracefully calls a Uniswap pricing function.
|
||||||
|
/// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange.
|
||||||
|
/// @param functionSelector Selector of the target function.
|
||||||
|
/// @param inputAmount Quantity parameter particular to the pricing function.
|
||||||
|
/// @return outputAmount The returned amount from the function call. Will be
|
||||||
|
/// zero if the call fails or if `uniswapExchangeAddress` is zero.
|
||||||
|
function _callUniswapExchangePriceFunction(
|
||||||
|
address uniswapExchangeAddress,
|
||||||
|
bytes4 functionSelector,
|
||||||
|
uint256 inputAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 outputAmount, bool didSucceed)
|
||||||
|
{
|
||||||
|
if (uniswapExchangeAddress == address(0)) {
|
||||||
|
return (outputAmount, didSucceed);
|
||||||
|
}
|
||||||
|
bytes memory resultData;
|
||||||
|
(didSucceed, resultData) =
|
||||||
|
uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
functionSelector,
|
||||||
|
inputAmount
|
||||||
|
));
|
||||||
|
if (didSucceed) {
|
||||||
|
outputAmount = abi.decode(resultData, (uint256));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Retrive an existing Uniswap exchange contract.
|
||||||
|
/// Throws if the exchange does not exist.
|
||||||
|
/// @param tokenAddress Address of the token contract.
|
||||||
|
/// @return exchange `IUniswapExchangeQuotes` for the token.
|
||||||
|
function _getUniswapExchange(address tokenAddress)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (IUniswapExchangeQuotes exchange)
|
||||||
|
{
|
||||||
|
exchange = IUniswapExchangeQuotes(
|
||||||
|
address(IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
|
||||||
|
.getExchange(tokenAddress))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "./IUniswapV2Router01.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract UniswapV2Sampler is
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
/// @dev Gas limit for UniswapV2 calls.
|
||||||
|
uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from UniswapV2.
|
||||||
|
/// @param path Token route. Should be takerToken -> makerToken
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromUniswapV2(
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
_getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
IUniswapV2Router01(0).getAmountsOut.selector,
|
||||||
|
takerTokenAmounts[i],
|
||||||
|
path
|
||||||
|
));
|
||||||
|
uint256 buyAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
buyAmount = abi.decode(resultData, (uint256[]))[path.length - 1];
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
makerTokenAmounts[i] = buyAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from UniswapV2.
|
||||||
|
/// @param path Token route. Should be takerToken -> makerToken.
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromUniswapV2(
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
_getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
IUniswapV2Router01(0).getAmountsIn.selector,
|
||||||
|
makerTokenAmounts[i],
|
||||||
|
path
|
||||||
|
));
|
||||||
|
uint256 sellAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
sellAmount = abi.decode(resultData, (uint256[]))[0];
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
takerTokenAmounts[i] = sellAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -36,9 +36,9 @@
|
|||||||
"compile:truffle": "truffle compile"
|
"compile:truffle": "truffle compile"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler,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/@(DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberHintHandler|IKyberNetwork|IKyberNetworkProxy|IKyberStorage|ILiquidityProvider|ILiquidityProviderRegistry|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|TestERC20BridgeSampler).json"
|
"abis": "./test/generated-artifacts/@(ApproximateBuys|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|ICurve|IDevUtils|IEth2Dai|IKyberHintHandler|IKyberNetwork|IKyberNetworkProxy|IKyberStorage|ILiquidityProvider|ILiquidityProviderRegistry|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|UniswapSampler|UniswapV2Sampler).json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -8,12 +8,10 @@ import { ContractArtifact } from 'ethereum-types';
|
|||||||
import * as DummyLiquidityProvider from '../generated-artifacts/DummyLiquidityProvider.json';
|
import * as DummyLiquidityProvider from '../generated-artifacts/DummyLiquidityProvider.json';
|
||||||
import * as DummyLiquidityProviderRegistry from '../generated-artifacts/DummyLiquidityProviderRegistry.json';
|
import * as DummyLiquidityProviderRegistry from '../generated-artifacts/DummyLiquidityProviderRegistry.json';
|
||||||
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
|
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
|
||||||
import * as IERC20BridgeSampler from '../generated-artifacts/IERC20BridgeSampler.json';
|
|
||||||
import * as ILiquidityProvider from '../generated-artifacts/ILiquidityProvider.json';
|
import * as ILiquidityProvider from '../generated-artifacts/ILiquidityProvider.json';
|
||||||
import * as ILiquidityProviderRegistry from '../generated-artifacts/ILiquidityProviderRegistry.json';
|
import * as ILiquidityProviderRegistry from '../generated-artifacts/ILiquidityProviderRegistry.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||||
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
|
||||||
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
|
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
|
||||||
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
|
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
|
||||||
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
||||||
|
@ -6,6 +6,5 @@
|
|||||||
export * from '../generated-wrappers/dummy_liquidity_provider';
|
export * from '../generated-wrappers/dummy_liquidity_provider';
|
||||||
export * from '../generated-wrappers/dummy_liquidity_provider_registry';
|
export * from '../generated-wrappers/dummy_liquidity_provider_registry';
|
||||||
export * from '../generated-wrappers/erc20_bridge_sampler';
|
export * from '../generated-wrappers/erc20_bridge_sampler';
|
||||||
export * from '../generated-wrappers/i_erc20_bridge_sampler';
|
|
||||||
export * from '../generated-wrappers/i_liquidity_provider';
|
export * from '../generated-wrappers/i_liquidity_provider';
|
||||||
export * from '../generated-wrappers/i_liquidity_provider_registry';
|
export * from '../generated-wrappers/i_liquidity_provider_registry';
|
||||||
|
@ -5,12 +5,14 @@
|
|||||||
*/
|
*/
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
|
||||||
|
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
|
||||||
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
|
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
|
||||||
import * as DummyLiquidityProviderRegistry from '../test/generated-artifacts/DummyLiquidityProviderRegistry.json';
|
import * as DummyLiquidityProviderRegistry from '../test/generated-artifacts/DummyLiquidityProviderRegistry.json';
|
||||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||||
|
import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json';
|
||||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||||
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
|
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
|
||||||
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.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 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';
|
||||||
@ -21,14 +23,23 @@ import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquid
|
|||||||
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
|
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
|
||||||
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
||||||
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
|
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
|
||||||
|
import * as KyberSampler from '../test/generated-artifacts/KyberSampler.json';
|
||||||
|
import * as LiquidityProviderSampler from '../test/generated-artifacts/LiquidityProviderSampler.json';
|
||||||
|
import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSampler.json';
|
||||||
|
import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json';
|
||||||
|
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
|
||||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||||
|
import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json';
|
||||||
|
import * as UniswapV2Sampler from '../test/generated-artifacts/UniswapV2Sampler.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
|
ApproximateBuys: ApproximateBuys as ContractArtifact,
|
||||||
|
CurveSampler: CurveSampler as ContractArtifact,
|
||||||
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
|
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
|
||||||
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
||||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||||
|
Eth2DaiSampler: Eth2DaiSampler as ContractArtifact,
|
||||||
ICurve: ICurve as ContractArtifact,
|
ICurve: ICurve as ContractArtifact,
|
||||||
IDevUtils: IDevUtils as ContractArtifact,
|
IDevUtils: IDevUtils as ContractArtifact,
|
||||||
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
|
||||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
IKyberHintHandler: IKyberHintHandler as ContractArtifact,
|
IKyberHintHandler: IKyberHintHandler as ContractArtifact,
|
||||||
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
||||||
@ -39,5 +50,12 @@ export const artifacts = {
|
|||||||
IMultiBridge: IMultiBridge as ContractArtifact,
|
IMultiBridge: IMultiBridge as ContractArtifact,
|
||||||
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
|
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
|
||||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
||||||
|
KyberSampler: KyberSampler as ContractArtifact,
|
||||||
|
LiquidityProviderSampler: LiquidityProviderSampler as ContractArtifact,
|
||||||
|
MultiBridgeSampler: MultiBridgeSampler as ContractArtifact,
|
||||||
|
NativeOrderSampler: NativeOrderSampler as ContractArtifact,
|
||||||
|
SamplerUtils: SamplerUtils as ContractArtifact,
|
||||||
|
UniswapSampler: UniswapSampler as ContractArtifact,
|
||||||
|
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
|
||||||
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
|
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -34,10 +34,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const MAKER_TOKEN = randomAddress();
|
const MAKER_TOKEN = randomAddress();
|
||||||
const TAKER_TOKEN = randomAddress();
|
const TAKER_TOKEN = randomAddress();
|
||||||
let devUtilsAddress: string;
|
let devUtilsAddress: string;
|
||||||
const FAKE_BUY_OPTS = {
|
|
||||||
targetSlippageBps: new BigNumber(5),
|
|
||||||
maxIterations: new BigNumber(5),
|
|
||||||
};
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
||||||
@ -443,14 +439,12 @@ 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, [], FAKE_BUY_OPTS).callAsync();
|
const tx = testContract.sampleBuysFromKyberNetwork(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
|
const quotes = await testContract.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
|
||||||
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS)
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq([]);
|
expect(quotes).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
const expectQuotesWithinRange = (
|
const expectQuotesWithinRange = (
|
||||||
@ -485,7 +479,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const [ethToMakerQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
const [ethToMakerQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], ethToMakerQuotes);
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], ethToMakerQuotes);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||||
});
|
});
|
||||||
@ -495,7 +489,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
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, FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
@ -504,7 +498,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
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, FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||||
});
|
});
|
||||||
@ -514,7 +508,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
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, FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
@ -523,7 +517,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
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, FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||||
});
|
});
|
||||||
@ -533,7 +527,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
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, FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
@ -924,13 +918,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
it('should be able to query buys from the liquidity provider', async () => {
|
it('should be able to query buys from the liquidity provider', async () => {
|
||||||
const result = await testContract
|
const result = await testContract
|
||||||
.sampleBuysFromLiquidityProviderRegistry(
|
.sampleBuysFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts)
|
||||||
registryContract.address,
|
|
||||||
yAsset,
|
|
||||||
xAsset,
|
|
||||||
sampleAmounts,
|
|
||||||
FAKE_BUY_OPTS,
|
|
||||||
)
|
|
||||||
.callAsync();
|
.callAsync();
|
||||||
result.forEach((value, idx) => {
|
result.forEach((value, idx) => {
|
||||||
expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
|
expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
|
||||||
@ -944,7 +932,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
yAsset,
|
yAsset,
|
||||||
randomAddress(),
|
randomAddress(),
|
||||||
sampleAmounts,
|
sampleAmounts,
|
||||||
FAKE_BUY_OPTS,
|
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
result.forEach(value => {
|
result.forEach(value => {
|
||||||
@ -954,7 +941,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
it('should just return zeros if the registry does not exist', async () => {
|
it('should just return zeros if the registry does not exist', async () => {
|
||||||
const result = await testContract
|
const result = await testContract
|
||||||
.sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts, FAKE_BUY_OPTS)
|
.sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
result.forEach(value => {
|
result.forEach(value => {
|
||||||
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
export * from '../test/generated-wrappers/approximate_buys';
|
||||||
|
export * from '../test/generated-wrappers/curve_sampler';
|
||||||
export * from '../test/generated-wrappers/dummy_liquidity_provider';
|
export * from '../test/generated-wrappers/dummy_liquidity_provider';
|
||||||
export * from '../test/generated-wrappers/dummy_liquidity_provider_registry';
|
export * from '../test/generated-wrappers/dummy_liquidity_provider_registry';
|
||||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
||||||
|
export * from '../test/generated-wrappers/eth2_dai_sampler';
|
||||||
export * from '../test/generated-wrappers/i_curve';
|
export * from '../test/generated-wrappers/i_curve';
|
||||||
export * from '../test/generated-wrappers/i_dev_utils';
|
export * from '../test/generated-wrappers/i_dev_utils';
|
||||||
export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
|
|
||||||
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_hint_handler';
|
||||||
export * from '../test/generated-wrappers/i_kyber_network';
|
export * from '../test/generated-wrappers/i_kyber_network';
|
||||||
@ -19,4 +21,11 @@ export * from '../test/generated-wrappers/i_liquidity_provider_registry';
|
|||||||
export * from '../test/generated-wrappers/i_multi_bridge';
|
export * from '../test/generated-wrappers/i_multi_bridge';
|
||||||
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
|
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
|
||||||
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
|
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
|
||||||
|
export * from '../test/generated-wrappers/kyber_sampler';
|
||||||
|
export * from '../test/generated-wrappers/liquidity_provider_sampler';
|
||||||
|
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/test_erc20_bridge_sampler';
|
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
|
||||||
|
export * from '../test/generated-wrappers/uniswap_sampler';
|
||||||
|
export * from '../test/generated-wrappers/uniswap_v2_sampler';
|
||||||
|
@ -6,15 +6,16 @@
|
|||||||
"generated-artifacts/DummyLiquidityProvider.json",
|
"generated-artifacts/DummyLiquidityProvider.json",
|
||||||
"generated-artifacts/DummyLiquidityProviderRegistry.json",
|
"generated-artifacts/DummyLiquidityProviderRegistry.json",
|
||||||
"generated-artifacts/ERC20BridgeSampler.json",
|
"generated-artifacts/ERC20BridgeSampler.json",
|
||||||
"generated-artifacts/IERC20BridgeSampler.json",
|
|
||||||
"generated-artifacts/ILiquidityProvider.json",
|
"generated-artifacts/ILiquidityProvider.json",
|
||||||
"generated-artifacts/ILiquidityProviderRegistry.json",
|
"generated-artifacts/ILiquidityProviderRegistry.json",
|
||||||
|
"test/generated-artifacts/ApproximateBuys.json",
|
||||||
|
"test/generated-artifacts/CurveSampler.json",
|
||||||
"test/generated-artifacts/DummyLiquidityProvider.json",
|
"test/generated-artifacts/DummyLiquidityProvider.json",
|
||||||
"test/generated-artifacts/DummyLiquidityProviderRegistry.json",
|
"test/generated-artifacts/DummyLiquidityProviderRegistry.json",
|
||||||
"test/generated-artifacts/ERC20BridgeSampler.json",
|
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||||
|
"test/generated-artifacts/Eth2DaiSampler.json",
|
||||||
"test/generated-artifacts/ICurve.json",
|
"test/generated-artifacts/ICurve.json",
|
||||||
"test/generated-artifacts/IDevUtils.json",
|
"test/generated-artifacts/IDevUtils.json",
|
||||||
"test/generated-artifacts/IERC20BridgeSampler.json",
|
|
||||||
"test/generated-artifacts/IEth2Dai.json",
|
"test/generated-artifacts/IEth2Dai.json",
|
||||||
"test/generated-artifacts/IKyberHintHandler.json",
|
"test/generated-artifacts/IKyberHintHandler.json",
|
||||||
"test/generated-artifacts/IKyberNetwork.json",
|
"test/generated-artifacts/IKyberNetwork.json",
|
||||||
@ -25,7 +26,14 @@
|
|||||||
"test/generated-artifacts/IMultiBridge.json",
|
"test/generated-artifacts/IMultiBridge.json",
|
||||||
"test/generated-artifacts/IUniswapExchangeQuotes.json",
|
"test/generated-artifacts/IUniswapExchangeQuotes.json",
|
||||||
"test/generated-artifacts/IUniswapV2Router01.json",
|
"test/generated-artifacts/IUniswapV2Router01.json",
|
||||||
"test/generated-artifacts/TestERC20BridgeSampler.json"
|
"test/generated-artifacts/KyberSampler.json",
|
||||||
|
"test/generated-artifacts/LiquidityProviderSampler.json",
|
||||||
|
"test/generated-artifacts/MultiBridgeSampler.json",
|
||||||
|
"test/generated-artifacts/NativeOrderSampler.json",
|
||||||
|
"test/generated-artifacts/SamplerUtils.json",
|
||||||
|
"test/generated-artifacts/TestERC20BridgeSampler.json",
|
||||||
|
"test/generated-artifacts/UniswapSampler.json",
|
||||||
|
"test/generated-artifacts/UniswapV2Sampler.json"
|
||||||
],
|
],
|
||||||
"exclude": ["./deploy/solc/solc_bin"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "2.7.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Update curveBridge tests",
|
||||||
|
"pr": 2633
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "2.6.0",
|
"version": "2.6.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -102,6 +102,7 @@
|
|||||||
"@0x/contracts-multisig": "^4.1.7",
|
"@0x/contracts-multisig": "^4.1.7",
|
||||||
"@0x/contracts-staking": "^2.0.14",
|
"@0x/contracts-staking": "^2.0.14",
|
||||||
"@0x/contracts-test-utils": "^5.3.4",
|
"@0x/contracts-test-utils": "^5.3.4",
|
||||||
|
"@0x/subproviders": "^6.1.1",
|
||||||
"@0x/types": "^3.2.0",
|
"@0x/types": "^3.2.0",
|
||||||
"@0x/typescript-typings": "^5.1.1",
|
"@0x/typescript-typings": "^5.1.1",
|
||||||
"@0x/utils": "^5.5.1",
|
"@0x/utils": "^5.5.1",
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import { artifacts, ERC20BridgeSamplerContract } from '@0x/contracts-erc20-bridge-sampler';
|
import { artifacts, ERC20BridgeSamplerContract } from '@0x/contracts-erc20-bridge-sampler';
|
||||||
import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine } from '@0x/contracts-test-utils';
|
import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine } from '@0x/contracts-test-utils';
|
||||||
import { RPCSubprovider } from '@0x/dev-utils/node_modules/@0x/subproviders';
|
import { RPCSubprovider } from '@0x/subproviders';
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
import { BigNumber, providerUtils } from '@0x/utils';
|
||||||
|
|
||||||
export const VB = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
|
export const VB = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
|
||||||
blockchainTests.configure({
|
|
||||||
fork: {
|
|
||||||
unlockedAccounts: [VB],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
blockchainTests.skip('Mainnet Sampler Tests', env => {
|
||||||
let testContract: ERC20BridgeSamplerContract;
|
let testContract: ERC20BridgeSamplerContract;
|
||||||
const fakeSamplerAddress = '0x1111111111111111111111111111111111111111';
|
const fakeSamplerAddress = '0x1111111111111111111111111111111111111111';
|
||||||
const overrides = {
|
const overrides = {
|
||||||
@ -21,7 +16,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
const provider = new Web3ProviderEngine();
|
const provider = new Web3ProviderEngine();
|
||||||
// tslint:disable-next-line:no-non-null-assertion
|
// tslint:disable-next-line:no-non-null-assertion
|
||||||
provider.addProvider(new RPCSubprovider(process.env.FORK_RPC_URL!));
|
provider.addProvider(new RPCSubprovider(process.env.RPC_URL!));
|
||||||
providerUtils.startProviderEngine(provider);
|
providerUtils.startProviderEngine(provider);
|
||||||
testContract = new ERC20BridgeSamplerContract(fakeSamplerAddress, provider, {
|
testContract = new ERC20BridgeSamplerContract(fakeSamplerAddress, provider, {
|
||||||
...env.txDefaults,
|
...env.txDefaults,
|
||||||
@ -29,14 +24,19 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Curve', () => {
|
describe('Curve', () => {
|
||||||
const CURVE_ADDRESS = '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56';
|
const CURVE_ADDRESS = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51';
|
||||||
const DAI_TOKEN_INDEX = new BigNumber(0);
|
const DAI_TOKEN_INDEX = new BigNumber(0);
|
||||||
const USDC_TOKEN_INDEX = new BigNumber(1);
|
const USDC_TOKEN_INDEX = new BigNumber(1);
|
||||||
|
const CURVE_INFO = {
|
||||||
|
poolAddress: CURVE_ADDRESS,
|
||||||
|
sellQuoteFunctionSelector: '0x07211ef7',
|
||||||
|
buyQuoteFunctionSelector: '0x0e71d1b9',
|
||||||
|
};
|
||||||
|
|
||||||
describe('sampleSellsFromCurve()', () => {
|
describe('sampleSellsFromCurve()', () => {
|
||||||
it('samples sells from Curve DAI->USDC', async () => {
|
it('samples sells from Curve DAI->USDC', async () => {
|
||||||
const samples = await testContract
|
const samples = await testContract
|
||||||
.sampleSellsFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1)])
|
.sampleSellsFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [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);
|
||||||
@ -44,7 +44,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
|
|
||||||
it('samples sells from Curve USDC->DAI', async () => {
|
it('samples sells from Curve USDC->DAI', async () => {
|
||||||
const samples = await testContract
|
const samples = await testContract
|
||||||
.sampleSellsFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
|
.sampleSellsFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
|
||||||
.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);
|
||||||
@ -56,7 +56,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
// From DAI to USDC
|
// From DAI to USDC
|
||||||
// I want to buy 1 USDC
|
// I want to buy 1 USDC
|
||||||
const samples = await testContract
|
const samples = await testContract
|
||||||
.sampleBuysFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
|
.sampleBuysFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
|
||||||
.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);
|
||||||
@ -66,7 +66,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
// From USDC to DAI
|
// From USDC to DAI
|
||||||
// I want to buy 1 DAI
|
// I want to buy 1 DAI
|
||||||
const samples = await testContract
|
const samples = await testContract
|
||||||
.sampleBuysFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1)])
|
.sampleBuysFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [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);
|
||||||
@ -74,10 +74,6 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Kyber', () => {
|
describe('Kyber', () => {
|
||||||
const FAKE_BUY_OPTS = {
|
|
||||||
targetSlippageBps: new BigNumber(5),
|
|
||||||
maxIterations: new BigNumber(5),
|
|
||||||
};
|
|
||||||
const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||||
const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f';
|
const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f';
|
||||||
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
|
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
|
||||||
@ -110,7 +106,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
// 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)], FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(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);
|
||||||
@ -120,7 +116,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
// 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)], FAKE_BUY_OPTS)
|
.sampleBuysFromKyberNetwork(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);
|
||||||
|
@ -4,107 +4,128 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
|
|||||||
import { blockchainTests, constants, describe, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
import { blockchainTests, constants, describe, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
||||||
import { AbiEncoder } from '@0x/utils';
|
import { AbiEncoder } from '@0x/utils';
|
||||||
|
|
||||||
|
const USDC_WALLET = '0xF977814e90dA44bFA03b6295A0616a897441aceC';
|
||||||
|
const DAI_WALLET = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
|
||||||
|
const WBTC_WALLET = '0x56178a0d5F301bAf6CF3e1Cd53d9863437345Bf9';
|
||||||
|
blockchainTests.configure({
|
||||||
|
fork: {
|
||||||
|
unlockedAccounts: [USDC_WALLET, DAI_WALLET, WBTC_WALLET],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
blockchainTests.fork.resets('Mainnet curve bridge tests', env => {
|
blockchainTests.fork.resets('Mainnet curve bridge tests', env => {
|
||||||
let testContract: CurveBridgeContract;
|
let testContract: CurveBridgeContract;
|
||||||
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
|
const RECEIVER = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
|
||||||
const usdcWallet = '0xF977814e90dA44bFA03b6295A0616a897441aceC';
|
|
||||||
const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
|
|
||||||
const daiWallet = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
|
|
||||||
const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
|
|
||||||
const curveAddressUsdcDai = '0x2e60CF74d81ac34eB21eEff58Db4D385920ef419';
|
|
||||||
const curveAddressUsdcDaiUsdt = '0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C';
|
|
||||||
const daiTokenIdx = 0;
|
|
||||||
const usdcTokenIdx = 1;
|
|
||||||
const bridgeDataEncoder = AbiEncoder.create([
|
const bridgeDataEncoder = AbiEncoder.create([
|
||||||
{ name: 'curveAddress', type: 'address' },
|
{ name: 'curveAddress', type: 'address' },
|
||||||
|
{ name: 'exchangeFunctionSelector', type: 'bytes4' },
|
||||||
|
{ name: 'fromTokenAddress', type: 'address' },
|
||||||
{ name: 'fromTokenIdx', type: 'int128' },
|
{ name: 'fromTokenIdx', type: 'int128' },
|
||||||
{ name: 'toTokenIdx', type: 'int128' },
|
{ name: 'toTokenIdx', type: 'int128' },
|
||||||
{ name: 'version', type: 'int128' },
|
|
||||||
]);
|
]);
|
||||||
before(async () => {
|
before(async () => {
|
||||||
testContract = await CurveBridgeContract.deployFrom0xArtifactAsync(
|
testContract = await CurveBridgeContract.deployFrom0xArtifactAsync(
|
||||||
assetProxyArtifacts.CurveBridge,
|
assetProxyArtifacts.CurveBridge,
|
||||||
env.provider,
|
env.provider,
|
||||||
{ ...env.txDefaults, from: daiWallet },
|
{ ...env.txDefaults },
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('bridgeTransferFrom()', () => {
|
describe('bridgeTransferFrom()', () => {
|
||||||
describe('Version 0', () => {
|
describe('exchange_underlying()', () => {
|
||||||
const version = 0;
|
const USDC_ADDRESS = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
|
||||||
|
const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
|
||||||
|
const DAI_TOKEN_IDX = 0;
|
||||||
|
const USDC_TOKEN_IDX = 1;
|
||||||
|
const EXCHANGE_UNDERLYING_SELECTOR = '0xa6417ed6';
|
||||||
|
const CURVE_ADDRESS = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51';
|
||||||
it('succeeds exchanges DAI for USDC', async () => {
|
it('succeeds exchanges DAI for USDC', async () => {
|
||||||
const bridgeData = bridgeDataEncoder.encode([curveAddressUsdcDai, daiTokenIdx, usdcTokenIdx, version]);
|
const bridgeData = bridgeDataEncoder.encode([
|
||||||
|
CURVE_ADDRESS,
|
||||||
|
EXCHANGE_UNDERLYING_SELECTOR,
|
||||||
|
DAI_ADDRESS,
|
||||||
|
DAI_TOKEN_IDX,
|
||||||
|
USDC_TOKEN_IDX,
|
||||||
|
]);
|
||||||
// Fund the Bridge
|
// Fund the Bridge
|
||||||
const dai = new ERC20TokenContract(daiAddress, env.provider, { ...env.txDefaults, from: daiWallet });
|
const dai = new ERC20TokenContract(DAI_ADDRESS, env.provider, { ...env.txDefaults, from: DAI_WALLET });
|
||||||
await dai
|
await dai
|
||||||
.transfer(testContract.address, toBaseUnitAmount(1))
|
.transfer(testContract.address, toBaseUnitAmount(1))
|
||||||
.awaitTransactionSuccessAsync({ from: daiWallet }, { shouldValidate: false });
|
.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||||
// Exchange via Curve
|
// Exchange via Curve
|
||||||
await testContract
|
await testContract
|
||||||
.bridgeTransferFrom(
|
.bridgeTransferFrom(
|
||||||
usdcAddress,
|
USDC_ADDRESS,
|
||||||
constants.NULL_ADDRESS,
|
constants.NULL_ADDRESS,
|
||||||
receiver,
|
RECEIVER,
|
||||||
constants.ZERO_AMOUNT,
|
constants.ZERO_AMOUNT,
|
||||||
bridgeData,
|
bridgeData,
|
||||||
)
|
)
|
||||||
.awaitTransactionSuccessAsync({ from: daiWallet, gasPrice: 1 }, { shouldValidate: false });
|
.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||||
});
|
});
|
||||||
it('succeeds exchanges USDC for DAI', async () => {
|
it('succeeds exchanges USDC for DAI', async () => {
|
||||||
const bridgeData = bridgeDataEncoder.encode([curveAddressUsdcDai, usdcTokenIdx, daiTokenIdx, version]);
|
const bridgeData = bridgeDataEncoder.encode([
|
||||||
|
CURVE_ADDRESS,
|
||||||
|
EXCHANGE_UNDERLYING_SELECTOR,
|
||||||
|
USDC_ADDRESS,
|
||||||
|
USDC_TOKEN_IDX,
|
||||||
|
DAI_TOKEN_IDX,
|
||||||
|
]);
|
||||||
// Fund the Bridge
|
// Fund the Bridge
|
||||||
const usdc = new ERC20TokenContract(usdcAddress, env.provider, { ...env.txDefaults, from: usdcWallet });
|
const usdc = new ERC20TokenContract(USDC_ADDRESS, env.provider, {
|
||||||
|
...env.txDefaults,
|
||||||
|
from: USDC_WALLET,
|
||||||
|
});
|
||||||
await usdc
|
await usdc
|
||||||
.transfer(testContract.address, toBaseUnitAmount(1, 6))
|
.transfer(testContract.address, toBaseUnitAmount(1, 6))
|
||||||
.awaitTransactionSuccessAsync({ from: usdcWallet }, { shouldValidate: false });
|
.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||||
// Exchange via Curve
|
// Exchange via Curve
|
||||||
await testContract
|
await testContract
|
||||||
.bridgeTransferFrom(daiAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData)
|
.bridgeTransferFrom(
|
||||||
.awaitTransactionSuccessAsync({ from: usdcWallet, gasPrice: 1 }, { shouldValidate: false });
|
DAI_ADDRESS,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
RECEIVER,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
bridgeData,
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Version 1', () => {
|
|
||||||
const version = 1;
|
describe('exchange()', () => {
|
||||||
it('succeeds exchanges DAI for USDC', async () => {
|
const WBTC_ADDRESS = '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599';
|
||||||
|
const RENBTC_ADDRESS = '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d';
|
||||||
|
const RENBTC_TOKEN_IDX = 0;
|
||||||
|
const WBTC_TOKEN_IDX = 1;
|
||||||
|
const EXCHANGE_SELECTOR = '0x3df02124';
|
||||||
|
const CURVE_ADDRESS = '0x7fc77b5c7614e1533320ea6ddc2eb61fa00a9714';
|
||||||
|
it('succeeds exchanges WBTC for renBTC', async () => {
|
||||||
const bridgeData = bridgeDataEncoder.encode([
|
const bridgeData = bridgeDataEncoder.encode([
|
||||||
curveAddressUsdcDaiUsdt,
|
CURVE_ADDRESS,
|
||||||
daiTokenIdx,
|
EXCHANGE_SELECTOR,
|
||||||
usdcTokenIdx,
|
WBTC_ADDRESS,
|
||||||
version,
|
WBTC_TOKEN_IDX,
|
||||||
|
RENBTC_TOKEN_IDX,
|
||||||
]);
|
]);
|
||||||
// Fund the Bridge
|
// Fund the Bridge
|
||||||
const dai = new ERC20TokenContract(daiAddress, env.provider, { ...env.txDefaults, from: daiWallet });
|
const wbtc = new ERC20TokenContract(WBTC_ADDRESS, env.provider, {
|
||||||
await dai
|
...env.txDefaults,
|
||||||
.transfer(testContract.address, toBaseUnitAmount(1))
|
from: WBTC_WALLET,
|
||||||
.awaitTransactionSuccessAsync({ from: daiWallet }, { shouldValidate: false });
|
});
|
||||||
|
await wbtc
|
||||||
|
.transfer(testContract.address, toBaseUnitAmount(1, 8))
|
||||||
|
.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||||
// Exchange via Curve
|
// Exchange via Curve
|
||||||
await testContract
|
await testContract
|
||||||
.bridgeTransferFrom(
|
.bridgeTransferFrom(
|
||||||
usdcAddress,
|
RENBTC_ADDRESS,
|
||||||
constants.NULL_ADDRESS,
|
constants.NULL_ADDRESS,
|
||||||
receiver,
|
RECEIVER,
|
||||||
constants.ZERO_AMOUNT,
|
constants.ZERO_AMOUNT,
|
||||||
bridgeData,
|
bridgeData,
|
||||||
)
|
)
|
||||||
.awaitTransactionSuccessAsync({ from: daiWallet, gasPrice: 1 }, { shouldValidate: false });
|
.awaitTransactionSuccessAsync({ gas: 6e6 }, { shouldValidate: false });
|
||||||
});
|
|
||||||
it('succeeds exchanges USDC for DAI', async () => {
|
|
||||||
const bridgeData = bridgeDataEncoder.encode([
|
|
||||||
curveAddressUsdcDaiUsdt,
|
|
||||||
usdcTokenIdx,
|
|
||||||
daiTokenIdx,
|
|
||||||
version,
|
|
||||||
]);
|
|
||||||
// Fund the Bridge
|
|
||||||
const usdc = new ERC20TokenContract(usdcAddress, env.provider, { ...env.txDefaults, from: usdcWallet });
|
|
||||||
await usdc
|
|
||||||
.transfer(testContract.address, toBaseUnitAmount(1, 6))
|
|
||||||
.awaitTransactionSuccessAsync({ from: usdcWallet }, { shouldValidate: false });
|
|
||||||
// Exchange via Curve
|
|
||||||
await testContract
|
|
||||||
.bridgeTransferFrom(daiAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData)
|
|
||||||
.awaitTransactionSuccessAsync({ from: usdcWallet, gasPrice: 1 }, { shouldValidate: false });
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user