bsc<->development rebase (#189)

* FQT: Pack Protocol/source name into source ID (#162)

* `@0x/contracts-zero-ex`: Encode protocol ID and source name in bridge source ID
`@0x/asset-swapper`: Use new bridge source ID encoding.

* fix linter issues

* contracts cleanup (#164)

* `@0x/contracts-zero-ex`: Add PancakeSwapFeature

* `@0x/contracts-zero-ex`: Remove tokenspender/allowance target/greedy tokens stuff.'
`@0x/contract-addresses`: Add BSC addresses. Remove exchangeProxyAllowanceTarget.
`@0x/migrations`: Remove exchangeProxyAllowanceTarget.

* Update contracts/zero-ex/contracts/src/features/IPancakeSwapFeature.sol

Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>

* `@0x/contracts-zero-ex`: Add sushiswap support to PancakeSwap

* `@0x/contract-artifacts`: Regenerate artifacts
`@0x/contract-wrappers`: Regenerate wrappers

* `@0x/contract-addresses`: Add BSC addresses

Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>

Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>

* feat: Better chain support (#163)

* feat: Better chain support

* feat: better chain support refactor deployment constants (#166)

* proliferate the chainId

* Refactor sampler to remove DeploymentConstants dependency and fixed addresses

* Rework WETH out, replacing with address(0)

* wat

* hack DeploymentConstants for now

* proliferate the chainId

* Refactor sampler to remove DeploymentConstants dependency and fixed addresses

* remove duped network addresses

* Rework the bridge source encoder

* Use the constants NATIVE_FEE_TOKEN in EP consumer

* `@0x/contract-addresses`: Fix WBNB address (#170)

Co-authored-by: Lawrence Forman <lawrence@0xproject.com>

* multichain enable cakez vip (#171)

* feat: Better chain support

* feat: better chain support refactor deployment constants (#166)

* proliferate the chainId

* Refactor sampler to remove DeploymentConstants dependency and fixed addresses

* Rework WETH out, replacing with address(0)

* wat

* hack DeploymentConstants for now

* proliferate the chainId

* Refactor sampler to remove DeploymentConstants dependency and fixed addresses

* remove duped network addresses

* `asset-swapper`: enable pancake VIP route generation

Co-authored-by: Jacob Evans <jacob@dekz.net>
Co-authored-by: Lawrence Forman <me@merklejerk.com>

* `@0x/contracts-zero-ex`: Fix `PancakeSwapFeature` sushi values (#172)

* `@0x/contracts-zero-ex`: Fix `PancakeSwapFeature` sushi values

* `@0x/contracts-zero-ex`: I am a bad protocologist

Co-authored-by: Lawrence Forman <me@merklejerk.com>

* feat: BSC Nerve + Dodo + Nerve + Ellipsis (#181)

* feat: BSC Nerve + DODO v1

* CHANGELOGs

* Remove extra balance fetch

* Add Belt

* Added Ellipsis

* Update FQT address

* `@0x/contracts-zero-ex`: Delete TokenSpenderFeature and get stuff compiling

* `@0x/asset-swapper`: fix compilation

* prettier

* `@0x/asset-swapper`: Truncate LiquidityProvider source ID name

* Update packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts

Co-authored-by: Jacob Evans <jacob@dekz.net>

* Update packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts

Co-authored-by: Jacob Evans <jacob@dekz.net>

* `@0x/contracts-zero-ex`: Fix BakerySwap on PackageSwapFeature (#190)

* address review comments

Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>
Co-authored-by: Jacob Evans <jacob@dekz.net>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
Lawrence Forman 2021-03-31 18:49:44 -04:00 committed by GitHub
parent 70ddab0231
commit 164a5d44d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 2559 additions and 3029 deletions

View File

@ -1,4 +1,33 @@
[
{
"version": "0.21.0",
"changes": [
{
"note": "Encoding protocol ID and source name in bridge source ID",
"pr": 162
},
{
"note": "Add PancakeSwapFeature",
"pr": 164
},
{
"note": "Remove TokenSpender/AllowanceTarget/greedy tokens stuff",
"pr": 164
},
{
"note": "Added Nerve in BridgeAdapter",
"pr": 181
},
{
"note": "Delete TokenSpenderFeature",
"pr": 189
},
{
"note": "Fix PancakeSwapFeature BakerySwap swap selector",
"pr": 190
}
]
},
{
"version": "0.20.0",
"changes": [

View File

@ -26,6 +26,7 @@ import "./features/interfaces/ITokenSpenderFeature.sol";
import "./features/interfaces/ITransformERC20Feature.sol";
import "./features/interfaces/IMetaTransactionsFeature.sol";
import "./features/interfaces/IUniswapFeature.sol";
import "./features/interfaces/IPancakeSwapFeature.sol";
import "./features/interfaces/ILiquidityProviderFeature.sol";
import "./features/interfaces/INativeOrdersFeature.sol";
import "./features/interfaces/IBatchFillNativeOrdersFeature.sol";
@ -36,10 +37,10 @@ import "./features/interfaces/IMultiplexFeature.sol";
interface IZeroEx is
IOwnableFeature,
ISimpleFunctionRegistryFeature,
ITokenSpenderFeature,
ITransformERC20Feature,
IMetaTransactionsFeature,
IUniswapFeature,
IPancakeSwapFeature,
ILiquidityProviderFeature,
INativeOrdersFeature,
IBatchFillNativeOrdersFeature,

View File

@ -1,47 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
library LibSpenderRichErrors {
// solhint-disable func-name-mixedcase
function SpenderERC20TransferFromFailedError(
address token,
address owner,
address to,
uint256 amount,
bytes memory errorData
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("SpenderERC20TransferFromFailedError(address,address,address,uint256,bytes)")),
token,
owner,
to,
amount,
errorData
);
}
}

View File

@ -1,56 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol";
import "../errors/LibSpenderRichErrors.sol";
import "./IAllowanceTarget.sol";
/// @dev The allowance target for the TokenSpender feature.
contract AllowanceTarget is
IAllowanceTarget,
AuthorizableV06
{
// solhint-disable no-unused-vars,indent,no-empty-blocks
using LibRichErrorsV06 for bytes;
/// @dev Execute an arbitrary call. Only an authority can call this.
/// @param target The call target.
/// @param callData The call data.
/// @return resultData The data returned by the call.
function executeCall(
address payable target,
bytes calldata callData
)
external
override
onlyAuthorized
returns (bytes memory resultData)
{
bool success;
(success, resultData) = target.call(callData);
if (!success) {
resultData.rrevert();
}
}
}

View File

@ -1,40 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/interfaces/IAuthorizableV06.sol";
/// @dev The allowance target for the TokenSpender feature.
interface IAllowanceTarget is
IAuthorizableV06
{
/// @dev Execute an arbitrary call. Only an authority can call this.
/// @param target The call target.
/// @param callData The call data.
/// @return resultData The data returned by the call.
function executeCall(
address payable target,
bytes calldata callData
)
external
returns (bytes memory resultData);
}

View File

@ -47,15 +47,14 @@ contract LiquidityProviderFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "LiquidityProviderFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 3);
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 4);
/// @dev The sandbox contract address.
ILiquidityProviderSandbox public immutable sandbox;
constructor(LiquidityProviderSandbox sandbox_, bytes32 greedyTokensBloomFilter)
constructor(LiquidityProviderSandbox sandbox_)
public
FixinCommon()
FixinTokenSpender(greedyTokensBloomFilter)
{
sandbox = sandbox_;
}

View File

@ -78,7 +78,7 @@ contract MetaTransactionsFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "MetaTransactions";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1);
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
"MetaTransactionData("
@ -105,11 +105,10 @@ contract MetaTransactionsFeature is
}
}
constructor(address zeroExAddress, bytes32 greedyTokensBloomFilter)
constructor(address zeroExAddress)
public
FixinCommon()
FixinEIP712(zeroExAddress)
FixinTokenSpender(greedyTokensBloomFilter)
{
// solhint-disable-next-line no-empty-blocks
}

View File

@ -55,7 +55,7 @@ contract MultiplexFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "MultiplexFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
/// @dev The WETH token contract.
IEtherTokenV06 private immutable weth;
@ -73,12 +73,10 @@ contract MultiplexFeature is
constructor(
address zeroExAddress,
IEtherTokenV06 weth_,
ILiquidityProviderSandbox sandbox_,
bytes32 greedyTokensBloomFilter
ILiquidityProviderSandbox sandbox_
)
public
FixinEIP712(zeroExAddress)
FixinTokenSpender(greedyTokensBloomFilter)
{
weth = weth_;
sandbox = sandbox_;

View File

@ -34,15 +34,14 @@ contract NativeOrdersFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "LimitOrders";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
constructor(
address zeroExAddress,
IEtherTokenV06 weth,
IStaking staking,
FeeCollectorController feeCollectorController,
uint32 protocolFeeMultiplier,
bytes32 greedyTokensBloomFilter
uint32 protocolFeeMultiplier
)
public
NativeOrdersSettlement(
@ -50,8 +49,7 @@ contract NativeOrdersFeature is
weth,
staking,
feeCollectorController,
protocolFeeMultiplier,
greedyTokensBloomFilter
protocolFeeMultiplier
)
{
// solhint-disable no-empty-blocks

View File

@ -0,0 +1,423 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "../migrations/LibMigrate.sol";
import "../fixins/FixinCommon.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IPancakeSwapFeature.sol";
/// @dev VIP pancake fill functions.
contract PancakeSwapFeature is
IFeature,
IPancakeSwapFeature,
FixinCommon
{
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "PancakeSwapFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
/// @dev WBNB contract.
IEtherTokenV06 private immutable WBNB;
// 0xFF + address of the PancakeSwap factory contract.
uint256 constant private FF_PANCAKESWAP_FACTORY = 0xffbcfccbde45ce874adcb698cc183debcf179528120000000000000000000000;
// 0xFF + address of the BakerySwap factory contract.
uint256 constant private FF_BAKERYSWAP_FACTORY = 0xff01bf7c66c6bd861915cdaae475042d3c4bae16a70000000000000000000000;
// 0xFF + address of the SushiSwap factory contract.
uint256 constant private FF_SUSHISWAP_FACTORY = 0xffc35DADB65012eC5796536bD9864eD8773aBc74C40000000000000000000000;
// Init code hash of the PancakeSwap pair contract.
uint256 constant private PANCAKESWAP_PAIR_INIT_CODE_HASH = 0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66;
// Init code hash of the BakerySwap pair contract.
uint256 constant private BAKERYSWAP_PAIR_INIT_CODE_HASH = 0xe2e87433120e32c4738a7d8f3271f3d872cbe16241d67537139158d90bac61d3;
// Init code hash of the SushiSwap pair contract.
uint256 constant private SUSHISWAP_PAIR_INIT_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303;
// Mask of the lower 20 bytes of a bytes32.
uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
// BNB pseudo-token address.
uint256 constant private ETH_TOKEN_ADDRESS_32 = 0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee;
// Maximum token quantity that can be swapped against the PancakeSwapPair contract.
uint256 constant private MAX_SWAP_AMOUNT = 2**112;
// bytes4(keccak256("executeCall(address,bytes)"))
uint256 constant private ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32 = 0xbca8c7b500000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("getReserves()"))
uint256 constant private PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32 = 0x0902f1ac00000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("swap(uint256,uint256,address,bytes)"))
uint256 constant private PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x022c0d9f00000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("swap(uint256,uint256,address)"))
uint256 constant private BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x6d9a640a00000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("transferFrom(address,address,uint256)"))
uint256 constant private TRANSFER_FROM_CALL_SELECTOR_32 = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("allowance(address,address)"))
uint256 constant private ALLOWANCE_CALL_SELECTOR_32 = 0xdd62ed3e00000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("withdraw(uint256)"))
uint256 constant private WETH_WITHDRAW_CALL_SELECTOR_32 = 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("deposit()"))
uint256 constant private WETH_DEPOSIT_CALL_SELECTOR_32 = 0xd0e30db000000000000000000000000000000000000000000000000000000000;
// bytes4(keccak256("transfer(address,uint256)"))
uint256 constant private ERC20_TRANSFER_CALL_SELECTOR_32 = 0xa9059cbb00000000000000000000000000000000000000000000000000000000;
/// @dev Construct this contract.
/// @param wbnb The WBNB contract.
constructor(IEtherTokenV06 wbnb) public {
WBNB = wbnb;
}
/// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`.
/// @return success `LibMigrate.SUCCESS` on success.
function migrate()
external
returns (bytes4 success)
{
_registerFeatureFunction(this.sellToPancakeSwap.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
/// @dev Efficiently sell directly to pancake/BakerySwap/SushiSwap.
/// @param tokens Sell path.
/// @param sellAmount of `tokens[0]` Amount to sell.
/// @param minBuyAmount Minimum amount of `tokens[-1]` to buy.
/// @param fork The protocol fork to use.
/// @return buyAmount Amount of `tokens[-1]` bought.
function sellToPancakeSwap(
IERC20TokenV06[] calldata tokens,
uint256 sellAmount,
uint256 minBuyAmount,
ProtocolFork fork
)
external
payable
override
returns (uint256 buyAmount)
{
require(tokens.length > 1, "PancakeSwapFeature/InvalidTokensLength");
{
// Load immutables onto the stack.
IEtherTokenV06 wbnb = WBNB;
// Store some vars in memory to get around stack limits.
assembly {
// calldataload(mload(0xA00)) == first element of `tokens` array
mstore(0xA00, add(calldataload(0x04), 0x24))
// mload(0xA20) == fork
mstore(0xA20, fork)
// mload(0xA40) == WBNB
mstore(0xA40, wbnb)
}
}
assembly {
// numPairs == tokens.length - 1
let numPairs := sub(calldataload(add(calldataload(0x04), 0x4)), 1)
// We use the previous buy amount as the sell amount for the next
// pair in a path. So for the first swap we want to set it to `sellAmount`.
buyAmount := sellAmount
let buyToken
let nextPair := 0
for {let i := 0} lt(i, numPairs) {i := add(i, 1)} {
// sellToken = tokens[i]
let sellToken := loadTokenAddress(i)
// buyToken = tokens[i+1]
buyToken := loadTokenAddress(add(i, 1))
// The canonical ordering of this token pair.
let pairOrder := lt(normalizeToken(sellToken), normalizeToken(buyToken))
// Compute the pair address if it hasn't already been computed
// from the last iteration.
let pair := nextPair
if iszero(pair) {
pair := computePairAddress(sellToken, buyToken)
nextPair := 0
}
if iszero(i) {
// This is the first token in the path.
switch eq(sellToken, ETH_TOKEN_ADDRESS_32)
case 0 { // Not selling BNB. Selling an ERC20 instead.
// Make sure BNB was not attached to the call.
if gt(callvalue(), 0) {
revert(0, 0)
}
// For the first pair we need to transfer sellTokens into the
// pair contract.
moveTakerTokensTo(sellToken, pair, sellAmount)
}
default {
// If selling BNB, we need to wrap it to WBNB and transfer to the
// pair contract.
if iszero(eq(callvalue(), sellAmount)) {
revert(0, 0)
}
sellToken := mload(0xA40)// Re-assign to WBNB
// Call `WBNB.deposit{value: sellAmount}()`
mstore(0xB00, WETH_DEPOSIT_CALL_SELECTOR_32)
if iszero(call(gas(), sellToken, sellAmount, 0xB00, 0x4, 0x00, 0x0)) {
bubbleRevert()
}
// Call `WBNB.transfer(pair, sellAmount)`
mstore(0xB00, ERC20_TRANSFER_CALL_SELECTOR_32)
mstore(0xB04, pair)
mstore(0xB24, sellAmount)
if iszero(call(gas(), sellToken, 0, 0xB00, 0x44, 0x00, 0x0)) {
bubbleRevert()
}
}
// No need to check results, if deposit/transfers failed the PancakeSwapPair will
// reject our trade (or it may succeed if somehow the reserve was out of sync)
// this is fine for the taker.
}
// Call pair.getReserves(), store the results at `0xC00`
mstore(0xB00, PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32)
if iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) {
bubbleRevert()
}
// Revert if the pair contract does not return at least two words.
if lt(returndatasize(), 0x40) {
mstore(0, pair)
revert(0, 32)
}
// Sell amount for this hop is the previous buy amount.
let pairSellAmount := buyAmount
// Compute the buy amount based on the pair reserves.
{
let sellReserve
let buyReserve
switch iszero(pairOrder)
case 0 {
// Transpose if pair order is different.
sellReserve := mload(0xC00)
buyReserve := mload(0xC20)
}
default {
sellReserve := mload(0xC20)
buyReserve := mload(0xC00)
}
// Ensure that the sellAmount is < 2¹¹².
if gt(pairSellAmount, MAX_SWAP_AMOUNT) {
revert(0, 0)
}
// Pairs are in the range (0, 2¹¹²) so this shouldn't overflow.
// buyAmount = (pairSellAmount * 997 * buyReserve) /
// (pairSellAmount * 997 + sellReserve * 1000);
let sellAmountWithFee := mul(pairSellAmount, 997)
buyAmount := div(
mul(sellAmountWithFee, buyReserve),
add(sellAmountWithFee, mul(sellReserve, 1000))
)
}
let receiver
// Is this the last pair contract?
switch eq(add(i, 1), numPairs)
case 0 {
// Not the last pair contract, so forward bought tokens to
// the next pair contract.
nextPair := computePairAddress(
buyToken,
loadTokenAddress(add(i, 2))
)
receiver := nextPair
}
default {
// The last pair contract.
// Forward directly to taker UNLESS they want BNB back.
switch eq(buyToken, ETH_TOKEN_ADDRESS_32)
case 0 {
receiver := caller()
}
default {
receiver := address()
}
}
// Call pair.swap()
switch mload(0xA20) // fork
case 1 {
mstore(0xB00, BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32)
}
default {
mstore(0xB00, PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32)
}
switch pairOrder
case 0 {
mstore(0xB04, buyAmount)
mstore(0xB24, 0)
}
default {
mstore(0xB04, 0)
mstore(0xB24, buyAmount)
}
mstore(0xB44, receiver)
mstore(0xB64, 0x80)
mstore(0xB84, 0)
if iszero(call(gas(), pair, 0, 0xB00, 0xA4, 0, 0)) {
bubbleRevert()
}
} // End for-loop.
// If buying BNB, unwrap the WBNB first
if eq(buyToken, ETH_TOKEN_ADDRESS_32) {
// Call `WBNB.withdraw(buyAmount)`
mstore(0xB00, WETH_WITHDRAW_CALL_SELECTOR_32)
mstore(0xB04, buyAmount)
if iszero(call(gas(), mload(0xA40), 0, 0xB00, 0x24, 0x00, 0x0)) {
bubbleRevert()
}
// Transfer BNB to the caller.
if iszero(call(gas(), caller(), buyAmount, 0xB00, 0x0, 0x00, 0x0)) {
bubbleRevert()
}
}
// Functions ///////////////////////////////////////////////////////
// Load a token address from the `tokens` calldata argument.
function loadTokenAddress(idx) -> addr {
addr := and(ADDRESS_MASK, calldataload(add(mload(0xA00), mul(idx, 0x20))))
}
// Convert BNB pseudo-token addresses to WBNB.
function normalizeToken(token) -> normalized {
normalized := token
// Translate BNB pseudo-tokens to WBNB.
if eq(token, ETH_TOKEN_ADDRESS_32) {
normalized := mload(0xA40)
}
}
// Compute the address of the PancakeSwapPair contract given two
// tokens.
function computePairAddress(tokenA, tokenB) -> pair {
// Convert BNB pseudo-token addresses to WBNB.
tokenA := normalizeToken(tokenA)
tokenB := normalizeToken(tokenB)
// There is one contract for every combination of tokens,
// which is deployed using CREATE2.
// The derivation of this address is given by:
// address(keccak256(abi.encodePacked(
// bytes(0xFF),
// address(PANCAKESWAP_FACTORY_ADDRESS),
// keccak256(abi.encodePacked(
// tokenA < tokenB ? tokenA : tokenB,
// tokenA < tokenB ? tokenB : tokenA,
// )),
// bytes32(PANCAKESWAP_PAIR_INIT_CODE_HASH),
// )));
// Compute the salt (the hash of the sorted tokens).
// Tokens are written in reverse memory order to packed encode
// them as two 20-byte values in a 40-byte chunk of memory
// starting at 0xB0C.
switch lt(tokenA, tokenB)
case 0 {
mstore(0xB14, tokenA)
mstore(0xB00, tokenB)
}
default {
mstore(0xB14, tokenB)
mstore(0xB00, tokenA)
}
let salt := keccak256(0xB0C, 0x28)
// Compute the pair address by hashing all the components together.
switch mload(0xA20) // fork
case 0 {
mstore(0xB00, FF_PANCAKESWAP_FACTORY)
mstore(0xB15, salt)
mstore(0xB35, PANCAKESWAP_PAIR_INIT_CODE_HASH)
}
case 1 {
mstore(0xB00, FF_BAKERYSWAP_FACTORY)
mstore(0xB15, salt)
mstore(0xB35, BAKERYSWAP_PAIR_INIT_CODE_HASH)
}
default {
mstore(0xB00, FF_SUSHISWAP_FACTORY)
mstore(0xB15, salt)
mstore(0xB35, SUSHISWAP_PAIR_INIT_CODE_HASH)
}
pair := and(ADDRESS_MASK, keccak256(0xB00, 0x55))
}
// Revert with the return data from the most recent call.
function bubbleRevert() {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// Move `amount` tokens from the taker/caller to `to`.
function moveTakerTokensTo(token, to, amount) {
// Perform a `transferFrom()`
mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32)
mstore(0xB04, caller())
mstore(0xB24, to)
mstore(0xB44, amount)
let success := call(
gas(),
token,
0,
0xB00,
0x64,
0xC00,
// Copy only the first 32 bytes of return data. We
// only care about reading a boolean in the success
// case. We will use returndatacopy() in the failure case.
0x20
)
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should
// return a boolean, but some return nothing or
// extra data. We accept 0-length return data as
// success, or at least 32 bytes that starts with
// a 32-byte boolean true.
success := and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
iszero(lt(rdsize, 32)), // at least 32 bytes
eq(mload(0xC00), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
// Revert with the data returned from the transferFrom call.
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
// Revert if we bought too little.
require(buyAmount >= minBuyAmount, "PancakeSwapFeature/UnderBought");
}
}

View File

@ -1,137 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "../errors/LibSpenderRichErrors.sol";
import "../fixins/FixinCommon.sol";
import "../migrations/LibMigrate.sol";
import "../external/IAllowanceTarget.sol";
import "../storage/LibTokenSpenderStorage.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/ITokenSpenderFeature.sol";
/// @dev Feature that allows spending token allowances.
contract TokenSpenderFeature is
IFeature,
ITokenSpenderFeature,
FixinCommon
{
// solhint-disable
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "TokenSpender";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
// solhint-enable
using LibRichErrorsV06 for bytes;
/// @dev Initialize and register this feature. Should be delegatecalled
/// into during a `Migrate.migrate()`.
/// @param allowanceTarget An `allowanceTarget` instance, configured to have
/// the ZeroeEx contract as an authority.
/// @return success `MIGRATE_SUCCESS` on success.
function migrate(IAllowanceTarget allowanceTarget)
external
returns (bytes4 success)
{
LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget;
_registerFeatureFunction(this.getAllowanceTarget.selector);
_registerFeatureFunction(this._spendERC20Tokens.selector);
_registerFeatureFunction(this.getSpendableERC20BalanceOf.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
/// @dev Transfers ERC20 tokens from `owner` to `to`. Only callable from within.
/// @param token The token to spend.
/// @param owner The owner of the tokens.
/// @param to The recipient of the tokens.
/// @param amount The amount of `token` to transfer.
function _spendERC20Tokens(
IERC20TokenV06 token,
address owner,
address to,
uint256 amount
)
external
override
onlySelf
{
IAllowanceTarget spender = LibTokenSpenderStorage.getStorage().allowanceTarget;
// Have the allowance target execute an ERC20 `transferFrom()`.
(bool didSucceed, bytes memory resultData) = address(spender).call(
abi.encodeWithSelector(
IAllowanceTarget.executeCall.selector,
address(token),
abi.encodeWithSelector(
IERC20TokenV06.transferFrom.selector,
owner,
to,
amount
)
)
);
if (didSucceed) {
resultData = abi.decode(resultData, (bytes));
}
if (!didSucceed || !LibERC20TokenV06.isSuccessfulResult(resultData)) {
LibSpenderRichErrors.SpenderERC20TransferFromFailedError(
address(token),
owner,
to,
amount,
resultData
).rrevert();
}
}
/// @dev Gets the maximum amount of an ERC20 token `token` that can be
/// pulled from `owner` by the token spender.
/// @param token The token to spend.
/// @param owner The owner of the tokens.
/// @return amount The amount of tokens that can be pulled.
function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
external
override
view
returns (uint256 amount)
{
return LibSafeMathV06.min256(
token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)),
token.balanceOf(owner)
);
}
/// @dev Get the address of the allowance target.
/// @return target The target of token allowances.
function getAllowanceTarget()
external
override
view
returns (address target)
{
return address(LibTokenSpenderStorage.getStorage().allowanceTarget);
}
}

View File

@ -60,10 +60,7 @@ contract TransformERC20Feature is
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 1);
constructor(bytes32 greedyTokensBloomFilter)
public
FixinTokenSpender(greedyTokensBloomFilter)
{}
constructor() public {}
/// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`.

View File

@ -23,7 +23,6 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "../migrations/LibMigrate.sol";
import "../external/IAllowanceTarget.sol";
import "../fixins/FixinCommon.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IUniswapFeature.sol";
@ -38,13 +37,9 @@ contract UniswapFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "UniswapFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1);
/// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails.
bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER;
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 2);
/// @dev WETH contract.
IEtherTokenV06 private immutable WETH;
/// @dev AllowanceTarget instance.
IAllowanceTarget private immutable ALLOWANCE_TARGET;
// 0xFF + address of the UniswapV2Factory contract.
uint256 constant private FF_UNISWAP_FACTORY = 0xFF5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f0000000000000000000000;
@ -80,16 +75,8 @@ contract UniswapFeature is
/// @dev Construct this contract.
/// @param weth The WETH contract.
/// @param allowanceTarget The AllowanceTarget contract.
/// @param greedyTokensBloomFilter The bloom filter for greedy tokens.
constructor(
IEtherTokenV06 weth,
IAllowanceTarget allowanceTarget,
bytes32 greedyTokensBloomFilter
) public {
constructor(IEtherTokenV06 weth) public {
WETH = weth;
ALLOWANCE_TARGET = allowanceTarget;
GREEDY_TOKENS_BLOOM_FILTER = greedyTokensBloomFilter;
}
/// @dev Initialize and register this feature.
@ -124,8 +111,6 @@ contract UniswapFeature is
{
// Load immutables onto the stack.
IEtherTokenV06 weth = WETH;
IAllowanceTarget allowanceTarget = ALLOWANCE_TARGET;
bytes32 greedyTokensBloomFilter = GREEDY_TOKENS_BLOOM_FILTER;
// Store some vars in memory to get around stack limits.
assembly {
@ -135,10 +120,6 @@ contract UniswapFeature is
mstore(0xA20, isSushi)
// mload(0xA40) == WETH
mstore(0xA40, weth)
// mload(0xA60) == ALLOWANCE_TARGET
mstore(0xA60, allowanceTarget)
// mload(0xA80) == GREEDY_TOKENS_BLOOM_FILTER
mstore(0xA80, greedyTokensBloomFilter)
}
}
@ -373,38 +354,7 @@ contract UniswapFeature is
// Move `amount` tokens from the taker/caller to `to`.
function moveTakerTokensTo(token, to, amount) {
// If the token is possibly greedy, we check the allowance rather
// than relying on letting the transferFrom() call fail and
// falling through to legacy allowance target because the token
// will eat all our gas.
if isTokenPossiblyGreedy(token) {
// Check if we have enough direct allowance by calling
// `token.allowance()`
mstore(0xB00, ALLOWANCE_CALL_SELECTOR_32)
mstore(0xB04, caller())
mstore(0xB24, address())
let success := staticcall(gas(), token, 0xB00, 0x44, 0xC00, 0x20)
if iszero(success) {
// Call to allowance() failed.
bubbleRevert()
}
// Make sure the allowance call returned at least a word.
if lt(returndatasize(), 0x20) {
revert(0, 0)
}
// Call succeeded.
// Result is stored in 0xC00-0xC20.
if lt(mload(0xC00), amount) {
// We don't have enough direct allowance, so try
// going through the legacy allowance taregt.
moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount)
leave
}
}
// Otherwise we will optimistically try to perform a `transferFrom()`
// directly then if it fails we will go through the legacy allowance target.
// Perform a `transferFrom()`
mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32)
mstore(0xB04, caller())
mstore(0xB24, to)
@ -419,8 +369,7 @@ contract UniswapFeature is
0xC00,
// Copy only the first 32 bytes of return data. We
// only care about reading a boolean in the success
// case, and we discard the return data in the
// failure case.
// case. We will use returndatacopy() in the failure case.
0x20
)
@ -443,37 +392,11 @@ contract UniswapFeature is
)
if iszero(success) {
// Try to fall back to the allowance target.
moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount)
// Revert with the data returned from the transferFrom call.
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// Move tokens by going through the legacy allowance target contract.
function moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) {
mstore(0xB00, ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32)
mstore(0xB04, token)
mstore(0xB24, 0x40)
mstore(0xB44, 0x64)
mstore(0xB64, TRANSFER_FROM_CALL_SELECTOR_32)
mstore(0xB68, caller())
mstore(0xB88, to)
mstore(0xBA8, amount)
if iszero(call(gas(), mload(0xA60), 0, 0xB00, 0xC8, 0x00, 0x0)) {
bubbleRevert()
}
// If this fall back failed, the swap will most likely fail
// so there's no need to validate the result.
}
// Checks if a token possibly belongs to the GREEDY_TOKENS_BLOOM_FILTER
// bloom filter.
function isTokenPossiblyGreedy(token) -> isPossiblyGreedy {
// The hash is given by:
// (1 << (keccak256(token) % 256)) | (1 << (token % 256))
mstore(0, token)
let h := or(shl(mod(keccak256(0, 32), 256), 1), shl(mod(token, 256), 1))
isPossiblyGreedy := eq(and(h, mload(0xA80)), h)
}
}
// Revert if we bought too little.

View File

@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
/// @dev VIP PancakeSwap/BakerySwap/SushiSwap fill functions.
interface IPancakeSwapFeature {
enum ProtocolFork {
PancakeSwap,
BakerySwap,
SushiSwap
}
/// @dev Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap.
/// @param tokens Sell path.
/// @param sellAmount of `tokens[0]` Amount to sell.
/// @param minBuyAmount Minimum amount of `tokens[-1]` to buy.
/// @param fork The protocol fork to use.
/// @return buyAmount Amount of `tokens[-1]` bought.
function sellToPancakeSwap(
IERC20TokenV06[] calldata tokens,
uint256 sellAmount,
uint256 minBuyAmount,
ProtocolFork fork
)
external
payable
returns (uint256 buyAmount);
}

View File

@ -40,11 +40,10 @@ abstract contract NativeOrdersCancellation is
uint256 private constant HIGH_BIT = 1 << 255;
constructor(
address zeroExAddress,
bytes32 greedyTokensBloomFilter
address zeroExAddress
)
internal
NativeOrdersInfo(zeroExAddress, greedyTokensBloomFilter)
NativeOrdersInfo(zeroExAddress)
{
// solhint-disable no-empty-blocks
}

View File

@ -51,12 +51,10 @@ abstract contract NativeOrdersInfo is
uint256 private constant HIGH_BIT = 1 << 255;
constructor(
address zeroExAddress,
bytes32 greedyTokensBloomFilter
address zeroExAddress
)
internal
FixinEIP712(zeroExAddress)
FixinTokenSpender(greedyTokensBloomFilter)
{
// solhint-disable no-empty-blocks
}

View File

@ -96,11 +96,10 @@ abstract contract NativeOrdersSettlement is
IEtherTokenV06 weth,
IStaking staking,
FeeCollectorController feeCollectorController,
uint32 protocolFeeMultiplier,
bytes32 greedyTokensBloomFilter
uint32 protocolFeeMultiplier
)
public
NativeOrdersCancellation(zeroExAddress, greedyTokensBloomFilter)
NativeOrdersCancellation(zeroExAddress)
NativeOrdersProtocolFees(weth, staking, feeCollectorController, protocolFeeMultiplier)
{
// solhint-disable no-empty-blocks

View File

@ -22,28 +22,13 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../features/interfaces/ITokenSpenderFeature.sol";
import "../errors/LibSpenderRichErrors.sol";
import "../external/FeeCollector.sol";
import "../vendor/v3/IStaking.sol";
import "../vendor/v3/IStaking.sol";
/// @dev Helpers for moving tokens around.
abstract contract FixinTokenSpender {
using LibRichErrorsV06 for bytes;
// Mask of the lower 20 bytes of a bytes32.
uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
/// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails.
bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER;
/// @param greedyTokensBloomFilter The bloom filter for all greedy tokens.
constructor(bytes32 greedyTokensBloomFilter)
internal
{
GREEDY_TOKENS_BLOOM_FILTER = greedyTokensBloomFilter;
}
/// @dev Transfers ERC20 tokens from `owner` to `to`.
/// @param token The token to spend.
@ -58,29 +43,8 @@ abstract contract FixinTokenSpender {
)
internal
{
bool success;
bytes memory revertData;
require(address(token) != address(this), "FixinTokenSpender/CANNOT_INVOKE_SELF");
// If the token eats all gas when failing, we do not want to perform
// optimistic fall through to the old AllowanceTarget contract if the
// direct transferFrom() fails.
if (_isTokenPossiblyGreedy(token)) {
// If the token does not have a direct allowance on us then we use
// the allowance target.
if (token.allowance(owner, address(this)) < amount) {
_transferFromLegacyAllowanceTarget(
token,
owner,
to,
amount,
""
);
return;
}
}
assembly {
let ptr := mload(0x40) // free memory pointer
@ -90,20 +54,18 @@ abstract contract FixinTokenSpender {
mstore(add(ptr, 0x24), and(to, ADDRESS_MASK))
mstore(add(ptr, 0x44), amount)
success := call(
let success := call(
gas(),
and(token, ADDRESS_MASK),
0,
ptr,
0x64,
0,
0
ptr,
32
)
let rdsize := returndatasize()
returndatacopy(add(ptr, 0x20), 0, rdsize) // reuse memory
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
@ -113,30 +75,16 @@ abstract contract FixinTokenSpender {
iszero(rdsize), // no return data, or
and(
iszero(lt(rdsize, 32)), // at least 32 bytes
eq(mload(add(ptr, 0x20)), 1) // starts with uint256(1)
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
// revertData is a bytes, so length-prefixed data
mstore(ptr, rdsize)
revertData := ptr
// update free memory pointer (ptr + 32-byte length + return data)
mstore(0x40, add(add(ptr, 0x20), rdsize))
returndatacopy(ptr, 0, rdsize)
revert(ptr, rdsize)
}
}
if (!success) {
_transferFromLegacyAllowanceTarget(
token,
owner,
to,
amount,
revertData
);
}
}
/// @dev Gets the maximum amount of an ERC20 token `token` that can be
@ -157,53 +105,4 @@ abstract contract FixinTokenSpender {
token.balanceOf(owner)
);
}
/// @dev Check if a token possibly belongs to the `GREEDY_TOKENS_BLOOM_FILTER`
/// bloom filter.
function _isTokenPossiblyGreedy(IERC20TokenV06 token)
internal
view
returns (bool isPossiblyGreedy)
{
// The hash is given by:
// (1 << (keccak256(token) % 256)) | (1 << (token % 256))
bytes32 h;
assembly {
mstore(0, token)
h := or(shl(mod(keccak256(0, 32), 256), 1), shl(mod(token, 256), 1))
}
return (h & GREEDY_TOKENS_BLOOM_FILTER) == h;
}
/// @dev Transfer tokens using the legacy allowance target instead of
/// allowances directly set on the exchange proxy.
function _transferFromLegacyAllowanceTarget(
IERC20TokenV06 token,
address owner,
address to,
uint256 amount,
bytes memory initialRevertData
)
private
{
// Try the old AllowanceTarget.
try ITokenSpenderFeature(address(this))._spendERC20Tokens(
token,
owner,
to,
amount
) {
} catch (bytes memory revertData) {
// Bubble up the first error message. (In general, the fallback to the
// allowance target is opportunistic. We ignore the specific error
// message if it fails.)
LibSpenderRichErrors.SpenderERC20TransferFromFailedError(
address(token),
owner,
to,
amount,
initialRevertData.length != 0 ? initialRevertData : revertData
).rrevert();
}
}
}

View File

@ -22,11 +22,9 @@ pragma experimental ABIEncoderV2;
import "../ZeroEx.sol";
import "../features/interfaces/IOwnableFeature.sol";
import "../features/TokenSpenderFeature.sol";
import "../features/TransformERC20Feature.sol";
import "../features/MetaTransactionsFeature.sol";
import "../features/NativeOrdersFeature.sol";
import "../external/AllowanceTarget.sol";
import "./InitialMigration.sol";
@ -39,7 +37,6 @@ contract FullMigration {
struct Features {
SimpleFunctionRegistryFeature registry;
OwnableFeature ownable;
TokenSpenderFeature tokenSpender;
TransformERC20Feature transformERC20;
MetaTransactionsFeature metaTransactions;
NativeOrdersFeature nativeOrders;
@ -107,7 +104,7 @@ contract FullMigration {
);
// Add features.
_addFeatures(zeroEx, owner, features, migrateOpts);
_addFeatures(zeroEx, features, migrateOpts);
// Transfer ownership to the real owner.
IOwnableFeature(address(zeroEx)).transferOwnership(owner);
@ -132,36 +129,16 @@ contract FullMigration {
/// @dev Deploy and register features to the ZeroEx contract.
/// @param zeroEx The bootstrapped ZeroEx contract.
/// @param owner The ultimate owner of the ZeroEx contract.
/// @param features Features to add to the proxy.
/// @param migrateOpts Parameters needed to initialize features.
function _addFeatures(
ZeroEx zeroEx,
address owner,
Features memory features,
MigrateOpts memory migrateOpts
)
private
{
IOwnableFeature ownable = IOwnableFeature(address(zeroEx));
// TokenSpenderFeature
{
// Create the allowance target.
AllowanceTarget allowanceTarget = new AllowanceTarget();
// Let the ZeroEx contract use the allowance target.
allowanceTarget.addAuthorizedAddress(address(zeroEx));
// Transfer ownership of the allowance target to the (real) owner.
allowanceTarget.transferOwnership(owner);
// Register the feature.
ownable.migrate(
address(features.tokenSpender),
abi.encodeWithSelector(
TokenSpenderFeature.migrate.selector,
allowanceTarget
),
address(this)
);
}
// TransformERC20Feature
{
// Register the feature.

View File

@ -1,46 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "./LibStorage.sol";
import "../external/IAllowanceTarget.sol";
/// @dev Storage helpers for the `TokenSpender` feature.
library LibTokenSpenderStorage {
/// @dev Storage bucket for this feature.
struct Storage {
// Allowance target contract.
IAllowanceTarget allowanceTarget;
}
/// @dev Get the storage bucket for this contract.
function getStorage() internal pure returns (Storage storage stor) {
uint256 storageSlot = LibStorage.getStorageSlot(
LibStorage.StorageId.TokenSpender
);
// Dip into assembly to change the slot pointed to by the local
// variable `stor`.
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
assembly { stor_slot := storageSlot }
}
}

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "./IBridgeAdapter.sol";
import "./BridgeSource.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinCoFiX.sol";
@ -32,9 +32,9 @@ import "./mixins/MixinDodoV2.sol";
import "./mixins/MixinKyber.sol";
import "./mixins/MixinMooniswap.sol";
import "./mixins/MixinMStable.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinOasis.sol";
import "./mixins/MixinShell.sol";
import "./mixins/MixinSushiswap.sol";
import "./mixins/MixinUniswap.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinZeroExBridge.sol";
@ -51,9 +51,9 @@ contract BridgeAdapter is
MixinKyber,
MixinMooniswap,
MixinMStable,
MixinNerve,
MixinOasis,
MixinShell,
MixinSushiswap,
MixinUniswap,
MixinUniswapV2,
MixinZeroExBridge
@ -70,9 +70,9 @@ contract BridgeAdapter is
MixinKyber(weth)
MixinMooniswap(weth)
MixinMStable()
MixinNerve()
MixinOasis()
MixinShell()
MixinSushiswap()
MixinUniswap(weth)
MixinUniswapV2()
MixinZeroExBridge()
@ -88,109 +88,106 @@ contract BridgeAdapter is
override
returns (uint256 boughtAmount)
{
if (order.source == BridgeSource.CURVE ||
order.source == BridgeSource.SWERVE ||
order.source == BridgeSource.SNOWSWAP) {
uint128 protocolId = uint128(uint256(order.source) >> 128);
if (protocolId == BridgeProtocols.CURVE) {
boughtAmount = _tradeCurve(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.SUSHISWAP) {
boughtAmount = _tradeSushiswap(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.UNISWAPV2 ||
order.source == BridgeSource.LINKSWAP) {
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
boughtAmount = _tradeUniswapV2(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.UNISWAP) {
} else if (protocolId == BridgeProtocols.UNISWAP) {
boughtAmount = _tradeUniswap(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.BALANCER ||
order.source == BridgeSource.CREAM) {
} else if (protocolId == BridgeProtocols.BALANCER) {
boughtAmount = _tradeBalancer(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.KYBER) {
} else if (protocolId == BridgeProtocols.KYBER) {
boughtAmount = _tradeKyber(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.MOONISWAP) {
} else if (protocolId == BridgeProtocols.MOONISWAP) {
boughtAmount = _tradeMooniswap(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.MSTABLE) {
} else if (protocolId == BridgeProtocols.MSTABLE) {
boughtAmount = _tradeMStable(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.OASIS) {
} else if (protocolId == BridgeProtocols.OASIS) {
boughtAmount = _tradeOasis(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.SHELL) {
} else if (protocolId == BridgeProtocols.SHELL) {
boughtAmount = _tradeShell(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.DODO) {
} else if (protocolId == BridgeProtocols.DODO) {
boughtAmount = _tradeDodo(
sellToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.DODOV2) {
} else if (protocolId == BridgeProtocols.DODOV2) {
boughtAmount = _tradeDodoV2(
sellToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.CRYPTOCOM) {
} else if (protocolId == BridgeProtocols.CRYPTOCOM) {
boughtAmount = _tradeCryptoCom(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.BANCOR) {
} else if (protocolId == BridgeProtocols.BANCOR) {
boughtAmount = _tradeBancor(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.COFIX) {
} else if (protocolId == BridgeProtocols.COFIX) {
boughtAmount = _tradeCoFiX(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.NERVE) {
boughtAmount = _tradeNerve(
sellToken,
sellAmount,
order.bridgeData
);
} else {
boughtAmount = _tradeZeroExBridge(
sellToken,

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
library BridgeProtocols {
// A incrementally increasing, append-only list of protocol IDs.
// We don't use an enum so solidity doesn't throw when we pass in a
// new protocol ID that hasn't been rolled up yet.
uint128 internal constant UNKNOWN = 0;
uint128 internal constant CURVE = 1;
uint128 internal constant UNISWAPV2 = 2;
uint128 internal constant UNISWAP = 3;
uint128 internal constant BALANCER = 4;
uint128 internal constant KYBER = 5;
uint128 internal constant MOONISWAP = 6;
uint128 internal constant MSTABLE = 7;
uint128 internal constant OASIS = 8;
uint128 internal constant SHELL = 9;
uint128 internal constant DODO = 10;
uint128 internal constant DODOV2 = 11;
uint128 internal constant CRYPTOCOM = 12;
uint128 internal constant BANCOR = 13;
uint128 internal constant COFIX = 14;
uint128 internal constant NERVE = 15;
}

View File

@ -1,47 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
library BridgeSource {
uint256 constant internal BALANCER = 0;
uint256 constant internal BANCOR = 1;
uint256 constant internal COFIX = 2;
uint256 constant internal CURVE = 3;
uint256 constant internal CREAM = 4;
uint256 constant internal CRYPTOCOM = 5;
uint256 constant internal DODO = 6;
uint256 constant internal KYBER = 7;
uint256 constant internal LIQUIDITYPROVIDER = 8;
uint256 constant internal MOONISWAP = 9;
uint256 constant internal MSTABLE = 10;
uint256 constant internal OASIS = 11;
uint256 constant internal SHELL = 12;
uint256 constant internal SNOWSWAP = 13;
uint256 constant internal SUSHISWAP = 14;
uint256 constant internal SWERVE = 15;
uint256 constant internal UNISWAP = 16;
uint256 constant internal UNISWAPV2 = 17;
uint256 constant internal DODOV2 = 18;
uint256 constant internal LINKSWAP = 19;
// New sources should be APPENDED to this list, taking the next highest
// integer value.
}

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -26,20 +26,24 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
interface IBridgeAdapter {
struct BridgeOrder {
uint256 source;
// Upper 16 bytes: uint128 protocol ID (right-aligned)
// Lower 16 bytes: ASCII source name (left-aligned)
bytes32 source;
uint256 takerTokenAmount;
uint256 makerTokenAmount;
bytes bridgeData;
}
/// @dev Emitted when tokens are swapped with an external source.
/// @param source The unique ID for the source. See `BridgeSource.sol`
/// @param source A unique ID for the source, where the upper 16 bytes
/// encodes the (right-aligned) uint128 protocol ID and the
/// lower 16 bytes encodes an ASCII source name.
/// @param inputToken The token the bridge is converting from.
/// @param outputToken The token the bridge is converting to.
/// @param inputTokenAmount Amount of input token sold.
/// @param outputTokenAmount Amount of output token bought.
event BridgeFill(
uint256 source,
bytes32 source,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 inputTokenAmount,

View File

@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
contract MixinNerve {
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
struct NerveBridgeData {
address pool;
bytes4 exchangeFunctionSelector;
int128 fromCoinIdx;
int128 toCoinIdx;
}
function _tradeNerve(
IERC20TokenV06 sellToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// Basically a Curve fork but the swap option has a deadline
// Decode the bridge data to get the Curve metadata.
NerveBridgeData memory data = abi.decode(bridgeData, (NerveBridgeData));
sellToken.approveIfBelow(data.pool, sellAmount);
(bool success, bytes memory resultData) =
data.pool.call(abi.encodeWithSelector(
data.exchangeFunctionSelector,
data.fromCoinIdx,
data.toCoinIdx,
// dx
sellAmount,
// min dy
1,
// deadline
block.timestamp
));
if (!success) {
resultData.rrevert();
}
return abi.decode(resultData, (uint256));
}
}

View File

@ -1,75 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./MixinUniswapV2.sol";
contract MixinSushiswap {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeSushiswap(
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
IERC20TokenV06[] memory path;
IUniswapV2Router02 router;
{
address[] memory _path;
(router, _path) =
abi.decode(bridgeData, (IUniswapV2Router02, address[]));
// To get around `abi.decode()` not supporting interface array types.
assembly { path := _path }
}
require(path.length >= 2, "MixinSushiswap/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
path[path.length - 1] == buyToken,
"MixinSushiswap/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
// Grant the Uniswap router an allowance to sell the first token.
path[0].approveIfBelow(
address(router),
sellAmount
);
uint[] memory amounts = router.swapExactTokensForTokens(
// Sell all tokens we hold.
sellAmount,
// Minimum buy amount.
1,
// Convert to `buyToken` along this path.
path,
// Recipient is `this`.
address(this),
// Expires after this block.
block.timestamp
);
return amounts[amounts.length-1];
}
}

View File

@ -26,12 +26,7 @@ import "../src/fixins/FixinTokenSpender.sol";
contract TestFixinTokenSpender is
FixinTokenSpender
{
uint256 constant private TRIGGER_FALLBACK_SUCCESS_AMOUNT = 1340;
constructor(bytes32 greedyTokensBloomFilter)
public
FixinTokenSpender(greedyTokensBloomFilter)
{}
constructor() public {}
function transferERC20Tokens(
IERC20TokenV06 token,
@ -56,21 +51,6 @@ contract TestFixinTokenSpender is
uint256 amount
);
// This is called as a fallback when the original transferFrom() fails.
function _spendERC20Tokens(
IERC20TokenV06 token,
address owner,
address to,
uint256 amount
)
external
{
require(amount == TRIGGER_FALLBACK_SUCCESS_AMOUNT,
"TokenSpenderFallback/FAILURE_AMOUNT");
emit FallbackCalled(address(token), owner, to, amount);
}
function getSpendableERC20BalanceOf(
IERC20TokenV06 token,
address owner
@ -81,12 +61,4 @@ contract TestFixinTokenSpender is
{
return _getSpendableERC20BalanceOf(token, owner);
}
function isTokenPossiblyGreedy(IERC20TokenV06 token)
external
view
returns (bool)
{
return _isTokenPossiblyGreedy(token);
}
}

View File

@ -35,8 +35,7 @@ contract TestMetaTransactionsNativeOrdersFeature is
IEtherTokenV06(0),
IStaking(0),
FeeCollectorController(address(new TestFeeCollectorController())),
0,
bytes32(0)
0
)
{}

View File

@ -38,7 +38,7 @@ contract TestMetaTransactionsTransformERC20Feature is
Transformation[] transformations
);
constructor() public TransformERC20Feature(0) {}
constructor() public TransformERC20Feature() {}
function _transformERC20(TransformERC20Args memory args)
public

View File

@ -13,8 +13,7 @@ contract TestNativeOrdersFeature is
IEtherTokenV06 weth,
IStaking staking,
FeeCollectorController _feeCollectorController, // Unused but necessary for artifact compatibility.
uint32 protocolFeeMultiplier,
bytes32 greedyTokensBloomFilter
uint32 protocolFeeMultiplier
)
public
NativeOrdersFeature(
@ -22,8 +21,7 @@ contract TestNativeOrdersFeature is
weth,
staking,
FeeCollectorController(address(new TestFeeCollectorController())),
protocolFeeMultiplier,
greedyTokensBloomFilter
protocolFeeMultiplier
)
{
// solhint-disable no-empty-blocks

View File

@ -1,31 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../src/features/TokenSpenderFeature.sol";
contract TestTokenSpender is
TokenSpenderFeature
{
modifier onlySelf() override {
_;
}
}

View File

@ -30,5 +30,5 @@ contract TestTransformERC20 is
_;
}
constructor() public TransformERC20Feature(0) {}
constructor() public TransformERC20Feature() {}
}

View File

@ -41,9 +41,9 @@
"rollback": "node ./lib/scripts/rollback.js"
},
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature",
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeSource|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",

View File

@ -23,7 +23,6 @@ import * as INativeOrdersFeature from '../generated-artifacts/INativeOrdersFeatu
import * as InitialMigration from '../generated-artifacts/InitialMigration.json';
import * as IOwnableFeature from '../generated-artifacts/IOwnableFeature.json';
import * as ISimpleFunctionRegistryFeature from '../generated-artifacts/ISimpleFunctionRegistryFeature.json';
import * as ITokenSpenderFeature from '../generated-artifacts/ITokenSpenderFeature.json';
import * as ITransformERC20Feature from '../generated-artifacts/ITransformERC20Feature.json';
import * as IZeroEx from '../generated-artifacts/IZeroEx.json';
import * as LiquidityProviderFeature from '../generated-artifacts/LiquidityProviderFeature.json';
@ -35,7 +34,6 @@ import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json';
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
import * as PositiveSlippageFeeTransformer from '../generated-artifacts/PositiveSlippageFeeTransformer.json';
import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
import * as TokenSpenderFeature from '../generated-artifacts/TokenSpenderFeature.json';
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
import * as WethTransformer from '../generated-artifacts/WethTransformer.json';
import * as ZeroEx from '../generated-artifacts/ZeroEx.json';
@ -49,7 +47,6 @@ export const artifacts = {
IERC20Transformer: IERC20Transformer as ContractArtifact,
IOwnableFeature: IOwnableFeature as ContractArtifact,
ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact,
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
@ -58,7 +55,6 @@ export const artifacts = {
OwnableFeature: OwnableFeature as ContractArtifact,
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
TransformERC20Feature: TransformERC20Feature as ContractArtifact,
TokenSpenderFeature: TokenSpenderFeature as ContractArtifact,
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,

View File

@ -41,7 +41,6 @@ export {
IOwnableFeatureEvents,
ISimpleFunctionRegistryFeatureContract,
ISimpleFunctionRegistryFeatureEvents,
ITokenSpenderFeatureContract,
ITransformERC20FeatureContract,
IZeroExContract,
LogMetadataTransformerContract,

View File

@ -5,7 +5,6 @@ import { TxData } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { ZERO_BYTES32 } from './constants';
import {
FeeCollectorControllerContract,
FullMigrationContract,
@ -15,7 +14,6 @@ import {
NativeOrdersFeatureContract,
OwnableFeatureContract,
SimpleFunctionRegistryFeatureContract,
TokenSpenderFeatureContract,
TransformERC20FeatureContract,
ZeroExContract,
} from './wrappers';
@ -108,7 +106,6 @@ export async function initialMigrateAsync(
* Addresses of features for a full deployment of the Exchange Proxy.
*/
export interface FullFeatures extends BootstrapFeatures {
tokenSpender: string;
transformERC20: string;
metaTransactions: string;
nativeOrders: string;
@ -118,7 +115,6 @@ export interface FullFeatures extends BootstrapFeatures {
* Artifacts to use when deploying full features.
*/
export interface FullFeatureArtifacts extends BootstrapFeatureArtifacts {
tokenSpender: SimpleContractArtifact;
transformERC20: SimpleContractArtifact;
metaTransactions: SimpleContractArtifact;
nativeOrders: SimpleContractArtifact;
@ -133,7 +129,6 @@ export interface FullFeaturesDeployConfig {
wethAddress: string;
stakingAddress: string;
protocolFeeMultiplier: number;
greedyTokensBloomFilter: string;
}
/**
@ -149,11 +144,9 @@ const DEFAULT_FULL_FEATURES_DEPLOY_CONFIG = {
stakingAddress: NULL_ADDRESS,
feeCollectorController: NULL_ADDRESS,
protocolFeeMultiplier: 70e3,
greedyTokensBloomFilter: ZERO_BYTES32,
};
const DEFAULT_FULL_FEATURES_ARTIFACTS = {
tokenSpender: artifacts.TokenSpenderFeature,
transformERC20: artifacts.TransformERC20Feature,
metaTransactions: artifacts.MetaTransactionsFeature,
nativeOrders: artifacts.NativeOrdersFeature,
@ -187,14 +180,6 @@ export async function deployFullFeaturesAsync(
}
return {
...(await deployBootstrapFeaturesAsync(provider, txDefaults)),
tokenSpender:
features.tokenSpender ||
(await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
_featureArtifacts.tokenSpender,
provider,
txDefaults,
artifacts,
)).address,
transformERC20:
features.transformERC20 ||
(await TransformERC20FeatureContract.deployFrom0xArtifactAsync(
@ -202,7 +187,6 @@ export async function deployFullFeaturesAsync(
provider,
txDefaults,
artifacts,
_config.greedyTokensBloomFilter,
)).address,
metaTransactions:
features.metaTransactions ||
@ -212,7 +196,6 @@ export async function deployFullFeaturesAsync(
txDefaults,
artifacts,
_config.zeroExAddress,
_config.greedyTokensBloomFilter,
)).address,
nativeOrders:
features.nativeOrders ||
@ -226,7 +209,6 @@ export async function deployFullFeaturesAsync(
_config.stakingAddress,
_config.feeCollectorController,
_config.protocolFeeMultiplier,
_config.greedyTokensBloomFilter,
)).address,
};
}

View File

@ -20,7 +20,6 @@ export * from '../generated-wrappers/i_multiplex_feature';
export * from '../generated-wrappers/i_native_orders_feature';
export * from '../generated-wrappers/i_ownable_feature';
export * from '../generated-wrappers/i_simple_function_registry_feature';
export * from '../generated-wrappers/i_token_spender_feature';
export * from '../generated-wrappers/i_transform_erc20_feature';
export * from '../generated-wrappers/i_zero_ex';
export * from '../generated-wrappers/initial_migration';
@ -33,7 +32,6 @@ export * from '../generated-wrappers/ownable_feature';
export * from '../generated-wrappers/pay_taker_transformer';
export * from '../generated-wrappers/positive_slippage_fee_transformer';
export * from '../generated-wrappers/simple_function_registry_feature';
export * from '../generated-wrappers/token_spender_feature';
export * from '../generated-wrappers/transform_erc20_feature';
export * from '../generated-wrappers/weth_transformer';
export * from '../generated-wrappers/zero_ex';

View File

@ -1,82 +0,0 @@
import { blockchainTests, constants, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { AuthorizableRevertErrors, hexUtils, StringRevertError } from '@0x/utils';
import { artifacts } from './artifacts';
import { AllowanceTargetContract, TestCallTargetContract, TestCallTargetEvents } from './wrappers';
blockchainTests.resets('AllowanceTarget', env => {
let owner: string;
let authority: string;
let allowanceTarget: AllowanceTargetContract;
let callTarget: TestCallTargetContract;
before(async () => {
[owner, authority] = await env.getAccountAddressesAsync();
allowanceTarget = await AllowanceTargetContract.deployFrom0xArtifactAsync(
artifacts.AllowanceTarget,
env.provider,
env.txDefaults,
artifacts,
);
await allowanceTarget.addAuthorizedAddress(authority).awaitTransactionSuccessAsync();
callTarget = await TestCallTargetContract.deployFrom0xArtifactAsync(
artifacts.TestCallTarget,
env.provider,
env.txDefaults,
artifacts,
);
});
const TARGET_RETURN_VALUE = hexUtils.rightPad('0x12345678');
const REVERTING_DATA = '0x1337';
describe('executeCall()', () => {
it('non-authority cannot call executeCall()', async () => {
const notAuthority = randomAddress();
const tx = allowanceTarget
.executeCall(randomAddress(), hexUtils.random())
.callAsync({ from: notAuthority });
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(notAuthority));
});
it('authority can call executeCall()', async () => {
const targetData = hexUtils.random(128);
const receipt = await allowanceTarget
.executeCall(callTarget.address, targetData)
.awaitTransactionSuccessAsync({ from: authority });
verifyEventsFromLogs(
receipt.logs,
[
{
context: callTarget.address,
sender: allowanceTarget.address,
data: targetData,
value: constants.ZERO_AMOUNT,
},
],
TestCallTargetEvents.CallTargetCalled,
);
});
it('AllowanceTarget returns call result', async () => {
const result = await allowanceTarget
.executeCall(callTarget.address, hexUtils.random(128))
.callAsync({ from: authority });
expect(result).to.eq(TARGET_RETURN_VALUE);
});
it('AllowanceTarget returns raw call revert', async () => {
const tx = allowanceTarget.executeCall(callTarget.address, REVERTING_DATA).callAsync({ from: authority });
return expect(tx).to.revertWith(new StringRevertError('TestCallTarget/REVERT'));
});
it('AllowanceTarget cannot receive ETH', async () => {
const tx = env.web3Wrapper.sendTransactionAsync({
to: allowanceTarget.address,
from: owner,
value: 0,
});
return expect(tx).to.eventually.be.rejected();
});
});
});

View File

@ -6,11 +6,10 @@
import { ContractArtifact } from 'ethereum-types';
import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json';
import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.json';
import * as BatchFillNativeOrdersFeature from '../test/generated-artifacts/BatchFillNativeOrdersFeature.json';
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
import * as BridgeSource from '../test/generated-artifacts/BridgeSource.json';
import * as BridgeProtocols from '../test/generated-artifacts/BridgeProtocols.json';
import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json';
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
@ -22,7 +21,6 @@ import * as FixinReentrancyGuard from '../test/generated-artifacts/FixinReentran
import * as FixinTokenSpender from '../test/generated-artifacts/FixinTokenSpender.json';
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json';
import * as IBatchFillNativeOrdersFeature from '../test/generated-artifacts/IBatchFillNativeOrdersFeature.json';
import * as IBootstrapFeature from '../test/generated-artifacts/IBootstrapFeature.json';
import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json';
@ -40,6 +38,7 @@ import * as INativeOrdersEvents from '../test/generated-artifacts/INativeOrdersE
import * as INativeOrdersFeature from '../test/generated-artifacts/INativeOrdersFeature.json';
import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json';
import * as IOwnableFeature from '../test/generated-artifacts/IOwnableFeature.json';
import * as IPancakeSwapFeature from '../test/generated-artifacts/IPancakeSwapFeature.json';
import * as ISimpleFunctionRegistryFeature from '../test/generated-artifacts/ISimpleFunctionRegistryFeature.json';
import * as IStaking from '../test/generated-artifacts/IStaking.json';
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
@ -68,9 +67,7 @@ import * as LibSignature from '../test/generated-artifacts/LibSignature.json';
import * as LibSignatureRichErrors from '../test/generated-artifacts/LibSignatureRichErrors.json';
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
import * as LibSimpleFunctionRegistryStorage from '../test/generated-artifacts/LibSimpleFunctionRegistryStorage.json';
import * as LibSpenderRichErrors from '../test/generated-artifacts/LibSpenderRichErrors.json';
import * as LibStorage from '../test/generated-artifacts/LibStorage.json';
import * as LibTokenSpenderStorage from '../test/generated-artifacts/LibTokenSpenderStorage.json';
import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json';
import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json';
import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json';
@ -88,9 +85,9 @@ import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json';
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json';
import * as MixinOasis from '../test/generated-artifacts/MixinOasis.json';
import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
import * as MixinSushiswap from '../test/generated-artifacts/MixinSushiswap.json';
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
@ -102,6 +99,7 @@ import * as NativeOrdersInfo from '../test/generated-artifacts/NativeOrdersInfo.
import * as NativeOrdersProtocolFees from '../test/generated-artifacts/NativeOrdersProtocolFees.json';
import * as NativeOrdersSettlement from '../test/generated-artifacts/NativeOrdersSettlement.json';
import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
import * as PancakeSwapFeature from '../test/generated-artifacts/PancakeSwapFeature.json';
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/PermissionlessTransformerDeployer.json';
import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json';
@ -134,7 +132,6 @@ import * as TestRfqOriginRegistration from '../test/generated-artifacts/TestRfqO
import * as TestSimpleFunctionRegistryFeatureImpl1 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json';
import * as TestSimpleFunctionRegistryFeatureImpl2 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json';
import * as TestStaking from '../test/generated-artifacts/TestStaking.json';
import * as TestTokenSpender from '../test/generated-artifacts/TestTokenSpender.json';
import * as TestTokenSpenderERC20Token from '../test/generated-artifacts/TestTokenSpenderERC20Token.json';
import * as TestTransformerBase from '../test/generated-artifacts/TestTransformerBase.json';
import * as TestTransformERC20 from '../test/generated-artifacts/TestTransformERC20.json';
@ -143,7 +140,6 @@ import * as TestTransformerHost from '../test/generated-artifacts/TestTransforme
import * as TestWeth from '../test/generated-artifacts/TestWeth.json';
import * as TestWethTransformerHost from '../test/generated-artifacts/TestWethTransformerHost.json';
import * as TestZeroExFeature from '../test/generated-artifacts/TestZeroExFeature.json';
import * as TokenSpenderFeature from '../test/generated-artifacts/TokenSpenderFeature.json';
import * as Transformer from '../test/generated-artifacts/Transformer.json';
import * as TransformERC20Feature from '../test/generated-artifacts/TransformERC20Feature.json';
import * as TransformerDeployer from '../test/generated-artifacts/TransformerDeployer.json';
@ -163,14 +159,11 @@ export const artifacts = {
LibProxyRichErrors: LibProxyRichErrors as ContractArtifact,
LibSignatureRichErrors: LibSignatureRichErrors as ContractArtifact,
LibSimpleFunctionRegistryRichErrors: LibSimpleFunctionRegistryRichErrors as ContractArtifact,
LibSpenderRichErrors: LibSpenderRichErrors as ContractArtifact,
LibTransformERC20RichErrors: LibTransformERC20RichErrors as ContractArtifact,
LibWalletRichErrors: LibWalletRichErrors as ContractArtifact,
AllowanceTarget: AllowanceTarget as ContractArtifact,
FeeCollector: FeeCollector as ContractArtifact,
FeeCollectorController: FeeCollectorController as ContractArtifact,
FlashWallet: FlashWallet as ContractArtifact,
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
IFlashWallet: IFlashWallet as ContractArtifact,
ILiquidityProviderSandbox: ILiquidityProviderSandbox as ContractArtifact,
LibFeeCollector: LibFeeCollector as ContractArtifact,
@ -184,8 +177,8 @@ export const artifacts = {
MultiplexFeature: MultiplexFeature as ContractArtifact,
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
OwnableFeature: OwnableFeature as ContractArtifact,
PancakeSwapFeature: PancakeSwapFeature as ContractArtifact,
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
TokenSpenderFeature: TokenSpenderFeature as ContractArtifact,
TransformERC20Feature: TransformERC20Feature as ContractArtifact,
UniswapFeature: UniswapFeature as ContractArtifact,
IBatchFillNativeOrdersFeature: IBatchFillNativeOrdersFeature as ContractArtifact,
@ -197,6 +190,7 @@ export const artifacts = {
INativeOrdersEvents: INativeOrdersEvents as ContractArtifact,
INativeOrdersFeature: INativeOrdersFeature as ContractArtifact,
IOwnableFeature: IOwnableFeature as ContractArtifact,
IPancakeSwapFeature: IPancakeSwapFeature as ContractArtifact,
ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact,
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
@ -225,7 +219,6 @@ export const artifacts = {
LibReentrancyGuardStorage: LibReentrancyGuardStorage as ContractArtifact,
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
LibStorage: LibStorage as ContractArtifact,
LibTokenSpenderStorage: LibTokenSpenderStorage as ContractArtifact,
LibTransformERC20Storage: LibTransformERC20Storage as ContractArtifact,
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
@ -237,7 +230,7 @@ export const artifacts = {
Transformer: Transformer as ContractArtifact,
WethTransformer: WethTransformer as ContractArtifact,
BridgeAdapter: BridgeAdapter as ContractArtifact,
BridgeSource: BridgeSource as ContractArtifact,
BridgeProtocols: BridgeProtocols as ContractArtifact,
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
MixinBalancer: MixinBalancer as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact,
@ -249,9 +242,9 @@ export const artifacts = {
MixinKyber: MixinKyber as ContractArtifact,
MixinMStable: MixinMStable as ContractArtifact,
MixinMooniswap: MixinMooniswap as ContractArtifact,
MixinNerve: MixinNerve as ContractArtifact,
MixinOasis: MixinOasis as ContractArtifact,
MixinShell: MixinShell as ContractArtifact,
MixinSushiswap: MixinSushiswap as ContractArtifact,
MixinUniswap: MixinUniswap as ContractArtifact,
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
@ -289,7 +282,6 @@ export const artifacts = {
TestSimpleFunctionRegistryFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1 as ContractArtifact,
TestSimpleFunctionRegistryFeatureImpl2: TestSimpleFunctionRegistryFeatureImpl2 as ContractArtifact,
TestStaking: TestStaking as ContractArtifact,
TestTokenSpender: TestTokenSpender as ContractArtifact,
TestTokenSpenderERC20Token: TestTokenSpenderERC20Token as ContractArtifact,
TestTransformERC20: TestTransformERC20 as ContractArtifact,
TestTransformerBase: TestTransformerBase as ContractArtifact,

View File

@ -2,7 +2,6 @@ import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contra
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { BigNumber, OwnableRevertErrors, ZeroExRevertErrors } from '@0x/utils';
import { ZERO_BYTES32 } from '../../src/constants';
import { IOwnableFeatureContract, IZeroExContract, LiquidityProviderFeatureContract } from '../../src/wrappers';
import { artifacts } from '../artifacts';
import { abis } from '../utils/abis';
@ -63,7 +62,6 @@ blockchainTests('LiquidityProvider feature', env => {
env.txDefaults,
artifacts,
sandbox.address,
ZERO_BYTES32,
);
await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis)
.migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner)

View File

@ -8,7 +8,8 @@ import {
} from '@0x/contracts-erc20';
import { blockchainTests, constants, expect, filterLogsToArguments, toBaseUnitAmount } from '@0x/contracts-test-utils';
import {
BridgeSource,
BridgeProtocol,
encodeBridgeSourceId,
encodeFillQuoteTransformerData,
encodePayTakerTransformerData,
FillQuoteTransformerOrderType,
@ -72,8 +73,9 @@ blockchainTests.fork.skip('Multiplex feature', env => {
const WETH_DAI_PLP_ADDRESS = '0x1db681925786441ba82adefac7bf492089665ca0';
const WETH_USDC_PLP_ADDRESS = '0x8463c03c0c57ff19fa8b431e0d3a34e2df89888e';
const USDC_USDT_PLP_ADDRESS = '0xc340ef96449514cea4dfa11d847a06d7f03d437c';
const GREEDY_TOKENS_BLOOM_FILTER = '0x0000100800000480002c00401000000820000000000000020000001010800001';
const BALANCER_WETH_DAI = '0x8b6e6e7b5b3801fed2cafd4b22b8a16c2f2db21a';
const CURVE_BRIDGE_SOURCE_ID = encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve');
const BALANCER_BRIDGE_SOURCE_ID = encodeBridgeSourceId(BridgeProtocol.Bancor, 'Balancer');
const fqtNonce = findTransformerNonce(
'0xfa6282736af206cb4cfc5cb786d82aecdf1186f9',
'0x39dce47a67ad34344eab877eae3ef1fa2a1d50bb',
@ -112,7 +114,6 @@ blockchainTests.fork.skip('Multiplex feature', env => {
zeroEx.address,
WETH_ADDRESS,
PLP_SANDBOX_ADDRESS,
GREEDY_TOKENS_BLOOM_FILTER,
);
await registry
.extend(multiplex.getSelector('batchFill'), multiplexImpl.address)
@ -283,7 +284,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
buyToken: WETH_ADDRESS,
bridgeOrders: [
{
source: BridgeSource.Balancer,
source: BALANCER_BRIDGE_SOURCE_ID,
takerTokenAmount: expiredRfqCall.sellAmount,
makerTokenAmount: expiredRfqCall.sellAmount,
bridgeData: poolEncoder.encode([BALANCER_WETH_DAI]),
@ -339,7 +340,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
tx.logs,
BridgeAdapterEvents.BridgeFill,
);
expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Balancer);
expect(bridgeFillEvent.source).to.bignumber.equal(BALANCER_BRIDGE_SOURCE_ID);
expect(bridgeFillEvent.inputToken).to.equal(DAI_ADDRESS);
expect(bridgeFillEvent.outputToken).to.equal(WETH_ADDRESS);
expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(expiredRfqCall.sellAmount);
@ -472,7 +473,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
buyToken: USDC_ADDRESS,
bridgeOrders: [
{
source: BridgeSource.Curve,
source: CURVE_BRIDGE_SOURCE_ID,
takerTokenAmount: sellAmount,
makerTokenAmount: sellAmount,
bridgeData: curveEncoder.encode([
@ -531,7 +532,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
tx.logs,
BridgeAdapterEvents.BridgeFill,
);
expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Curve);
expect(bridgeFillEvent.source).to.bignumber.equal(CURVE_BRIDGE_SOURCE_ID);
expect(bridgeFillEvent.inputToken).to.equal(DAI_ADDRESS);
expect(bridgeFillEvent.outputToken).to.equal(USDC_ADDRESS);
expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(sellAmount);
@ -563,7 +564,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
buyToken: DAI_ADDRESS,
bridgeOrders: [
{
source: BridgeSource.Curve,
source: CURVE_BRIDGE_SOURCE_ID,
takerTokenAmount: constants.MAX_UINT256,
makerTokenAmount: constants.MAX_UINT256,
bridgeData: curveEncoder.encode([
@ -640,7 +641,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
tx.logs,
BridgeAdapterEvents.BridgeFill,
);
expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Curve);
expect(bridgeFillEvent.source).to.bignumber.equal(CURVE_BRIDGE_SOURCE_ID);
expect(bridgeFillEvent.inputToken).to.equal(USDC_ADDRESS);
expect(bridgeFillEvent.outputToken).to.equal(DAI_ADDRESS);
expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(uniswapOutputAmount);

View File

@ -1,137 +0,0 @@
import {
blockchainTests,
expect,
getRandomInteger,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
import { IZeroExContract, TokenSpenderFeatureContract } from '../../src/wrappers';
import { artifacts } from '../artifacts';
import { abis } from '../utils/abis';
import { fullMigrateAsync } from '../utils/migration';
import { TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents } from '../wrappers';
blockchainTests.resets('TokenSpender feature', env => {
let zeroEx: IZeroExContract;
let feature: TokenSpenderFeatureContract;
let token: TestTokenSpenderERC20TokenContract;
let allowanceTarget: string;
before(async () => {
const [owner] = await env.getAccountAddressesAsync();
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
tokenSpender: (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
artifacts.TestTokenSpender,
env.provider,
env.txDefaults,
artifacts,
)).address,
});
feature = new TokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.TestTokenSpenderERC20Token,
env.provider,
env.txDefaults,
artifacts,
);
allowanceTarget = await feature.getAllowanceTarget().callAsync();
});
describe('_spendERC20Tokens()', () => {
const EMPTY_RETURN_AMOUNT = 1337;
const FALSE_RETURN_AMOUNT = 1338;
const REVERT_RETURN_AMOUNT = 1339;
it('_spendERC20Tokens() successfully calls compliant ERC20 token', async () => {
const tokenFrom = randomAddress();
const tokenTo = randomAddress();
const tokenAmount = new BigNumber(123456);
const receipt = await feature
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
verifyEventsFromLogs(
receipt.logs,
[
{
sender: allowanceTarget,
from: tokenFrom,
to: tokenTo,
amount: tokenAmount,
},
],
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
);
});
it('_spendERC20Tokens() successfully calls non-compliant ERC20 token', async () => {
const tokenFrom = randomAddress();
const tokenTo = randomAddress();
const tokenAmount = new BigNumber(EMPTY_RETURN_AMOUNT);
const receipt = await feature
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
verifyEventsFromLogs(
receipt.logs,
[
{
sender: allowanceTarget,
from: tokenFrom,
to: tokenTo,
amount: tokenAmount,
},
],
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
);
});
it('_spendERC20Tokens() reverts if ERC20 token reverts', async () => {
const tokenFrom = randomAddress();
const tokenTo = randomAddress();
const tokenAmount = new BigNumber(REVERT_RETURN_AMOUNT);
const tx = feature
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
const expectedError = new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
token.address,
tokenFrom,
tokenTo,
tokenAmount,
new StringRevertError('TestTokenSpenderERC20Token/Revert').encode(),
);
return expect(tx).to.revertWith(expectedError);
});
it('_spendERC20Tokens() reverts if ERC20 token returns false', async () => {
const tokenFrom = randomAddress();
const tokenTo = randomAddress();
const tokenAmount = new BigNumber(FALSE_RETURN_AMOUNT);
const tx = feature
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(
new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
token.address,
tokenFrom,
tokenTo,
tokenAmount,
hexUtils.leftPad(0),
),
);
});
});
describe('getSpendableERC20BalanceOf()', () => {
it("returns the minimum of the owner's balance and allowance", async () => {
const balance = getRandomInteger(1, '1e18');
const allowance = getRandomInteger(1, '1e18');
const tokenOwner = randomAddress();
await token
.setBalanceAndAllowanceOf(tokenOwner, balance, allowanceTarget, allowance)
.awaitTransactionSuccessAsync();
const spendableBalance = await feature.getSpendableERC20BalanceOf(token.address, tokenOwner).callAsync();
expect(spendableBalance).to.bignumber.eq(BigNumber.min(balance, allowance));
});
});
});

View File

@ -5,14 +5,11 @@ import {
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
import { getTokenListBloomFilter } from '../src/bloom_filter_utils';
import { BigNumber, hexUtils, RawRevertError, StringRevertError } from '@0x/utils';
import { artifacts } from './artifacts';
import {
TestFixinTokenSpenderContract,
TestFixinTokenSpenderEvents,
TestTokenSpenderERC20TokenContract,
TestTokenSpenderERC20TokenEvents,
} from './wrappers';
@ -21,7 +18,6 @@ blockchainTests.resets('FixinTokenSpender', env => {
let tokenSpender: TestFixinTokenSpenderContract;
let token: TestTokenSpenderERC20TokenContract;
let greedyToken: TestTokenSpenderERC20TokenContract;
let greedyTokensBloomFilter: string;
before(async () => {
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
@ -38,14 +34,11 @@ blockchainTests.resets('FixinTokenSpender', env => {
);
await greedyToken.setGreedyRevert(true).awaitTransactionSuccessAsync();
greedyTokensBloomFilter = getTokenListBloomFilter([greedyToken.address]);
tokenSpender = await TestFixinTokenSpenderContract.deployFrom0xArtifactAsync(
artifacts.TestFixinTokenSpender,
env.provider,
env.txDefaults,
artifacts,
greedyTokensBloomFilter,
);
});
@ -53,7 +46,6 @@ blockchainTests.resets('FixinTokenSpender', env => {
const EMPTY_RETURN_AMOUNT = 1337;
const FALSE_RETURN_AMOUNT = 1338;
const REVERT_RETURN_AMOUNT = 1339;
const TRIGGER_FALLBACK_SUCCESS_AMOUNT = 1340;
const EXTRA_RETURN_TRUE_AMOUNT = 1341;
const EXTRA_RETURN_FALSE_AMOUNT = 1342;
@ -106,13 +98,7 @@ blockchainTests.resets('FixinTokenSpender', env => {
const tx = tokenSpender
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
const expectedError = new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
token.address,
tokenFrom,
tokenTo,
tokenAmount,
new StringRevertError('TestTokenSpenderERC20Token/Revert').encode(),
);
const expectedError = new StringRevertError('TestTokenSpenderERC20Token/Revert');
return expect(tx).to.revertWith(expectedError);
});
@ -123,36 +109,7 @@ blockchainTests.resets('FixinTokenSpender', env => {
const tx = tokenSpender
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(
new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
token.address,
tokenFrom,
tokenTo,
tokenAmount,
hexUtils.leftPad(0),
),
);
});
it('transferERC20Tokens() falls back successfully to TokenSpender._spendERC20Tokens()', async () => {
const tokenFrom = randomAddress();
const tokenTo = randomAddress();
const tokenAmount = new BigNumber(TRIGGER_FALLBACK_SUCCESS_AMOUNT);
const receipt = await tokenSpender
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
verifyEventsFromLogs(
receipt.logs,
[
{
token: token.address,
owner: tokenFrom,
to: tokenTo,
amount: tokenAmount,
},
],
TestFixinTokenSpenderEvents.FallbackCalled,
);
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0)));
});
it('transferERC20Tokens() allows extra data after true', async () => {
@ -185,15 +142,7 @@ blockchainTests.resets('FixinTokenSpender', env => {
const tx = tokenSpender
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(
new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
token.address,
tokenFrom,
tokenTo,
tokenAmount,
hexUtils.leftPad(EXTRA_RETURN_FALSE_AMOUNT, 64),
),
);
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(EXTRA_RETURN_FALSE_AMOUNT, 64)));
});
it('transferERC20Tokens() cannot call self', async () => {
@ -206,73 +155,6 @@ blockchainTests.resets('FixinTokenSpender', env => {
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith('FixinTokenSpender/CANNOT_INVOKE_SELF');
});
it('calls transferFrom() directly for a greedy token with allowance', async () => {
const tokenFrom = randomAddress();
const tokenTo = randomAddress();
const tokenAmount = new BigNumber(123456);
await greedyToken.approveAs(tokenFrom, tokenSpender.address, tokenAmount).awaitTransactionSuccessAsync();
const receipt = await tokenSpender
.transferERC20Tokens(greedyToken.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
// Because there is an allowance set, we will call transferFrom()
// directly, which succeds and emits an event.
verifyEventsFromLogs(
receipt.logs,
[
{
sender: tokenSpender.address,
from: tokenFrom,
to: tokenTo,
amount: tokenAmount,
},
], // No events.
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
);
});
it('calls only the fallback for a greedy token with no allowance', async () => {
const tokenFrom = randomAddress();
const tokenTo = randomAddress();
const tokenAmount = new BigNumber(TRIGGER_FALLBACK_SUCCESS_AMOUNT);
const receipt = await tokenSpender
.transferERC20Tokens(greedyToken.address, tokenFrom, tokenTo, tokenAmount)
.awaitTransactionSuccessAsync();
// Because this is a greedy token and there is no allowance set, transferFrom()
// will not be called before hitting the fallback, which only emits an event
// in the test contract.
verifyEventsFromLogs(
receipt.logs,
[], // No events.
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
);
verifyEventsFromLogs(
receipt.logs,
[
{
token: greedyToken.address,
owner: tokenFrom,
to: tokenTo,
amount: tokenAmount,
},
],
TestFixinTokenSpenderEvents.FallbackCalled,
);
});
});
describe('isTokenPossiblyGreedy()', () => {
it('returns true for greedy token', async () => {
const isGreedy = await tokenSpender.isTokenPossiblyGreedy(greedyToken.address).callAsync();
expect(isGreedy).to.eq(true);
});
it('returns false for non-greedy token', async () => {
const isGreedy = await tokenSpender.isTokenPossiblyGreedy(token.address).callAsync();
expect(isGreedy).to.eq(false);
});
});
describe('getSpendableERC20BalanceOf()', () => {

View File

@ -8,11 +8,9 @@ import { artifacts } from './artifacts';
import { abis } from './utils/abis';
import { deployFullFeaturesAsync, FullFeatures } from './utils/migration';
import {
AllowanceTargetContract,
IMetaTransactionsFeatureContract,
INativeOrdersFeatureContract,
IOwnableFeatureContract,
ITokenSpenderFeatureContract,
ITransformERC20FeatureContract,
TestFullMigrationContract,
ZeroExContract,
@ -69,10 +67,6 @@ blockchainTests.resets('Full migration', env => {
});
const FEATURE_FNS = {
TokenSpender: {
contractType: ITokenSpenderFeatureContract,
fns: ['_spendERC20Tokens'],
},
TransformERC20: {
contractType: ITransformERC20FeatureContract,
fns: [
@ -207,27 +201,6 @@ blockchainTests.resets('Full migration', env => {
});
}
describe("TokenSpender's allowance target", () => {
let allowanceTarget: AllowanceTargetContract;
before(async () => {
const contract = new ITokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults);
allowanceTarget = new AllowanceTargetContract(
await contract.getAllowanceTarget().callAsync(),
env.provider,
env.txDefaults,
);
});
it('is owned by owner', async () => {
return expect(allowanceTarget.owner().callAsync()).to.become(owner);
});
it('Proxy is authorized', async () => {
return expect(allowanceTarget.authorized(zeroEx.address).callAsync()).to.become(true);
});
});
describe('TransformERC20', () => {
let feature: ITransformERC20FeatureContract;

View File

@ -52,7 +52,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
let singleProtocolFee: BigNumber;
const GAS_PRICE = 1337;
const TEST_BRIDGE_SOURCE = 12345678;
const TEST_BRIDGE_SOURCE = hexUtils.random(32);
const HIGH_BIT = new BigNumber(2).pow(255);
const REVERT_AMOUNT = new BigNumber('0xdeadbeef');

View File

@ -4,11 +4,10 @@
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/affiliate_fee_transformer';
export * from '../test/generated-wrappers/allowance_target';
export * from '../test/generated-wrappers/batch_fill_native_orders_feature';
export * from '../test/generated-wrappers/bootstrap_feature';
export * from '../test/generated-wrappers/bridge_adapter';
export * from '../test/generated-wrappers/bridge_source';
export * from '../test/generated-wrappers/bridge_protocols';
export * from '../test/generated-wrappers/curve_liquidity_provider';
export * from '../test/generated-wrappers/fee_collector';
export * from '../test/generated-wrappers/fee_collector_controller';
@ -20,7 +19,6 @@ export * from '../test/generated-wrappers/fixin_reentrancy_guard';
export * from '../test/generated-wrappers/fixin_token_spender';
export * from '../test/generated-wrappers/flash_wallet';
export * from '../test/generated-wrappers/full_migration';
export * from '../test/generated-wrappers/i_allowance_target';
export * from '../test/generated-wrappers/i_batch_fill_native_orders_feature';
export * from '../test/generated-wrappers/i_bootstrap_feature';
export * from '../test/generated-wrappers/i_bridge_adapter';
@ -37,6 +35,7 @@ export * from '../test/generated-wrappers/i_multiplex_feature';
export * from '../test/generated-wrappers/i_native_orders_events';
export * from '../test/generated-wrappers/i_native_orders_feature';
export * from '../test/generated-wrappers/i_ownable_feature';
export * from '../test/generated-wrappers/i_pancake_swap_feature';
export * from '../test/generated-wrappers/i_simple_function_registry_feature';
export * from '../test/generated-wrappers/i_staking';
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
@ -66,9 +65,7 @@ export * from '../test/generated-wrappers/lib_signature';
export * from '../test/generated-wrappers/lib_signature_rich_errors';
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
export * from '../test/generated-wrappers/lib_simple_function_registry_storage';
export * from '../test/generated-wrappers/lib_spender_rich_errors';
export * from '../test/generated-wrappers/lib_storage';
export * from '../test/generated-wrappers/lib_token_spender_storage';
export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors';
export * from '../test/generated-wrappers/lib_transform_erc20_storage';
export * from '../test/generated-wrappers/lib_wallet_rich_errors';
@ -86,9 +83,9 @@ export * from '../test/generated-wrappers/mixin_dodo_v2';
export * from '../test/generated-wrappers/mixin_kyber';
export * from '../test/generated-wrappers/mixin_m_stable';
export * from '../test/generated-wrappers/mixin_mooniswap';
export * from '../test/generated-wrappers/mixin_nerve';
export * from '../test/generated-wrappers/mixin_oasis';
export * from '../test/generated-wrappers/mixin_shell';
export * from '../test/generated-wrappers/mixin_sushiswap';
export * from '../test/generated-wrappers/mixin_uniswap';
export * from '../test/generated-wrappers/mixin_uniswap_v2';
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
@ -100,6 +97,7 @@ export * from '../test/generated-wrappers/native_orders_info';
export * from '../test/generated-wrappers/native_orders_protocol_fees';
export * from '../test/generated-wrappers/native_orders_settlement';
export * from '../test/generated-wrappers/ownable_feature';
export * from '../test/generated-wrappers/pancake_swap_feature';
export * from '../test/generated-wrappers/pay_taker_transformer';
export * from '../test/generated-wrappers/permissionless_transformer_deployer';
export * from '../test/generated-wrappers/positive_slippage_fee_transformer';
@ -132,7 +130,6 @@ export * from '../test/generated-wrappers/test_rfq_origin_registration';
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl1';
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl2';
export * from '../test/generated-wrappers/test_staking';
export * from '../test/generated-wrappers/test_token_spender';
export * from '../test/generated-wrappers/test_token_spender_erc20_token';
export * from '../test/generated-wrappers/test_transform_erc20';
export * from '../test/generated-wrappers/test_transformer_base';
@ -141,7 +138,6 @@ export * from '../test/generated-wrappers/test_transformer_host';
export * from '../test/generated-wrappers/test_weth';
export * from '../test/generated-wrappers/test_weth_transformer_host';
export * from '../test/generated-wrappers/test_zero_ex_feature';
export * from '../test/generated-wrappers/token_spender_feature';
export * from '../test/generated-wrappers/transform_erc20_feature';
export * from '../test/generated-wrappers/transformer';
export * from '../test/generated-wrappers/transformer_deployer';

View File

@ -20,7 +20,6 @@
"generated-artifacts/INativeOrdersFeature.json",
"generated-artifacts/IOwnableFeature.json",
"generated-artifacts/ISimpleFunctionRegistryFeature.json",
"generated-artifacts/ITokenSpenderFeature.json",
"generated-artifacts/ITransformERC20Feature.json",
"generated-artifacts/IZeroEx.json",
"generated-artifacts/InitialMigration.json",
@ -33,16 +32,14 @@
"generated-artifacts/PayTakerTransformer.json",
"generated-artifacts/PositiveSlippageFeeTransformer.json",
"generated-artifacts/SimpleFunctionRegistryFeature.json",
"generated-artifacts/TokenSpenderFeature.json",
"generated-artifacts/TransformERC20Feature.json",
"generated-artifacts/WethTransformer.json",
"generated-artifacts/ZeroEx.json",
"test/generated-artifacts/AffiliateFeeTransformer.json",
"test/generated-artifacts/AllowanceTarget.json",
"test/generated-artifacts/BatchFillNativeOrdersFeature.json",
"test/generated-artifacts/BootstrapFeature.json",
"test/generated-artifacts/BridgeAdapter.json",
"test/generated-artifacts/BridgeSource.json",
"test/generated-artifacts/BridgeProtocols.json",
"test/generated-artifacts/CurveLiquidityProvider.json",
"test/generated-artifacts/FeeCollector.json",
"test/generated-artifacts/FeeCollectorController.json",
@ -54,7 +51,6 @@
"test/generated-artifacts/FixinTokenSpender.json",
"test/generated-artifacts/FlashWallet.json",
"test/generated-artifacts/FullMigration.json",
"test/generated-artifacts/IAllowanceTarget.json",
"test/generated-artifacts/IBatchFillNativeOrdersFeature.json",
"test/generated-artifacts/IBootstrapFeature.json",
"test/generated-artifacts/IBridgeAdapter.json",
@ -71,6 +67,7 @@
"test/generated-artifacts/INativeOrdersEvents.json",
"test/generated-artifacts/INativeOrdersFeature.json",
"test/generated-artifacts/IOwnableFeature.json",
"test/generated-artifacts/IPancakeSwapFeature.json",
"test/generated-artifacts/ISimpleFunctionRegistryFeature.json",
"test/generated-artifacts/IStaking.json",
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
@ -100,9 +97,7 @@
"test/generated-artifacts/LibSignatureRichErrors.json",
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
"test/generated-artifacts/LibSimpleFunctionRegistryStorage.json",
"test/generated-artifacts/LibSpenderRichErrors.json",
"test/generated-artifacts/LibStorage.json",
"test/generated-artifacts/LibTokenSpenderStorage.json",
"test/generated-artifacts/LibTransformERC20RichErrors.json",
"test/generated-artifacts/LibTransformERC20Storage.json",
"test/generated-artifacts/LibWalletRichErrors.json",
@ -120,9 +115,9 @@
"test/generated-artifacts/MixinKyber.json",
"test/generated-artifacts/MixinMStable.json",
"test/generated-artifacts/MixinMooniswap.json",
"test/generated-artifacts/MixinNerve.json",
"test/generated-artifacts/MixinOasis.json",
"test/generated-artifacts/MixinShell.json",
"test/generated-artifacts/MixinSushiswap.json",
"test/generated-artifacts/MixinUniswap.json",
"test/generated-artifacts/MixinUniswapV2.json",
"test/generated-artifacts/MixinZeroExBridge.json",
@ -134,6 +129,7 @@
"test/generated-artifacts/NativeOrdersProtocolFees.json",
"test/generated-artifacts/NativeOrdersSettlement.json",
"test/generated-artifacts/OwnableFeature.json",
"test/generated-artifacts/PancakeSwapFeature.json",
"test/generated-artifacts/PayTakerTransformer.json",
"test/generated-artifacts/PermissionlessTransformerDeployer.json",
"test/generated-artifacts/PositiveSlippageFeeTransformer.json",
@ -166,7 +162,6 @@
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json",
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json",
"test/generated-artifacts/TestStaking.json",
"test/generated-artifacts/TestTokenSpender.json",
"test/generated-artifacts/TestTokenSpenderERC20Token.json",
"test/generated-artifacts/TestTransformERC20.json",
"test/generated-artifacts/TestTransformerBase.json",
@ -175,7 +170,6 @@
"test/generated-artifacts/TestWeth.json",
"test/generated-artifacts/TestWethTransformerHost.json",
"test/generated-artifacts/TestZeroExFeature.json",
"test/generated-artifacts/TokenSpenderFeature.json",
"test/generated-artifacts/TransformERC20Feature.json",
"test/generated-artifacts/Transformer.json",
"test/generated-artifacts/TransformerDeployer.json",

View File

@ -17,6 +17,22 @@
{
"note": "improve logging for alt RFQ requests",
"pr": 158
},
{
"note": "Use new bridge source ID encoding.",
"pr": 162
},
{
"note": "Refactor to provide chain id specific addresses",
"pr": 163
},
{
"note": "Added PancakeSwap and BakerySwap on Chain 56",
"pr": 163
},
{
"note": "Added Nerve and Dodo (v1) to BSC",
"pr": 181
}
]
},

View File

@ -93,6 +93,10 @@ contract BalancerSampler {
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
@ -151,6 +155,10 @@ contract BalancerSampler {
returns (uint256 amount)
{
takerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;

View File

@ -20,21 +20,23 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IBancor.sol";
contract DeploymentConstants {}
contract BancorSampler is
DeploymentConstants
contract BancorSampler is DeploymentConstants
{
/// @dev Base gas limit for Bancor calls.
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
address constant private BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
struct BancorSamplerOpts {
address registry;
address[][] paths;
}
/// @dev Sample sell quotes from Bancor.
/// @param paths The paths to check for Bancor. Only the best is used
/// @param opts BancorSamplerOpts The Bancor registry contract address and paths
/// @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.
@ -43,7 +45,7 @@ contract BancorSampler is
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromBancor(
address[][] memory paths,
BancorSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
@ -52,38 +54,13 @@ contract BancorSampler is
view
returns (address bancorNetwork, address[] memory path, uint256[] memory makerTokenAmounts)
{
bancorNetwork = _getBancorNetwork();
if (paths.length == 0) {
if (opts.paths.length == 0) {
return (bancorNetwork, path, makerTokenAmounts);
}
uint256 maxBoughtAmount = 0;
// Find the best path by selling the largest taker amount
for (uint256 i = 0; i < paths.length; i++) {
if (paths[i].length < 2) {
continue;
}
(bancorNetwork, path) = _findBestPath(opts, takerToken, makerToken, takerTokenAmounts);
makerTokenAmounts = new uint256[](takerTokenAmounts.length);
try
IBancorNetwork(bancorNetwork)
.rateByPath
{gas: BANCOR_CALL_GAS}
(paths[i], takerTokenAmounts[takerTokenAmounts.length-1])
returns (uint256 amount)
{
if (amount > maxBoughtAmount) {
maxBoughtAmount = amount;
path = paths[i];
}
} catch {
// Swallow failures, leaving all results as zero.
continue;
}
}
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
try
IBancorNetwork(bancorNetwork)
.rateByPath
@ -92,6 +69,10 @@ contract BancorSampler is
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch {
// Swallow failures, leaving all results as zero.
break;
@ -101,7 +82,7 @@ contract BancorSampler is
}
/// @dev Sample buy quotes from Bancor. Unimplemented
/// @param paths The paths to check for Bancor. Only the best is used
/// @param opts BancorSamplerOpts The Bancor registry contract address and paths
/// @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.
@ -110,7 +91,7 @@ contract BancorSampler is
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromBancor(
address[][] memory paths,
BancorSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
@ -121,12 +102,51 @@ contract BancorSampler is
{
}
function _getBancorNetwork()
function _findBestPath(
BancorSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
internal
view
returns (address bancorNetwork, address[] memory path)
{
bancorNetwork = _getBancorNetwork(opts.registry);
if (opts.paths.length == 0) {
return (bancorNetwork, path);
}
uint256 maxBoughtAmount = 0;
// Find the best path by selling the largest taker amount
for (uint256 i = 0; i < opts.paths.length; i++) {
if (opts.paths[i].length < 2) {
continue;
}
try
IBancorNetwork(bancorNetwork)
.rateByPath
{gas: BANCOR_CALL_GAS}
(opts.paths[i], takerTokenAmounts[takerTokenAmounts.length-1])
returns (uint256 amount)
{
if (amount > maxBoughtAmount) {
maxBoughtAmount = amount;
path = opts.paths[i];
}
} catch {
// Swallow failures, leaving all results as zero.
continue;
}
}
}
function _getBancorNetwork(address registry)
private
view
returns (address)
{
IBancorRegistry registry = IBancorRegistry(_getBancorRegistryAddress());
IBancorRegistry registry = IBancorRegistry(registry);
return registry.getAddress(registry.BANCOR_NETWORK());
}
}

View File

@ -72,11 +72,11 @@ contract CurveSampler is
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
}
// Exit early if the amount is too high for the source to serve
if (buyAmount == 0) {
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}
@ -123,11 +123,11 @@ contract CurveSampler is
if (didSucceed) {
sellAmount = abi.decode(resultData, (uint256));
}
// Exit early if the amount is too high for the source to serve
if (sellAmount == 0) {
takerTokenAmounts[i] = sellAmount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
takerTokenAmounts[i] = sellAmount;
}
}

View File

@ -20,7 +20,6 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
@ -39,15 +38,19 @@ interface IDODO {
}
contract DODOSampler is
DeploymentConstants,
SamplerUtils,
ApproximateBuys
{
/// @dev Gas limit for DODO calls.
uint256 constant private DODO_CALL_GAS = 300e3; // 300k
struct DODOSamplerOpts {
address registry;
address helper;
}
/// @dev Sample sell quotes from DODO.
/// @param opts DODOSamplerOpts DODO Registry and helper addresses
/// @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.
@ -56,6 +59,7 @@ contract DODOSampler is
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromDODO(
DODOSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
@ -68,14 +72,14 @@ contract DODOSampler is
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
pool = IDODOZoo(_getDODORegistryAddress()).getDODO(takerToken, makerToken);
pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
address baseToken;
// If pool exists we have the correct order of Base/Quote
if (pool != address(0)) {
baseToken = takerToken;
sellBase = true;
} else {
pool = IDODOZoo(_getDODORegistryAddress()).getDODO(makerToken, takerToken);
pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
// No pool either direction
if (address(pool) == address(0)) {
return (sellBase, pool, makerTokenAmounts);
@ -91,19 +95,20 @@ contract DODOSampler is
for (uint256 i = 0; i < numSamples; i++) {
uint256 buyAmount = _sampleSellForApproximateBuyFromDODO(
abi.encode(takerToken, pool, baseToken), // taker token data
abi.encode(makerToken, pool, baseToken), // maker token data
abi.encode(takerToken, pool, baseToken, opts.helper), // taker token data
abi.encode(makerToken, pool, baseToken, opts.helper), // maker token data
takerTokenAmounts[i]
);
// Exit early if the amount is too high for the source to serve
if (buyAmount == 0) {
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}
/// @dev Sample buy quotes from DODO.
/// @param opts DODOSamplerOpts DODO Registry and helper addresses
/// @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.
@ -112,6 +117,7 @@ contract DODOSampler is
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromDODO(
DODOSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
@ -126,7 +132,7 @@ contract DODOSampler is
// Pool is BASE/QUOTE
// Look up the pool from the taker/maker combination
pool = IDODOZoo(_getDODORegistryAddress()).getDODO(takerToken, makerToken);
pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
address baseToken;
// If pool exists we have the correct order of Base/Quote
if (pool != address(0)) {
@ -134,7 +140,7 @@ contract DODOSampler is
sellBase = true;
} else {
// Look up the pool from the maker/taker combination
pool = IDODOZoo(_getDODORegistryAddress()).getDODO(makerToken, takerToken);
pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
// No pool either direction
if (address(pool) == address(0)) {
return (sellBase, pool, takerTokenAmounts);
@ -150,8 +156,8 @@ contract DODOSampler is
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, pool, baseToken),
takerTokenData: abi.encode(takerToken, pool, baseToken),
makerTokenData: abi.encode(makerToken, pool, baseToken, opts.helper),
takerTokenData: abi.encode(takerToken, pool, baseToken, opts.helper),
getSellQuoteCallback: _sampleSellForApproximateBuyFromDODO
}),
makerTokenAmounts
@ -167,9 +173,9 @@ contract DODOSampler is
view
returns (uint256)
{
(address takerToken, address pool, address baseToken) = abi.decode(
(address takerToken, address pool, address baseToken, address helper) = abi.decode(
takerTokenData,
(address, address, address)
(address, address, address, address)
);
// We will get called to sell both the taker token and also to sell the maker token
@ -189,7 +195,7 @@ contract DODOSampler is
} else {
// If quote token then use helper, this is less accurate
try
IDODOHelper(_getDODOHelperAddress()).querySellQuoteToken
IDODOHelper(helper).querySellQuoteToken
{gas: DODO_CALL_GAS}
(pool, sellAmount)
returns (uint256 amount)

View File

@ -20,7 +20,6 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
@ -44,7 +43,6 @@ interface IDODOV2Pool {
}
contract DODOV2Sampler is
DeploymentConstants,
SamplerUtils,
ApproximateBuys
{
@ -89,11 +87,11 @@ contract DODOV2Sampler is
abi.encode(makerToken, pool, sellBase), // maker token data
takerTokenAmounts[i]
);
// Exit early if the amount is too high for the source to serve
if (buyAmount == 0) {
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}

View File

@ -1,353 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
contract DeploymentConstants {
// solhint-disable separate-by-one-line-in-contract
// Mainnet addresses ///////////////////////////////////////////////////////
/// @dev Mainnet address of the WETH contract.
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev Mainnet address of the KyberNetworkProxy contract.
address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x9AAb3f75489902f3a48495025729a0AF77d4b11e;
/// @dev Mainnet address of the KyberHintHandler contract.
address constant private KYBER_HINT_HANDLER_ADDRESS = 0xa1C0Fa73c39CFBcC11ec9Eb1Afc665aba9996E2C;
/// @dev Mainnet address of the `UniswapExchangeFactory` contract.
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
/// @dev Mainnet address of the `UniswapV2Router01` contract.
address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
/// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract.
address constant private ETH2DAI_ADDRESS = 0x794e6e91555438aFc3ccF1c5076A74F42133d08D;
/// @dev Mainnet address of the `ERC20BridgeProxy` contract
address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0x8ED95d1746bf1E4dAb58d8ED4724f1Ef95B20Db0;
///@dev Mainnet address of the `Dai` (multi-collateral) contract
address constant private DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
/// @dev Mainnet address of the `Chai` contract
address constant private CHAI_ADDRESS = 0x06AF07097C9Eeb7fD685c692751D5C66dB49c215;
/// @dev Mainnet address of the 0x DevUtils contract.
address constant private DEV_UTILS_ADDRESS = 0x74134CF88b21383713E096a5ecF59e297dc7f547;
/// @dev Kyber ETH pseudo-address.
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev Mainnet address of the dYdX contract.
address constant private DYDX_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
/// @dev Mainnet address of the GST2 contract
address constant private GST_ADDRESS = 0x0000000000b3F879cb30FE243b4Dfee438691c04;
/// @dev Mainnet address of the GST Collector
address constant private GST_COLLECTOR_ADDRESS = 0x000000D3b08566BE75A6DB803C03C85C0c1c5B96;
/// @dev Mainnet address of the mStable mUSD contract.
address constant private MUSD_ADDRESS = 0xe2f2a5C287993345a840Db3B0845fbC70f5935a5;
/// @dev Mainnet address of the Mooniswap Registry contract
address constant private MOONISWAP_REGISTRY = 0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303;
/// @dev Mainnet address of the Shell contract
address constant private SHELL_CONTRACT = 0x2E703D658f8dd21709a7B458967aB4081F8D3d05;
/// @dev Mainnet address of the DODO Registry (ZOO) contract
address constant private DODO_REGISTRY = 0x3A97247DF274a17C59A3bd12735ea3FcDFb49950;
/// @dev Mainnet address of the DODO Helper contract
address constant private DODO_HELPER = 0x533dA777aeDCE766CEAe696bf90f8541A4bA80Eb;
/// @dev Mainnet address of the Bancor Registry contract
address constant private BANCOR_REGISTRY = 0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4;
// // Ropsten addresses ///////////////////////////////////////////////////////
// /// @dev Mainnet address of the WETH contract.
// address constant private WETH_ADDRESS = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
// /// @dev Mainnet address of the KyberNetworkProxy contract.
// address constant private KYBER_NETWORK_PROXY_ADDRESS = 0xd719c34261e099Fdb33030ac8909d5788D3039C4;
// /// @dev Mainnet address of the `UniswapExchangeFactory` contract.
// address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0x9c83dCE8CA20E9aAF9D3efc003b2ea62aBC08351;
// /// @dev Mainnet address of the `UniswapV2Router01` contract.
// address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
// /// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract.
// address constant private ETH2DAI_ADDRESS = address(0);
// /// @dev Mainnet address of the `ERC20BridgeProxy` contract
// address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xb344afeD348de15eb4a9e180205A2B0739628339;
// ///@dev Mainnet address of the `Dai` (multi-collateral) contract
// address constant private DAI_ADDRESS = address(0);
// /// @dev Mainnet address of the `Chai` contract
// address constant private CHAI_ADDRESS = address(0);
// /// @dev Mainnet address of the 0x DevUtils contract.
// address constant private DEV_UTILS_ADDRESS = 0xC812AF3f3fBC62F76ea4262576EC0f49dB8B7f1c;
// /// @dev Kyber ETH pseudo-address.
// address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// /// @dev Mainnet address of the dYdX contract.
// address constant private DYDX_ADDRESS = address(0);
// /// @dev Mainnet address of the GST2 contract
// address constant private GST_ADDRESS = address(0);
// /// @dev Mainnet address of the GST Collector
// address constant private GST_COLLECTOR_ADDRESS = address(0);
// /// @dev Mainnet address of the mStable mUSD contract.
// address constant private MUSD_ADDRESS = 0x4E1000616990D83e56f4b5fC6CC8602DcfD20459;
// // Rinkeby addresses ///////////////////////////////////////////////////////
// /// @dev Mainnet address of the WETH contract.
// address constant private WETH_ADDRESS = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
// /// @dev Mainnet address of the KyberNetworkProxy contract.
// address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x0d5371e5EE23dec7DF251A8957279629aa79E9C5;
// /// @dev Mainnet address of the `UniswapExchangeFactory` contract.
// address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36;
// /// @dev Mainnet address of the `UniswapV2Router01` contract.
// address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
// /// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract.
// address constant private ETH2DAI_ADDRESS = address(0);
// /// @dev Mainnet address of the `ERC20BridgeProxy` contract
// address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xA2AA4bEFED748Fba27a3bE7Dfd2C4b2c6DB1F49B;
// ///@dev Mainnet address of the `Dai` (multi-collateral) contract
// address constant private DAI_ADDRESS = address(0);
// /// @dev Mainnet address of the `Chai` contract
// address constant private CHAI_ADDRESS = address(0);
// /// @dev Mainnet address of the 0x DevUtils contract.
// address constant private DEV_UTILS_ADDRESS = 0x46B5BC959e8A754c0256FFF73bF34A52Ad5CdfA9;
// /// @dev Kyber ETH pseudo-address.
// address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// /// @dev Mainnet address of the dYdX contract.
// address constant private DYDX_ADDRESS = address(0);
// /// @dev Mainnet address of the GST2 contract
// address constant private GST_ADDRESS = address(0);
// /// @dev Mainnet address of the GST Collector
// address constant private GST_COLLECTOR_ADDRESS = address(0);
// /// @dev Mainnet address of the mStable mUSD contract.
// address constant private MUSD_ADDRESS = address(0);
// // Kovan addresses /////////////////////////////////////////////////////////
// /// @dev Kovan address of the WETH contract.
// address constant private WETH_ADDRESS = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
// /// @dev Kovan address of the KyberNetworkProxy contract.
// address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D;
// /// @dev Kovan address of the `UniswapExchangeFactory` contract.
// address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30;
// /// @dev Kovan address of the `UniswapV2Router01` contract.
// address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
// /// @dev Kovan address of the Eth2Dai `MatchingMarket` contract.
// address constant private ETH2DAI_ADDRESS = 0xe325acB9765b02b8b418199bf9650972299235F4;
// /// @dev Kovan address of the `ERC20BridgeProxy` contract
// address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0x3577552C1Fb7A44aD76BeEB7aB53251668A21F8D;
// /// @dev Kovan address of the `Chai` contract
// address constant private CHAI_ADDRESS = address(0);
// /// @dev Kovan address of the `Dai` (multi-collateral) contract
// address constant private DAI_ADDRESS = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa;
// /// @dev Kovan address of the 0x DevUtils contract.
// address constant private DEV_UTILS_ADDRESS = 0x9402639A828BdF4E9e4103ac3B69E1a6E522eB59;
// /// @dev Kyber ETH pseudo-address.
// address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// /// @dev Kovan address of the dYdX contract.
// address constant private DYDX_ADDRESS = address(0);
// /// @dev Kovan address of the GST2 contract
// address constant private GST_ADDRESS = address(0);
// /// @dev Kovan address of the GST Collector
// address constant private GST_COLLECTOR_ADDRESS = address(0);
// /// @dev Mainnet address of the mStable mUSD contract.
// address constant private MUSD_ADDRESS = address(0);
/// @dev Overridable way to get the `KyberNetworkProxy` address.
/// @return kyberAddress The `IKyberNetworkProxy` address.
function _getKyberNetworkProxyAddress()
virtual
internal
view
returns (address kyberAddress)
{
return KYBER_NETWORK_PROXY_ADDRESS;
}
/// @dev Overridable way to get the `KyberHintHandler` address.
/// @return hintHandlerAddress The `IKyberHintHandler` address.
function _getKyberHintHandlerAddress()
virtual
internal
view
returns (address hintHandlerAddress)
{
return KYBER_HINT_HANDLER_ADDRESS;
}
/// @dev Overridable way to get the WETH address.
/// @return wethAddress The WETH address.
function _getWethAddress()
internal
view
returns (address wethAddress)
{
return WETH_ADDRESS;
}
/// @dev Overridable way to get the `UniswapExchangeFactory` address.
/// @return uniswapAddress The `UniswapExchangeFactory` address.
function _getUniswapExchangeFactoryAddress()
virtual
internal
view
returns (address uniswapAddress)
{
return UNISWAP_EXCHANGE_FACTORY_ADDRESS;
}
/// @dev Overridable way to get the `UniswapV2Router01` address.
/// @return uniswapRouterAddress The `UniswapV2Router01` address.
function _getUniswapV2Router01Address()
virtual
internal
view
returns (address uniswapRouterAddress)
{
return UNISWAP_V2_ROUTER_01_ADDRESS;
}
/// @dev An overridable way to retrieve the Eth2Dai `MatchingMarket` contract.
/// @return eth2daiAddress The Eth2Dai `MatchingMarket` contract.
function _getEth2DaiAddress()
virtual
internal
view
returns (address eth2daiAddress)
{
return ETH2DAI_ADDRESS;
}
/// @dev An overridable way to retrieve the `ERC20BridgeProxy` contract.
/// @return erc20BridgeProxyAddress The `ERC20BridgeProxy` contract.
function _getERC20BridgeProxyAddress()
internal
view
returns (address erc20BridgeProxyAddress)
{
return ERC20_BRIDGE_PROXY_ADDRESS;
}
/// @dev An overridable way to retrieve the `Dai` contract.
/// @return daiAddress The `Dai` contract.
function _getDaiAddress()
internal
view
returns (address daiAddress)
{
return DAI_ADDRESS;
}
/// @dev An overridable way to retrieve the `Chai` contract.
/// @return chaiAddress The `Chai` contract.
function _getChaiAddress()
internal
view
returns (address chaiAddress)
{
return CHAI_ADDRESS;
}
/// @dev An overridable way to retrieve the 0x `DevUtils` contract address.
/// @return devUtils The 0x `DevUtils` contract address.
function _getDevUtilsAddress()
internal
view
returns (address devUtils)
{
return DEV_UTILS_ADDRESS;
}
/// @dev Overridable way to get the DyDx contract.
/// @return dydxAddress exchange The DyDx exchange contract.
function _getDydxAddress()
internal
view
returns (address dydxAddress)
{
return DYDX_ADDRESS;
}
/// @dev An overridable way to retrieve the GST2 contract address.
/// @return gst The GST contract.
function _getGstAddress()
internal
view
returns (address gst)
{
return GST_ADDRESS;
}
/// @dev An overridable way to retrieve the GST Collector address.
/// @return collector The GST collector address.
function _getGstCollectorAddress()
internal
view
returns (address collector)
{
return GST_COLLECTOR_ADDRESS;
}
/// @dev An overridable way to retrieve the mStable mUSD address.
/// @return musd The mStable mUSD address.
function _getMUsdAddress()
internal
view
returns (address musd)
{
return MUSD_ADDRESS;
}
/// @dev An overridable way to retrieve the Mooniswap registry address.
/// @return registry The Mooniswap registry address.
function _getMooniswapAddress()
internal
view
returns (address)
{
return MOONISWAP_REGISTRY;
}
/// @dev An overridable way to retrieve the Shell contract address.
/// @return registry The Shell contract address.
function _getShellAddress()
internal
view
returns (address registry)
{
return SHELL_CONTRACT;
}
/// @dev An overridable way to retrieve the DODO Registry contract address.
/// @return registry The DODO Registry contract address.
function _getDODORegistryAddress()
internal
view
returns (address registry)
{
return DODO_REGISTRY;
}
/// @dev An overridable way to retrieve the DODO Helper contract address.
/// @return registry The DODO Helper contract address.
function _getDODOHelperAddress()
internal
view
returns (address registry)
{
return DODO_HELPER;
}
/// @dev An overridable way to retrieve the Bancor Registry contract address.
/// @return registry The Bancor registry contract address.
function _getBancorRegistryAddress()
internal
view
returns (address registry)
{
return BANCOR_REGISTRY;
}
}

View File

@ -33,7 +33,6 @@ import "./MStableSampler.sol";
import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol";
import "./ShellSampler.sol";
import "./SushiSwapSampler.sol";
import "./TwoHopSampler.sol";
import "./UniswapSampler.sol";
import "./UniswapV2Sampler.sol";
@ -54,7 +53,6 @@ contract ERC20BridgeSampler is
MultiBridgeSampler,
NativeOrderSampler,
ShellSampler,
SushiSwapSampler,
TwoHopSampler,
UniswapSampler,
UniswapV2Sampler,

View File

@ -20,25 +20,25 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/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 router Address of the Eth2Dai/Oasis 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 sampleSellsFromEth2Dai(
address router,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
@ -52,12 +52,16 @@ contract Eth2DaiSampler is
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IEth2Dai(_getEth2DaiAddress()).getBuyAmount
IEth2Dai(router).getBuyAmount
{gas: ETH2DAI_CALL_GAS}
(makerToken, takerToken, takerTokenAmounts[i])
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
@ -66,12 +70,14 @@ contract Eth2DaiSampler is
}
/// @dev Sample buy quotes from Eth2Dai/Oasis.
/// @param router Address of the Eth2Dai/Oasis contract
/// @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 router,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
@ -85,12 +91,16 @@ contract Eth2DaiSampler is
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IEth2Dai(_getEth2DaiAddress()).getPayAmount
IEth2Dai(router).getPayAmount
{gas: ETH2DAI_CALL_GAS}
(takerToken, makerToken, makerTokenAmounts[i])
returns (uint256 amount)
{
takerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;

View File

@ -20,22 +20,30 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IKyberNetwork.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 = 500e3; // 500k
/// @dev Kyber ETH pseudo-address.
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
struct KyberSamplerOpts {
uint256 reserveOffset;
address hintHandler;
address networkProxy;
address weth;
bytes hint;
}
/// @dev Sample sell quotes from Kyber.
/// @param reserveOffset The nth reserve
/// @param opts KyberSamplerOpts The nth reserve
/// @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.
@ -43,7 +51,7 @@ contract KyberSampler is
/// @return hint The hint for the selected reserve
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
function sampleSellsFromKyberNetwork(
uint256 reserveOffset,
KyberSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
@ -53,31 +61,32 @@ contract KyberSampler is
returns (bytes32 reserveId, bytes memory hint, uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
reserveId = _getNextReserveId(takerToken, makerToken, reserveOffset);
reserveId = _getNextReserveId(opts, takerToken, makerToken);
if (reserveId == 0x0) {
return (reserveId, hint, makerTokenAmounts);
}
hint = this.encodeKyberHint(reserveId, takerToken, makerToken);
opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
hint = opts.hint;
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
uint256 value = this.sampleSellFromKyberNetwork(
hint,
opts,
takerToken,
makerToken,
takerTokenAmounts[i]
);
// Return early if the source has no liquidity
if (value == 0) {
return (reserveId, hint, makerTokenAmounts);
}
makerTokenAmounts[i] = value;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from Kyber.
/// @param reserveOffset The nth reserve
/// @param opts KyberSamplerOpts The nth reserve
/// @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.
@ -85,7 +94,7 @@ contract KyberSampler is
/// @return hint The hint for the selected reserve
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
function sampleBuysFromKyberNetwork(
uint256 reserveOffset,
KyberSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
@ -96,16 +105,17 @@ contract KyberSampler is
{
_assertValidPair(makerToken, takerToken);
reserveId = _getNextReserveId(takerToken, makerToken, reserveOffset);
reserveId = _getNextReserveId(opts, takerToken, makerToken);
if (reserveId == 0x0) {
return (reserveId, hint, takerTokenAmounts);
}
hint = this.encodeKyberHint(reserveId, takerToken, makerToken);
opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
hint = opts.hint;
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, hint),
takerTokenData: abi.encode(takerToken, hint),
makerTokenData: abi.encode(makerToken, opts),
takerTokenData: abi.encode(takerToken, opts),
getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber
}),
makerTokenAmounts
@ -114,6 +124,7 @@ contract KyberSampler is
}
function encodeKyberHint(
KyberSamplerOpts memory opts,
bytes32 reserveId,
address takerToken,
address makerToken
@ -123,14 +134,14 @@ contract KyberSampler is
returns (bytes memory hint)
{
// Build a hint selecting the single reserve
IKyberHintHandler kyberHint = IKyberHintHandler(_getKyberHintHandlerAddress());
IKyberHintHandler kyberHint = IKyberHintHandler(opts.hintHandler);
// All other reserves should be ignored with this hint
bytes32[] memory selectedReserves = new bytes32[](1);
selectedReserves[0] = reserveId;
uint256[] memory emptySplits = new uint256[](0);
if (takerToken == _getWethAddress()) {
if (takerToken == opts.weth) {
// ETH to Token
try
kyberHint.buildEthToTokenHint
@ -147,7 +158,7 @@ contract KyberSampler is
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
}
} else if (makerToken == _getWethAddress()) {
} else if (makerToken == opts.weth) {
// Token to ETH
try
kyberHint.buildTokenToEthHint
@ -199,13 +210,13 @@ contract KyberSampler is
view
returns (uint256)
{
(address makerToken, bytes memory hint) =
abi.decode(makerTokenData, (address, bytes));
(address makerToken, KyberSamplerOpts memory opts) =
abi.decode(makerTokenData, (address, KyberSamplerOpts));
(address takerToken, ) =
abi.decode(takerTokenData, (address, bytes));
abi.decode(takerTokenData, (address, KyberSamplerOpts));
try
this.sampleSellFromKyberNetwork
(hint, takerToken, makerToken, sellAmount)
(opts, takerToken, makerToken, sellAmount)
returns (uint256 amount)
{
return amount;
@ -216,7 +227,7 @@ contract KyberSampler is
}
function sampleSellFromKyberNetwork(
bytes memory hint,
KyberSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256 takerTokenAmount
@ -226,19 +237,19 @@ contract KyberSampler is
returns (uint256 makerTokenAmount)
{
// If there is no hint do not continue
if (hint.length == 0) {
if (opts.hint.length == 0) {
return 0;
}
try
IKyberNetworkProxy(_getKyberNetworkProxyAddress()).getExpectedRateAfterFee
IKyberNetworkProxy(opts.networkProxy).getExpectedRateAfterFee
{gas: KYBER_CALL_GAS}
(
takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken,
makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken,
takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken,
makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken,
takerTokenAmount,
0, // fee
hint
opts.hint
)
returns (uint256 rate)
{
@ -258,28 +269,28 @@ contract KyberSampler is
}
function _getNextReserveId(
KyberSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256 reserveOffset
address makerToken
)
internal
view
returns (bytes32 reserveId)
{
// Fetch the registered reserves for this pair
IKyberHintHandler kyberHint = IKyberHintHandler(_getKyberHintHandlerAddress());
IKyberHintHandler kyberHint = IKyberHintHandler(opts.hintHandler);
(bytes32[] memory reserveIds, ,) = kyberHint.getTradingReserves(
takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken,
makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken,
takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken,
makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken,
true,
new bytes(0) // empty hint
);
if (reserveOffset >= reserveIds.length) {
if (opts.reserveOffset >= reserveIds.length) {
return 0x0;
}
reserveId = reserveIds[reserveOffset];
reserveId = reserveIds[opts.reserveOffset];
// Ignore Kyber Bridged Reserves (0xbb)
if (uint256(reserveId >> 248) == 0xbb) {
return 0x0;

View File

@ -66,6 +66,10 @@ contract LiquidityProviderSampler is
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;

View File

@ -20,27 +20,27 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IMStable.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
contract MStableSampler is
DeploymentConstants,
SamplerUtils,
ApproximateBuys
{
/// @dev Default gas limit for mStable calls.
uint256 constant private DEFAULT_CALL_GAS = 800e3; // 800k
/// @dev Sample sell quotes from the mStable mUSD contract
/// @dev Sample sell quotes from the mStable contract
/// @param router Address of the mStable 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 sampleSellsFromMStable(
address router,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
@ -55,12 +55,16 @@ contract MStableSampler is
for (uint256 i = 0; i < numSamples; i++) {
try
IMStable(_getMUsdAddress()).getSwapOutput
IMStable(router).getSwapOutput
{gas: DEFAULT_CALL_GAS}
(takerToken, makerToken, takerTokenAmounts[i])
returns (bool, string memory, uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
@ -68,13 +72,15 @@ contract MStableSampler is
}
}
/// @dev Sample buy quotes from MStable mUSD contract
/// @dev Sample buy quotes from MStable contract
/// @param router Address of the mStable 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 sampleBuysFromMStable(
address router,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
@ -85,8 +91,8 @@ contract MStableSampler is
{
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken),
takerTokenData: abi.encode(takerToken),
makerTokenData: abi.encode(makerToken, router),
takerTokenData: abi.encode(takerToken, router),
getSellQuoteCallback: _sampleSellForApproximateBuyFromMStable
}),
makerTokenAmounts
@ -102,13 +108,13 @@ contract MStableSampler is
view
returns (uint256 buyAmount)
{
(address takerToken) =
abi.decode(takerTokenData, (address));
(address takerToken, address router) =
abi.decode(takerTokenData, (address, address));
(address makerToken) =
abi.decode(makerTokenData, (address));
try
this.sampleSellsFromMStable
(takerToken, makerToken, _toSingleValueArray(sellAmount))
(router, takerToken, makerToken, _toSingleValueArray(sellAmount))
returns (uint256[] memory amounts)
{
return amounts[0];

View File

@ -20,14 +20,12 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IMooniswap.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
contract MooniswapSampler is
DeploymentConstants,
SamplerUtils,
ApproximateBuys
{
@ -56,25 +54,22 @@ contract MooniswapSampler is
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
address mooniswapTakerToken = takerToken == _getWethAddress() ? address(0) : takerToken;
address mooniswapMakerToken = makerToken == _getWethAddress() ? address(0) : makerToken;
for (uint256 i = 0; i < numSamples; i++) {
uint256 buyAmount = sampleSingleSellFromMooniswapPool(
registry,
mooniswapTakerToken,
mooniswapMakerToken,
takerToken,
makerToken,
takerTokenAmounts[i]
);
// Exit early if the amount is too high for the source to serve
if (buyAmount == 0) {
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
pool = IMooniswap(
IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken)
IMooniswapRegistry(registry).pools(takerToken, makerToken)
);
}
@ -139,20 +134,17 @@ contract MooniswapSampler is
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
address mooniswapTakerToken = takerToken == _getWethAddress() ? address(0) : takerToken;
address mooniswapMakerToken = makerToken == _getWethAddress() ? address(0) : makerToken;
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(registry, mooniswapMakerToken),
takerTokenData: abi.encode(registry, mooniswapTakerToken),
makerTokenData: abi.encode(registry, makerToken),
takerTokenData: abi.encode(registry, takerToken),
getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap
}),
makerTokenAmounts
);
pool = IMooniswap(
IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken)
IMooniswapRegistry(registry).pools(takerToken, makerToken)
);
}

View File

@ -20,11 +20,9 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IShell.sol";
contract ShellSampler is
DeploymentConstants
contract ShellSampler
{
/// @dev Default gas limit for Shell calls.
uint256 constant private DEFAULT_CALL_GAS = 300e3; // 300k
@ -58,6 +56,10 @@ contract ShellSampler is
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
@ -94,6 +96,10 @@ contract ShellSampler is
returns (uint256 amount)
{
takerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;

View File

@ -1,96 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IUniswapV2Router01.sol";
contract SushiSwapSampler is
DeploymentConstants
{
/// @dev Gas limit for SushiSwap calls.
uint256 constant private SUSHISWAP_CALL_GAS = 150e3; // 150k
/// @dev Sample sell quotes from SushiSwap.
/// @param router Router to look up tokens and amounts
/// @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 sampleSellsFromSushiSwap(
address router,
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++) {
try
IUniswapV2Router01(router).getAmountsOut
{gas: SUSHISWAP_CALL_GAS}
(takerTokenAmounts[i], path)
returns (uint256[] memory amounts)
{
makerTokenAmounts[i] = amounts[path.length - 1];
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from SushiSwap
/// @param router Router to look up tokens and amounts
/// @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 sampleBuysFromSushiSwap(
address router,
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++) {
try
IUniswapV2Router01(router).getAmountsIn
{gas: SUSHISWAP_CALL_GAS}
(makerTokenAmounts[i], path)
returns (uint256[] memory amounts)
{
takerTokenAmounts[i] = amounts[0];
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
}

View File

@ -20,7 +20,6 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IUniswapExchangeQuotes.sol";
import "./SamplerUtils.sol";
@ -37,21 +36,20 @@ interface IUniswapExchangeFactory {
contract UniswapSampler is
DeploymentConstants,
SamplerUtils
{
/// @dev Gas limit for Uniswap calls.
uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k
// @dev The BNB token is poisoned on uniswap v1.
address constant private BAD_MAKER_TOKEN = 0xB8c77482e45F1F44dE1745F52C74426C631bDD52;
/// @dev Sample sell quotes from Uniswap.
/// @param router Address of the Uniswap Router
/// @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 router,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
@ -63,23 +61,20 @@ contract UniswapSampler is
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
if (makerToken == BAD_MAKER_TOKEN) {
// BNB is poisoned on v1. You can only sell to it.
return makerTokenAmounts;
}
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
for (uint256 i = 0; i < numSamples; i++) {
bool didSucceed = true;
if (makerToken == _getWethAddress()) {
if (makerToken == address(0)) {
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthInputPrice.selector,
takerTokenAmounts[i]
);
} else if (takerToken == _getWethAddress()) {
} else if (takerToken == address(0)) {
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenInputPrice.selector,
@ -102,7 +97,8 @@ contract UniswapSampler is
makerTokenAmounts[i] = 0;
}
}
if (!didSucceed) {
// Break early if amounts are 0
if (!didSucceed || makerTokenAmounts[i] == 0) {
break;
}
}
@ -115,6 +111,7 @@ contract UniswapSampler is
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromUniswap(
address router,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
@ -126,23 +123,20 @@ contract UniswapSampler is
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
if (makerToken == BAD_MAKER_TOKEN) {
// BNB is poisoned on v1. You can only sell to it.
return takerTokenAmounts;
}
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
for (uint256 i = 0; i < numSamples; i++) {
bool didSucceed = true;
if (makerToken == _getWethAddress()) {
if (makerToken == address(0)) {
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthOutputPrice.selector,
makerTokenAmounts[i]
);
} else if (takerToken == _getWethAddress()) {
} else if (takerToken == address(0)) {
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenOutputPrice.selector,
@ -165,7 +159,8 @@ contract UniswapSampler is
takerTokenAmounts[i] = 0;
}
}
if (!didSucceed) {
// Break early if amounts are 0
if (!didSucceed || takerTokenAmounts[i] == 0) {
break;
}
}
@ -203,15 +198,16 @@ contract UniswapSampler is
/// @dev Retrive an existing Uniswap exchange contract.
/// Throws if the exchange does not exist.
/// @param router Address of the Uniswap router.
/// @param tokenAddress Address of the token contract.
/// @return exchange `IUniswapExchangeQuotes` for the token.
function _getUniswapExchange(address tokenAddress)
function _getUniswapExchange(address router, address tokenAddress)
private
view
returns (IUniswapExchangeQuotes exchange)
{
exchange = IUniswapExchangeQuotes(
address(IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
address(IUniswapExchangeFactory(router)
.getExchange(tokenAddress))
);
}

View File

@ -20,12 +20,10 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./DeploymentConstants.sol";
import "./interfaces/IUniswapV2Router01.sol";
contract UniswapV2Sampler is
DeploymentConstants
contract UniswapV2Sampler
{
/// @dev Gas limit for UniswapV2 calls.
uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
@ -55,6 +53,10 @@ contract UniswapV2Sampler is
returns (uint256[] memory amounts)
{
makerTokenAmounts[i] = amounts[path.length - 1];
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
@ -87,6 +89,10 @@ contract UniswapV2Sampler is
returns (uint256[] memory amounts)
{
takerTokenAmounts[i] = amounts[0];
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;

View File

@ -88,6 +88,25 @@ library LibDeterministicQuotes {
}
}
contract TestDeploymentConstants {
// solhint-disable separate-by-one-line-in-contract
// Mainnet addresses ///////////////////////////////////////////////////////
/// @dev Mainnet address of the WETH contract.
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev Overridable way to get the WETH address.
/// @return wethAddress The WETH address.
function _getWethAddress()
internal
view
returns (address wethAddress)
{
return WETH_ADDRESS;
}
}
contract FailTrigger {
@ -109,7 +128,7 @@ contract FailTrigger {
contract TestERC20BridgeSamplerUniswapExchange is
IUniswapExchangeQuotes,
DeploymentConstants,
TestDeploymentConstants,
FailTrigger
{
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
@ -198,7 +217,7 @@ contract TestERC20BridgeSamplerUniswapExchange is
contract TestERC20BridgeSamplerUniswapV2Router01 is
IUniswapV2Router01,
DeploymentConstants,
TestDeploymentConstants,
FailTrigger
{
bytes32 constant private SALT = 0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1;
@ -249,7 +268,7 @@ contract TestERC20BridgeSamplerUniswapV2Router01 is
// solhint-disable space-after-comma
contract TestERC20BridgeSamplerKyberNetwork is
DeploymentConstants,
TestDeploymentConstants,
FailTrigger
{
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
@ -357,24 +376,6 @@ contract TestERC20BridgeSamplerKyberNetwork is
toToken
);
}
function _getKyberNetworkProxyAddress()
override
internal
view
returns (address)
{
return address(this);
}
function _getKyberHintHandlerAddress()
override
internal
view
returns (address)
{
return address(this);
}
}
@ -502,54 +503,4 @@ contract TestERC20BridgeSampler is
{
return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress);
}
// Overriden to point to a custom contract.
function _getEth2DaiAddress()
override
internal
view
returns (address eth2daiAddress)
{
return address(eth2Dai);
}
// Overriden to point to a custom contract.
function _getUniswapExchangeFactoryAddress()
override
internal
view
returns (address uniswapAddress)
{
return address(uniswap);
}
// Overriden to point to a custom contract.
function _getUniswapV2Router01Address()
override
internal
view
returns (address uniswapV2RouterAddress)
{
return address(uniswapV2Router);
}
// Overriden to point to a custom contract.
function _getKyberNetworkProxyAddress()
override
internal
view
returns (address kyberAddress)
{
return address(kyber);
}
// Overriden to point to a custom contract.
function _getKyberHintHandlerAddress()
override
internal
view
returns (address kyberAddress)
{
return address(kyber);
}
}

View File

@ -38,7 +38,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DeploymentConstants|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json",
"postpublish": {
"assets": []
}

View File

@ -15,8 +15,8 @@ import {
} from './types';
import {
DEFAULT_GET_MARKET_ORDERS_OPTS,
DEFAULT_INTERMEDIATE_TOKENS,
DEFAULT_TOKEN_ADJACENCY_GRAPH,
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
} from './utils/market_operation_utils/constants';
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
@ -54,7 +54,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
makerAssetOfferings: {},
txOriginBlacklist: new Set(),
},
tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH,
tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID[ChainId.Mainnet],
};
const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = {
@ -112,7 +112,7 @@ export const constants = {
ONE_SECOND_MS,
ONE_MINUTE_MS,
DEFAULT_SWAP_QUOTER_OPTS,
DEFAULT_INTERMEDIATE_TOKENS,
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS,
DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS,

View File

@ -110,9 +110,11 @@ export {
} from './types';
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
export {
DEFAULT_TOKEN_ADJACENCY_GRAPH,
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
DEFAULT_GAS_SCHEDULE,
SOURCE_FLAGS,
BUY_SOURCE_FILTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
} from './utils/market_operation_utils/constants';
export {
Parameters,
@ -146,12 +148,7 @@ export {
NativeLimitOrderFillData,
NativeFillData,
OptimizedMarketOrder,
SnowSwapFillData,
SnowSwapInfo,
SourceQuoteOperation,
SushiSwapFillData,
SwerveFillData,
SwerveInfo,
TokenAdjacencyGraph,
UniswapV2FillData,
} from './utils/market_operation_utils/types';

View File

@ -1,6 +1,6 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { WETH9Contract } from '@0x/contracts-erc20';
import { IZeroExContract, MultiplexFeatureContract } from '@0x/contracts-zero-ex';
import { ChainId, ContractAddresses } from '@0x/contract-addresses';
import { IZeroExContract, WETH9Contract } from '@0x/contract-wrappers';
import { MultiplexFeatureContract } from '@0x/contracts-zero-ex';
import {
encodeAffiliateFeeTransformerData,
encodeCurveLiquidityProviderData,
@ -34,6 +34,7 @@ import { assert } from '../utils/assert';
import {
CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID,
MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID,
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
} from '../utils/market_operation_utils/constants';
import { poolEncoder } from '../utils/market_operation_utils/orders';
import {
@ -62,10 +63,16 @@ import {
// tslint:disable-next-line:custom-no-magic-numbers
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
const { NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants;
const PANCAKE_SWAP_FORKS = [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.BakerySwap, ERC20BridgeSource.SushiSwap];
const DUMMY_WETH_CONTRACT = new WETH9Contract(NULL_ADDRESS, {
sendAsync(): void {
return;
},
} as any);
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
public readonly provider: ZeroExProvider;
public readonly chainId: number;
public readonly chainId: ChainId;
public readonly transformerNonces: {
wethTransformer: number;
payTakerTransformer: number;
@ -139,6 +146,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
// VIP routes.
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap])
) {
const source = quote.orders[0].source;
@ -167,7 +175,44 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
};
}
if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])) {
if (
this.chainId === ChainId.BSC &&
isDirectSwapCompatible(quote, optsWithDefaults, [
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.SushiSwap,
])
) {
const source = quote.orders[0].source;
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
return {
calldataHexString: this._exchangeProxy
.sellToPancakeSwap(
fillData.tokenAddressPath.map((a, i) => {
if (i === 0 && isFromETH) {
return ETH_TOKEN_ADDRESS;
}
if (i === fillData.tokenAddressPath.length - 1 && isToETH) {
return ETH_TOKEN_ADDRESS;
}
return a;
}),
sellAmount,
minBuyAmount,
PANCAKE_SWAP_FORKS.indexOf(source),
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])
) {
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
const target = fillData.poolAddress;
return {
@ -189,7 +234,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
};
}
if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])) {
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])
) {
const fillData = quote.orders[0].fills[0].fillData as CurveFillData;
return {
calldataHexString: this._exchangeProxy
@ -215,7 +263,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
};
}
if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])) {
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
) {
const fillData = quote.orders[0].fills[0].fillData as MooniswapFillData;
return {
calldataHexString: this._exchangeProxy
@ -236,7 +287,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
};
}
if (isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexBatchFillCalldata(quote),
ethAmount,
@ -245,7 +296,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
gasOverhead: ZERO_AMOUNT,
};
}
if (isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) {
if (this.chainId === ChainId.Mainnet && isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexMultiHopFillCalldata(quote, optsWithDefaults),
ethAmount,
@ -317,7 +368,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
transforms.push({
deploymentNonce: this.transformerNonces.wethTransformer,
data: encodeWethTransformerData({
token: this.contractAddresses.etherToken,
token: NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId],
amount: MAX_UINT256,
}),
});
@ -491,11 +542,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}
private _encodeMultiplexMultiHopFillCalldata(quote: SwapQuote, opts: ExchangeProxyContractOpts): string {
const weth = new WETH9Contract(NULL_ADDRESS, this.provider);
const wrappedMultiHopCalls = [];
if (opts.isFromETH) {
wrappedMultiHopCalls.push({
selector: weth.getSelector('deposit'),
selector: DUMMY_WETH_CONTRACT.getSelector('deposit'),
data: NULL_BYTES,
});
}
@ -532,7 +582,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}
if (opts.isToETH) {
wrappedMultiHopCalls.push({
selector: weth.getSelector('withdraw'),
selector: DUMMY_WETH_CONTRACT.getSelector('withdraw'),
data: NULL_BYTES,
});
}

View File

@ -3,7 +3,7 @@ import { FillQuoteTransformerData, FillQuoteTransformerOrderType } from '@0x/pro
import { AffiliateFeeType, ExchangeProxyContractOpts, MarketBuySwapQuote, MarketOperation, SwapQuote } from '../types';
import {
createBridgeDataForBridgeOrder,
getERC20BridgeSourceToBridgeSource,
getErc20BridgeSourceToBridgeSource,
} from '../utils/market_operation_utils/orders';
import {
ERC20BridgeSource,
@ -128,7 +128,7 @@ export function getFQTTransformerDataFromOptimizedOrders(
bridgeData: createBridgeDataForBridgeOrder(order),
makerTokenAmount: order.makerAmount,
takerTokenAmount: order.takerAmount,
source: getERC20BridgeSourceToBridgeSource(order.source),
source: getErc20BridgeSourceToBridgeSource(order.source),
});
} else if (isOptimizedLimitOrder(order)) {
fqtData.limitOrders.push({

View File

@ -132,6 +132,7 @@ export class SwapQuoter {
this._marketOperationUtils = new MarketOperationUtils(
new DexOrderSampler(
this.chainId,
samplerContract,
samplerOverrides,
undefined, // balancer pool cache

View File

@ -1,15 +1,25 @@
import { ChainId } from '@0x/contract-addresses';
import { BigNumber, NULL_BYTES } from '@0x/utils';
import {
BAKERYSWAP_ROUTER_BY_CHAIN_ID,
BELT_BSC_INFOS,
CRYPTO_COM_ROUTER_BY_CHAIN_ID,
ELLIPSIS_BSC_INFOS,
KYBER_BRIDGED_LIQUIDITY_PREFIX,
MAINNET_CURVE_INFOS,
MAINNET_SHELL_POOLS,
MAINNET_SNOWSWAP_INFOS,
MAINNET_SWERVE_INFOS,
MAX_DODOV2_POOLS_QUERIED,
MAX_KYBER_RESERVES_QUERIED,
NERVE_BSC_INFOS,
NULL_ADDRESS,
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
SHELL_POOLS_BY_CHAIN_ID,
SUSHISWAP_ROUTER_BY_CHAIN_ID,
UNISWAPV2_ROUTER_BY_CHAIN_ID,
} from './constants';
import { CurveInfo, SnowSwapInfo, SwerveInfo } from './types';
import { CurveInfo, ERC20BridgeSource } from './types';
/**
* Filter Kyber reserves which should not be used (0xbb bridged reserves)
@ -19,6 +29,11 @@ export function isAllowedKyberReserveId(reserveId: string): boolean {
return reserveId !== NULL_BYTES && !reserveId.startsWith(KYBER_BRIDGED_LIQUIDITY_PREFIX);
}
// tslint:disable-next-line: completed-docs ban-types
export function isValidAddress(address: string | String): address is string {
return (typeof address === 'string' || address instanceof String) && address.toString() !== NULL_ADDRESS;
}
/**
* Returns the offsets to be used to discover Kyber reserves
*/
@ -36,14 +51,20 @@ export function getDodoV2Offsets(): BigNumber[] {
}
// tslint:disable completed-docs
export function getShellsForPair(takerToken: string, makerToken: string): string[] {
return Object.values(MAINNET_SHELL_POOLS)
export function getShellsForPair(chainId: ChainId, takerToken: string, makerToken: string): string[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(SHELL_POOLS_BY_CHAIN_ID[chainId])
.filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t)))
.map(i => i.poolAddress);
}
// tslint:disable completed-docs
export function getCurveInfosForPair(takerToken: string, makerToken: string): CurveInfo[] {
export function getCurveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(MAINNET_CURVE_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
@ -53,7 +74,10 @@ export function getCurveInfosForPair(takerToken: string, makerToken: string): Cu
);
}
export function getSwerveInfosForPair(takerToken: string, makerToken: string): SwerveInfo[] {
export function getSwerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(MAINNET_SWERVE_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
@ -63,7 +87,10 @@ export function getSwerveInfosForPair(takerToken: string, makerToken: string): S
);
}
export function getSnowSwapInfosForPair(takerToken: string, makerToken: string): SnowSwapInfo[] {
export function getSnowSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(MAINNET_SNOWSWAP_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
@ -72,3 +99,107 @@ export function getSnowSwapInfosForPair(takerToken: string, makerToken: string):
),
);
}
export function getNerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.BSC) {
return [];
}
return Object.values(NERVE_BSC_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
(c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)),
),
);
}
export function getBeltInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.BSC) {
return [];
}
return Object.values(BELT_BSC_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
(c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)),
),
);
}
export function getEllipsisInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.BSC) {
return [];
}
return Object.values(ELLIPSIS_BSC_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
(c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)),
),
);
}
export function getCurveLikeInfosForPair(
chainId: ChainId,
takerToken: string,
makerToken: string,
source:
| ERC20BridgeSource.Curve
| ERC20BridgeSource.Swerve
| ERC20BridgeSource.SnowSwap
| ERC20BridgeSource.Nerve
| ERC20BridgeSource.Belt
| ERC20BridgeSource.Ellipsis,
): CurveInfo[] {
switch (source) {
case ERC20BridgeSource.Curve:
return getCurveInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Swerve:
return getSwerveInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.SnowSwap:
return getSnowSwapInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Nerve:
return getNerveInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Belt:
return getBeltInfosForPair(chainId, takerToken, makerToken);
case ERC20BridgeSource.Ellipsis:
return getEllipsisInfosForPair(chainId, takerToken, makerToken);
default:
throw new Error(`Unknown Curve like source ${source}`);
}
}
export function uniswapV2LikeRouterAddress(
chainId: ChainId,
source:
| ERC20BridgeSource.UniswapV2
| ERC20BridgeSource.SushiSwap
| ERC20BridgeSource.CryptoCom
| ERC20BridgeSource.PancakeSwap
| ERC20BridgeSource.BakerySwap,
): string {
switch (source) {
case ERC20BridgeSource.UniswapV2:
return UNISWAPV2_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.SushiSwap:
return SUSHISWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.CryptoCom:
return CRYPTO_COM_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.PancakeSwap:
return PANCAKESWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.BakerySwap:
return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId];
default:
throw new Error(`Unknown UniswapV2 like source ${source}`);
}
}
const BAD_TOKENS_BY_SOURCE: Partial<{ [key in ERC20BridgeSource]: string[] }> = {
[ERC20BridgeSource.Uniswap]: [
'0xb8c77482e45f1f44de1745f52c74426c631bdd52', // BNB
],
};
export function isBadTokenForSource(token: string, source: ERC20BridgeSource): boolean {
return (BAD_TOKENS_BY_SOURCE[source] || []).includes(token.toLowerCase());
}

View File

@ -1,3 +1,4 @@
import { ChainId } from '@0x/contract-addresses';
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
@ -14,11 +15,10 @@ import {
FeeSchedule,
FillData,
GetMarketOrdersOpts,
KyberSamplerOpts,
LiquidityProviderFillData,
LiquidityProviderRegistry,
MultiHopFillData,
SnowSwapFillData,
SushiSwapFillData,
TokenAdjacencyGraph,
UniswapV2FillData,
} from './types';
@ -38,59 +38,115 @@ export const NULL_BYTES = '0x';
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
export const COMPARISON_PRICE_DECIMALS = 10;
function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue: T): { [key in ChainId]: T } {
// TODO I don't like this but iterating through enums is weird
return {
[ChainId.Mainnet]: defaultValue,
[ChainId.Ropsten]: defaultValue,
[ChainId.Rinkeby]: defaultValue,
[ChainId.Kovan]: defaultValue,
[ChainId.Ganache]: defaultValue,
[ChainId.BSC]: defaultValue,
...(rest || {}),
};
}
/**
* Valid sources for market sell.
*/
export const SELL_SOURCE_FILTER = new SourceFilters([
ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Balancer,
ERC20BridgeSource.Bancor,
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.Swerve,
ERC20BridgeSource.SnowSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Shell,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Cream,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
]);
export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
{
[ChainId.Mainnet]: new SourceFilters([
ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Balancer,
ERC20BridgeSource.Bancor,
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.Swerve,
ERC20BridgeSource.SnowSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Shell,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Cream,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
]),
[ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.BSC]: new SourceFilters([
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.Belt,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Ellipsis,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Nerve,
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.SushiSwap,
]),
},
new SourceFilters([]),
);
/**
* Valid sources for market buy.
*/
export const BUY_SOURCE_FILTER = new SourceFilters([
ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Balancer,
// ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.Shell,
ERC20BridgeSource.Swerve,
ERC20BridgeSource.SnowSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Cream,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
]);
export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
{
[ChainId.Mainnet]: new SourceFilters([
ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Balancer,
// ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.Shell,
ERC20BridgeSource.Swerve,
ERC20BridgeSource.SnowSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Cream,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
]),
[ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.BSC]: new SourceFilters([
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.Belt,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Ellipsis,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Nerve,
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.SushiSwap,
]),
},
new SourceFilters([]),
);
/**
* 0x Protocol Fee Multiplier
@ -100,7 +156,13 @@ export const PROTOCOL_FEE_MULTIPLIER = new BigNumber(70000);
/**
* Sources to poll for ETH fee price estimates.
*/
export const FEE_QUOTE_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2];
export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>(
{
[ChainId.Mainnet]: [ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2],
[ChainId.BSC]: [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.Mooniswap, ERC20BridgeSource.SushiSwap],
},
[],
);
// HACK(mzhu25): Limit and RFQ orders need to be treated as different sources
// when computing the exchange proxy gas overhead.
@ -173,7 +235,7 @@ export const TOKENS = {
...MIRROR_WRAPPED_TOKENS,
};
export const POOLS = {
export const CURVE_POOLS = {
curve_compound: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56', // 0.Compound
// 1.USDT is dead
curve_PAX: '0x06364f10b501e868329afbc005b3492902d6c763', // 2.PAX
@ -202,21 +264,52 @@ export const POOLS = {
curve_aave: '0xdebf20617708857ebe4f679508e7b7863a8a8eee', // 25.aave
};
export const DEFAULT_INTERMEDIATE_TOKENS = [TOKENS.WETH, TOKENS.USDT, TOKENS.DAI, TOKENS.USDC, TOKENS.WBTC];
export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
{
[ChainId.Mainnet]: [TOKENS.WETH, TOKENS.USDT, TOKENS.DAI, TOKENS.USDC, TOKENS.WBTC],
[ChainId.BSC]: [
'0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', // WBNB
'0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
'0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3', // DAI
'0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC
'0x2170ed0880ac9a755fd29b2688956bd959f933f8', // ETH
'0x55d398326f99059ff775485246999027b3197955', // BUSD-T
],
},
[],
);
export const DEFAULT_TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS,
})
// Mirror Protocol
.tap(builder => {
builder
.add(TOKENS.MIR, TOKENS.UST)
.add(TOKENS.UST, [TOKENS.MIR, ...Object.values(MIRROR_WRAPPED_TOKENS)])
.add(TOKENS.USDT, TOKENS.UST);
Object.values(MIRROR_WRAPPED_TOKENS).forEach(t => builder.add(t, TOKENS.UST));
})
// Build
.build();
export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdjacencyGraph>(
{
[ChainId.Mainnet]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Mainnet],
})
// Mirror Protocol
.tap(builder => {
builder
.add(TOKENS.MIR, TOKENS.UST)
.add(TOKENS.UST, [TOKENS.MIR, ...Object.values(MIRROR_WRAPPED_TOKENS)])
.add(TOKENS.USDT, TOKENS.UST);
Object.values(MIRROR_WRAPPED_TOKENS).forEach(t => builder.add(t, TOKENS.UST));
})
// Build
.build(),
[ChainId.BSC]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.BSC],
}).build(),
},
new TokenAdjacencyGraphBuilder({ default: [] }).build(),
);
export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: TOKENS.WETH,
[ChainId.BSC]: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', // WBNB
},
NULL_ADDRESS,
);
export const NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID = valueByChainId({}, ONE_ETHER);
/**
* Mainnet Curve configuration
@ -224,147 +317,147 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = new TokenAdjac
* I.e DaiUsdc curve has DAI as index 0 and USDC as index 1
*/
export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = {
[POOLS.curve_compound]: {
[CURVE_POOLS.curve_compound]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying,
poolAddress: POOLS.curve_compound,
poolAddress: CURVE_POOLS.curve_compound,
tokens: [TOKENS.DAI, TOKENS.USDC],
metaToken: undefined,
},
[POOLS.curve_PAX]: {
[CURVE_POOLS.curve_PAX]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_PAX,
poolAddress: CURVE_POOLS.curve_PAX,
tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT, TOKENS.PAX],
metaToken: undefined,
},
[POOLS.curve_sUSD]: {
[CURVE_POOLS.curve_sUSD]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_sUSD,
poolAddress: CURVE_POOLS.curve_sUSD,
tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT, TOKENS.sUSD],
metaToken: undefined,
},
[POOLS.curve_renBTC]: {
[CURVE_POOLS.curve_renBTC]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_renBTC,
poolAddress: CURVE_POOLS.curve_renBTC,
tokens: [TOKENS.RenBTC, TOKENS.WBTC],
metaToken: undefined,
},
[POOLS.curve_sBTC]: {
[CURVE_POOLS.curve_sBTC]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_sBTC,
poolAddress: CURVE_POOLS.curve_sBTC,
tokens: [TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC],
metaToken: undefined,
},
[POOLS.curve_HBTC]: {
[CURVE_POOLS.curve_HBTC]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_HBTC,
poolAddress: CURVE_POOLS.curve_HBTC,
tokens: [TOKENS.hBTC, TOKENS.WBTC],
metaToken: undefined,
},
[POOLS.curve_TRI]: {
[CURVE_POOLS.curve_TRI]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_TRI,
poolAddress: CURVE_POOLS.curve_TRI,
tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: undefined,
},
[POOLS.curve_GUSD]: {
[CURVE_POOLS.curve_GUSD]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_GUSD,
poolAddress: CURVE_POOLS.curve_GUSD,
tokens: [TOKENS.GUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: TOKENS.GUSD,
},
[POOLS.curve_HUSD]: {
[CURVE_POOLS.curve_HUSD]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_HUSD,
poolAddress: CURVE_POOLS.curve_HUSD,
tokens: [TOKENS.HUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: TOKENS.HUSD,
},
[POOLS.curve_USDN]: {
[CURVE_POOLS.curve_USDN]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_USDN,
poolAddress: CURVE_POOLS.curve_USDN,
tokens: [TOKENS.USDN, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: TOKENS.USDN,
},
[POOLS.curve_mUSD]: {
[CURVE_POOLS.curve_mUSD]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_mUSD,
poolAddress: CURVE_POOLS.curve_mUSD,
tokens: [TOKENS.mUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: TOKENS.mUSD,
},
[POOLS.curve_tBTC]: {
[CURVE_POOLS.curve_tBTC]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_tBTC,
poolAddress: CURVE_POOLS.curve_tBTC,
tokens: [TOKENS.tBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC],
metaToken: TOKENS.tBTC,
},
[POOLS.curve_dUSD]: {
[CURVE_POOLS.curve_dUSD]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_dUSD,
poolAddress: CURVE_POOLS.curve_dUSD,
tokens: [TOKENS.dUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: TOKENS.dUSD,
},
[POOLS.curve_pBTC]: {
[CURVE_POOLS.curve_pBTC]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_pBTC,
poolAddress: CURVE_POOLS.curve_pBTC,
tokens: [TOKENS.pBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC],
metaToken: TOKENS.pBTC,
},
[POOLS.curve_bBTC]: {
[CURVE_POOLS.curve_bBTC]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_bBTC,
poolAddress: CURVE_POOLS.curve_bBTC,
tokens: [TOKENS.bBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC],
metaToken: TOKENS.bBTC,
},
[POOLS.curve_oBTC]: {
[CURVE_POOLS.curve_oBTC]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_oBTC,
poolAddress: CURVE_POOLS.curve_oBTC,
tokens: [TOKENS.oBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC],
metaToken: TOKENS.oBTC,
},
[POOLS.curve_UST]: {
[CURVE_POOLS.curve_UST]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_UST,
poolAddress: CURVE_POOLS.curve_UST,
tokens: [TOKENS.UST, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: TOKENS.UST,
},
[POOLS.curve_eurs]: {
[CURVE_POOLS.curve_eurs]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_eurs,
poolAddress: CURVE_POOLS.curve_eurs,
tokens: [TOKENS.EURS, TOKENS.sEUR],
metaToken: undefined,
},
@ -376,19 +469,19 @@ export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = {
// tokens: [TOKENS.ETH, TOKENS.sETH],
// metaToken: undefined,
// },
[POOLS.curve_aave]: {
[CURVE_POOLS.curve_aave]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_aave,
poolAddress: CURVE_POOLS.curve_aave,
tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT],
metaToken: undefined,
},
[POOLS.curve_aave]: {
[CURVE_POOLS.curve_aave]: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: POOLS.curve_aave,
poolAddress: CURVE_POOLS.curve_aave,
tokens: [TOKENS.aDAI, TOKENS.aUSDC, TOKENS.aUSDT],
metaToken: undefined,
},
@ -443,6 +536,52 @@ export const MAINNET_SNOWSWAP_INFOS: { [name: string]: CurveInfo } = {
},
};
export const NERVE_BSC_INFOS: { [name: string]: CurveInfo } = {
['0x1b3771a66ee31180906972580ade9b81afc5fcdc']: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: '0x1b3771a66ee31180906972580ade9b81afc5fcdc',
tokens: [
'0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
'0x55d398326f99059ff775485246999027b3197955', // BUSD-T
'0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC
],
metaToken: undefined,
},
};
export const BELT_BSC_INFOS: { [name: string]: CurveInfo } = {
['0xf16d312d119c13dd27fd0dc814b0bcdcaaa62dfd']: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: '0xf16d312d119c13dd27fd0dc814b0bcdcaaa62dfd',
tokens: [
'0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3', // bDAI
'0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC
'0x55d398326f99059ff775485246999027b3197955', // BUSD-T
'0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
],
metaToken: undefined,
},
};
export const ELLIPSIS_BSC_INFOS: { [name: string]: CurveInfo } = {
['0x160caed03795365f3a589f10c379ffa7d75d4e76']: {
exchangeFunctionSelector: CurveFunctionSelectors.exchange,
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: '0x160caed03795365f3a589f10c379ffa7d75d4e76',
tokens: [
'0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
'0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC
'0x55d398326f99059ff775485246999027b3197955', // BUSD-T
],
metaToken: undefined,
},
};
/**
* Kyber reserve prefixes
* 0xff Fed price reserve
@ -451,56 +590,181 @@ export const MAINNET_SNOWSWAP_INFOS: { [name: string]: CurveInfo } = {
*/
export const KYBER_BRIDGED_LIQUIDITY_PREFIX = '0xbb';
export const MAX_KYBER_RESERVES_QUERIED = 5;
export const MAINNET_KYBER_NETWORK_PROXY = '0x9aab3f75489902f3a48495025729a0af77d4b11e';
export const KYBER_CONFIG_BY_CHAIN_ID = valueByChainId<KyberSamplerOpts>(
{
[ChainId.Mainnet]: {
networkProxy: '0x9aab3f75489902f3a48495025729a0af77d4b11e',
hintHandler: '0xa1C0Fa73c39CFBcC11ec9Eb1Afc665aba9996E2C',
weth: TOKENS.WETH,
},
},
{
networkProxy: NULL_ADDRESS,
hintHandler: NULL_ADDRESS,
weth: NULL_ADDRESS,
},
);
export const LIQUIDITY_PROVIDER_REGISTRY: LiquidityProviderRegistry = {};
export const MAINNET_UNISWAP_V1_ROUTER = '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95';
export const MAINNET_UNISWAP_V2_ROUTER = '0xf164fc0ec4e93095b804a4795bbe1e041497b92a';
export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f';
export const MAINNET_CRYPTO_COM_ROUTER = '0xceb90e4c17d626be0facd78b79c9c87d7ca181b3';
export const MAINNET_LINKSWAP_ROUTER = '0xa7ece0911fe8c60bff9e99f8fafcdbe56e07aff1';
export const UNISWAPV1_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95',
},
NULL_ADDRESS,
);
export const MAINNET_MSTABLE_ROUTER = '0xe2f2a5c287993345a840db3b0845fbc70f5935a5';
export const MAINNET_OASIS_ROUTER = '0x794e6e91555438afc3ccf1c5076a74f42133d08d';
export const UNISWAPV2_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a',
},
NULL_ADDRESS,
);
export const MAINNET_MOONISWAP_REGISTRY = '0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303';
export const MAINNET_MOONISWAP_V2_REGISTRY = '0xc4a8b7e29e3c8ec560cd4945c1cf3461a85a148d';
export const MAINNET_MOONISWAP_V2_1_REGISTRY = '0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643';
export const SUSHISWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f',
[ChainId.BSC]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
},
NULL_ADDRESS,
);
export const MAINNET_DODO_HELPER = '0x533da777aedce766ceae696bf90f8541a4ba80eb';
export const MAINNET_DODOV2_PRIVATE_POOL_FACTORY = '0x6b4fa0bc61eddc928e0df9c7f01e407bfcd3e5ef';
export const MAINNET_DODOV2_VENDING_MACHINE_FACTORY = '0x72d220ce168c4f361dd4dee5d826a01ad8598f6c';
export const CRYPTO_COM_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xceb90e4c17d626be0facd78b79c9c87d7ca181b3',
},
NULL_ADDRESS,
);
export const LINKSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{ [ChainId.Mainnet]: '0xa7ece0911fe8c60bff9e99f8fafcdbe56e07aff1' },
NULL_ADDRESS,
);
export const MSTABLE_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xe2f2a5c287993345a840db3b0845fbc70f5935a5',
},
NULL_ADDRESS,
);
export const OASIS_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x5e3e0548935a83ad29fb2a9153d331dc6d49020f',
},
NULL_ADDRESS,
);
export const MOONISWAP_REGISTRIES_BY_CHAIN_ID = valueByChainId(
{
[ChainId.Mainnet]: [
'0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303',
'0xc4a8b7e29e3c8ec560cd4945c1cf3461a85a148d',
'0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643',
],
[ChainId.BSC]: ['0xd41b24bba51fac0e4827b6f94c0d6ddeb183cd64'],
},
[] as string[],
);
export const DODO_CONFIG_BY_CHAIN_ID = valueByChainId(
{
[ChainId.Mainnet]: {
helper: '0x533da777aedce766ceae696bf90f8541a4ba80eb',
registry: '0x3A97247DF274a17C59A3bd12735ea3FcDFb49950',
},
[ChainId.BSC]: {
helper: '0x0f859706aee7fcf61d5a8939e8cb9dbb6c1eda33',
registry: '0xca459456a45e300aa7ef447dbb60f87cccb42828',
},
},
{ helper: NULL_ADDRESS, registry: NULL_ADDRESS },
);
export const DODOV2_FACTORIES_BY_CHAIN_ID = valueByChainId<string[]>(
{
[ChainId.Mainnet]: [
'0x6b4fa0bc61eddc928e0df9c7f01e407bfcd3e5ef', // Private Pool
'0x72d220ce168c4f361dd4dee5d826a01ad8598f6c', // Vending Machine
],
[ChainId.BSC]: [
'0xafe0a75dffb395eaabd0a7e1bbbd0b11f8609eef', // Private Pool
'0x790b4a80fb1094589a3c0efc8740aa9b0c1733fb', // Vending Machine
],
},
[] as string[],
);
export const MAX_DODOV2_POOLS_QUERIED = 3;
export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID: { [id: string]: string } = {
'1': '0x561b94454b65614ae3db0897b74303f4acf7cc75',
'3': '0xae241c6fc7f28f6dc0cb58b4112ba7f63fcaf5e2',
'1337': NULL_ADDRESS,
};
// TODO(dorothy-zbornak): Point these to real addresses after deploying.
export const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID: { [id: string]: string } = {
'1': '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889',
'3': '0x87e0393aee0fb8c10b8653c6507c182264fe5a34',
'1337': NULL_ADDRESS,
};
export const MAINNET_SHELL_POOLS = {
StableCoins: {
poolAddress: '0x8f26d7bab7a73309141a291525c965ecdea7bf42',
tokens: [TOKENS.USDC, TOKENS.USDT, TOKENS.sUSD, TOKENS.DAI],
export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x7a6F6a048fE2Dc1397ABa0bf7879d3eacF371C53',
[ChainId.Ropsten]: '0xAa213dcDFbF104e08cbAeC3d1628eD197553AfCc',
},
Bitcoin: {
poolAddress: '0xc2d019b901f8d4fdb2b9a65b5d226ad88c66ee8d',
tokens: [TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC],
NULL_ADDRESS,
);
export const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889',
[ChainId.Ropsten]: '0x87e0393aee0fb8c10b8653c6507c182264fe5a34',
},
};
NULL_ADDRESS,
);
export const BANCOR_REGISTRY_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4',
},
NULL_ADDRESS,
);
export const SHELL_POOLS_BY_CHAIN_ID = valueByChainId(
{
[ChainId.Mainnet]: {
StableCoins: {
poolAddress: '0x8f26d7bab7a73309141a291525c965ecdea7bf42',
tokens: [TOKENS.USDC, TOKENS.USDT, TOKENS.sUSD, TOKENS.DAI],
},
Bitcoin: {
poolAddress: '0xc2d019b901f8d4fdb2b9a65b5d226ad88c66ee8d',
tokens: [TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC],
},
},
},
{
StableCoins: {
poolAddress: NULL_ADDRESS,
tokens: [] as string[],
},
Bitcoin: {
poolAddress: NULL_ADDRESS,
tokens: [] as string[],
},
},
);
export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3;
//
// BSC
//
export const PANCAKESWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.BSC]: '0x05ff2b0db69458a0750badebc4f9e13add608c7f',
},
NULL_ADDRESS,
);
export const BAKERYSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.BSC]: '0xcde540d7eafe93ac5fe6233bee57e1270d3e330f',
},
NULL_ADDRESS,
);
/**
* Calculated gross gas cost of the underlying exchange.
* The cost of switching from one source to another, assuming
@ -527,32 +791,32 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.Curve]: fillData => {
const poolAddress = (fillData as CurveFillData).pool.poolAddress.toLowerCase();
switch (poolAddress) {
case POOLS.curve_renBTC:
case POOLS.curve_sBTC:
case POOLS.curve_sUSD:
case POOLS.curve_HBTC:
case POOLS.curve_TRI:
case CURVE_POOLS.curve_renBTC:
case CURVE_POOLS.curve_sBTC:
case CURVE_POOLS.curve_sUSD:
case CURVE_POOLS.curve_HBTC:
case CURVE_POOLS.curve_TRI:
return 150e3;
case POOLS.curve_USDN:
case POOLS.curve_mUSD:
case CURVE_POOLS.curve_USDN:
case CURVE_POOLS.curve_mUSD:
return 300e3;
case POOLS.curve_GUSD:
case POOLS.curve_HUSD:
case CURVE_POOLS.curve_GUSD:
case CURVE_POOLS.curve_HUSD:
return 310e3;
case POOLS.curve_tBTC:
case CURVE_POOLS.curve_tBTC:
return 370e3;
case POOLS.curve_UST:
case CURVE_POOLS.curve_UST:
return 500e3;
case POOLS.curve_dUSD:
case POOLS.curve_bBTC:
case POOLS.curve_oBTC:
case POOLS.curve_eurs:
case CURVE_POOLS.curve_dUSD:
case CURVE_POOLS.curve_bBTC:
case CURVE_POOLS.curve_oBTC:
case CURVE_POOLS.curve_eurs:
return 600e3;
case POOLS.curve_compound:
case CURVE_POOLS.curve_compound:
return 750e3;
case POOLS.curve_aave:
case CURVE_POOLS.curve_aave:
return 800e3;
case POOLS.curve_PAX:
case CURVE_POOLS.curve_PAX:
return 850e3;
// case POOLS.curve_seth:
default:
@ -572,7 +836,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.SushiSwap]: (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
let gas = 90e3;
const path = (fillData as SushiSwapFillData).tokenAddressPath;
const path = (fillData as UniswapV2FillData).tokenAddressPath;
if (path.length > 2) {
gas += (path.length - 2) * 60e3; // +60k for each hop.
}
@ -601,6 +865,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.MStable]: () => 700e3,
[ERC20BridgeSource.Mooniswap]: () => 130e3,
[ERC20BridgeSource.Swerve]: () => 150e3,
[ERC20BridgeSource.Nerve]: () => 150e3,
[ERC20BridgeSource.Shell]: () => 170e3,
[ERC20BridgeSource.MultiHop]: (fillData?: FillData) => {
const firstHop = (fillData as MultiHopFillData).firstHopSource;
@ -620,7 +885,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
},
[ERC20BridgeSource.DodoV2]: (_fillData?: FillData) => 100e3,
[ERC20BridgeSource.SnowSwap]: fillData => {
switch ((fillData as SnowSwapFillData).pool.poolAddress.toLowerCase()) {
switch ((fillData as CurveFillData).pool.poolAddress.toLowerCase()) {
case '0xbf7ccd6c446acfcc5df023043f2167b62e81899b':
return 1000e3;
case '0x4571753311e37ddb44faa8fb78a6df9a6e3c6c0b':
@ -637,6 +902,29 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
}
return gas;
},
//
// BSC
//
[ERC20BridgeSource.PancakeSwap]: (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
let gas = 90e3;
const path = (fillData as UniswapV2FillData).tokenAddressPath;
if (path.length > 2) {
gas += (path.length - 2) * 60e3; // +60k for each hop.
}
return gas;
},
[ERC20BridgeSource.BakerySwap]: (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
let gas = 90e3;
const path = (fillData as UniswapV2FillData).tokenAddressPath;
if (path.length > 2) {
gas += (path.length - 2) * 60e3; // +60k for each hop.
}
return gas;
},
[ERC20BridgeSource.Belt]: () => 4500e3,
[ERC20BridgeSource.Ellipsis]: () => 150e3,
};
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = { ...DEFAULT_GAS_SCHEDULE };

View File

@ -19,11 +19,12 @@ import {
import { generateQuoteReport, QuoteReport } from './../quote_report_generator';
import { getComparisonPrices } from './comparison_price';
import {
BUY_SOURCE_FILTER,
BUY_SOURCE_FILTER_BY_CHAIN_ID,
DEFAULT_GET_MARKET_ORDERS_OPTS,
FEE_QUOTE_SOURCES,
ONE_ETHER,
SELL_SOURCE_FILTER,
FEE_QUOTE_SOURCES_BY_CHAIN_ID,
NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID,
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
SOURCE_FLAGS,
ZERO_AMOUNT,
} from './constants';
@ -50,10 +51,11 @@ import {
// tslint:disable:boolean-naming
export class MarketOperationUtils {
private readonly _wethAddress: string;
private readonly _sellSources: SourceFilters;
private readonly _buySources: SourceFilters;
private readonly _feeSources = new SourceFilters(FEE_QUOTE_SOURCES);
private readonly _feeSources: SourceFilters;
private readonly _nativeFeeToken: string;
private readonly _nativeFeeTokenAmount: BigNumber;
private static _computeQuoteReport(
quoteRequestor: QuoteRequestor | undefined,
@ -71,9 +73,11 @@ export class MarketOperationUtils {
private readonly contractAddresses: AssetSwapperContractAddresses,
private readonly _orderDomain: OrderDomain,
) {
this._wethAddress = contractAddresses.etherToken.toLowerCase();
this._buySources = BUY_SOURCE_FILTER;
this._sellSources = SELL_SOURCE_FILTER;
this._buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId];
this._sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId];
this._feeSources = new SourceFilters(FEE_QUOTE_SOURCES_BY_CHAIN_ID[_sampler.chainId]);
this._nativeFeeToken = NATIVE_FEE_TOKEN_BY_CHAIN_ID[_sampler.chainId];
this._nativeFeeTokenAmount = NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID[_sampler.chainId];
}
/**
@ -128,9 +132,19 @@ export class MarketOperationUtils {
// Get native order fillable amounts.
this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
// Get ETH -> maker token price.
this._sampler.getMedianSellRate(feeSourceFilters.sources, makerToken, this._wethAddress, ONE_ETHER),
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
makerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
),
// Get ETH -> taker token price.
this._sampler.getMedianSellRate(feeSourceFilters.sources, takerToken, this._wethAddress, ONE_ETHER),
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
takerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
),
// Get sell quotes for taker -> maker.
this._sampler.getSellQuotes(
quoteSourceFilters.exclude(offChainSources).sources,
@ -254,9 +268,19 @@ export class MarketOperationUtils {
// Get native order fillable amounts.
this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
// Get ETH -> makerToken token price.
this._sampler.getMedianSellRate(feeSourceFilters.sources, makerToken, this._wethAddress, ONE_ETHER),
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
makerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
),
// Get ETH -> taker token price.
this._sampler.getMedianSellRate(feeSourceFilters.sources, takerToken, this._wethAddress, ONE_ETHER),
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
takerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
),
// Get buy quotes for taker -> maker.
this._sampler.getBuyQuotes(
quoteSourceFilters.exclude(offChainSources).sources,
@ -362,8 +386,8 @@ export class MarketOperationUtils {
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
orders[0].order.takerToken,
this._wethAddress,
ONE_ETHER,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
),
),
...batchNativeOrders.map((orders, i) =>

View File

@ -1,17 +1,9 @@
import { BridgeSource, FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BridgeProtocol, encodeBridgeSourceId, FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { AbiEncoder, BigNumber } from '@0x/utils';
import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
import {
MAINNET_DODO_HELPER,
MAINNET_KYBER_NETWORK_PROXY,
MAINNET_MSTABLE_ROUTER,
MAINNET_OASIS_ROUTER,
MAINNET_UNISWAP_V1_ROUTER,
MAX_UINT256,
ZERO_AMOUNT,
} from './constants';
import { MAX_UINT256, ZERO_AMOUNT } from './constants';
import {
AggregationError,
BalancerFillData,
@ -21,6 +13,7 @@ import {
DexSample,
DODOFillData,
ERC20BridgeSource,
GenericRouterFillData,
KyberFillData,
LiquidityProviderFillData,
MooniswapFillData,
@ -33,9 +26,6 @@ import {
OptimizedMarketOrderBase,
OrderDomain,
ShellFillData,
SnowSwapFillData,
SushiSwapFillData,
SwerveFillData,
UniswapV2FillData,
} from './types';
@ -80,48 +70,59 @@ export function createOrdersFromTwoHopSample(
];
}
export function getERC20BridgeSourceToBridgeSource(source: ERC20BridgeSource): BridgeSource {
export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): string {
switch (source) {
case ERC20BridgeSource.Balancer:
return BridgeSource.Balancer;
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer');
case ERC20BridgeSource.Bancor:
return BridgeSource.Bancor;
return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor');
// case ERC20BridgeSource.CoFiX:
// return BridgeSource.CoFiX;
// return encodeBridgeSourceId(BridgeProtocol.CoFiX, 'CoFiX');
case ERC20BridgeSource.Curve:
return BridgeSource.Curve;
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve');
case ERC20BridgeSource.Cream:
return BridgeSource.Cream;
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Cream');
case ERC20BridgeSource.CryptoCom:
return BridgeSource.CryptoCom;
return encodeBridgeSourceId(BridgeProtocol.CryptoCom, 'CryptoCom');
case ERC20BridgeSource.Dodo:
return BridgeSource.Dodo;
return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo');
case ERC20BridgeSource.Kyber:
return BridgeSource.Kyber;
return encodeBridgeSourceId(BridgeProtocol.Kyber, 'Kyber');
case ERC20BridgeSource.LiquidityProvider:
return BridgeSource.LiquidityProvider;
// "LiquidityProvider" is too long to encode (17 characters).
return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP');
case ERC20BridgeSource.Mooniswap:
return BridgeSource.Mooniswap;
return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap');
case ERC20BridgeSource.MStable:
return BridgeSource.MStable;
return encodeBridgeSourceId(BridgeProtocol.MStable, 'MStable');
case ERC20BridgeSource.Eth2Dai:
return BridgeSource.Oasis;
return encodeBridgeSourceId(BridgeProtocol.Oasis, 'Eth2Dai');
case ERC20BridgeSource.Shell:
return BridgeSource.Shell;
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Shell');
case ERC20BridgeSource.SnowSwap:
return BridgeSource.Snowswap;
return encodeBridgeSourceId(BridgeProtocol.Curve, 'SnowSwap');
case ERC20BridgeSource.SushiSwap:
return BridgeSource.Sushiswap;
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SushiSwap');
case ERC20BridgeSource.Swerve:
return BridgeSource.Swerve;
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Swerve');
case ERC20BridgeSource.Uniswap:
return BridgeSource.Uniswap;
return encodeBridgeSourceId(BridgeProtocol.Uniswap, 'Uniswap');
case ERC20BridgeSource.UniswapV2:
return BridgeSource.UniswapV2;
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UniswapV2');
case ERC20BridgeSource.DodoV2:
return BridgeSource.DodoV2;
return encodeBridgeSourceId(BridgeProtocol.DodoV2, 'DodoV2');
case ERC20BridgeSource.Linkswap:
return BridgeSource.Linkswap;
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Linkswap');
case ERC20BridgeSource.PancakeSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwap');
case ERC20BridgeSource.BakerySwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BakerySwap');
case ERC20BridgeSource.Nerve:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Nerve');
case ERC20BridgeSource.Belt:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Belt');
case ERC20BridgeSource.Ellipsis:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Ellipsis');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@ -146,9 +147,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.Curve:
case ERC20BridgeSource.Swerve:
case ERC20BridgeSource.SnowSwap:
const curveFillData = (order as OptimizedMarketBridgeOrder<
CurveFillData | SwerveFillData | SnowSwapFillData
>).fillData;
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
const curveFillData = (order as OptimizedMarketBridgeOrder<CurveFillData>).fillData;
bridgeData = encoder.encode([
curveFillData.pool.poolAddress,
curveFillData.pool.exchangeFunctionSelector,
@ -169,13 +171,14 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.SushiSwap:
case ERC20BridgeSource.CryptoCom:
case ERC20BridgeSource.Linkswap:
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData | SushiSwapFillData>)
.fillData;
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.BakerySwap:
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
break;
case ERC20BridgeSource.Kyber:
const kyberFillData = (order as OptimizedMarketBridgeOrder<KyberFillData>).fillData;
bridgeData = encoder.encode([MAINNET_KYBER_NETWORK_PROXY, kyberFillData.hint]);
bridgeData = encoder.encode([kyberFillData.networkProxy, kyberFillData.hint]);
break;
case ERC20BridgeSource.Mooniswap:
const mooniswapFillData = (order as OptimizedMarketBridgeOrder<MooniswapFillData>).fillData;
@ -183,7 +186,11 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
break;
case ERC20BridgeSource.Dodo:
const dodoFillData = (order as OptimizedMarketBridgeOrder<DODOFillData>).fillData;
bridgeData = encoder.encode([MAINNET_DODO_HELPER, dodoFillData.poolAddress, dodoFillData.isSellBase]);
bridgeData = encoder.encode([
dodoFillData.helperAddress,
dodoFillData.poolAddress,
dodoFillData.isSellBase,
]);
break;
case ERC20BridgeSource.DodoV2:
const dodoV2FillData = (order as OptimizedMarketBridgeOrder<DODOFillData>).fillData;
@ -198,13 +205,16 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
bridgeData = encoder.encode([lpFillData.poolAddress, tokenAddressEncoder.encode([order.takerToken])]);
break;
case ERC20BridgeSource.Uniswap:
bridgeData = encoder.encode([MAINNET_UNISWAP_V1_ROUTER]);
const uniFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
bridgeData = encoder.encode([uniFillData.router]);
break;
case ERC20BridgeSource.Eth2Dai:
bridgeData = encoder.encode([MAINNET_OASIS_ROUTER]);
const oasisFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
bridgeData = encoder.encode([oasisFillData.router]);
break;
case ERC20BridgeSource.MStable:
bridgeData = encoder.encode([MAINNET_MSTABLE_ROUTER]);
const mStableFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
bridgeData = encoder.encode([mStableFillData.router]);
break;
default:
throw new Error(AggregationError.NoBridgeForSource);
@ -275,6 +285,9 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Curve]: curveEncoder,
[ERC20BridgeSource.Swerve]: curveEncoder,
[ERC20BridgeSource.SnowSwap]: curveEncoder,
[ERC20BridgeSource.Nerve]: curveEncoder,
[ERC20BridgeSource.Belt]: curveEncoder,
[ERC20BridgeSource.Ellipsis]: curveEncoder,
// UniswapV2 like, (router, address[])
[ERC20BridgeSource.Bancor]: routerAddressPathEncoder,
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
@ -289,6 +302,9 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Balancer]: poolEncoder,
[ERC20BridgeSource.Cream]: poolEncoder,
[ERC20BridgeSource.Uniswap]: poolEncoder,
// BSC
[ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.BakerySwap]: routerAddressPathEncoder,
};
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@ -1,3 +1,4 @@
import { ChainId } from '@0x/contract-addresses';
import { BigNumber, NULL_BYTES } from '@0x/utils';
import { SamplerOverrides } from '../../types';
@ -33,6 +34,7 @@ type BatchedOperationResult<T> = T extends BatchedOperation<infer TResult> ? TRe
*/
export class DexOrderSampler extends SamplerOperations {
constructor(
public readonly chainId: ChainId,
_samplerContract: ERC20BridgeSamplerContract,
private readonly _samplerOverrides?: SamplerOverrides,
balancerPoolsCache?: BalancerPoolsCache,
@ -42,6 +44,7 @@ export class DexOrderSampler extends SamplerOperations {
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
) {
super(
chainId,
_samplerContract,
balancerPoolsCache,
creamPoolsCache,

View File

@ -1,8 +0,0 @@
import { MAINNET_SHELL_POOLS } from './constants';
// tslint:disable completed-docs
export function getShellsForPair(takerToken: string, makerToken: string): string[] {
return Object.values(MAINNET_SHELL_POOLS)
.filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t)))
.map(i => i.poolAddress);
}

View File

@ -58,6 +58,12 @@ export enum ERC20BridgeSource {
DodoV2 = 'DODO_V2',
CryptoCom = 'CryptoCom',
Linkswap = 'Linkswap',
// Other
PancakeSwap = 'PancakeSwap',
BakerySwap = 'BakerySwap',
Nerve = 'Nerve',
Belt = 'Belt',
Ellipsis = 'Ellipsis',
}
// tslint:disable: enum-naming
@ -72,6 +78,9 @@ export enum CurveFunctionSelectors {
get_dx_underlying = '0x0e71d1b9',
get_dy = '0x5e0d443f',
get_dx = '0x67df02ca',
// Nerve BSC
swap = '0x91695586',
calculateSwap = '0xa95b089f',
}
// tslint:enable: enum-naming
@ -87,9 +96,6 @@ export interface CurveInfo {
metaToken: string | undefined;
}
export interface SwerveInfo extends CurveInfo {}
export interface SnowSwapInfo extends CurveInfo {}
// Internal `fillData` field for `Fill` objects.
export interface FillData {}
@ -111,18 +117,6 @@ export interface CurveFillData extends FillData {
pool: CurveInfo;
}
export interface SwerveFillData extends FillData {
fromTokenIdx: number;
toTokenIdx: number;
pool: SwerveInfo;
}
export interface SnowSwapFillData extends FillData {
fromTokenIdx: number;
toTokenIdx: number;
pool: SnowSwapInfo;
}
export interface BalancerFillData extends FillData {
poolAddress: string;
}
@ -132,8 +126,6 @@ export interface UniswapV2FillData extends FillData {
router: string;
}
export interface SushiSwapFillData extends UniswapV2FillData {}
export interface ShellFillData extends FillData {
poolAddress: string;
}
@ -151,6 +143,7 @@ export interface BancorFillData extends FillData {
export interface KyberFillData extends FillData {
hint: string;
reserveId: string;
networkProxy: string;
}
export interface MooniswapFillData extends FillData {
@ -160,7 +153,13 @@ export interface MooniswapFillData extends FillData {
export interface DODOFillData extends FillData {
poolAddress: string;
isSellBase: boolean;
helperAddress: string;
}
export interface GenericRouterFillData extends FillData {
router: string;
}
export interface MultiHopFillData extends FillData {
firstHopSource: SourceQuoteOperation;
secondHopSource: SourceQuoteOperation;
@ -440,3 +439,9 @@ export interface GenerateOptimizedOrdersOpts {
export interface ComparisonPrice {
wholeOrder: BigNumber | undefined;
}
export interface KyberSamplerOpts {
networkProxy: string;
hintHandler: string;
weth: string;
}

View File

@ -10,7 +10,6 @@ import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json';
import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json';
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json';
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json';
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
@ -36,7 +35,6 @@ import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSamp
import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json';
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
import * as SushiSwapSampler from '../test/generated-artifacts/SushiSwapSampler.json';
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
@ -51,7 +49,6 @@ export const artifacts = {
CurveSampler: CurveSampler as ContractArtifact,
DODOSampler: DODOSampler as ContractArtifact,
DODOV2Sampler: DODOV2Sampler as ContractArtifact,
DeploymentConstants: DeploymentConstants as ContractArtifact,
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
Eth2DaiSampler: Eth2DaiSampler as ContractArtifact,
FakeTaker: FakeTaker as ContractArtifact,
@ -63,7 +60,6 @@ export const artifacts = {
NativeOrderSampler: NativeOrderSampler as ContractArtifact,
SamplerUtils: SamplerUtils as ContractArtifact,
ShellSampler: ShellSampler as ContractArtifact,
SushiSwapSampler: SushiSwapSampler as ContractArtifact,
TwoHopSampler: TwoHopSampler as ContractArtifact,
UniswapSampler: UniswapSampler as ContractArtifact,
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,

View File

@ -1,7 +1,9 @@
import { ChainId } from '@0x/contract-addresses';
import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine } from '@0x/contracts-test-utils';
import { RPCSubprovider } from '@0x/subproviders';
import { BigNumber, providerUtils } from '@0x/utils';
import { BigNumber, NULL_BYTES, providerUtils } from '@0x/utils';
import { KYBER_CONFIG_BY_CHAIN_ID, TOKENS } from '../../src/utils/market_operation_utils/constants';
import { artifacts } from '../artifacts';
import { ERC20BridgeSamplerContract } from '../wrappers';
@ -78,28 +80,33 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
});
});
describe('Kyber', () => {
const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f';
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
const WETH = TOKENS.WETH;
const DAI = TOKENS.DAI;
const USDC = TOKENS.USDC;
const RESERVE_OFFSET = new BigNumber(0);
const KYBER_OPTS = {
...KYBER_CONFIG_BY_CHAIN_ID[ChainId.Mainnet],
reserveOffset: RESERVE_OFFSET,
hint: NULL_BYTES,
};
describe('sampleSellsFromKyberNetwork()', () => {
it('samples sells from Kyber DAI->WETH', async () => {
const [, samples] = await testContract
.sampleSellsFromKyberNetwork(RESERVE_OFFSET, DAI, WETH, [toBaseUnitAmount(1)])
.sampleSellsFromKyberNetwork(KYBER_OPTS, DAI, WETH, [toBaseUnitAmount(1)])
.callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0);
});
it('samples sells from Kyber WETH->DAI', async () => {
const [, samples] = await testContract
.sampleSellsFromKyberNetwork(RESERVE_OFFSET, WETH, DAI, [toBaseUnitAmount(1)])
.sampleSellsFromKyberNetwork(KYBER_OPTS, WETH, DAI, [toBaseUnitAmount(1)])
.callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0);
});
it('samples sells from Kyber DAI->USDC', async () => {
const [, samples] = await testContract
.sampleSellsFromKyberNetwork(RESERVE_OFFSET, DAI, USDC, [toBaseUnitAmount(1)])
.sampleSellsFromKyberNetwork(KYBER_OPTS, DAI, USDC, [toBaseUnitAmount(1)])
.callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0);
@ -111,7 +118,7 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
// From ETH to DAI
// I want to buy 1 DAI
const [, samples] = await testContract
.sampleBuysFromKyberNetwork(RESERVE_OFFSET, WETH, DAI, [toBaseUnitAmount(1)])
.sampleBuysFromKyberNetwork(KYBER_OPTS, WETH, DAI, [toBaseUnitAmount(1)])
.callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0);
@ -121,7 +128,7 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
// From USDC to DAI
// I want to buy 1 WETH
const [, samples] = await testContract
.sampleBuysFromKyberNetwork(RESERVE_OFFSET, DAI, WETH, [toBaseUnitAmount(1)])
.sampleBuysFromKyberNetwork(KYBER_OPTS, DAI, WETH, [toBaseUnitAmount(1)])
.callAsync({ overrides });
expect(samples.length).to.be.bignumber.greaterThan(0);
expect(samples[0]).to.be.bignumber.greaterThan(0);

View File

@ -31,12 +31,15 @@ blockchainTests('erc20-bridge-sampler', env => {
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
const UNISWAP_V2_SALT = '0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1';
let UNISWAP_V2_ROUTER = '';
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
const INTERMEDIATE_TOKEN = randomAddress();
const KYBER_RESERVE_OFFSET = new BigNumber(0);
let KYBER_ADDRESS = '';
let ETH2DAI_ADDRESS = '';
let UNISWAP_ADDRESS = '';
let UNISWAP_V2_ROUTER = '';
before(async () => {
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
@ -46,6 +49,9 @@ blockchainTests('erc20-bridge-sampler', env => {
{},
);
UNISWAP_V2_ROUTER = await testContract.uniswapV2Router().callAsync();
KYBER_ADDRESS = await testContract.kyber().callAsync();
ETH2DAI_ADDRESS = await testContract.eth2Dai().callAsync();
UNISWAP_ADDRESS = await testContract.uniswap().callAsync();
});
function getPackedHash(...args: string[]): string {
@ -331,20 +337,30 @@ blockchainTests('erc20-bridge-sampler', env => {
});
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
let kyberOpts = {
hintHandler: NULL_ADDRESS,
networkProxy: NULL_ADDRESS,
weth: WETH_ADDRESS,
reserveOffset: KYBER_RESERVE_OFFSET,
hint: NULL_BYTES,
};
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
kyberOpts = {
...kyberOpts,
hintHandler: KYBER_ADDRESS,
networkProxy: KYBER_ADDRESS,
};
});
it('throws if tokens are the same', async () => {
const tx = testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, MAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
const tx = testContract.sampleSellsFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const [, , quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, [])
.sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@ -354,7 +370,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const [, , quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -363,7 +379,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
const [, , quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -372,7 +388,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
const [, , quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -382,7 +398,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const [, , quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -391,7 +407,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
const [, , quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleSellsFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -401,28 +417,38 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const [, , quotes] = await testContract
.sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleSellsFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
blockchainTests.resets('sampleBuysFromKyberNetwork()', () => {
let kyberOpts = {
hintHandler: NULL_ADDRESS,
networkProxy: NULL_ADDRESS,
weth: WETH_ADDRESS,
reserveOffset: KYBER_RESERVE_OFFSET,
hint: NULL_BYTES,
};
const ACCEPTABLE_SLIPPAGE = 0.0005;
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
kyberOpts = {
...kyberOpts,
hintHandler: KYBER_ADDRESS,
networkProxy: KYBER_ADDRESS,
};
});
it('throws if tokens are the same', async () => {
const tx = testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, MAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
const tx = testContract.sampleBuysFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const [, , quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, [])
.sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@ -431,7 +457,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
const [, , quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
});
@ -441,7 +467,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const [, , quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -450,7 +476,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
const [, , quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
});
@ -460,7 +486,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const [, , quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -469,7 +495,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
const [, , quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
});
@ -479,7 +505,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const [, , quotes] = await testContract
.sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -491,12 +517,14 @@ blockchainTests('erc20-bridge-sampler', env => {
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleSellsFromEth2Dai(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const tx = testContract.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@ -504,7 +532,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -514,7 +542,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -523,7 +551,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -533,7 +561,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -542,7 +570,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -552,7 +580,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -564,12 +592,14 @@ blockchainTests('erc20-bridge-sampler', env => {
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleBuysFromEth2Dai(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const tx = testContract.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@ -577,7 +607,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -587,7 +617,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -596,7 +626,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -606,7 +636,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -615,7 +645,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -625,24 +655,27 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
blockchainTests.resets('sampleSellsFromUniswap()', () => {
const UNISWAP_ETH_ADDRESS = NULL_ADDRESS;
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleSellsFromUniswap(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const tx = testContract.sampleSellsFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const quotes = await testContract
.sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@ -650,7 +683,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -660,7 +693,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -669,7 +702,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -679,7 +712,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -688,7 +721,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -698,7 +731,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -708,7 +741,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -718,24 +751,27 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(nonExistantToken);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
.sampleSellsFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
blockchainTests.resets('sampleBuysFromUniswap()', () => {
const UNISWAP_ETH_ADDRESS = NULL_ADDRESS;
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleBuysFromUniswap(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const tx = testContract.sampleBuysFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
const quotes = await testContract
.sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@ -743,7 +779,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -753,7 +789,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -762,7 +798,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -772,7 +808,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -781,7 +817,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -791,7 +827,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -801,7 +837,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(nonExistantToken);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -811,7 +847,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
.sampleBuysFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@ -1060,10 +1096,10 @@ blockchainTests('erc20-bridge-sampler', env => {
.getABIEncodedTransactionData();
const eth2DaiFirstHop = testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT])
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const eth2DaiSecondHop = testContract
.sampleSellsFromEth2Dai(INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT])
.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const firstHopQuotes = [
@ -1111,10 +1147,10 @@ blockchainTests('erc20-bridge-sampler', env => {
.getABIEncodedTransactionData();
const eth2DaiFirstHop = testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT])
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const eth2DaiSecondHop = testContract
.sampleBuysFromEth2Dai(INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT])
.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const secondHopQuotes = [

View File

@ -1,4 +1,4 @@
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import {
constants,
expect,
@ -26,6 +26,7 @@ const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000
describe('DexSampler tests', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
const chainId = ChainId.Mainnet;
const wethAddress = getContractAddressesForChainOrThrow(CHAIN_ID).etherToken;
const exchangeProxyAddress = getContractAddressesForChainOrThrow(CHAIN_ID).exchangeProxy;
@ -105,6 +106,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -129,6 +131,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -157,6 +160,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -167,6 +171,7 @@ describe('DexSampler tests', () => {
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getKyberSellQuotes(
{ hintHandler: randomAddress(), networkProxy: randomAddress(), weth: randomAddress() },
new BigNumber(0),
expectedMakerToken,
expectedTakerToken,
@ -190,6 +195,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -234,6 +240,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -270,7 +277,7 @@ describe('DexSampler tests', () => {
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const sampler = new MockSamplerContract({
sampleSellsFromEth2Dai: (takerToken, makerToken, fillAmounts) => {
sampleSellsFromEth2Dai: (_router, takerToken, makerToken, fillAmounts) => {
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
@ -278,6 +285,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -287,7 +295,12 @@ describe('DexSampler tests', () => {
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getEth2DaiSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
dexOrderSampler.getEth2DaiSellQuotes(
randomAddress(),
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
),
);
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
});
@ -298,7 +311,7 @@ describe('DexSampler tests', () => {
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const sampler = new MockSamplerContract({
sampleSellsFromUniswap: (takerToken, makerToken, fillAmounts) => {
sampleSellsFromUniswap: (_router, takerToken, makerToken, fillAmounts) => {
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
@ -306,6 +319,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -315,7 +329,12 @@ describe('DexSampler tests', () => {
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getUniswapSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
dexOrderSampler.getUniswapSellQuotes(
randomAddress(),
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
),
);
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
});
@ -333,6 +352,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -357,7 +377,7 @@ describe('DexSampler tests', () => {
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const sampler = new MockSamplerContract({
sampleBuysFromEth2Dai: (takerToken, makerToken, fillAmounts) => {
sampleBuysFromEth2Dai: (_router, takerToken, makerToken, fillAmounts) => {
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
@ -365,6 +385,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -374,7 +395,12 @@ describe('DexSampler tests', () => {
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getEth2DaiBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
dexOrderSampler.getEth2DaiBuyQuotes(
randomAddress(),
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
),
);
expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts);
});
@ -385,7 +411,7 @@ describe('DexSampler tests', () => {
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10);
const sampler = new MockSamplerContract({
sampleBuysFromUniswap: (takerToken, makerToken, fillAmounts) => {
sampleBuysFromUniswap: (_router, takerToken, makerToken, fillAmounts) => {
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
@ -393,6 +419,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -402,7 +429,12 @@ describe('DexSampler tests', () => {
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getUniswapBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
dexOrderSampler.getUniswapBuyQuotes(
randomAddress(),
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
),
);
expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts);
});
@ -423,21 +455,25 @@ describe('DexSampler tests', () => {
};
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
let uniswapRouter: string;
let uniswapV2Router: string;
let eth2DaiRouter: string;
const sampler = new MockSamplerContract({
sampleSellsFromUniswap: (takerToken, makerToken, fillAmounts) => {
sampleSellsFromUniswap: (router, takerToken, makerToken, fillAmounts) => {
uniswapRouter = router;
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue());
},
sampleSellsFromEth2Dai: (takerToken, makerToken, fillAmounts) => {
sampleSellsFromEth2Dai: (router, takerToken, makerToken, fillAmounts) => {
eth2DaiRouter = router;
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue());
},
sampleSellsFromUniswapV2: (router, path, fillAmounts) => {
uniswapRouter = router;
uniswapV2Router = router;
if (path.length === 2) {
expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]);
} else if (path.length === 3) {
@ -450,6 +486,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -471,10 +508,22 @@ describe('DexSampler tests', () => {
source: s,
input: a,
output: a.times(ratesBySource[s]).integerValue(),
fillData:
s === ERC20BridgeSource.UniswapV2
? { router: uniswapRouter, tokenAddressPath: [expectedTakerToken, expectedMakerToken] }
: {},
fillData: (() => {
if (s === ERC20BridgeSource.UniswapV2) {
return {
router: uniswapV2Router,
tokenAddressPath: [expectedTakerToken, expectedMakerToken],
};
}
if (s === ERC20BridgeSource.Eth2Dai) {
return { router: eth2DaiRouter };
}
// TODO jacob pass through
if (s === ERC20BridgeSource.Uniswap) {
return { router: uniswapRouter };
}
return {};
})(),
})),
);
const uniswapV2ETHQuotes = [
@ -483,7 +532,7 @@ describe('DexSampler tests', () => {
input: a,
output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(),
fillData: {
router: uniswapRouter,
router: uniswapV2Router,
tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken],
},
})),
@ -509,6 +558,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
new MockSamplerContract({}),
undefined,
balancerPoolsCache,
@ -535,21 +585,25 @@ describe('DexSampler tests', () => {
};
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
let uniswapRouter: string;
let uniswapV2Router: string;
let eth2DaiRouter: string;
const sampler = new MockSamplerContract({
sampleBuysFromUniswap: (takerToken, makerToken, fillAmounts) => {
sampleBuysFromUniswap: (router, takerToken, makerToken, fillAmounts) => {
uniswapRouter = router;
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue());
},
sampleBuysFromEth2Dai: (takerToken, makerToken, fillAmounts) => {
sampleBuysFromEth2Dai: (router, takerToken, makerToken, fillAmounts) => {
eth2DaiRouter = router;
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue());
},
sampleBuysFromUniswapV2: (router, path, fillAmounts) => {
uniswapRouter = router;
uniswapV2Router = router;
if (path.length === 2) {
expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]);
} else if (path.length === 3) {
@ -562,6 +616,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
@ -578,10 +633,21 @@ describe('DexSampler tests', () => {
source: s,
input: a,
output: a.times(ratesBySource[s]).integerValue(),
fillData:
s === ERC20BridgeSource.UniswapV2
? { router: uniswapRouter, tokenAddressPath: [expectedTakerToken, expectedMakerToken] }
: {},
fillData: (() => {
if (s === ERC20BridgeSource.UniswapV2) {
return {
router: uniswapV2Router,
tokenAddressPath: [expectedTakerToken, expectedMakerToken],
};
}
if (s === ERC20BridgeSource.Eth2Dai) {
return { router: eth2DaiRouter };
}
if (s === ERC20BridgeSource.Uniswap) {
return { router: uniswapRouter };
}
return {};
})(),
})),
);
const uniswapV2ETHQuotes = [
@ -590,7 +656,7 @@ describe('DexSampler tests', () => {
input: a,
output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(),
fillData: {
router: uniswapRouter,
router: uniswapV2Router,
tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken],
},
})),
@ -612,6 +678,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
new MockSamplerContract({}),
undefined,
balancerPoolsCache,
@ -646,6 +713,7 @@ describe('DexSampler tests', () => {
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,

View File

@ -19,9 +19,9 @@ import { NativeOrderWithFillableAmounts } from '../src/types';
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
import { BalancerPoolsCache } from '../src/utils/market_operation_utils/balancer_utils';
import {
BUY_SOURCE_FILTER,
BUY_SOURCE_FILTER_BY_CHAIN_ID,
POSITIVE_INF,
SELL_SOURCE_FILTER,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
SOURCE_FLAGS,
} from '../src/utils/market_operation_utils/constants';
import { CreamPoolsCache } from '../src/utils/market_operation_utils/cream_utils';
@ -65,9 +65,11 @@ const DEFAULT_EXCLUDED = [
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.BakerySwap,
];
const BUY_SOURCES = BUY_SOURCE_FILTER.sources;
const SELL_SOURCES = SELL_SOURCE_FILTER.sources;
const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = { default: [] };
const SIGNATURE = { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign };
@ -301,6 +303,8 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.DodoV2]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.CryptoCom]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Linkswap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.PancakeSwap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.BakerySwap]: _.times(NUM_SAMPLES, () => 0),
};
const DEFAULT_RATES: RatesBySource = {
@ -318,7 +322,7 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.UniswapV2]: { tokenAddressPath: [] },
[ERC20BridgeSource.Balancer]: { poolAddress: randomAddress() },
[ERC20BridgeSource.Bancor]: { path: [], networkAddress: randomAddress() },
[ERC20BridgeSource.Kyber]: { hint: '0x', reserveId: '0x' },
[ERC20BridgeSource.Kyber]: { hint: '0x', reserveId: '0x', networkAddress: randomAddress() },
[ERC20BridgeSource.Curve]: {
pool: {
poolAddress: randomAddress(),
@ -363,6 +367,8 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.DodoV2]: {},
[ERC20BridgeSource.CryptoCom]: { tokenAddressPath: [] },
[ERC20BridgeSource.Linkswap]: { tokenAddressPath: [] },
[ERC20BridgeSource.Uniswap]: { router: randomAddress() },
[ERC20BridgeSource.Eth2Dai]: { router: randomAddress() },
};
const DEFAULT_OPS = {
@ -441,6 +447,7 @@ describe('MarketOperationUtils tests', () => {
balancerPoolsCache: new BalancerPoolsCache(),
creamPoolsCache: new CreamPoolsCache(),
liquidityProviderRegistry: {},
chainId: CHAIN_ID,
} as any) as DexOrderSampler;
function replaceSamplerOps(ops: Partial<typeof DEFAULT_OPS> = {}): void {

View File

@ -1,9 +1,10 @@
import { ContractTxFunctionObj } from '@0x/base-contract';
import { constants } from '@0x/contracts-test-utils';
import { LimitOrderFields, Signature } from '@0x/protocol-utils';
import { BigNumber, hexUtils } from '@0x/utils';
import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils';
import { SamplerCallResult } from '../../src/types';
import { KyberSamplerOpts } from '../../src/utils/market_operation_utils/types';
import { ERC20BridgeSamplerContract } from '../../src/wrappers';
export type GetOrderFillableAssetAmountResult = BigNumber[];
@ -14,18 +15,32 @@ export type GetOrderFillableAssetAmountHandler = (
) => GetOrderFillableAssetAmountResult;
export type SampleResults = BigNumber[];
export type SampleSellsHandler = (
export type SampleSellsUniswapHandler = (
router: string,
takerToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
) => SampleResults;
export type SampleBuysHandler = (
export type SampleBuysUniswapHandler = (
router: string,
takerToken: string,
makerToken: string,
makerTokenAmounts: BigNumber[],
) => SampleResults;
export type SampleSellsEth2DaiHandler = (
router: string,
takerToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
) => SampleResults;
export type SampleBuysEth2DaiHandler = (
router: string,
takerToken: string,
makerToken: string,
makerTokenAmounts: BigNumber[],
) => SampleResults;
export type SampleSellsKyberHandler = (
reserveOffset: BigNumber,
opts: KyberSamplerOpts,
takerToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
@ -57,11 +72,11 @@ interface Handlers {
getLimitOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler;
sampleSellsFromKyberNetwork: SampleSellsKyberHandler;
sampleSellsFromLiquidityProvider: SampleSellsLPHandler;
sampleSellsFromEth2Dai: SampleSellsHandler;
sampleSellsFromUniswap: SampleSellsHandler;
sampleSellsFromEth2Dai: SampleSellsEth2DaiHandler;
sampleSellsFromUniswap: SampleSellsUniswapHandler;
sampleSellsFromUniswapV2: SampleUniswapV2Handler;
sampleBuysFromEth2Dai: SampleBuysHandler;
sampleBuysFromUniswap: SampleBuysHandler;
sampleBuysFromEth2Dai: SampleBuysEth2DaiHandler;
sampleBuysFromUniswap: SampleBuysUniswapHandler;
sampleBuysFromUniswapV2: SampleUniswapV2Handler;
sampleBuysFromLiquidityProvider: SampleSellsLPHandler;
}
@ -111,7 +126,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
}
public sampleSellsFromKyberNetwork(
reserveOffset: BigNumber,
opts: KyberSamplerOpts,
takerToken: string,
makerToken: string,
takerAssetAmounts: BigNumber[],
@ -119,7 +134,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
return this._wrapCall(
super.sampleSellsFromKyberNetwork,
this._handlers.sampleSellsFromKyberNetwork,
reserveOffset,
{ ...opts, reserveOffset: new BigNumber(1), hint: NULL_BYTES },
takerToken,
makerToken,
takerAssetAmounts,
@ -127,6 +142,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
}
public sampleSellsFromEth2Dai(
router: string,
takerToken: string,
makerToken: string,
takerAssetAmounts: BigNumber[],
@ -134,6 +150,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
return this._wrapCall(
super.sampleSellsFromEth2Dai,
this._handlers.sampleSellsFromEth2Dai,
router,
takerToken,
makerToken,
takerAssetAmounts,
@ -141,6 +158,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
}
public sampleSellsFromUniswap(
router: string,
takerToken: string,
makerToken: string,
takerAssetAmounts: BigNumber[],
@ -148,6 +166,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
return this._wrapCall(
super.sampleSellsFromUniswap,
this._handlers.sampleSellsFromUniswap,
router,
takerToken,
makerToken,
takerAssetAmounts,
@ -185,6 +204,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
}
public sampleBuysFromEth2Dai(
router: string,
takerToken: string,
makerToken: string,
makerAssetAmounts: BigNumber[],
@ -192,6 +212,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
return this._wrapCall(
super.sampleBuysFromEth2Dai,
this._handlers.sampleBuysFromEth2Dai,
router,
takerToken,
makerToken,
makerAssetAmounts,
@ -199,6 +220,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
}
public sampleBuysFromUniswap(
router: string,
takerToken: string,
makerToken: string,
makerAssetAmounts: BigNumber[],
@ -206,6 +228,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
return this._wrapCall(
super.sampleBuysFromUniswap,
this._handlers.sampleBuysFromUniswap,
router,
takerToken,
makerToken,
makerAssetAmounts,

View File

@ -43,7 +43,7 @@ export const testHelpers = {
afterResponseCallback: () => Promise<void>,
axiosClient: AxiosInstance = axios,
): Promise<void> => {
const mockedAxios = new AxiosMockAdapter(axiosClient, { onNoMatch: 'throwException' });
const mockedAxios = new AxiosMockAdapter(axiosClient, { onNoMatch: 'throwException' } as any);
try {
// Mock out Standard RFQ-T/M responses
for (const mockedResponse of standardMockedResponses) {

View File

@ -10,7 +10,6 @@ export * from '../test/generated-wrappers/bancor_sampler';
export * from '../test/generated-wrappers/curve_sampler';
export * from '../test/generated-wrappers/d_o_d_o_sampler';
export * from '../test/generated-wrappers/d_o_d_o_v2_sampler';
export * from '../test/generated-wrappers/deployment_constants';
export * from '../test/generated-wrappers/dummy_liquidity_provider';
export * from '../test/generated-wrappers/erc20_bridge_sampler';
export * from '../test/generated-wrappers/eth2_dai_sampler';
@ -34,7 +33,6 @@ export * from '../test/generated-wrappers/multi_bridge_sampler';
export * from '../test/generated-wrappers/native_order_sampler';
export * from '../test/generated-wrappers/sampler_utils';
export * from '../test/generated-wrappers/shell_sampler';
export * from '../test/generated-wrappers/sushi_swap_sampler';
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
export * from '../test/generated-wrappers/test_native_order_sampler';
export * from '../test/generated-wrappers/two_hop_sampler';

View File

@ -13,7 +13,6 @@
"test/generated-artifacts/CurveSampler.json",
"test/generated-artifacts/DODOSampler.json",
"test/generated-artifacts/DODOV2Sampler.json",
"test/generated-artifacts/DeploymentConstants.json",
"test/generated-artifacts/DummyLiquidityProvider.json",
"test/generated-artifacts/ERC20BridgeSampler.json",
"test/generated-artifacts/Eth2DaiSampler.json",
@ -37,7 +36,6 @@
"test/generated-artifacts/NativeOrderSampler.json",
"test/generated-artifacts/SamplerUtils.json",
"test/generated-artifacts/ShellSampler.json",
"test/generated-artifacts/SushiSwapSampler.json",
"test/generated-artifacts/TestERC20BridgeSampler.json",
"test/generated-artifacts/TestNativeOrderSampler.json",
"test/generated-artifacts/TwoHopSampler.json",

View File

@ -1,4 +1,21 @@
[
{
"version": "6.0.0-bsc.0",
"changes": [
{
"note": "Add BSC chain addresses",
"pr": 164
},
{
"note": "Remove exchangeProxyAllowanceTarget",
"pr": 164
},
{
"note": "Redeployed FQT on BSC",
"pr": 181
}
]
},
{
"version": "5.11.0",
"changes": [

View File

@ -29,7 +29,6 @@
"dexForwarderBridge": "0xc47b7094f378e54347e281aab170e8cca69d880a",
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x39dce47a67ad34344eab877eae3ef1fa2a1d50bb",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
@ -71,7 +70,6 @@
"dexForwarderBridge": "0x3261ea1411a1a840aed708896f779e1b837c917e",
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0x53a3a41047ae6f6a593df847e3bb287ecd3ac825",
@ -113,7 +111,6 @@
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0x2e2090562076197f94f8d1beac0963b6d4c118b6",
@ -155,7 +152,6 @@
"dexForwarderBridge": "0x985d1a95c6a86a3bf85c4d425af984abceaf01de",
"exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x1b62de2dbb5e7aa519e9c442721ecef75702807f",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0x4022e3982f326455f0905de3dbc4449999baf2dc",
@ -167,6 +163,47 @@
"positiveSlippageFeeTransformer": "0x0000000000000000000000000000000000000000"
}
},
"56": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"exchangeV2": "0x0000000000000000000000000000000000000000",
"exchange": "0x0000000000000000000000000000000000000000",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0x0000000000000000000000000000000000000000",
"coordinatorRegistry": "0x0000000000000000000000000000000000000000",
"coordinator": "0x0000000000000000000000000000000000000000",
"multiAssetProxy": "0x0000000000000000000000000000000000000000",
"staticCallProxy": "0x0000000000000000000000000000000000000000",
"erc1155Proxy": "0x0000000000000000000000000000000000000000",
"devUtils": "0x0000000000000000000000000000000000000000",
"zrxVault": "0x0000000000000000000000000000000000000000",
"staking": "0x0000000000000000000000000000000000000000",
"stakingProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0xccc9769c1a58766e79423a34b2cc5052d65c1983",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyTransformerDeployer": "0x8224aa8fe5c9f07d5a59c735386ff6cc6aaeb568",
"exchangeProxyFlashWallet": "0xdb6f1920a889355780af7570773609bd8cb1f498",
"exchangeProxyLiquidityProviderSandbox": "0xde7b2747624a647600fdb349184d0448ab954929",
"transformers": {
"wethTransformer": "0xac3d95668c092e895cd83a9cbafe9c7d9906471f",
"payTakerTransformer": "0x4f5e8ca2cadecd4a467ae441e4b03de4278a4574",
"affiliateFeeTransformer": "0x1be34ab9b2acb5c4ddd89454bdce637967e65230",
"fillQuoteTransformer": "0xfa8ca57cb24cd59e74ae1659a00104188e7e8a3e",
"positiveSlippageFeeTransformer": "0x7f5c79ad1788573b1145f4651a248523c54f5d1f"
}
},
"1337": {
"erc20Proxy": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
"erc721Proxy": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401",
@ -197,16 +234,15 @@
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0x0000000000000000000000000000000000000000",
"exchangeProxy": "0x5315e44798395d4a952530d131249fe00f554565",
"exchangeProxyAllowanceTarget": "0x8362c3ebd90041b30ec45908332e592721642637",
"exchangeProxyTransformerDeployer": "0x5409ed021d9299bf6814279a6a1411a7e866a631",
"exchangeProxyFlashWallet": "0xb9682a8e7920b431f1d412b8510f0077410c8faa",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3",
"payTakerTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a",
"affiliateFeeTransformer": "0x99356167edba8fbdc36959e3f5d0c43d1ba9c6db",
"fillQuoteTransformer": "0x45b3a72221e571017c0f0ec42189e11d149d0ace",
"positiveSlippageFeeTransformer": "0xdd66c23e07b4d6925b6089b5fe6fc9e62941afe8"
"wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5",
"payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3",
"affiliateFeeTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a",
"fillQuoteTransformer": "0x99356167edba8fbdc36959e3f5d0c43d1ba9c6db",
"positiveSlippageFeeTransformer": "0x45b3a72221e571017c0f0ec42189e11d149d0ace"
}
}
}

View File

@ -30,7 +30,6 @@ export interface ContractAddresses {
dexForwarderBridge: string;
exchangeProxyGovernor: string;
exchangeProxy: string;
exchangeProxyAllowanceTarget: string;
exchangeProxyTransformerDeployer: string;
exchangeProxyFlashWallet: string;
exchangeProxyLiquidityProviderSandbox: string;
@ -49,6 +48,7 @@ export enum ChainId {
Rinkeby = 4,
Kovan = 42,
Ganache = 1337,
BSC = 56,
}
/**

View File

@ -1,4 +1,13 @@
[
{
"version": "3.14.0",
"changes": [
{
"note": "Update artifacts",
"pr": 164
}
]
},
{
"version": "3.13.0",
"changes": [

View File

@ -284,18 +284,6 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06", "name": "token", "type": "address" },
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "_spendERC20Tokens",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
@ -973,13 +961,6 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getAllowanceTarget",
"outputs": [{ "internalType": "address", "name": "target", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
@ -1302,16 +1283,6 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06", "name": "token", "type": "address" },
{ "internalType": "address", "name": "owner", "type": "address" }
],
"name": "getSpendableERC20BalanceOf",
"outputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getTransformWallet",
@ -1406,6 +1377,18 @@
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06[]", "name": "tokens", "type": "address[]" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" },
{ "internalType": "enum IPancakeSwapFeature.ProtocolFork", "name": "fork", "type": "uint8" }
],
"name": "sellToPancakeSwap",
"outputs": [{ "internalType": "uint256", "name": "buyAmount", "type": "uint256" }],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06[]", "name": "tokens", "type": "address[]" },
@ -1499,15 +1482,6 @@
"takerTokenFilledAmount": "How much maker token was filled."
}
},
"_spendERC20Tokens(address,address,address,uint256)": {
"details": "Transfers ERC20 tokens from `owner` to `to`. Only callable from within.",
"params": {
"amount": "The amount of `token` to transfer.",
"owner": "The owner of the tokens.",
"to": "The recipient of the tokens.",
"token": "The token to spend."
}
},
"_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[]))": {
"details": "Internal version of `transformERC20()`. Only callable from within.",
"params": { "args": "A `TransformERC20Args` struct." },
@ -1679,10 +1653,6 @@
"takerTokenFilledAmount": "How much maker token was filled."
}
},
"getAllowanceTarget()": {
"details": "Get the address of the allowance target.",
"returns": { "target": "The target of token allowances." }
},
"getLimitOrderHash((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256))": {
"details": "Get the canonical hash of a limit order.",
"params": { "order": "The limit order." },
@ -1756,11 +1726,6 @@
"rollbackLength": "The number of items in the rollback history for the function."
}
},
"getSpendableERC20BalanceOf(address,address)": {
"details": "Gets the maximum amount of an ERC20 token `token` that can be pulled from `owner`.",
"params": { "owner": "The owner of the tokens.", "token": "The token to spend." },
"returns": { "amount": "The amount of tokens that can be pulled." }
},
"getTransformWallet()": {
"details": "Return the current wallet instance that will serve as the execution context for transformations.",
"returns": { "wallet": "The wallet instance." }
@ -1816,6 +1781,16 @@
},
"returns": { "boughtAmount": "The amount of `outputToken` bought." }
},
"sellToPancakeSwap(address[],uint256,uint256,uint8)": {
"details": "Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap.",
"params": {
"fork": "The protocol fork to use.",
"minBuyAmount": "Minimum amount of `tokens[-1]` to buy.",
"sellAmount": "of `tokens[0]` Amount to sell.",
"tokens": "Sell path."
},
"returns": { "buyAmount": "Amount of `tokens[-1]` bought." }
},
"sellToUniswap(address[],uint256,uint256,bool)": {
"details": "Efficiently sell directly to uniswap/sushiswap.",
"params": {

View File

@ -1,4 +1,13 @@
[
{
"version": "13.15.0",
"changes": [
{
"note": "Regenerate wrappers",
"pr": 164
}
]
},
{
"version": "13.14.0",
"changes": [

View File

@ -910,30 +910,6 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
name: 'token',
type: 'address',
},
{
name: 'owner',
type: 'address',
},
{
name: 'to',
type: 'address',
},
{
name: 'amount',
type: 'uint256',
},
],
name: '_spendERC20Tokens',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
@ -2314,18 +2290,6 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'getAllowanceTarget',
outputs: [
{
name: 'target',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
@ -3017,27 +2981,6 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
name: 'token',
type: 'address',
},
{
name: 'owner',
type: 'address',
},
],
name: 'getSpendableERC20BalanceOf',
outputs: [
{
name: 'amount',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'getTransformWallet',
@ -3212,6 +3155,35 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{
name: 'tokens',
type: 'address[]',
},
{
name: 'sellAmount',
type: 'uint256',
},
{
name: 'minBuyAmount',
type: 'uint256',
},
{
name: 'fork',
type: 'uint8',
},
],
name: 'sellToPancakeSwap',
outputs: [
{
name: 'buyAmount',
type: 'uint256',
},
],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{
@ -3581,69 +3553,6 @@ export class IZeroExContract extends BaseContract {
},
};
}
/**
* Transfers ERC20 tokens from `owner` to `to`.
* Only callable from within.
* @param token The token to spend.
* @param owner The owner of the tokens.
* @param to The recipient of the tokens.
* @param amount The amount of `token` to transfer.
*/
public _spendERC20Tokens(token: string, owner: string, to: string, amount: BigNumber): ContractTxFunctionObj<void> {
const self = (this as any) as IZeroExContract;
assert.isString('token', token);
assert.isString('owner', owner);
assert.isString('to', to);
assert.isBigNumber('amount', amount);
const functionSignature = '_spendERC20Tokens(address,address,address,uint256)';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [
token.toLowerCase(),
owner.toLowerCase(),
to.toLowerCase(),
amount,
]);
},
};
}
/**
* Internal version of `transformERC20()`. Only callable from within.
* @param args A `TransformERC20Args` struct.
@ -5169,55 +5078,6 @@ export class IZeroExContract extends BaseContract {
},
};
}
/**
* Get the address of the allowance target.
*/
public getAllowanceTarget(): ContractTxFunctionObj<string> {
const self = (this as any) as IZeroExContract;
const functionSignature = 'getAllowanceTarget()';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, []);
},
};
}
/**
* Get the canonical hash of a limit order.
* @param order The limit order.
@ -6020,60 +5880,6 @@ export class IZeroExContract extends BaseContract {
},
};
}
/**
* Gets the maximum amount of an ERC20 token `token` that can be
* pulled from `owner`.
* @param token The token to spend.
* @param owner The owner of the tokens.
*/
public getSpendableERC20BalanceOf(token: string, owner: string): ContractTxFunctionObj<BigNumber> {
const self = (this as any) as IZeroExContract;
assert.isString('token', token);
assert.isString('owner', owner);
const functionSignature = 'getSpendableERC20BalanceOf(address,address)';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [token.toLowerCase(), owner.toLowerCase()]);
},
};
}
/**
* Return the current wallet instance that will serve as the execution
* context for transformations.
@ -6536,6 +6342,68 @@ export class IZeroExContract extends BaseContract {
},
};
}
/**
* Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap.
* @param tokens Sell path.
* @param sellAmount of `tokens[0]` Amount to sell.
* @param minBuyAmount Minimum amount of `tokens[-1]` to buy.
* @param fork The protocol fork to use.
*/
public sellToPancakeSwap(
tokens: string[],
sellAmount: BigNumber,
minBuyAmount: BigNumber,
fork: number | BigNumber,
): ContractTxFunctionObj<BigNumber> {
const self = (this as any) as IZeroExContract;
assert.isArray('tokens', tokens);
assert.isBigNumber('sellAmount', sellAmount);
assert.isBigNumber('minBuyAmount', minBuyAmount);
assert.isNumberOrBigNumber('fork', fork);
const functionSignature = 'sellToPancakeSwap(address[],uint256,uint256,uint8)';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [tokens, sellAmount, minBuyAmount, fork]);
},
};
}
/**
* Efficiently sell directly to uniswap/sushiswap.
* @param tokens Sell path.

View File

@ -1,4 +1,12 @@
[
{
"version": "8.0.0",
"changes": [
{
"note": "Remove exchangeProxyAllowanceTarget"
}
]
},
{
"timestamp": 1616005394,
"version": "7.0.1",

View File

@ -315,7 +315,6 @@ export async function runMigrationsAsync(
);
const exchangeProxy = await fullMigrateExchangeProxyAsync(txDefaults.from, provider, txDefaults);
const exchangeProxyAllowanceTargetAddress = await exchangeProxy.getAllowanceTarget().callAsync();
const exchangeProxyFlashWalletAddress = await exchangeProxy.getTransformWallet().callAsync();
// Deploy transformers.
@ -382,7 +381,6 @@ export async function runMigrationsAsync(
dexForwarderBridge: NULL_ADDRESS,
exchangeProxyGovernor: NULL_ADDRESS,
exchangeProxy: exchangeProxy.address,
exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress,
exchangeProxyTransformerDeployer: txDefaults.from,
exchangeProxyFlashWallet: exchangeProxyFlashWalletAddress,
exchangeProxyLiquidityProviderSandbox: NULL_ADDRESS,

View File

@ -1,4 +1,13 @@
[
{
"version": "1.4.0",
"changes": [
{
"note": "Added Nerve",
"pr": 181
}
]
},
{
"timestamp": 1616005394,
"version": "1.3.1",

View File

@ -1,11 +1,11 @@
import { AbiEncoder, BigNumber, NULL_ADDRESS } from '@0x/utils';
import { AbiEncoder, BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
import * as ethjs from 'ethereumjs-util';
import { LimitOrder, LimitOrderFields, RfqOrder, RfqOrderFields } from './orders';
import { Signature, SIGNATURE_ABI } from './signature_utils';
const BRIDGE_ORDER_ABI_COMPONENTS = [
{ name: 'source', type: 'uint256' },
{ name: 'source', type: 'bytes32' },
{ name: 'takerTokenAmount', type: 'uint256' },
{ name: 'makerTokenAmount', type: 'uint256' },
{ name: 'bridgeData', type: 'bytes' },
@ -104,38 +104,38 @@ export interface FillQuoteTransformerData {
refundReceiver: string;
}
// tslint:disable: enum-naming
/**
* Identifies the DEX type of a bridge order.
* Identifies the DEX protocol used to fill a bridge order.
*/
export enum BridgeSource {
Balancer,
Bancor,
// tslint:disable-next-line: enum-naming
CoFiX,
export enum BridgeProtocol {
Unknown,
Curve,
Cream,
CryptoCom,
Dodo,
UniswapV2,
Uniswap,
Balancer,
Kyber,
LiquidityProvider,
Mooniswap,
MStable,
Oasis,
Shell,
Snowswap,
Sushiswap,
Swerve,
Uniswap,
UniswapV2,
Dodo,
DodoV2,
Linkswap,
CryptoCom,
Bancor,
CoFiX,
Nerve,
}
// tslint:enable: enum-naming
/**
* `FillQuoteTransformer.BridgeOrder`
*/
export interface FillQuoteTransformerBridgeOrder {
source: BridgeSource;
// A bytes32 hex where the upper 16 bytes are an int128, right-aligned
// protocol ID and the lower 16 bytes are a bytes16, left-aligned,
// ASCII source name.
source: string;
takerTokenAmount: BigNumber;
makerTokenAmount: BigNumber;
bridgeData: string;
@ -353,3 +353,17 @@ export function encodePositiveSlippageFeeTransformerData(data: PositiveSlippageF
export function decodePositiveSlippageFeeTransformerData(encoded: string): PositiveSlippageFeeTransformerData {
return positiveSlippageFeeTransformerDataEncoder.decode(encoded);
}
/**
* Packs a bridge protocol ID and an ASCII DEX name into a single byte32.
*/
export function encodeBridgeSourceId(protocol: BridgeProtocol, name: string): string {
const nameBuf = Buffer.from(name);
if (nameBuf.length > 16) {
throw new Error(`"${name}" is too long to be a bridge source name (max of 16 ascii chars)`);
}
return hexUtils.concat(
hexUtils.leftPad(hexUtils.toHex(protocol), 16),
hexUtils.rightPad(hexUtils.toHex(Buffer.from(name)), 16),
);
}