Merge pull request #2344 from 0xProject/feat/erc20-bridge-aggregator
ERC20BridgeSampler
This commit is contained in:
commit
f15e21faad
@ -77,7 +77,7 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator
|
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
|
||||||
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
||||||
# 3.0. At that time, also remove exclusion from monorepo
|
# 3.0. At that time, also remove exclusion from monorepo
|
||||||
# package.json's test script.
|
# package.json's test script.
|
||||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -79,6 +79,8 @@ TODO.md
|
|||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# generated contract artifacts/
|
# generated contract artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/generated-artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/test/generated-artifacts/
|
||||||
contracts/integrations/generated-artifacts/
|
contracts/integrations/generated-artifacts/
|
||||||
contracts/integrations/test/generated-artifacts/
|
contracts/integrations/test/generated-artifacts/
|
||||||
contracts/staking/generated-artifacts/
|
contracts/staking/generated-artifacts/
|
||||||
@ -111,6 +113,7 @@ packages/sol-tracing-utils/test/fixtures/artifacts/
|
|||||||
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||||
|
|
||||||
# generated truffle contract artifacts/
|
# generated truffle contract artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/build/
|
||||||
contracts/staking/build/
|
contracts/staking/build/
|
||||||
contracts/coordinator/build/
|
contracts/coordinator/build/
|
||||||
contracts/exchange/build/
|
contracts/exchange/build/
|
||||||
@ -127,6 +130,8 @@ contracts/dev-utils/build/
|
|||||||
|
|
||||||
# generated contract wrappers
|
# generated contract wrappers
|
||||||
packages/python-contract-wrappers/generated/
|
packages/python-contract-wrappers/generated/
|
||||||
|
contracts/erc20-bridge-sampler/generated-wrappers/
|
||||||
|
contracts/erc20-bridge-sampler/test/generated-wrappers/
|
||||||
contracts/integrations/generated-wrappers/
|
contracts/integrations/generated-wrappers/
|
||||||
contracts/integrations/test/generated-wrappers/
|
contracts/integrations/test/generated-wrappers/
|
||||||
contracts/staking/generated-wrappers/
|
contracts/staking/generated-wrappers/
|
||||||
|
@ -36,6 +36,10 @@ lib
|
|||||||
/contracts/erc20/test/generated-wrappers
|
/contracts/erc20/test/generated-wrappers
|
||||||
/contracts/erc20/generated-artifacts
|
/contracts/erc20/generated-artifacts
|
||||||
/contracts/erc20/test/generated-artifacts
|
/contracts/erc20/test/generated-artifacts
|
||||||
|
/contracts/erc20-bridge-sampler/generated-wrappers
|
||||||
|
/contracts/erc20-bridge-sampler/test/generated-wrappers
|
||||||
|
/contracts/erc20-bridge-sampler/generated-artifacts
|
||||||
|
/contracts/erc20-bridge-sampler/test/generated-artifacts
|
||||||
/contracts/erc721/generated-wrappers
|
/contracts/erc721/generated-wrappers
|
||||||
/contracts/erc721/test/generated-wrappers
|
/contracts/erc721/test/generated-wrappers
|
||||||
/contracts/erc721/generated-artifacts
|
/contracts/erc721/generated-artifacts
|
||||||
|
@ -210,7 +210,9 @@ contract UniswapBridge is
|
|||||||
if (fromTokenAddress == address(getWethContract())) {
|
if (fromTokenAddress == address(getWethContract())) {
|
||||||
exchangeTokenAddress = toTokenAddress;
|
exchangeTokenAddress = toTokenAddress;
|
||||||
}
|
}
|
||||||
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
|
exchange = IUniswapExchange(
|
||||||
|
getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress)
|
||||||
|
);
|
||||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||||
return exchange;
|
return exchange;
|
||||||
}
|
}
|
||||||
|
@ -67,11 +67,4 @@ interface IUniswapExchange {
|
|||||||
)
|
)
|
||||||
external
|
external
|
||||||
returns (uint256 tokensBought);
|
returns (uint256 tokensBought);
|
||||||
|
|
||||||
/// @dev Retrieves the token that is associated with this exchange.
|
|
||||||
/// @return tokenAddress The token address.
|
|
||||||
function toTokenAddress()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address tokenAddress);
|
|
||||||
}
|
}
|
||||||
|
@ -28,5 +28,5 @@ interface IUniswapExchangeFactory {
|
|||||||
function getExchange(address tokenAddress)
|
function getExchange(address tokenAddress)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (IUniswapExchange);
|
returns (address);
|
||||||
}
|
}
|
||||||
|
@ -407,9 +407,9 @@ contract TestUniswapBridge is
|
|||||||
function getExchange(address tokenAddress)
|
function getExchange(address tokenAddress)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (IUniswapExchange)
|
returns (address)
|
||||||
{
|
{
|
||||||
return IUniswapExchange(_testExchanges[tokenAddress]);
|
return address(_testExchanges[tokenAddress]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @dev Use `wethToken`.
|
// @dev Use `wethToken`.
|
||||||
|
10
contracts/erc20-bridge-sampler/.npmignore
Normal file
10
contracts/erc20-bridge-sampler/.npmignore
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Blacklist all files
|
||||||
|
.*
|
||||||
|
*
|
||||||
|
# Whitelist lib
|
||||||
|
!lib/**/*
|
||||||
|
# Whitelist Solidity contracts
|
||||||
|
!contracts/src/**/*
|
||||||
|
# Blacklist tests in lib
|
||||||
|
/lib/test/*
|
||||||
|
# Package specific ignore
|
11
contracts/erc20-bridge-sampler/CHANGELOG.json
Normal file
11
contracts/erc20-bridge-sampler/CHANGELOG.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"version": "1.0.0-beta.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Created package.",
|
||||||
|
"pr": 2344
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
0
contracts/erc20-bridge-sampler/CHANGELOG.md
Normal file
0
contracts/erc20-bridge-sampler/CHANGELOG.md
Normal file
1
contracts/erc20-bridge-sampler/DEPLOYS.json
Normal file
1
contracts/erc20-bridge-sampler/DEPLOYS.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
67
contracts/erc20-bridge-sampler/README.md
Normal file
67
contracts/erc20-bridge-sampler/README.md
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
## ERC20BridgeSampler
|
||||||
|
|
||||||
|
This package contains contracts used in DEX aggregation.
|
||||||
|
|
||||||
|
This is an MVP implementation, which agnostically samples DEXes for off-chain sorting and order generation. It is entirely read-only and never not touches any funds.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
**Install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @0x/contracts-erc20-bridge-sampler --save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||||
|
|
||||||
|
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||||
|
|
||||||
|
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||||
|
|
||||||
|
### Install Dependencies
|
||||||
|
|
||||||
|
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn config set workspaces-experimental true
|
||||||
|
```
|
||||||
|
|
||||||
|
Then install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PKG=@0x/contracts-erc20-bridge-sampler yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Or continuously rebuild on change:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PKG=@0x/contracts-erc20-bridge-sampler yarn watch
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clean
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn test
|
||||||
|
```
|
26
contracts/erc20-bridge-sampler/compiler.json
Normal file
26
contracts/erc20-bridge-sampler/compiler.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"artifactsDir": "./test/generated-artifacts",
|
||||||
|
"contractsDir": "./contracts",
|
||||||
|
"useDockerisedSolc": false,
|
||||||
|
"isOfflineMode": false,
|
||||||
|
"compilerSettings": {
|
||||||
|
"evmVersion": "constantinople",
|
||||||
|
"optimizer": {
|
||||||
|
"enabled": true,
|
||||||
|
"runs": 1000000,
|
||||||
|
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||||
|
},
|
||||||
|
"outputSelection": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"abi",
|
||||||
|
"devdoc",
|
||||||
|
"evm.bytecode.object",
|
||||||
|
"evm.bytecode.sourceMap",
|
||||||
|
"evm.deployedBytecode.object",
|
||||||
|
"evm.deployedBytecode.sourceMap"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "./IEth2Dai.sol";
|
||||||
|
import "./IKyberNetwork.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract DeploymentConstants {
|
||||||
|
|
||||||
|
/// @dev Address of the 0x Exchange contract.
|
||||||
|
address constant public EXCHANGE_ADDRESS = 0x080bf510FCbF18b91105470639e9561022937712;
|
||||||
|
/// @dev Address of the Eth2Dai MatchingMarket contract.
|
||||||
|
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
|
||||||
|
/// @dev Address of the UniswapExchangeFactory contract.
|
||||||
|
address constant public UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
|
||||||
|
/// @dev Address of the KyberNeworkProxy contract.
|
||||||
|
address constant public KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
|
||||||
|
/// @dev Address of the WETH contract.
|
||||||
|
address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||||
|
/// @dev Kyber ETH pseudo-address.
|
||||||
|
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
|
||||||
|
/// @dev An overridable way to retrieve the 0x Exchange contract.
|
||||||
|
/// @return zeroex The 0x Exchange contract.
|
||||||
|
function _getExchangeContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IExchange zeroex)
|
||||||
|
{
|
||||||
|
return IExchange(EXCHANGE_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev An overridable way to retrieve the Eth2Dai exchange contract.
|
||||||
|
/// @return eth2dai The Eth2Dai exchange contract.
|
||||||
|
function _getEth2DaiContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IEth2Dai eth2dai)
|
||||||
|
{
|
||||||
|
return IEth2Dai(ETH2DAI_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev An overridable way to retrieve the Uniswap exchange factory contract.
|
||||||
|
/// @return uniswap The UniswapExchangeFactory contract.
|
||||||
|
function _getUniswapExchangeFactoryContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IUniswapExchangeFactory uniswap)
|
||||||
|
{
|
||||||
|
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev An overridable way to retrieve the Kyber network proxy contract.
|
||||||
|
/// @return kyber The KyberNeworkProxy contract.
|
||||||
|
function _getKyberNetworkContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IKyberNetwork kyber)
|
||||||
|
{
|
||||||
|
return IKyberNetwork(KYBER_NETWORK_PROXY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev An overridable way to retrieve the WETH contract address.
|
||||||
|
/// @return weth The WETH contract address.
|
||||||
|
function _getWETHAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address weth)
|
||||||
|
{
|
||||||
|
return WETH_ADDRESS;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,451 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "./IERC20BridgeSampler.sol";
|
||||||
|
import "./IEth2Dai.sol";
|
||||||
|
import "./IKyberNetwork.sol";
|
||||||
|
import "./IUniswapExchangeQuotes.sol";
|
||||||
|
import "./DeploymentConstants.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract ERC20BridgeSampler is
|
||||||
|
IERC20BridgeSampler,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
||||||
|
|
||||||
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
||||||
|
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryOrdersAndSampleSells(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
address[] memory sources,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
LibOrder.OrderInfo[] memory orderInfos,
|
||||||
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
|
)
|
||||||
|
{
|
||||||
|
require(orders.length != 0, "EMPTY_ORDERS");
|
||||||
|
orderInfos = queryOrders(orders);
|
||||||
|
makerTokenAmountsBySource = sampleSells(
|
||||||
|
sources,
|
||||||
|
_assetDataToTokenAddress(orders[0].takerAssetData),
|
||||||
|
_assetDataToTokenAddress(orders[0].makerAssetData),
|
||||||
|
takerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
||||||
|
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryOrdersAndSampleBuys(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
address[] memory sources,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
LibOrder.OrderInfo[] memory orderInfos,
|
||||||
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
|
)
|
||||||
|
{
|
||||||
|
require(orders.length != 0, "EMPTY_ORDERS");
|
||||||
|
orderInfos = queryOrders(orders);
|
||||||
|
makerTokenAmountsBySource = sampleBuys(
|
||||||
|
sources,
|
||||||
|
_assetDataToTokenAddress(orders[0].takerAssetData),
|
||||||
|
_assetDataToTokenAddress(orders[0].makerAssetData),
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Queries the status of several native orders.
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @return orderInfos Order info for each respective order.
|
||||||
|
function queryOrders(LibOrder.Order[] memory orders)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (LibOrder.OrderInfo[] memory orderInfos)
|
||||||
|
{
|
||||||
|
uint256 numOrders = orders.length;
|
||||||
|
orderInfos = new LibOrder.OrderInfo[](numOrders);
|
||||||
|
for (uint256 i = 0; i < numOrders; i++) {
|
||||||
|
orderInfos[i] = _getExchangeContract().getOrderInfo(orders[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes on multiple DEXes at once.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @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 makerTokenAmountsBySource Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function sampleSells(
|
||||||
|
address[] memory sources,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[][] memory makerTokenAmountsBySource)
|
||||||
|
{
|
||||||
|
uint256 numSources = sources.length;
|
||||||
|
makerTokenAmountsBySource = new uint256[][](numSources);
|
||||||
|
for (uint256 i = 0; i < numSources; i++) {
|
||||||
|
makerTokenAmountsBySource[i] = _sampleSellSource(
|
||||||
|
sources[i],
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
takerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @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 takerTokenAmountsBySource Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function sampleBuys(
|
||||||
|
address[] memory sources,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[][] memory takerTokenAmountsBySource)
|
||||||
|
{
|
||||||
|
uint256 numSources = sources.length;
|
||||||
|
takerTokenAmountsBySource = new uint256[][](numSources);
|
||||||
|
for (uint256 i = 0; i < numSources; i++) {
|
||||||
|
takerTokenAmountsBySource[i] = _sampleBuySource(
|
||||||
|
sources[i],
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Kyber.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromKyberNetwork(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
address _takerToken = takerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : takerToken;
|
||||||
|
address _makerToken = makerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : makerToken;
|
||||||
|
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
|
||||||
|
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
(uint256 rate,) = _getKyberNetworkContract().getExpectedRate(
|
||||||
|
_takerToken,
|
||||||
|
_makerToken,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
makerTokenAmounts[i] =
|
||||||
|
rate *
|
||||||
|
takerTokenAmounts[i] *
|
||||||
|
10 ** makerTokenDecimals /
|
||||||
|
10 ** takerTokenDecimals /
|
||||||
|
10 ** 18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromEth2Dai(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
makerTokenAmounts[i] = _getEth2DaiContract().getBuyAmount(
|
||||||
|
makerToken,
|
||||||
|
takerToken,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Eth2Dai/Oasis.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Maker token sell amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromEth2Dai(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
takerTokenAmounts[i] = _getEth2DaiContract().getPayAmount(
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Uniswap.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromUniswap(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
||||||
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
if (makerToken == _getWETHAddress()) {
|
||||||
|
makerTokenAmounts[i] = takerTokenExchange.getTokenToEthInputPrice(
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else if (takerToken == _getWETHAddress()) {
|
||||||
|
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
uint256 ethBought = takerTokenExchange.getTokenToEthInputPrice(
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
|
||||||
|
ethBought
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Uniswap.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromUniswap(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
||||||
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
|
||||||
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
if (makerToken == _getWETHAddress()) {
|
||||||
|
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else if (takerToken == _getWETHAddress()) {
|
||||||
|
takerTokenAmounts[i] = makerTokenExchange.getEthToTokenOutputPrice(
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
uint256 ethSold = makerTokenExchange.getEthToTokenOutputPrice(
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
);
|
||||||
|
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
|
||||||
|
ethSold
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Overridable way to get token decimals.
|
||||||
|
/// @param tokenAddress Address of the token.
|
||||||
|
/// @return decimals The decimal places for the token.
|
||||||
|
function _getTokenDecimals(address tokenAddress)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint8 decimals)
|
||||||
|
{
|
||||||
|
return LibERC20Token.decimals(tokenAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Samples a supported sell source, defined by its address.
|
||||||
|
/// @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 _sampleSellSource(
|
||||||
|
address source,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
if (source == address(_getEth2DaiContract())) {
|
||||||
|
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
|
||||||
|
}
|
||||||
|
if (source == address(_getUniswapExchangeFactoryContract())) {
|
||||||
|
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
|
||||||
|
}
|
||||||
|
if (source == address(_getKyberNetworkContract())) {
|
||||||
|
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
|
||||||
|
}
|
||||||
|
revert("UNSUPPORTED_SOURCE");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Samples a supported buy source, defined by its address.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function _sampleBuySource(
|
||||||
|
address source,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
if (source == address(_getEth2DaiContract())) {
|
||||||
|
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
|
||||||
|
}
|
||||||
|
if (source == address(_getUniswapExchangeFactoryContract())) {
|
||||||
|
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
|
||||||
|
}
|
||||||
|
revert("UNSUPPORTED_SOURCE");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Retrive an existing Uniswap exchange contract.
|
||||||
|
/// Throws if the exchange does not exist.
|
||||||
|
/// @param tokenAddress Address of the token contract.
|
||||||
|
/// @return exchange `IUniswapExchangeQuotes` for the token.
|
||||||
|
function _getUniswapExchange(address tokenAddress)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (IUniswapExchangeQuotes exchange)
|
||||||
|
{
|
||||||
|
exchange = IUniswapExchangeQuotes(
|
||||||
|
address(_getUniswapExchangeFactoryContract().getExchange(tokenAddress))
|
||||||
|
);
|
||||||
|
require(address(exchange) != address(0), "UNSUPPORTED_UNISWAP_EXCHANGE");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Extract the token address from ERC20 proxy asset data.
|
||||||
|
/// @param assetData ERC20 asset data.
|
||||||
|
/// @return tokenAddress The decoded token address.
|
||||||
|
function _assetDataToTokenAddress(bytes memory assetData)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (address tokenAddress)
|
||||||
|
{
|
||||||
|
require(assetData.length == 36, "INVALID_ASSET_DATA");
|
||||||
|
bytes4 selector;
|
||||||
|
assembly {
|
||||||
|
selector := and(mload(add(assetData, 0x20)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
|
||||||
|
tokenAddress := mload(add(assetData, 0x24))
|
||||||
|
}
|
||||||
|
require(selector == ERC20_PROXY_ID, "UNSUPPORTED_ASSET_PROXY");
|
||||||
|
}
|
||||||
|
|
||||||
|
function _assertValidPair(address makerToken, address takerToken)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
require(makerToken != takerToken, "INVALID_TOKEN_PAIR");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
|
||||||
|
|
||||||
|
interface IERC20BridgeSampler {
|
||||||
|
|
||||||
|
/// @dev Query native orders and sample sell orders on multiple DEXes at once.
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
|
||||||
|
/// @param takerTokenAmounts Taker sell amount for each sample.
|
||||||
|
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
||||||
|
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryOrdersAndSampleSells(
|
||||||
|
LibOrder.Order[] calldata orders,
|
||||||
|
address[] calldata sources,
|
||||||
|
uint256[] calldata takerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
LibOrder.OrderInfo[] memory orderInfos,
|
||||||
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Query native orders and sample buy orders on multiple DEXes at once.
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
|
||||||
|
/// @param makerTokenAmounts Maker sell amount for each sample.
|
||||||
|
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
||||||
|
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryOrdersAndSampleBuys(
|
||||||
|
LibOrder.Order[] calldata orders,
|
||||||
|
address[] calldata sources,
|
||||||
|
uint256[] calldata makerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
LibOrder.OrderInfo[] memory orderInfos,
|
||||||
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Queries the status of several native orders.
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @return orderInfos Order info for each respective order.
|
||||||
|
function queryOrders(LibOrder.Order[] calldata orders)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (LibOrder.OrderInfo[] memory orderInfos);
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes on multiple DEXes at once.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @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 makerTokenAmountsBySource Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function sampleSells(
|
||||||
|
address[] calldata sources,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] calldata takerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[][] memory makerTokenAmountsBySource);
|
||||||
|
|
||||||
|
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @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 takerTokenAmountsBySource Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function sampleBuys(
|
||||||
|
address[] calldata sources,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] calldata makerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[][] memory takerTokenAmountsBySource);
|
||||||
|
}
|
41
contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol
Normal file
41
contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
interface IEth2Dai {
|
||||||
|
|
||||||
|
function getBuyAmount(
|
||||||
|
address buyToken,
|
||||||
|
address payToken,
|
||||||
|
uint256 payAmount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 buyAmount);
|
||||||
|
|
||||||
|
function getPayAmount(
|
||||||
|
address payToken,
|
||||||
|
address buyToken,
|
||||||
|
uint256 buyAmount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 payAmount);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
interface IKyberNetwork {
|
||||||
|
|
||||||
|
function getExpectedRate(
|
||||||
|
address fromToken,
|
||||||
|
address toToken,
|
||||||
|
uint256 fromAmount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 expectedRate, uint256 slippageRate);
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
interface IUniswapExchangeQuotes {
|
||||||
|
|
||||||
|
function getEthToTokenInputPrice(
|
||||||
|
uint256 ethSold
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 tokensBought);
|
||||||
|
|
||||||
|
function getEthToTokenOutputPrice(
|
||||||
|
uint256 tokensBought
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 ethSold);
|
||||||
|
|
||||||
|
function getTokenToEthInputPrice(
|
||||||
|
uint256 tokensSold
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 ethBought);
|
||||||
|
|
||||||
|
function getTokenToEthOutputPrice(
|
||||||
|
uint256 ethBought
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 tokensSold);
|
||||||
|
}
|
@ -0,0 +1,349 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "../src/ERC20BridgeSampler.sol";
|
||||||
|
import "../src/IEth2Dai.sol";
|
||||||
|
import "../src/IKyberNetwork.sol";
|
||||||
|
|
||||||
|
|
||||||
|
library LibDeterministicQuotes {
|
||||||
|
|
||||||
|
address private constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||||
|
uint256 private constant RATE_DENOMINATOR = 1 ether;
|
||||||
|
uint256 private constant MIN_RATE = RATE_DENOMINATOR / 100;
|
||||||
|
uint256 private constant MAX_RATE = 100 * RATE_DENOMINATOR;
|
||||||
|
uint8 private constant MIN_DECIMALS = 4;
|
||||||
|
uint8 private constant MAX_DECIMALS = 20;
|
||||||
|
|
||||||
|
function getDeterministicSellQuote(
|
||||||
|
bytes32 salt,
|
||||||
|
address sellToken,
|
||||||
|
address buyToken,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256 buyAmount)
|
||||||
|
{
|
||||||
|
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
|
||||||
|
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
|
||||||
|
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
|
||||||
|
return sellAmount * rate * buyBase / sellBase / RATE_DENOMINATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicBuyQuote(
|
||||||
|
bytes32 salt,
|
||||||
|
address sellToken,
|
||||||
|
address buyToken,
|
||||||
|
uint256 buyAmount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256 sellAmount)
|
||||||
|
{
|
||||||
|
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
|
||||||
|
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
|
||||||
|
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
|
||||||
|
return buyAmount * RATE_DENOMINATOR * sellBase / rate / buyBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicTokenDecimals(address token)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint8 decimals)
|
||||||
|
{
|
||||||
|
if (token == WETH_ADDRESS) {
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
bytes32 seed = keccak256(abi.encodePacked(token));
|
||||||
|
return uint8(uint256(seed) % (MAX_DECIMALS - MIN_DECIMALS)) + MIN_DECIMALS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicRate(bytes32 salt, address sellToken, address buyToken)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256 rate)
|
||||||
|
{
|
||||||
|
bytes32 seed = keccak256(abi.encodePacked(salt, sellToken, buyToken));
|
||||||
|
return uint256(seed) % (MAX_RATE - MIN_RATE) + MIN_RATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract TestERC20BridgeSamplerUniswapExchange is
|
||||||
|
IUniswapExchangeQuotes,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
|
||||||
|
|
||||||
|
address public tokenAddress;
|
||||||
|
bytes32 public salt;
|
||||||
|
|
||||||
|
constructor(address _tokenAddress) public {
|
||||||
|
tokenAddress = _tokenAddress;
|
||||||
|
salt = keccak256(abi.encodePacked(BASE_SALT, _tokenAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic `IUniswapExchangeQuotes.getEthToTokenInputPrice()`.
|
||||||
|
function getEthToTokenInputPrice(
|
||||||
|
uint256 ethSold
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 tokensBought)
|
||||||
|
{
|
||||||
|
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||||
|
salt,
|
||||||
|
tokenAddress,
|
||||||
|
WETH_ADDRESS,
|
||||||
|
ethSold
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic `IUniswapExchangeQuotes.getEthToTokenOutputPrice()`.
|
||||||
|
function getEthToTokenOutputPrice(
|
||||||
|
uint256 tokensBought
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 ethSold)
|
||||||
|
{
|
||||||
|
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||||
|
salt,
|
||||||
|
WETH_ADDRESS,
|
||||||
|
tokenAddress,
|
||||||
|
tokensBought
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic `IUniswapExchangeQuotes.getTokenToEthInputPrice()`.
|
||||||
|
function getTokenToEthInputPrice(
|
||||||
|
uint256 tokensSold
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 ethBought)
|
||||||
|
{
|
||||||
|
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||||
|
salt,
|
||||||
|
tokenAddress,
|
||||||
|
WETH_ADDRESS,
|
||||||
|
tokensSold
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic `IUniswapExchangeQuotes.getTokenToEthOutputPrice()`.
|
||||||
|
function getTokenToEthOutputPrice(
|
||||||
|
uint256 ethBought
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 tokensSold)
|
||||||
|
{
|
||||||
|
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||||
|
salt,
|
||||||
|
WETH_ADDRESS,
|
||||||
|
tokenAddress,
|
||||||
|
ethBought
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract TestERC20BridgeSamplerKyberNetwork is
|
||||||
|
IKyberNetwork,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
|
||||||
|
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
|
||||||
|
// Deterministic `IKyberNetwork.getExpectedRate()`.
|
||||||
|
function getExpectedRate(
|
||||||
|
address fromToken,
|
||||||
|
address toToken,
|
||||||
|
uint256
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 expectedRate, uint256)
|
||||||
|
{
|
||||||
|
fromToken = fromToken == ETH_ADDRESS ? WETH_ADDRESS : fromToken;
|
||||||
|
toToken = toToken == ETH_ADDRESS ? WETH_ADDRESS : toToken;
|
||||||
|
expectedRate = LibDeterministicQuotes.getDeterministicRate(
|
||||||
|
SALT,
|
||||||
|
fromToken,
|
||||||
|
toToken
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract TestERC20BridgeSamplerEth2Dai is
|
||||||
|
IEth2Dai
|
||||||
|
{
|
||||||
|
bytes32 constant private SALT = 0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7;
|
||||||
|
|
||||||
|
// Deterministic `IEth2Dai.getBuyAmount()`.
|
||||||
|
function getBuyAmount(
|
||||||
|
address buyToken,
|
||||||
|
address payToken,
|
||||||
|
uint256 payAmount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 buyAmount)
|
||||||
|
{
|
||||||
|
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||||
|
SALT,
|
||||||
|
payToken,
|
||||||
|
buyToken,
|
||||||
|
payAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic `IEth2Dai.getPayAmount()`.
|
||||||
|
function getPayAmount(
|
||||||
|
address payToken,
|
||||||
|
address buyToken,
|
||||||
|
uint256 buyAmount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 payAmount)
|
||||||
|
{
|
||||||
|
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||||
|
SALT,
|
||||||
|
payToken,
|
||||||
|
buyToken,
|
||||||
|
buyAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract TestERC20BridgeSamplerUniswapExchangeFactory is
|
||||||
|
IUniswapExchangeFactory
|
||||||
|
{
|
||||||
|
mapping (address => IUniswapExchangeQuotes) private _exchangesByToken;
|
||||||
|
|
||||||
|
// Creates Uniswap exchange contracts for tokens.
|
||||||
|
function createTokenExchanges(address[] calldata tokenAddresses)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
for (uint256 i = 0; i < tokenAddresses.length; i++) {
|
||||||
|
address tokenAddress = tokenAddresses[i];
|
||||||
|
_exchangesByToken[tokenAddress] =
|
||||||
|
new TestERC20BridgeSamplerUniswapExchange(tokenAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `IUniswapExchangeFactory.getExchange()`.
|
||||||
|
function getExchange(address tokenAddress)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(_exchangesByToken[tokenAddress]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract TestERC20BridgeSampler is
|
||||||
|
ERC20BridgeSampler
|
||||||
|
{
|
||||||
|
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
|
||||||
|
TestERC20BridgeSamplerEth2Dai public eth2Dai;
|
||||||
|
TestERC20BridgeSamplerKyberNetwork public kyber;
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
|
||||||
|
eth2Dai = new TestERC20BridgeSamplerEth2Dai();
|
||||||
|
kyber = new TestERC20BridgeSamplerKyberNetwork();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates Uniswap exchange contracts for tokens.
|
||||||
|
function createTokenExchanges(address[] calldata tokenAddresses)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
uniswap.createTokenExchanges(tokenAddresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `IExchange.getOrderInfo()`, overridden to return deterministic order infos.
|
||||||
|
function getOrderInfo(LibOrder.Order memory order)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (LibOrder.OrderInfo memory orderInfo)
|
||||||
|
{
|
||||||
|
// The order hash is just the hash of the salt.
|
||||||
|
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
||||||
|
// Everything else is derived from the hash.
|
||||||
|
orderInfo.orderHash = orderHash;
|
||||||
|
orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1));
|
||||||
|
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriden to return deterministic decimals.
|
||||||
|
function _getTokenDecimals(address tokenAddress)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint8 decimals)
|
||||||
|
{
|
||||||
|
return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriden to point to a this contract.
|
||||||
|
function _getExchangeContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IExchange zeroex)
|
||||||
|
{
|
||||||
|
return IExchange(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriden to point to a custom contract.
|
||||||
|
function _getEth2DaiContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IEth2Dai eth2dai_)
|
||||||
|
{
|
||||||
|
return eth2Dai;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriden to point to a custom contract.
|
||||||
|
function _getUniswapExchangeFactoryContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IUniswapExchangeFactory uniswap_)
|
||||||
|
{
|
||||||
|
return uniswap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriden to point to a custom contract.
|
||||||
|
function _getKyberNetworkContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IKyberNetwork kyber_)
|
||||||
|
{
|
||||||
|
return kyber;
|
||||||
|
}
|
||||||
|
}
|
92
contracts/erc20-bridge-sampler/package.json
Normal file
92
contracts/erc20-bridge-sampler/package.json
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"name": "@0x/contracts-erc20-bridge-sampler",
|
||||||
|
"version": "1.0.0-beta.1",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.12"
|
||||||
|
},
|
||||||
|
"description": "Sampler contracts for the 0x asset-swapper",
|
||||||
|
"main": "lib/src/index.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "yarn pre_build && tsc -b",
|
||||||
|
"build:ci": "yarn build",
|
||||||
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||||
|
"test": "yarn run_mocha",
|
||||||
|
"rebuild_and_test": "run-s build test",
|
||||||
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||||
|
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||||
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||||
|
"compile": "sol-compiler",
|
||||||
|
"watch": "sol-compiler -w",
|
||||||
|
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||||
|
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||||
|
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||||
|
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-wrappers/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||||
|
"coverage:report:text": "istanbul report text",
|
||||||
|
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||||
|
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||||
|
"coverage:report:lcov": "istanbul report lcov",
|
||||||
|
"test:circleci": "yarn test",
|
||||||
|
"contracts:gen": "contracts-gen generate",
|
||||||
|
"contracts:copy": "contracts-gen copy",
|
||||||
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
|
"compile:truffle": "truffle compile"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
|
||||||
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
|
"abis": "./test/generated-artifacts/@(DeploymentConstants|ERC20BridgeSampler|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||||
|
"devDependencies": {
|
||||||
|
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||||
|
"@0x/contracts-asset-proxy": "^2.3.0-beta.1",
|
||||||
|
"@0x/contracts-erc20": "^2.3.0-beta.1",
|
||||||
|
"@0x/contracts-exchange-libs": "^3.1.0-beta.1",
|
||||||
|
"@0x/contracts-exchange": "^2.2.0-beta.1",
|
||||||
|
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||||
|
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||||
|
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||||
|
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||||
|
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||||
|
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||||
|
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||||
|
"@types/lodash": "4.14.104",
|
||||||
|
"@types/mocha": "^5.2.7",
|
||||||
|
"@types/node": "*",
|
||||||
|
"chai": "^4.0.1",
|
||||||
|
"chai-as-promised": "^7.1.0",
|
||||||
|
"chai-bignumber": "^3.0.0",
|
||||||
|
"dirty-chai": "^2.0.1",
|
||||||
|
"make-promises-safe": "^1.1.0",
|
||||||
|
"mocha": "^6.2.0",
|
||||||
|
"npm-run-all": "^4.1.2",
|
||||||
|
"shx": "^0.2.2",
|
||||||
|
"solhint": "^1.4.1",
|
||||||
|
"truffle": "^5.0.32",
|
||||||
|
"tslint": "5.11.0",
|
||||||
|
"typescript": "3.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@0x/base-contract": "^5.5.0-beta.1",
|
||||||
|
"@0x/types": "^2.5.0-beta.1",
|
||||||
|
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||||
|
"@0x/utils": "^4.6.0-beta.1",
|
||||||
|
"ethereum-types": "^2.2.0-beta.1",
|
||||||
|
"lodash": "^4.17.11"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
13
contracts/erc20-bridge-sampler/src/artifacts.ts
Normal file
13
contracts/erc20-bridge-sampler/src/artifacts.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
|
||||||
|
import * as IERC20BridgeSampler from '../generated-artifacts/IERC20BridgeSampler.json';
|
||||||
|
export const artifacts = {
|
||||||
|
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||||
|
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
||||||
|
};
|
2
contracts/erc20-bridge-sampler/src/index.ts
Normal file
2
contracts/erc20-bridge-sampler/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './wrappers';
|
||||||
|
export * from './artifacts';
|
7
contracts/erc20-bridge-sampler/src/wrappers.ts
Normal file
7
contracts/erc20-bridge-sampler/src/wrappers.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export * from '../generated-wrappers/erc20_bridge_sampler';
|
||||||
|
export * from '../generated-wrappers/i_erc20_bridge_sampler';
|
23
contracts/erc20-bridge-sampler/test/artifacts.ts
Normal file
23
contracts/erc20-bridge-sampler/test/artifacts.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json';
|
||||||
|
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||||
|
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
|
||||||
|
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||||
|
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
|
||||||
|
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
||||||
|
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||||
|
export const artifacts = {
|
||||||
|
DeploymentConstants: DeploymentConstants as ContractArtifact,
|
||||||
|
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||||
|
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
||||||
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
|
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
||||||
|
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
|
||||||
|
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
|
||||||
|
};
|
852
contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts
Normal file
852
contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts
Normal file
@ -0,0 +1,852 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
getRandomInteger,
|
||||||
|
getRandomPortion,
|
||||||
|
hexConcat,
|
||||||
|
hexHash,
|
||||||
|
hexLeftPad,
|
||||||
|
hexRandom,
|
||||||
|
randomAddress,
|
||||||
|
toHex,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { Order, OrderInfo } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
import { TestERC20BridgeSamplerContract } from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests('erc20-bridge-sampler', env => {
|
||||||
|
let testContract: TestERC20BridgeSamplerContract;
|
||||||
|
let allSources: { [name: string]: string };
|
||||||
|
const RATE_DENOMINATOR = constants.ONE_ETHER;
|
||||||
|
const MIN_RATE = new BigNumber('0.01');
|
||||||
|
const MAX_RATE = new BigNumber('100');
|
||||||
|
const MIN_DECIMALS = 4;
|
||||||
|
const MAX_DECIMALS = 20;
|
||||||
|
const WETH_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
||||||
|
const KYBER_SALT = '0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7';
|
||||||
|
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
|
||||||
|
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
|
||||||
|
const ERC20_PROXY_ID = '0xf47261b0';
|
||||||
|
const INVALID_ASSET_PROXY_ASSET_DATA = hexConcat('0xf47261b1', hexLeftPad(randomAddress()));
|
||||||
|
const INVALID_ASSET_DATA = hexRandom(37);
|
||||||
|
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
|
||||||
|
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
|
||||||
|
const EMPTY_ORDERS_ERROR = 'EMPTY_ORDERS';
|
||||||
|
const UNSUPPORTED_ASSET_PROXY_ERROR = 'UNSUPPORTED_ASSET_PROXY';
|
||||||
|
const INVALID_ASSET_DATA_ERROR = 'INVALID_ASSET_DATA';
|
||||||
|
const UNSUPPORTED_UNISWAP_EXCHANGE_ERROR = 'UNSUPPORTED_UNISWAP_EXCHANGE';
|
||||||
|
const UNSUPPORTED_SOURCE_ERROR = 'UNSUPPORTED_SOURCE';
|
||||||
|
const INVALID_TOKEN_PAIR_ERROR = 'INVALID_TOKEN_PAIR';
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestERC20BridgeSampler,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
allSources = _.zipObject(
|
||||||
|
['Uniswap', 'Eth2Dai', 'Kyber'],
|
||||||
|
[
|
||||||
|
await testContract.uniswap().callAsync(),
|
||||||
|
await testContract.eth2Dai().callAsync(),
|
||||||
|
await testContract.kyber().callAsync(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getPackedHash(...args: string[]): string {
|
||||||
|
return hexHash(hexConcat(...args.map(a => toHex(a))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUniswapExchangeSalt(tokenAddress: string): string {
|
||||||
|
return getPackedHash(UNISWAP_BASE_SALT, tokenAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicRate(salt: string, sellToken: string, buyToken: string): BigNumber {
|
||||||
|
const hash = getPackedHash(salt, sellToken, buyToken);
|
||||||
|
const _minRate = RATE_DENOMINATOR.times(MIN_RATE);
|
||||||
|
const _maxRate = RATE_DENOMINATOR.times(MAX_RATE);
|
||||||
|
return new BigNumber(hash)
|
||||||
|
.mod(_maxRate.minus(_minRate))
|
||||||
|
.plus(_minRate)
|
||||||
|
.div(RATE_DENOMINATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicTokenDecimals(token: string): number {
|
||||||
|
if (token === WETH_ADDRESS) {
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
// HACK(dorothy-zbornak): Linter will complain about the addition not being
|
||||||
|
// between two numbers, even though they are.
|
||||||
|
// tslint:disable-next-line restrict-plus-operands
|
||||||
|
return new BigNumber(getPackedHash(token)).mod(MAX_DECIMALS - MIN_DECIMALS).toNumber() + MIN_DECIMALS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicSellQuote(
|
||||||
|
salt: string,
|
||||||
|
sellToken: string,
|
||||||
|
buyToken: string,
|
||||||
|
sellAmount: BigNumber,
|
||||||
|
): BigNumber {
|
||||||
|
const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken));
|
||||||
|
const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken));
|
||||||
|
const rate = getDeterministicRate(salt, sellToken, buyToken);
|
||||||
|
return sellAmount
|
||||||
|
.times(rate)
|
||||||
|
.times(buyBase)
|
||||||
|
.dividedToIntegerBy(sellBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicBuyQuote(
|
||||||
|
salt: string,
|
||||||
|
sellToken: string,
|
||||||
|
buyToken: string,
|
||||||
|
buyAmount: BigNumber,
|
||||||
|
): BigNumber {
|
||||||
|
const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken));
|
||||||
|
const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken));
|
||||||
|
const rate = getDeterministicRate(salt, sellToken, buyToken);
|
||||||
|
return buyAmount
|
||||||
|
.times(sellBase)
|
||||||
|
.dividedToIntegerBy(rate)
|
||||||
|
.dividedToIntegerBy(buyBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
function areAddressesEqual(a: string, b: string): boolean {
|
||||||
|
return a.toLowerCase() === b.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicUniswapSellQuote(sellToken: string, buyToken: string, sellAmount: BigNumber): BigNumber {
|
||||||
|
if (areAddressesEqual(buyToken, WETH_ADDRESS)) {
|
||||||
|
return getDeterministicSellQuote(getUniswapExchangeSalt(sellToken), sellToken, WETH_ADDRESS, sellAmount);
|
||||||
|
}
|
||||||
|
if (areAddressesEqual(sellToken, WETH_ADDRESS)) {
|
||||||
|
return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, sellAmount);
|
||||||
|
}
|
||||||
|
const ethBought = getDeterministicSellQuote(
|
||||||
|
getUniswapExchangeSalt(sellToken),
|
||||||
|
sellToken,
|
||||||
|
WETH_ADDRESS,
|
||||||
|
sellAmount,
|
||||||
|
);
|
||||||
|
return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, ethBought);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicUniswapBuyQuote(sellToken: string, buyToken: string, buyAmount: BigNumber): BigNumber {
|
||||||
|
if (areAddressesEqual(buyToken, WETH_ADDRESS)) {
|
||||||
|
return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, buyAmount);
|
||||||
|
}
|
||||||
|
if (areAddressesEqual(sellToken, WETH_ADDRESS)) {
|
||||||
|
return getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount);
|
||||||
|
}
|
||||||
|
const ethSold = getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount);
|
||||||
|
return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, ethSold);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicSellQuotes(
|
||||||
|
sellToken: string,
|
||||||
|
buyToken: string,
|
||||||
|
sources: string[],
|
||||||
|
sampleAmounts: BigNumber[],
|
||||||
|
): BigNumber[][] {
|
||||||
|
const quotes: BigNumber[][] = [];
|
||||||
|
for (const source of sources) {
|
||||||
|
const sampleOutputs = [];
|
||||||
|
for (const amount of sampleAmounts) {
|
||||||
|
if (source === 'Kyber' || source === 'Eth2Dai') {
|
||||||
|
sampleOutputs.push(
|
||||||
|
getDeterministicSellQuote(
|
||||||
|
source === 'Kyber' ? KYBER_SALT : ETH2DAI_SALT,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
amount,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (source === 'Uniswap') {
|
||||||
|
sampleOutputs.push(getDeterministicUniswapSellQuote(sellToken, buyToken, amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quotes.push(sampleOutputs);
|
||||||
|
}
|
||||||
|
return quotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicBuyQuotes(
|
||||||
|
sellToken: string,
|
||||||
|
buyToken: string,
|
||||||
|
sources: string[],
|
||||||
|
sampleAmounts: BigNumber[],
|
||||||
|
): BigNumber[][] {
|
||||||
|
const quotes: BigNumber[][] = [];
|
||||||
|
for (const source of sources) {
|
||||||
|
const sampleOutputs = [];
|
||||||
|
for (const amount of sampleAmounts) {
|
||||||
|
if (source === 'Eth2Dai') {
|
||||||
|
sampleOutputs.push(getDeterministicBuyQuote(ETH2DAI_SALT, sellToken, buyToken, amount));
|
||||||
|
} else if (source === 'Uniswap') {
|
||||||
|
sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quotes.push(sampleOutputs);
|
||||||
|
}
|
||||||
|
return quotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicOrderInfo(order: Order): OrderInfo {
|
||||||
|
const hash = getPackedHash(toHex(order.salt, 32));
|
||||||
|
return {
|
||||||
|
orderHash: hash,
|
||||||
|
orderStatus: new BigNumber(hash).mod(255).toNumber(),
|
||||||
|
orderTakerAssetFilledAmount: new BigNumber(hash).mod(order.takerAssetAmount),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getERC20AssetData(tokenAddress: string): string {
|
||||||
|
return hexConcat(ERC20_PROXY_ID, hexLeftPad(tokenAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
|
||||||
|
const tokenDecimals = getDeterministicTokenDecimals(tokenAddress);
|
||||||
|
const _upperLimit = getRandomPortion(getRandomInteger(1, 1000).times(10 ** tokenDecimals));
|
||||||
|
const _count = count || _.random(1, 16);
|
||||||
|
const d = _upperLimit.div(_count);
|
||||||
|
return _.times(_count, i => d.times((i + 1) / _count).integerValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOrder(makerToken: string, takerToken: string): Order {
|
||||||
|
return {
|
||||||
|
chainId: 1337,
|
||||||
|
exchangeAddress: randomAddress(),
|
||||||
|
makerAddress: randomAddress(),
|
||||||
|
takerAddress: randomAddress(),
|
||||||
|
senderAddress: randomAddress(),
|
||||||
|
feeRecipientAddress: randomAddress(),
|
||||||
|
makerAssetAmount: getRandomInteger(1, 1e18),
|
||||||
|
takerAssetAmount: getRandomInteger(1, 1e18),
|
||||||
|
makerFee: getRandomInteger(1, 1e18),
|
||||||
|
takerFee: getRandomInteger(1, 1e18),
|
||||||
|
makerAssetData: getERC20AssetData(makerToken),
|
||||||
|
takerAssetData: getERC20AssetData(takerToken),
|
||||||
|
makerFeeAssetData: getERC20AssetData(randomAddress()),
|
||||||
|
takerFeeAssetData: getERC20AssetData(randomAddress()),
|
||||||
|
salt: new BigNumber(hexRandom()),
|
||||||
|
expirationTimeSeconds: getRandomInteger(0, 2 ** 32),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOrders(makerToken: string, takerToken: string, count?: number): Order[] {
|
||||||
|
return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('queryOrders()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
it('returns the results of `getOrderInfo()` for each order', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
|
const expected = orders.map(getDeterministicOrderInfo);
|
||||||
|
const actual = await testContract.queryOrders(orders).callAsync();
|
||||||
|
expect(actual).to.deep.eq(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty for no orders', async () => {
|
||||||
|
const actual = await testContract.queryOrders([]).callAsync();
|
||||||
|
expect(actual).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('queryOrdersAndSampleSells()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the results of `getOrderInfo()` for each order', async () => {
|
||||||
|
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
|
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
|
||||||
|
const [orderInfos] = await testContract
|
||||||
|
.queryOrdersAndSampleSells(orders, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(orderInfos).to.deep.eq(expectedOrderInfos);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return quotes for all sources', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
||||||
|
const [, quotes] = await testContract
|
||||||
|
.queryOrdersAndSampleSells(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
||||||
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
|
sampleAmounts,
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if no orders are passed in', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleSells([], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with an unsupported source', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleSells(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
||||||
|
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
|
||||||
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with non-ERC20 maker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleSells(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with non-ERC20 taker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleSells(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with invalid maker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleSells(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
makerAssetData: INVALID_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with invalid taker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleSells(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
takerAssetData: INVALID_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('queryOrdersAndSampleBuys()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the results of `getOrderInfo()` for each order', async () => {
|
||||||
|
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
|
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
|
||||||
|
const [orderInfos] = await testContract
|
||||||
|
.queryOrdersAndSampleBuys(orders, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(orderInfos).to.deep.eq(expectedOrderInfos);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return quotes for all sources', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
||||||
|
const [, quotes] = await testContract
|
||||||
|
.queryOrdersAndSampleBuys(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
||||||
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
|
sampleAmounts,
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if no orders are passed in', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleBuys([], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with an unsupported source', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleBuys(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
||||||
|
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
|
||||||
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if kyber is passed in as a source', async () => {
|
||||||
|
const sources = [...BUY_SOURCES, 'Kyber'];
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleBuys(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
||||||
|
sources.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with non-ERC20 maker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleBuys(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with non-ERC20 taker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleBuys(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with invalid maker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleBuys(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
makerAssetData: INVALID_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with invalid taker asset data', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.queryOrdersAndSampleBuys(
|
||||||
|
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
||||||
|
...o,
|
||||||
|
takerAssetData: INVALID_ASSET_DATA,
|
||||||
|
})),
|
||||||
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleSells()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty quotes with no sample amounts', async () => {
|
||||||
|
const emptyQuotes = _.times(SELL_SOURCES.length, () => []);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(emptyQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return quotes for all sources', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return quotes for some sources', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const sources = _.sampleSize(SELL_SOURCES, 1);
|
||||||
|
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSells(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with an unsupported source', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.sampleSells(
|
||||||
|
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
|
||||||
|
TAKER_TOKEN,
|
||||||
|
MAKER_TOKEN,
|
||||||
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleBuys()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty quotes with no sample amounts', async () => {
|
||||||
|
const emptyQuotes = _.times(BUY_SOURCES.length, () => []);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(emptyQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return quotes for all sources', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return quotes for some sources', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const sources = _.sampleSize(BUY_SOURCES, 1);
|
||||||
|
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws with an unsupported source', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.sampleBuys(
|
||||||
|
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
|
||||||
|
TAKER_TOKEN,
|
||||||
|
MAKER_TOKEN,
|
||||||
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if kyber is passed in as a source', async () => {
|
||||||
|
const sources = [...BUY_SOURCES, 'Kyber'];
|
||||||
|
const tx = testContract
|
||||||
|
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, getSampleAmounts(MAKER_TOKEN))
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleSellsFromKyberNetwork()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if tokens are the same', async () => {
|
||||||
|
const tx = testContract.sampleSellsFromKyberNetwork(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(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
|
||||||
|
expect(quotes).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return many quotes', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote token -> ETH', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote ETH -> token', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleSellsFromEth2Dai()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if tokens are the same', async () => {
|
||||||
|
const tx = testContract.sampleSellsFromEth2Dai(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();
|
||||||
|
expect(quotes).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return many quotes', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote token -> ETH', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote ETH -> token', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleBuysFromEth2Dai()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if tokens are the same', async () => {
|
||||||
|
const tx = testContract.sampleBuysFromEth2Dai(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();
|
||||||
|
expect(quotes).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return many quotes', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote token -> ETH', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote ETH -> token', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleSellsFromUniswap()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
expect(quotes).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return many quotes', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote token -> ETH', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote ETH -> token', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if no exchange exists for the maker token', async () => {
|
||||||
|
const nonExistantToken = randomAddress();
|
||||||
|
const tx = testContract
|
||||||
|
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if no exchange exists for the taker token', async () => {
|
||||||
|
const nonExistantToken = randomAddress();
|
||||||
|
const tx = testContract
|
||||||
|
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleBuysFromUniswap()', () => {
|
||||||
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
expect(quotes).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return many quotes', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote token -> ETH', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote ETH -> token', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if no exchange exists for the maker token', async () => {
|
||||||
|
const nonExistantToken = randomAddress();
|
||||||
|
const tx = testContract
|
||||||
|
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if no exchange exists for the taker token', async () => {
|
||||||
|
const nonExistantToken = randomAddress();
|
||||||
|
const tx = testContract
|
||||||
|
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
|
||||||
|
.callAsync();
|
||||||
|
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
12
contracts/erc20-bridge-sampler/test/wrappers.ts
Normal file
12
contracts/erc20-bridge-sampler/test/wrappers.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export * from '../test/generated-wrappers/deployment_constants';
|
||||||
|
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
||||||
|
export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
|
||||||
|
export * from '../test/generated-wrappers/i_eth2_dai';
|
||||||
|
export * from '../test/generated-wrappers/i_kyber_network';
|
||||||
|
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
|
||||||
|
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
|
96
contracts/erc20-bridge-sampler/truffle-config.js
Normal file
96
contracts/erc20-bridge-sampler/truffle-config.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* Use this file to configure your truffle project. It's seeded with some
|
||||||
|
* common settings for different networks and features like migrations,
|
||||||
|
* compilation and testing. Uncomment the ones you need or modify
|
||||||
|
* them to suit your project as necessary.
|
||||||
|
*
|
||||||
|
* More information about configuration can be found at:
|
||||||
|
*
|
||||||
|
* truffleframework.com/docs/advanced/configuration
|
||||||
|
*
|
||||||
|
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
|
||||||
|
* to sign your transactions before they're sent to a remote public node. Infura accounts
|
||||||
|
* are available for free at: infura.io/register.
|
||||||
|
*
|
||||||
|
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
|
||||||
|
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
|
||||||
|
* phrase from a file you've .gitignored so it doesn't accidentally become public.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// const HDWalletProvider = require('truffle-hdwallet-provider');
|
||||||
|
// const infuraKey = "fj4jll3k.....";
|
||||||
|
//
|
||||||
|
// const fs = require('fs');
|
||||||
|
// const mnemonic = fs.readFileSync(".secret").toString().trim();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Networks define how you connect to your ethereum client and let you set the
|
||||||
|
* defaults web3 uses to send transactions. If you don't specify one truffle
|
||||||
|
* will spin up a development blockchain for you on port 9545 when you
|
||||||
|
* run `develop` or `test`. You can ask a truffle command to use a specific
|
||||||
|
* network from the command line, e.g
|
||||||
|
*
|
||||||
|
* $ truffle test --network <network-name>
|
||||||
|
*/
|
||||||
|
|
||||||
|
networks: {
|
||||||
|
// Useful for testing. The `development` name is special - truffle uses it by default
|
||||||
|
// if it's defined here and no other network is specified at the command line.
|
||||||
|
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
|
||||||
|
// tab if you use this network and you must also set the `host`, `port` and `network_id`
|
||||||
|
// options below to some value.
|
||||||
|
//
|
||||||
|
// development: {
|
||||||
|
// host: "127.0.0.1", // Localhost (default: none)
|
||||||
|
// port: 8545, // Standard Ethereum port (default: none)
|
||||||
|
// network_id: "*", // Any network (default: none)
|
||||||
|
// },
|
||||||
|
// Another network with more advanced options...
|
||||||
|
// advanced: {
|
||||||
|
// port: 8777, // Custom port
|
||||||
|
// network_id: 1342, // Custom network
|
||||||
|
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
|
||||||
|
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
|
||||||
|
// from: <address>, // Account to send txs from (default: accounts[0])
|
||||||
|
// websockets: true // Enable EventEmitter interface for web3 (default: false)
|
||||||
|
// },
|
||||||
|
// Useful for deploying to a public network.
|
||||||
|
// NB: It's important to wrap the provider as a function.
|
||||||
|
// ropsten: {
|
||||||
|
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
|
||||||
|
// network_id: 3, // Ropsten's id
|
||||||
|
// gas: 5500000, // Ropsten has a lower block limit than mainnet
|
||||||
|
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
|
||||||
|
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
|
||||||
|
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
|
||||||
|
// },
|
||||||
|
// Useful for private networks
|
||||||
|
// private: {
|
||||||
|
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
|
||||||
|
// network_id: 2111, // This network is yours, in the cloud.
|
||||||
|
// production: true // Treats this network as if it was a public net. (default: false)
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set default mocha options here, use special reporters etc.
|
||||||
|
mocha: {
|
||||||
|
// timeout: 100000
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure your compilers
|
||||||
|
compilers: {
|
||||||
|
solc: {
|
||||||
|
version: '0.5.9',
|
||||||
|
settings: {
|
||||||
|
evmVersion: 'constantinople',
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 1000000,
|
||||||
|
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
17
contracts/erc20-bridge-sampler/tsconfig.json
Normal file
17
contracts/erc20-bridge-sampler/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig",
|
||||||
|
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||||
|
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||||
|
"files": [
|
||||||
|
"generated-artifacts/ERC20BridgeSampler.json",
|
||||||
|
"generated-artifacts/IERC20BridgeSampler.json",
|
||||||
|
"test/generated-artifacts/DeploymentConstants.json",
|
||||||
|
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||||
|
"test/generated-artifacts/IERC20BridgeSampler.json",
|
||||||
|
"test/generated-artifacts/IEth2Dai.json",
|
||||||
|
"test/generated-artifacts/IKyberNetwork.json",
|
||||||
|
"test/generated-artifacts/IUniswapExchangeQuotes.json",
|
||||||
|
"test/generated-artifacts/TestERC20BridgeSampler.json"
|
||||||
|
],
|
||||||
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
|
}
|
10
contracts/erc20-bridge-sampler/tslint.json
Normal file
10
contracts/erc20-bridge-sampler/tslint.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": ["@0x/tslint-config"],
|
||||||
|
"rules": {
|
||||||
|
"custom-no-magic-numbers": false,
|
||||||
|
"max-file-line-count": false
|
||||||
|
},
|
||||||
|
"linterOptions": {
|
||||||
|
"exclude": ["src/artifacts.ts", "test/artifacts.ts"]
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||||
"pr": 2330
|
"pr": 2330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `decimals()` to `LibERC20Token`.",
|
||||||
|
"pr": 2344
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": 1574030254
|
"timestamp": 1574030254
|
||||||
|
@ -24,6 +24,7 @@ import "../src/interfaces/IERC20Token.sol";
|
|||||||
|
|
||||||
|
|
||||||
library LibERC20Token {
|
library LibERC20Token {
|
||||||
|
bytes constant private DECIMALS_CALL_DATA = hex"313ce567";
|
||||||
|
|
||||||
/// @dev Calls `IERC20Token(token).approve()`.
|
/// @dev Calls `IERC20Token(token).approve()`.
|
||||||
/// Reverts if `false` is returned or if the return
|
/// Reverts if `false` is returned or if the return
|
||||||
@ -91,6 +92,21 @@ library LibERC20Token {
|
|||||||
_callWithOptionalBooleanResult(token, callData);
|
_callWithOptionalBooleanResult(token, callData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Retrieves the number of decimals for a token.
|
||||||
|
/// Returns `18` if the call reverts.
|
||||||
|
/// @return The number of decimals places for the token.
|
||||||
|
function decimals(address token)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint8 tokenDecimals)
|
||||||
|
{
|
||||||
|
tokenDecimals = 18;
|
||||||
|
(bool didSucceed, bytes memory resultData) = token.staticcall(DECIMALS_CALL_DATA);
|
||||||
|
if (didSucceed && resultData.length == 32) {
|
||||||
|
tokenDecimals = uint8(LibBytes.readUint256(resultData, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Executes a call on address `target` with calldata `callData`
|
/// @dev Executes a call on address `target` with calldata `callData`
|
||||||
/// and asserts that either nothing was returned or a single boolean
|
/// and asserts that either nothing was returned or a single boolean
|
||||||
/// was returned equal to `true`.
|
/// was returned equal to `true`.
|
||||||
|
@ -69,4 +69,16 @@ contract TestLibERC20Token {
|
|||||||
target.setBehavior(shouldRevert, revertData, returnData);
|
target.setBehavior(shouldRevert, revertData, returnData);
|
||||||
LibERC20Token.transferFrom(address(target), from, to, amount);
|
LibERC20Token.transferFrom(address(target), from, to, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testDecimals(
|
||||||
|
bool shouldRevert,
|
||||||
|
bytes calldata revertData,
|
||||||
|
bytes calldata returnData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (uint8)
|
||||||
|
{
|
||||||
|
target.setBehavior(shouldRevert, revertData, returnData);
|
||||||
|
return LibERC20Token.decimals(address(target));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,14 @@ contract TestLibERC20TokenTarget {
|
|||||||
_execute();
|
_execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decimals()
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint8)
|
||||||
|
{
|
||||||
|
_execute();
|
||||||
|
}
|
||||||
|
|
||||||
function _execute() private view {
|
function _execute() private view {
|
||||||
if (_shouldRevert) {
|
if (_shouldRevert) {
|
||||||
bytes memory revertData = _revertData;
|
bytes memory revertData = _revertData;
|
||||||
|
@ -16,6 +16,7 @@ import { artifacts } from './artifacts';
|
|||||||
blockchainTests('LibERC20Token', env => {
|
blockchainTests('LibERC20Token', env => {
|
||||||
let testContract: TestLibERC20TokenContract;
|
let testContract: TestLibERC20TokenContract;
|
||||||
const REVERT_STRING = 'WHOOPSIE';
|
const REVERT_STRING = 'WHOOPSIE';
|
||||||
|
const ENCODED_REVERT = new StringRevertError(REVERT_STRING).encode();
|
||||||
const ENCODED_TRUE = hexLeftPad(1);
|
const ENCODED_TRUE = hexLeftPad(1);
|
||||||
const ENCODED_FALSE = hexLeftPad(0);
|
const ENCODED_FALSE = hexLeftPad(0);
|
||||||
const ENCODED_TWO = hexLeftPad(2);
|
const ENCODED_TWO = hexLeftPad(2);
|
||||||
@ -31,16 +32,12 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function encodeRevert(message: string): string {
|
|
||||||
return new StringRevertError(message).encode();
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('approve()', () => {
|
describe('approve()', () => {
|
||||||
it('calls the target with the correct arguments', async () => {
|
it('calls the target with the correct arguments', async () => {
|
||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const { logs } = await testContract
|
const { logs } = await testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(logs).to.be.length(1);
|
expect(logs).to.be.length(1);
|
||||||
verifyEventsFromLogs(logs, [{ spender, allowance }], TestLibERC20TokenTargetEvents.ApproveCalled);
|
verifyEventsFromLogs(logs, [{ spender, allowance }], TestLibERC20TokenTargetEvents.ApproveCalled);
|
||||||
@ -50,7 +47,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -58,7 +55,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, constants.NULL_BYTES, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -66,7 +63,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_FALSE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -76,7 +73,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TWO, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_TWO, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -86,7 +83,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -96,7 +93,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_LONG_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -106,7 +103,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
|
.testApprove(true, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
return expect(tx).to.revertWith(REVERT_STRING);
|
return expect(tx).to.revertWith(REVERT_STRING);
|
||||||
});
|
});
|
||||||
@ -126,7 +123,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const { logs } = await testContract
|
const { logs } = await testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(logs).to.be.length(1);
|
expect(logs).to.be.length(1);
|
||||||
verifyEventsFromLogs(logs, [{ to, amount }], TestLibERC20TokenTargetEvents.TransferCalled);
|
verifyEventsFromLogs(logs, [{ to, amount }], TestLibERC20TokenTargetEvents.TransferCalled);
|
||||||
@ -136,7 +133,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -144,7 +141,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, to, amount)
|
.testTransfer(false, ENCODED_REVERT, constants.NULL_BYTES, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -152,7 +149,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_FALSE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -162,7 +159,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TWO, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_TWO, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -172,7 +169,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -182,7 +179,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_LONG_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -192,7 +189,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
|
.testTransfer(true, ENCODED_REVERT, ENCODED_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
return expect(tx).to.revertWith(REVERT_STRING);
|
return expect(tx).to.revertWith(REVERT_STRING);
|
||||||
});
|
});
|
||||||
@ -213,7 +210,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const { logs } = await testContract
|
const { logs } = await testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(logs).to.be.length(1);
|
expect(logs).to.be.length(1);
|
||||||
verifyEventsFromLogs(logs, [{ from: owner, to, amount }], TestLibERC20TokenTargetEvents.TransferFromCalled);
|
verifyEventsFromLogs(logs, [{ from: owner, to, amount }], TestLibERC20TokenTargetEvents.TransferFromCalled);
|
||||||
@ -224,7 +221,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -233,7 +230,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, constants.NULL_BYTES, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -242,7 +239,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_FALSE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -253,7 +250,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TWO, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TWO, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -264,7 +261,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -275,7 +272,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_LONG_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -286,7 +283,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
|
.testTransferFrom(true, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
return expect(tx).to.revertWith(REVERT_STRING);
|
return expect(tx).to.revertWith(REVERT_STRING);
|
||||||
});
|
});
|
||||||
@ -301,4 +298,39 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
return expect(tx).to.be.rejectedWith('revert');
|
return expect(tx).to.be.rejectedWith('revert');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('decimals()', () => {
|
||||||
|
const DEFAULT_DECIMALS = 18;
|
||||||
|
const ENCODED_ZERO = hexLeftPad(0);
|
||||||
|
const ENCODED_SHORT_ZERO = hexLeftPad(0, 31);
|
||||||
|
const ENCODED_LONG_ZERO = hexLeftPad(0, 33);
|
||||||
|
const randomDecimals = () => Math.floor(Math.random() * 256) + 1;
|
||||||
|
|
||||||
|
it('returns the number of decimals defined by the token', async () => {
|
||||||
|
const decimals = randomDecimals();
|
||||||
|
const encodedDecimals = hexLeftPad(decimals);
|
||||||
|
const result = await testContract.testDecimals(false, ENCODED_REVERT, encodedDecimals).callAsync();
|
||||||
|
return expect(result).to.bignumber.eq(decimals);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 0 if the token returns 0', async () => {
|
||||||
|
const result = await testContract.testDecimals(false, ENCODED_REVERT, ENCODED_ZERO).callAsync();
|
||||||
|
return expect(result).to.bignumber.eq(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 18 if the token returns less than 32 bytes', async () => {
|
||||||
|
const result = await testContract.testDecimals(false, ENCODED_REVERT, ENCODED_SHORT_ZERO).callAsync();
|
||||||
|
return expect(result).to.bignumber.eq(DEFAULT_DECIMALS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 18 if the token returns greater than 32 bytes', async () => {
|
||||||
|
const result = await testContract.testDecimals(false, ENCODED_REVERT, ENCODED_LONG_ZERO).callAsync();
|
||||||
|
return expect(result).to.bignumber.eq(DEFAULT_DECIMALS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 18 if the token reverts', async () => {
|
||||||
|
const result = await testContract.testDecimals(true, ENCODED_REVERT, ENCODED_ZERO).callAsync();
|
||||||
|
return expect(result).to.bignumber.eq(DEFAULT_DECIMALS);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
"lint:contracts": "wsrun lint -p ${npm_package_config_contractsPackages} -c --fast-exit --stages --exclude-missing"
|
"lint:contracts": "wsrun lint -p ${npm_package_config_contractsPackages} -c --fast-exit --stages --exclude-missing"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"contractsPackages": "@0x/contracts-asset-proxy @0x/contracts-dev-utils @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-integrations @0x/contracts-multisig @0x/contracts-staking @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-coordinator @0x/contracts-tests",
|
"contractsPackages": "@0x/contracts-asset-proxy @0x/contracts-dev-utils @0x/contracts-erc20 @0x/contracts-erc20-bridge-sampler @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-integrations @0x/contracts-multisig @0x/contracts-staking @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-coordinator @0x/contracts-tests @0x/contracts-erc20-bridge-sampler",
|
||||||
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
|
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
|
||||||
"packagesWithDocPages": "0x.js @0x/contract-wrappers @0x/connect @0x/json-schemas @0x/subproviders @0x/web3-wrapper @0x/order-utils @0x/sol-compiler @0x/sol-coverage @0x/sol-profiler @0x/sol-trace @0x/dev-utils @0x/asset-swapper @0x/migrations @0x/orderbook @0x/contracts-asset-proxy @0x/contracts-coordinator @0x/contracts-dev-utils @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-extensions @0x/contracts-staking",
|
"packagesWithDocPages": "0x.js @0x/contract-wrappers @0x/connect @0x/json-schemas @0x/subproviders @0x/web3-wrapper @0x/order-utils @0x/sol-compiler @0x/sol-coverage @0x/sol-profiler @0x/sol-trace @0x/dev-utils @0x/asset-swapper @0x/migrations @0x/orderbook @0x/contracts-asset-proxy @0x/contracts-coordinator @0x/contracts-dev-utils @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-extensions @0x/contracts-staking",
|
||||||
"ignoreDependencyVersions": "@types/styled-components @types/node",
|
"ignoreDependencyVersions": "@types/styled-components @types/node",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user