Compare commits
190 Commits
@0x/migrat
...
@0x/sol-co
Author | SHA1 | Date | |
---|---|---|---|
|
5921208ea6 | ||
|
f89c78abd1 | ||
|
74d3b9334c | ||
|
919fc66b9d | ||
|
400fb5a5bb | ||
|
3bb4f9085c | ||
|
714c6cec3c | ||
|
cb69921202 | ||
|
277a0adac9 | ||
|
02d14f504f | ||
|
1ab7664a60 | ||
|
1be46ffb7e | ||
|
6ca52aed0d | ||
|
74e20970e2 | ||
|
93f2b6b4d8 | ||
|
00e616b57a | ||
|
e436673304 | ||
|
ce04d3ce41 | ||
|
4e46bf4697 | ||
|
9b95508f99 | ||
|
c8c24456c1 | ||
|
80a6e82e1b | ||
|
80cec20d38 | ||
|
26cb22020d | ||
|
09d05d09c9 | ||
|
160c91f908 | ||
|
58f41bcd42 | ||
|
0235429995 | ||
|
f79697e117 | ||
|
6f0019c71e | ||
|
1a04a18245 | ||
|
0cf3ff8209 | ||
|
7f56091fbd | ||
|
391f9b31f6 | ||
|
1d7c0f504a | ||
|
9cdc62f918 | ||
|
6027d0481e | ||
|
277fb92f9e | ||
|
79b6c3c1af | ||
|
ad17174119 | ||
|
6c2692eec0 | ||
|
08640e8575 | ||
|
4a2575136f | ||
|
e792afad17 | ||
|
5ecc4b027d | ||
|
bc540f0cf0 | ||
|
a24b14c465 | ||
|
c3f36c3123 | ||
|
1d34da7557 | ||
|
27d74a4327 | ||
|
8a20cc682c | ||
|
9b20359e7b | ||
|
8866d0ccef | ||
|
7fa91a9971 | ||
|
28d1f3eef0 | ||
|
f321cf6655 | ||
|
14ade737da | ||
|
41b1b1f141 | ||
|
25dfd47d32 | ||
|
6afa9c8b92 | ||
|
2fc449da4c | ||
|
5dd3b8cf9d | ||
|
e834fa0050 | ||
|
9a97401606 | ||
|
410a3fef18 | ||
|
969b9814d5 | ||
|
2275e27b87 | ||
|
62b06cd204 | ||
|
350934ca21 | ||
|
6332673434 | ||
|
f217840998 | ||
|
089ec35ceb | ||
|
fecd0b809e | ||
|
4707a46561 | ||
|
616533c5a8 | ||
|
c5b2991821 | ||
|
c36d0fdc7c | ||
|
544e09cf4b | ||
|
c110dc9e6a | ||
|
3bf37d6afd | ||
|
b80ae5796b | ||
|
2083632299 | ||
|
3ca2f8ac9e | ||
|
7172432084 | ||
|
0e6afd147f | ||
|
46275a4f43 | ||
|
1dca378e03 | ||
|
06669594b1 | ||
|
c09ac58ac0 | ||
|
e01d32ef1a | ||
|
5ea3bcf59e | ||
|
aa8b14b7ee | ||
|
e1722cf739 | ||
|
7a7f70e15d | ||
|
b3c3ec16e5 | ||
|
149f863951 | ||
|
684d09faac | ||
|
8a42691c80 | ||
|
d591b3dd98 | ||
|
a90fb4d8b6 | ||
|
ebd08d9c63 | ||
|
71731d223b | ||
|
726ea5e01e | ||
|
16c7d2964b | ||
|
5a6e494bda | ||
|
88c6d89fbb | ||
|
de12da18da | ||
|
8d10736934 | ||
|
2328e02d82 | ||
|
87cd5fca90 | ||
|
b70cb726c5 | ||
|
295811ed5a | ||
|
4bc55551c6 | ||
|
2b8c6dc8f9 | ||
|
8b27380feb | ||
|
8de3a90851 | ||
|
f8bb94d721 | ||
|
0c6d06e7bb | ||
|
b0aa5d3af2 | ||
|
27d09713fd | ||
|
ff18852879 | ||
|
e515c91e5e | ||
|
1d5800c4f7 | ||
|
de8f190945 | ||
|
f371e3c8d3 | ||
|
b15a6290a7 | ||
|
0f151db355 | ||
|
fa99b75d1f | ||
|
4a299c1f39 | ||
|
30a2015a68 | ||
|
c7c8a4891f | ||
|
fe9fc6b459 | ||
|
2113fb490d | ||
|
0afedbd252 | ||
|
3dec38450a | ||
|
d7a00b05e3 | ||
|
ff2cc8c887 | ||
|
0571a96cea | ||
|
b7b457b076 | ||
|
d2c12005b2 | ||
|
25f26d7e5f | ||
|
9d5724e1a0 | ||
|
784d23ec87 | ||
|
709689a7ee | ||
|
35d5d3d995 | ||
|
630a8d8a4e | ||
|
54eb1d9055 | ||
|
cb63caea61 | ||
|
8e046bb022 | ||
|
189b53b8c4 | ||
|
9b7277d464 | ||
|
551a65c069 | ||
|
4c21a697f4 | ||
|
a8506c07ae | ||
|
b0feb85b5c | ||
|
f371eba8ad | ||
|
265fa52ace | ||
|
c1f5322d38 | ||
|
4415e00b38 | ||
|
d4e46c5a9c | ||
|
bf5b9949fe | ||
|
3c11a2b1da | ||
|
1248868169 | ||
|
930b95a548 | ||
|
27c9f68c7c | ||
|
358d4d86a7 | ||
|
d55eea2239 | ||
|
4507954ea5 | ||
|
8e0a83f8d8 | ||
|
b6ec09e6cf | ||
|
ed4e90623d | ||
|
38cdb48748 | ||
|
71bfe9b745 | ||
|
9e7645a167 | ||
|
6dccc37143 | ||
|
3310310d8c | ||
|
abb499aad8 | ||
|
1afc09b08a | ||
|
0e86d72f05 | ||
|
c9857a2764 | ||
|
701ba3902c | ||
|
bb3ec970a9 | ||
|
1d023e6db5 | ||
|
1bd906ecb3 | ||
|
7cbffdb86b | ||
|
b979196ffd | ||
|
2949db5f49 | ||
|
47c3ed9705 | ||
|
e00f059a4a | ||
|
731a823cc2 |
@@ -23,7 +23,7 @@ jobs:
|
|||||||
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
||||||
- run:
|
- run:
|
||||||
name: install-yarn
|
name: install-yarn
|
||||||
command: npm install --global yarn@1.17.0
|
command: npm install --force --global yarn@1.17.0
|
||||||
- run:
|
- run:
|
||||||
name: yarn
|
name: yarn
|
||||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||||
@@ -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 @0x/contracts-erc20-bridge-sampler
|
- 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.
|
||||||
@@ -193,17 +193,14 @@ jobs:
|
|||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
- image: 0xorg/ganache-cli
|
- image: 0xorg/ganache-cli:6.0.0
|
||||||
- image: 0xorg/mesh:0xV3
|
- image: 0xorg/mesh:0xV3
|
||||||
environment:
|
environment:
|
||||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||||
ETHEREUM_NETWORK_ID: '50'
|
|
||||||
ETHEREUM_CHAIN_ID: '1337'
|
ETHEREUM_CHAIN_ID: '1337'
|
||||||
USE_BOOTSTRAP_LIST: 'true'
|
VERBOSITY: 5
|
||||||
VERBOSITY: 3
|
BLOCK_POLLING_INTERVAL: '50ms'
|
||||||
PRIVATE_KEY_PATH: ''
|
ETHEREUM_RPC_MAX_REQUESTS_PER_24_HR_UTC: '1778000'
|
||||||
BLOCK_POLLING_INTERVAL: '5s'
|
|
||||||
P2P_LISTEN_PORT: '60557'
|
|
||||||
command: |
|
command: |
|
||||||
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
||||||
- image: 0xorg/launch-kit-backend:v3
|
- image: 0xorg/launch-kit-backend:v3
|
||||||
|
28
.gitignore
vendored
28
.gitignore
vendored
@@ -160,33 +160,7 @@ contracts/exchange-forwarder/generated-wrappers/
|
|||||||
contracts/exchange-forwarder/test/generated-wrappers/
|
contracts/exchange-forwarder/test/generated-wrappers/
|
||||||
contracts/dev-utils/generated-wrappers/
|
contracts/dev-utils/generated-wrappers/
|
||||||
contracts/dev-utils/test/generated-wrappers/
|
contracts/dev-utils/test/generated-wrappers/
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
|
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_registry/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_bridge_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/forwarder/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_asset_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
|
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_vault/__init__.py
|
|
||||||
|
|
||||||
# solc-bin in sol-compiler
|
# solc-bin in sol-compiler
|
||||||
packages/sol-compiler/solc_bin/
|
packages/sol-compiler/solc_bin/
|
||||||
|
@@ -14,8 +14,8 @@ packages/abi-gen/ @feuGeneA
|
|||||||
packages/base-contract/ @xianny
|
packages/base-contract/ @xianny
|
||||||
packages/connect/ @fragosti
|
packages/connect/ @fragosti
|
||||||
packages/abi-gen-templates/ @feuGeneA @xianny
|
packages/abi-gen-templates/ @feuGeneA @xianny
|
||||||
packages/contract-addresses/ @albrow
|
packages/contract-addresses/ @abandeali1
|
||||||
packages/contract-artifacts/ @albrow
|
packages/contract-artifacts/ @abandeali1
|
||||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||||
packages/devnet/ @albrow
|
packages/devnet/ @albrow
|
||||||
packages/ethereum-types/ @LogvinovLeon
|
packages/ethereum-types/ @LogvinovLeon
|
||||||
|
@@ -1,4 +1,40 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "3.1.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "3.1.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "3.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Integration tests for DydxBridge with ERC20BridgeProxy.",
|
||||||
|
"pr": 2401
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix `UniswapBridge` token -> token transfer call.",
|
||||||
|
"pr": 2412
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix `KyberBridge` incorrect `minConversionRate` calculation.",
|
||||||
|
"pr": 2412
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1578272714
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@@ -5,6 +5,20 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.1.2 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.1.1 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.1.0 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Integration tests for DydxBridge with ERC20BridgeProxy. (#2401)
|
||||||
|
* Fix `UniswapBridge` token -> token transfer call. (#2412)
|
||||||
|
* Fix `KyberBridge` incorrect `minConversionRate` calculation. (#2412)
|
||||||
|
|
||||||
## v3.0.2 - _December 17, 2019_
|
## v3.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -24,6 +24,7 @@ import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
|||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
import "../interfaces/IKyberNetworkProxy.sol";
|
import "../interfaces/IKyberNetworkProxy.sol";
|
||||||
|
|
||||||
@@ -34,6 +35,8 @@ contract KyberBridge is
|
|||||||
IWallet,
|
IWallet,
|
||||||
DeploymentConstants
|
DeploymentConstants
|
||||||
{
|
{
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
// @dev Structure used internally to get around stack limits.
|
// @dev Structure used internally to get around stack limits.
|
||||||
struct TradeState {
|
struct TradeState {
|
||||||
IKyberNetworkProxy kyber;
|
IKyberNetworkProxy kyber;
|
||||||
@@ -41,6 +44,7 @@ contract KyberBridge is
|
|||||||
address fromTokenAddress;
|
address fromTokenAddress;
|
||||||
uint256 fromTokenBalance;
|
uint256 fromTokenBalance;
|
||||||
uint256 payableAmount;
|
uint256 payableAmount;
|
||||||
|
uint256 conversionRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Kyber ETH pseudo-address.
|
/// @dev Kyber ETH pseudo-address.
|
||||||
@@ -81,11 +85,23 @@ contract KyberBridge is
|
|||||||
state.weth = IEtherToken(_getWethAddress());
|
state.weth = IEtherToken(_getWethAddress());
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
// Decode the bridge data to get the `fromTokenAddress`.
|
||||||
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
|
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||||
|
// Query the balance of "from" tokens.
|
||||||
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
||||||
if (state.fromTokenBalance == 0) {
|
if (state.fromTokenBalance == 0) {
|
||||||
// Return failure if no input tokens.
|
// Return failure if no input tokens.
|
||||||
return BRIDGE_FAILED;
|
return BRIDGE_FAILED;
|
||||||
}
|
}
|
||||||
|
// Compute the conversion rate, expressed in 18 decimals.
|
||||||
|
// The sequential notation is to get around stack limits.
|
||||||
|
state.conversionRate = KYBER_RATE_BASE;
|
||||||
|
state.conversionRate = state.conversionRate.safeMul(amount);
|
||||||
|
state.conversionRate = state.conversionRate.safeMul(
|
||||||
|
10 ** uint256(LibERC20Token.decimals(state.fromTokenAddress))
|
||||||
|
);
|
||||||
|
state.conversionRate = state.conversionRate.safeDiv(state.fromTokenBalance);
|
||||||
|
state.conversionRate = state.conversionRate.safeDiv(
|
||||||
|
10 ** uint256(LibERC20Token.decimals(toTokenAddress))
|
||||||
|
);
|
||||||
if (state.fromTokenAddress == toTokenAddress) {
|
if (state.fromTokenAddress == toTokenAddress) {
|
||||||
// Just transfer the tokens if they're the same.
|
// Just transfer the tokens if they're the same.
|
||||||
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
|
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
|
||||||
@@ -118,7 +134,7 @@ contract KyberBridge is
|
|||||||
uint256(-1),
|
uint256(-1),
|
||||||
// Compute the minimum conversion rate, which is expressed in units with
|
// Compute the minimum conversion rate, which is expressed in units with
|
||||||
// 18 decimal places.
|
// 18 decimal places.
|
||||||
(KYBER_RATE_BASE * amount) / state.fromTokenBalance,
|
state.conversionRate,
|
||||||
// No affiliate address.
|
// No affiliate address.
|
||||||
address(0)
|
address(0)
|
||||||
);
|
);
|
||||||
|
@@ -134,8 +134,8 @@ contract UniswapBridge is
|
|||||||
state.fromTokenBalance,
|
state.fromTokenBalance,
|
||||||
// Minimum buy amount.
|
// Minimum buy amount.
|
||||||
amount,
|
amount,
|
||||||
// No minimum intermediate ETH buy amount.
|
// Must buy at least 1 intermediate ETH.
|
||||||
0,
|
1,
|
||||||
// Expires after this block.
|
// Expires after this block.
|
||||||
block.timestamp,
|
block.timestamp,
|
||||||
// Recipient is `to`.
|
// Recipient is `to`.
|
||||||
|
@@ -18,10 +18,22 @@
|
|||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract PotLike {
|
||||||
|
function chi() external returns (uint256);
|
||||||
|
function rho() external returns (uint256);
|
||||||
|
function drip() external returns (uint256);
|
||||||
|
function join(uint256) external;
|
||||||
|
function exit(uint256) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// The actual Chai contract can be found here: https://github.com/dapphub/chai
|
// The actual Chai contract can be found here: https://github.com/dapphub/chai
|
||||||
contract IChai {
|
contract IChai is
|
||||||
|
IERC20Token
|
||||||
|
{
|
||||||
/// @dev Withdraws Dai owned by `src`
|
/// @dev Withdraws Dai owned by `src`
|
||||||
/// @param src Address that owns Dai.
|
/// @param src Address that owns Dai.
|
||||||
/// @param wad Amount of Dai to withdraw.
|
/// @param wad Amount of Dai to withdraw.
|
||||||
@@ -30,4 +42,25 @@ contract IChai {
|
|||||||
uint256 wad
|
uint256 wad
|
||||||
)
|
)
|
||||||
external;
|
external;
|
||||||
|
|
||||||
|
/// @dev Queries Dai balance of Chai holder.
|
||||||
|
/// @param usr Address of Chai holder.
|
||||||
|
/// @return Dai balance.
|
||||||
|
function dai(address usr)
|
||||||
|
external
|
||||||
|
returns (uint256);
|
||||||
|
|
||||||
|
/// @dev Queries the Pot contract used by the Chai contract.
|
||||||
|
function pot()
|
||||||
|
external
|
||||||
|
returns (PotLike);
|
||||||
|
|
||||||
|
/// @dev Deposits Dai in exchange for Chai
|
||||||
|
/// @param dst Address to receive Chai.
|
||||||
|
/// @param wad Amount of Dai to deposit.
|
||||||
|
function join(
|
||||||
|
address dst,
|
||||||
|
uint256 wad
|
||||||
|
)
|
||||||
|
external;
|
||||||
}
|
}
|
||||||
|
@@ -19,9 +19,49 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "../src/bridges/DydxBridge.sol";
|
import "../src/bridges/DydxBridge.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestDydxBridgeToken {
|
||||||
|
|
||||||
|
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
|
||||||
|
mapping (address => uint256) private _balances;
|
||||||
|
|
||||||
|
/// @dev Sets initial balance of token holders.
|
||||||
|
constructor(address[] memory holders)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
for (uint256 i = 0; i != holders.length; ++i) {
|
||||||
|
_balances[holders[i]] = INIT_HOLDER_BALANCE;
|
||||||
|
}
|
||||||
|
_balances[msg.sender] = INIT_HOLDER_BALANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Basic transferFrom implementation.
|
||||||
|
function transferFrom(address from, address to, uint256 amount)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
if (_balances[from] < amount || _balances[to] + amount < _balances[to]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_balances[from] -= amount;
|
||||||
|
_balances[to] += amount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns balance of `holder`.
|
||||||
|
function balanceOf(address holder)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return _balances[holder];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable space-after-comma
|
// solhint-disable space-after-comma
|
||||||
contract TestDydxBridge is
|
contract TestDydxBridge is
|
||||||
IDydx,
|
IDydx,
|
||||||
@@ -29,6 +69,8 @@ contract TestDydxBridge is
|
|||||||
{
|
{
|
||||||
|
|
||||||
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
||||||
|
address private _testTokenAddress;
|
||||||
|
bool private _shouldRevertOnOperate;
|
||||||
|
|
||||||
event OperateAccount(
|
event OperateAccount(
|
||||||
address owner,
|
address owner,
|
||||||
@@ -49,6 +91,13 @@ contract TestDydxBridge is
|
|||||||
bytes data
|
bytes data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
constructor(address[] memory holders)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
// Deploy a test token. This represents the asset being deposited/withdrawn from dydx.
|
||||||
|
_testTokenAddress = address(new TestDydxBridgeToken(holders));
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Simulates `operate` in dydx contract.
|
/// @dev Simulates `operate` in dydx contract.
|
||||||
/// Emits events so that arguments can be validated client-side.
|
/// Emits events so that arguments can be validated client-side.
|
||||||
function operate(
|
function operate(
|
||||||
@@ -57,6 +106,10 @@ contract TestDydxBridge is
|
|||||||
)
|
)
|
||||||
external
|
external
|
||||||
{
|
{
|
||||||
|
if (_shouldRevertOnOperate) {
|
||||||
|
revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE");
|
||||||
|
}
|
||||||
|
|
||||||
for (uint i = 0; i < accounts.length; ++i) {
|
for (uint i = 0; i < accounts.length; ++i) {
|
||||||
emit OperateAccount(
|
emit OperateAccount(
|
||||||
accounts[i].owner,
|
accounts[i].owner,
|
||||||
@@ -78,9 +131,46 @@ contract TestDydxBridge is
|
|||||||
actions[i].otherAccountId,
|
actions[i].otherAccountId,
|
||||||
actions[i].data
|
actions[i].data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (actions[i].actionType == IDydx.ActionType.Withdraw) {
|
||||||
|
require(
|
||||||
|
IERC20Token(_testTokenAddress).transferFrom(
|
||||||
|
address(this),
|
||||||
|
actions[i].otherAddress,
|
||||||
|
actions[i].amount.value
|
||||||
|
),
|
||||||
|
"TestDydxBridge/WITHDRAW_FAILED"
|
||||||
|
);
|
||||||
|
} else if (actions[i].actionType == IDydx.ActionType.Deposit) {
|
||||||
|
require(
|
||||||
|
IERC20Token(_testTokenAddress).transferFrom(
|
||||||
|
actions[i].otherAddress,
|
||||||
|
address(this),
|
||||||
|
actions[i].amount.value
|
||||||
|
),
|
||||||
|
"TestDydxBridge/DEPOSIT_FAILED"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
revert("TestDydxBridge/UNSUPPORTED_ACTION");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev If `true` then subsequent calls to `operate` will revert.
|
||||||
|
function setRevertOnOperate(bool shouldRevert)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_shouldRevertOnOperate = shouldRevert;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns test token.
|
||||||
|
function getTestToken()
|
||||||
|
external
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return _testTokenAddress;
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
|
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
|
||||||
function _getDydxAddress()
|
function _getDydxAddress()
|
||||||
internal
|
internal
|
||||||
|
@@ -67,9 +67,11 @@ interface ITestContract {
|
|||||||
/// @dev A minimalist ERC20/WETH token.
|
/// @dev A minimalist ERC20/WETH token.
|
||||||
contract TestToken {
|
contract TestToken {
|
||||||
|
|
||||||
|
uint8 public decimals;
|
||||||
ITestContract private _testContract;
|
ITestContract private _testContract;
|
||||||
|
|
||||||
constructor() public {
|
constructor(uint8 decimals_) public {
|
||||||
|
decimals = decimals_;
|
||||||
_testContract = ITestContract(msg.sender);
|
_testContract = ITestContract(msg.sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +167,7 @@ contract TestKyberBridge is
|
|||||||
uint256 private _nextFillAmount;
|
uint256 private _nextFillAmount;
|
||||||
|
|
||||||
constructor() public {
|
constructor() public {
|
||||||
weth = IEtherToken(address(new TestToken()));
|
weth = IEtherToken(address(new TestToken(18)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Implementation of `IKyberNetworkProxy.trade()`
|
/// @dev Implementation of `IKyberNetworkProxy.trade()`
|
||||||
@@ -195,11 +197,11 @@ contract TestKyberBridge is
|
|||||||
return _nextFillAmount;
|
return _nextFillAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createToken()
|
function createToken(uint8 decimals)
|
||||||
external
|
external
|
||||||
returns (address tokenAddress)
|
returns (address tokenAddress)
|
||||||
{
|
{
|
||||||
return address(new TestToken());
|
return address(new TestToken(decimals));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNextFillAmount(uint256 amount)
|
function setNextFillAmount(uint256 amount)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-asset-proxy",
|
"name": "@0x/contracts-asset-proxy",
|
||||||
"version": "3.0.2",
|
"version": "3.1.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -51,12 +51,12 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
@@ -79,16 +79,16 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2",
|
"@0x/base-contract": "^6.1.1",
|
||||||
"@0x/contracts-dev-utils": "^1.0.2",
|
"@0x/contracts-dev-utils": "^1.0.5",
|
||||||
"@0x/contracts-erc1155": "^2.0.2",
|
"@0x/contracts-erc1155": "^2.0.5",
|
||||||
"@0x/contracts-erc20": "^3.0.2",
|
"@0x/contracts-erc20": "^3.0.5",
|
||||||
"@0x/contracts-erc721": "^3.0.2",
|
"@0x/contracts-erc721": "^3.0.5",
|
||||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
"@0x/contracts-exchange-libs": "^4.1.1",
|
||||||
"@0x/order-utils": "^10.0.1",
|
"@0x/order-utils": "^10.1.2",
|
||||||
"@0x/typescript-typings": "^5.0.1",
|
"@0x/typescript-typings": "^5.0.1",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"ethereum-types": "^3.0.0",
|
"ethereum-types": "^3.0.0",
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
},
|
},
|
||||||
|
40
contracts/asset-proxy/src/dydx_bridge_encoder.ts
Normal file
40
contracts/asset-proxy/src/dydx_bridge_encoder.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
export enum DydxBridgeActionType {
|
||||||
|
Deposit,
|
||||||
|
Withdraw,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DydxBrigeAction {
|
||||||
|
actionType: DydxBridgeActionType;
|
||||||
|
accountId: BigNumber;
|
||||||
|
marketId: BigNumber;
|
||||||
|
conversionRateNumerator: BigNumber;
|
||||||
|
conversionRateDenominator: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DydxBridgeData {
|
||||||
|
accountNumbers: BigNumber[];
|
||||||
|
actions: DydxBrigeAction[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dydxBridgeDataEncoder = AbiEncoder.create([
|
||||||
|
{
|
||||||
|
name: 'bridgeData',
|
||||||
|
type: 'tuple',
|
||||||
|
components: [
|
||||||
|
{ name: 'accountNumbers', type: 'uint256[]' },
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
type: 'tuple[]',
|
||||||
|
components: [
|
||||||
|
{ name: 'actionType', type: 'uint8' },
|
||||||
|
{ name: 'accountId', type: 'uint256' },
|
||||||
|
{ name: 'marketId', type: 'uint256' },
|
||||||
|
{ name: 'conversionRateNumerator', type: 'uint256' },
|
||||||
|
{ name: 'conversionRateDenominator', type: 'uint256' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
@@ -60,7 +60,7 @@ export class ERC1155ProxyWrapper {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._contractOwnerAddress);
|
||||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||||
}
|
}
|
||||||
return this._dummyTokenWrappers;
|
return this._dummyTokenWrappers;
|
||||||
|
@@ -6,12 +6,16 @@ export {
|
|||||||
ERC721ProxyContract,
|
ERC721ProxyContract,
|
||||||
Eth2DaiBridgeContract,
|
Eth2DaiBridgeContract,
|
||||||
DydxBridgeContract,
|
DydxBridgeContract,
|
||||||
|
TestDydxBridgeContract,
|
||||||
IAssetDataContract,
|
IAssetDataContract,
|
||||||
IAssetProxyContract,
|
IAssetProxyContract,
|
||||||
MultiAssetProxyContract,
|
MultiAssetProxyContract,
|
||||||
StaticCallProxyContract,
|
StaticCallProxyContract,
|
||||||
TestStaticCallTargetContract,
|
TestStaticCallTargetContract,
|
||||||
UniswapBridgeContract,
|
UniswapBridgeContract,
|
||||||
|
KyberBridgeContract,
|
||||||
|
ChaiBridgeContract,
|
||||||
|
IChaiContract,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
|
|
||||||
export { ERC20Wrapper } from './erc20_wrapper';
|
export { ERC20Wrapper } from './erc20_wrapper';
|
||||||
@@ -63,3 +67,4 @@ export {
|
|||||||
TupleDataItem,
|
TupleDataItem,
|
||||||
StateMutability,
|
StateMutability,
|
||||||
} from 'ethereum-types';
|
} from 'ethereum-types';
|
||||||
|
export * from './dydx_bridge_encoder';
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
|
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
|
||||||
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { DydxBridgeActionType, DydxBridgeData, dydxBridgeDataEncoder } from '../src/dydx_bridge_encoder';
|
||||||
|
import { ERC20BridgeProxyContract, IAssetDataContract } from '../src/wrappers';
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
import { artifacts } from './artifacts';
|
||||||
import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
|
import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
|
||||||
|
|
||||||
@@ -11,7 +15,24 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
|||||||
const marketId = new BigNumber(2);
|
const marketId = new BigNumber(2);
|
||||||
const defaultAmount = new BigNumber(4);
|
const defaultAmount = new BigNumber(4);
|
||||||
const notAuthorized = '0x0000000000000000000000000000000000000001';
|
const notAuthorized = '0x0000000000000000000000000000000000000001';
|
||||||
|
const defaultDepositAction = {
|
||||||
|
actionType: DydxBridgeActionType.Deposit,
|
||||||
|
accountId: constants.ZERO_AMOUNT,
|
||||||
|
marketId,
|
||||||
|
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||||
|
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||||
|
};
|
||||||
|
const defaultWithdrawAction = {
|
||||||
|
actionType: DydxBridgeActionType.Withdraw,
|
||||||
|
accountId: constants.ZERO_AMOUNT,
|
||||||
|
marketId,
|
||||||
|
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||||
|
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||||
|
};
|
||||||
let testContract: TestDydxBridgeContract;
|
let testContract: TestDydxBridgeContract;
|
||||||
|
let testProxyContract: ERC20BridgeProxyContract;
|
||||||
|
let assetDataEncoder: IAssetDataContract;
|
||||||
|
let owner: string;
|
||||||
let authorized: string;
|
let authorized: string;
|
||||||
let accountOwner: string;
|
let accountOwner: string;
|
||||||
let receiver: string;
|
let receiver: string;
|
||||||
@@ -19,7 +40,7 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
// Get accounts
|
// Get accounts
|
||||||
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
|
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
|
||||||
[, /* owner */ authorized, accountOwner, receiver] = accounts;
|
[owner, authorized, accountOwner, receiver] = accounts;
|
||||||
|
|
||||||
// Deploy dydx bridge
|
// Deploy dydx bridge
|
||||||
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
|
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
|
||||||
@@ -27,81 +48,57 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
|||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
|
[accountOwner, receiver],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Deploy test erc20 bridge proxy
|
||||||
|
testProxyContract = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ERC20BridgeProxy,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
await testProxyContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
||||||
|
|
||||||
|
// Setup asset data encoder
|
||||||
|
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('bridgeTransferFrom()', () => {
|
describe('bridgeTransferFrom()', () => {
|
||||||
enum BridgeActionType {
|
|
||||||
Deposit,
|
|
||||||
Withdraw,
|
|
||||||
}
|
|
||||||
interface BrigeAction {
|
|
||||||
actionType: BridgeActionType;
|
|
||||||
accountId: BigNumber;
|
|
||||||
marketId: BigNumber;
|
|
||||||
conversionRateNumerator: BigNumber;
|
|
||||||
conversionRateDenominator: BigNumber;
|
|
||||||
}
|
|
||||||
interface BridgeData {
|
|
||||||
accountNumbers: BigNumber[];
|
|
||||||
actions: BrigeAction[];
|
|
||||||
}
|
|
||||||
const defaultDepositAction = {
|
|
||||||
actionType: BridgeActionType.Deposit as number,
|
|
||||||
accountId: constants.ZERO_AMOUNT,
|
|
||||||
marketId,
|
|
||||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
|
||||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
|
||||||
};
|
|
||||||
const defaultWithdrawAction = {
|
|
||||||
actionType: BridgeActionType.Withdraw as number,
|
|
||||||
accountId: constants.ZERO_AMOUNT,
|
|
||||||
marketId,
|
|
||||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
|
||||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
|
||||||
};
|
|
||||||
const bridgeDataEncoder = AbiEncoder.create([
|
|
||||||
{
|
|
||||||
name: 'bridgeData',
|
|
||||||
type: 'tuple',
|
|
||||||
components: [
|
|
||||||
{ name: 'accountNumbers', type: 'uint256[]' },
|
|
||||||
{
|
|
||||||
name: 'actions',
|
|
||||||
type: 'tuple[]',
|
|
||||||
components: [
|
|
||||||
{ name: 'actionType', type: 'uint8' },
|
|
||||||
{ name: 'accountId', type: 'uint256' },
|
|
||||||
{ name: 'marketId', type: 'uint256' },
|
|
||||||
{ name: 'conversionRateNumerator', type: 'uint256' },
|
|
||||||
{ name: 'conversionRateDenominator', type: 'uint256' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const callBridgeTransferFrom = async (
|
const callBridgeTransferFrom = async (
|
||||||
from: string,
|
from: string,
|
||||||
to: string,
|
to: string,
|
||||||
amount: BigNumber,
|
amount: BigNumber,
|
||||||
bridgeData: BridgeData,
|
bridgeData: DydxBridgeData,
|
||||||
sender: string,
|
sender: string,
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const returnValue = await testContract
|
const returnValue = await testContract
|
||||||
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
|
.bridgeTransferFrom(
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||||
|
)
|
||||||
.callAsync({ from: sender });
|
.callAsync({ from: sender });
|
||||||
return returnValue;
|
return returnValue;
|
||||||
};
|
};
|
||||||
const callBridgeTransferFromAndVerifyEvents = async (
|
const executeBridgeTransferFromAndVerifyEvents = async (
|
||||||
from: string,
|
from: string,
|
||||||
to: string,
|
to: string,
|
||||||
amount: BigNumber,
|
amount: BigNumber,
|
||||||
bridgeData: BridgeData,
|
bridgeData: DydxBridgeData,
|
||||||
sender: string,
|
sender: string,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
// Execute transaction.
|
// Execute transaction.
|
||||||
const txReceipt = await testContract
|
const txReceipt = await testContract
|
||||||
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
|
.bridgeTransferFrom(
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||||
|
)
|
||||||
.awaitTransactionSuccessAsync({ from: sender });
|
.awaitTransactionSuccessAsync({ from: sender });
|
||||||
|
|
||||||
// Verify `OperateAccount` event.
|
// Verify `OperateAccount` event.
|
||||||
@@ -122,62 +119,139 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
|||||||
expectedOperateActionEvents.push({
|
expectedOperateActionEvents.push({
|
||||||
actionType: action.actionType as number,
|
actionType: action.actionType as number,
|
||||||
accountId: action.accountId,
|
accountId: action.accountId,
|
||||||
amountSign: action.actionType === BridgeActionType.Deposit ? true : false,
|
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
|
||||||
amountDenomination: weiDenomination,
|
amountDenomination: weiDenomination,
|
||||||
amountRef: deltaAmountRef,
|
amountRef: deltaAmountRef,
|
||||||
amountValue: action.conversionRateDenominator.gt(0)
|
amountValue: action.conversionRateDenominator.gt(0)
|
||||||
? amount.times(action.conversionRateNumerator).div(action.conversionRateDenominator)
|
? amount
|
||||||
|
.times(action.conversionRateNumerator)
|
||||||
|
.dividedToIntegerBy(action.conversionRateDenominator)
|
||||||
: amount,
|
: amount,
|
||||||
primaryMarketId: marketId,
|
primaryMarketId: marketId,
|
||||||
secondaryMarketId: constants.ZERO_AMOUNT,
|
secondaryMarketId: constants.ZERO_AMOUNT,
|
||||||
otherAddress: action.actionType === BridgeActionType.Deposit ? from : to,
|
otherAddress: action.actionType === DydxBridgeActionType.Deposit ? from : to,
|
||||||
otherAccountId: constants.ZERO_AMOUNT,
|
otherAccountId: constants.ZERO_AMOUNT,
|
||||||
data: '0x',
|
data: '0x',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
|
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
|
||||||
};
|
};
|
||||||
|
it('succeeds when calling with zero amount', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling with no accounts', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling with no actions', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
|
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
|
||||||
const bridgeData = {
|
const bridgeData = {
|
||||||
accountNumbers: [defaultAccountNumber],
|
accountNumbers: [defaultAccountNumber],
|
||||||
actions: [defaultDepositAction],
|
actions: [defaultDepositAction],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||||
const bridgeData = {
|
const bridgeData = {
|
||||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||||
actions: [defaultDepositAction],
|
actions: [defaultDepositAction],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('succeeds when calling `operate` with the `withdraw` action and a single accuont', async () => {
|
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
|
||||||
const bridgeData = {
|
const bridgeData = {
|
||||||
accountNumbers: [defaultAccountNumber],
|
accountNumbers: [defaultAccountNumber],
|
||||||
actions: [defaultWithdrawAction],
|
actions: [defaultWithdrawAction],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
|
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
|
||||||
const bridgeData = {
|
const bridgeData = {
|
||||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||||
actions: [defaultWithdrawAction],
|
actions: [defaultWithdrawAction],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||||
const bridgeData = {
|
const bridgeData = {
|
||||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
|
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
|
||||||
const bridgeData = {
|
const bridgeData = {
|
||||||
accountNumbers: [defaultAccountNumber],
|
accountNumbers: [defaultAccountNumber],
|
||||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('succeeds when scaling the `amount` to deposit', async () => {
|
it('succeeds when scaling the `amount` to deposit', async () => {
|
||||||
const conversionRateNumerator = new BigNumber(1);
|
const conversionRateNumerator = new BigNumber(1);
|
||||||
@@ -193,7 +267,13 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('succeeds when scaling the `amount` to withdraw', async () => {
|
it('succeeds when scaling the `amount` to withdraw', async () => {
|
||||||
const conversionRateNumerator = new BigNumber(1);
|
const conversionRateNumerator = new BigNumber(1);
|
||||||
@@ -209,7 +289,13 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
|
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
|
||||||
const bridgeData = {
|
const bridgeData = {
|
||||||
@@ -240,5 +326,74 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
|||||||
);
|
);
|
||||||
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
|
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
|
||||||
});
|
});
|
||||||
|
it('should revert when `Operate` reverts', async () => {
|
||||||
|
// Set revert flag.
|
||||||
|
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||||
|
|
||||||
|
// Execute transfer.
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
const tx = callBridgeTransferFrom(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||||
|
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
it('should revert when there is a rounding error', async () => {
|
||||||
|
// Setup a rounding error
|
||||||
|
const conversionRateNumerator = new BigNumber(5318);
|
||||||
|
const conversionRateDenominator = new BigNumber(47958);
|
||||||
|
const amount = new BigNumber(9000);
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [
|
||||||
|
defaultDepositAction,
|
||||||
|
{
|
||||||
|
...defaultWithdrawAction,
|
||||||
|
conversionRateNumerator,
|
||||||
|
conversionRateDenominator,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Execute transfer and assert error.
|
||||||
|
const tx = callBridgeTransferFrom(accountOwner, receiver, amount, bridgeData, authorized);
|
||||||
|
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||||
|
conversionRateNumerator,
|
||||||
|
conversionRateDenominator,
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ERC20BridgeProxy.transferFrom()', () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultWithdrawAction],
|
||||||
|
};
|
||||||
|
let assetData: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
const testTokenAddress = await testContract.getTestToken().callAsync();
|
||||||
|
assetData = assetDataEncoder
|
||||||
|
.ERC20Bridge(testTokenAddress, testContract.address, dydxBridgeDataEncoder.encode({ bridgeData }))
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed if `bridgeTransferFrom` succeeds', async () => {
|
||||||
|
await testProxyContract
|
||||||
|
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||||
|
.awaitTransactionSuccessAsync({ from: authorized });
|
||||||
|
});
|
||||||
|
it('should revert if `bridgeTransferFrom` reverts', async () => {
|
||||||
|
// Set revert flag.
|
||||||
|
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||||
|
const tx = testProxyContract
|
||||||
|
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||||
|
.awaitTransactionSuccessAsync({ from: authorized });
|
||||||
|
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -3,6 +3,7 @@ import {
|
|||||||
constants,
|
constants,
|
||||||
expect,
|
expect,
|
||||||
getRandomInteger,
|
getRandomInteger,
|
||||||
|
getRandomPortion,
|
||||||
randomAddress,
|
randomAddress,
|
||||||
verifyEventsFromLogs,
|
verifyEventsFromLogs,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
@@ -17,6 +18,12 @@ import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
|
|||||||
|
|
||||||
blockchainTests.resets('KyberBridge unit tests', env => {
|
blockchainTests.resets('KyberBridge unit tests', env => {
|
||||||
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
||||||
|
const FROM_TOKEN_DECIMALS = 6;
|
||||||
|
const TO_TOKEN_DECIMALS = 18;
|
||||||
|
const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS);
|
||||||
|
const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS);
|
||||||
|
const WETH_BASE = new BigNumber(10).pow(18);
|
||||||
|
const KYBER_RATE_BASE = WETH_BASE;
|
||||||
let testContract: TestKyberBridgeContract;
|
let testContract: TestKyberBridgeContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
@@ -45,10 +52,10 @@ blockchainTests.resets('KyberBridge unit tests', env => {
|
|||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
wethAddress = await testContract.weth().callAsync();
|
wethAddress = await testContract.weth().callAsync();
|
||||||
fromTokenAddress = await testContract.createToken().callAsync();
|
fromTokenAddress = await testContract.createToken(FROM_TOKEN_DECIMALS).callAsync();
|
||||||
await testContract.createToken().awaitTransactionSuccessAsync();
|
await testContract.createToken(FROM_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
||||||
toTokenAddress = await testContract.createToken().callAsync();
|
toTokenAddress = await testContract.createToken(TO_TOKEN_DECIMALS).callAsync();
|
||||||
await testContract.createToken().awaitTransactionSuccessAsync();
|
await testContract.createToken(TO_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
const STATIC_KYBER_TRADE_ARGS = {
|
const STATIC_KYBER_TRADE_ARGS = {
|
||||||
@@ -75,13 +82,14 @@ blockchainTests.resets('KyberBridge unit tests', env => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
||||||
|
const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100));
|
||||||
return {
|
return {
|
||||||
fromTokenAddress,
|
fromTokenAddress,
|
||||||
toTokenAddress,
|
toTokenAddress,
|
||||||
|
amount,
|
||||||
toAddress: randomAddress(),
|
toAddress: randomAddress(),
|
||||||
amount: getRandomInteger(1, 10e18),
|
fillAmount: getRandomPortion(amount),
|
||||||
fillAmount: getRandomInteger(1, 10e18),
|
fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)),
|
||||||
fromTokenBalance: getRandomInteger(1, 10e18),
|
|
||||||
...opts,
|
...opts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -119,9 +127,12 @@ blockchainTests.resets('KyberBridge unit tests', env => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
|
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
|
||||||
|
const fromBase = opts.fromTokenAddress === wethAddress ? WETH_BASE : FROM_TOKEN_BASE;
|
||||||
|
const toBase = opts.toTokenAddress === wethAddress ? WETH_BASE : TO_TOKEN_BASE;
|
||||||
return opts.amount
|
return opts.amount
|
||||||
.times(constants.ONE_ETHER)
|
.div(toBase)
|
||||||
.div(opts.fromTokenBalance)
|
.div(opts.fromTokenBalance.div(fromBase))
|
||||||
|
.times(KYBER_RATE_BASE)
|
||||||
.integerValue(BigNumber.ROUND_DOWN);
|
.integerValue(BigNumber.ROUND_DOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -176,7 +176,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
|||||||
expect(calls[0].exchange).to.eq(exchangeAddress);
|
expect(calls[0].exchange).to.eq(exchangeAddress);
|
||||||
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
||||||
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
|
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
|
||||||
expect(calls[0].minEthBought).to.bignumber.eq(0);
|
expect(calls[0].minEthBought).to.bignumber.eq(1);
|
||||||
expect(calls[0].deadline).to.bignumber.eq(blockTime);
|
expect(calls[0].deadline).to.bignumber.eq(blockTime);
|
||||||
expect(calls[0].recipient).to.eq(opts.toAddress);
|
expect(calls[0].recipient).to.eq(opts.toAddress);
|
||||||
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);
|
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -1,4 +1,31 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "3.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "3.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1578272714,
|
||||||
|
"version": "3.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.0.5 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.4 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v3.0.2 - _December 17, 2019_
|
## v3.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
"contractsDir": "./contracts",
|
"contractsDir": "./contracts",
|
||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-coordinator",
|
"name": "@0x/contracts-coordinator",
|
||||||
"version": "3.0.2",
|
"version": "3.0.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -52,19 +52,19 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
"@0x/contracts-asset-proxy": "^3.1.2",
|
||||||
"@0x/contracts-dev-utils": "^1.0.2",
|
"@0x/contracts-dev-utils": "^1.0.5",
|
||||||
"@0x/contracts-erc20": "^3.0.2",
|
"@0x/contracts-erc20": "^3.0.5",
|
||||||
"@0x/contracts-exchange": "^3.0.2",
|
"@0x/contracts-exchange": "^3.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/order-utils": "^10.0.1",
|
"@0x/order-utils": "^10.1.2",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -84,14 +84,14 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/assert": "^3.0.2",
|
"@0x/assert": "^3.0.4",
|
||||||
"@0x/base-contract": "^6.0.2",
|
"@0x/base-contract": "^6.1.1",
|
||||||
"@0x/contract-addresses": "^4.1.0",
|
"@0x/contract-addresses": "^4.4.0",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/json-schemas": "^5.0.2",
|
"@0x/json-schemas": "^5.0.4",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
"@0x/typescript-typings": "^5.0.1",
|
"@0x/typescript-typings": "^5.0.1",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"ethereum-types": "^3.0.0",
|
"ethereum-types": "^3.0.0",
|
||||||
"http-status-codes": "^1.3.2"
|
"http-status-codes": "^1.3.2"
|
||||||
},
|
},
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -1,4 +1,32 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "1.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "1.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Fixed ERC721 duplicate token ID bug",
|
||||||
|
"pr": 2400
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1578272714
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v1.0.5 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.0.4 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Fixed ERC721 duplicate token ID bug (#2400)
|
||||||
|
|
||||||
## v1.0.2 - _December 17, 2019_
|
## v1.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,10 +4,10 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1666,
|
"runs": 5000,
|
||||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||||
},
|
},
|
||||||
"outputSelection": {
|
"outputSelection": {
|
||||||
|
@@ -26,26 +26,33 @@ import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
|
|||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "./OrderValidationUtils.sol";
|
import "./OrderValidationUtils.sol";
|
||||||
import "./OrderTransferSimulationUtils.sol";
|
import "./OrderTransferSimulationUtils.sol";
|
||||||
import "./LibTransactionDecoder.sol";
|
|
||||||
import "./EthBalanceChecker.sol";
|
import "./EthBalanceChecker.sol";
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
contract DevUtils is
|
contract DevUtils is
|
||||||
OrderValidationUtils,
|
OrderValidationUtils,
|
||||||
LibTransactionDecoder,
|
|
||||||
LibEIP712ExchangeDomain,
|
LibEIP712ExchangeDomain,
|
||||||
EthBalanceChecker,
|
EthBalanceChecker
|
||||||
OrderTransferSimulationUtils
|
|
||||||
{
|
{
|
||||||
constructor (address _exchange)
|
constructor (
|
||||||
|
address _exchange,
|
||||||
|
address _chaiBridge
|
||||||
|
)
|
||||||
public
|
public
|
||||||
OrderValidationUtils(_exchange)
|
OrderValidationUtils(
|
||||||
|
_exchange,
|
||||||
|
_chaiBridge
|
||||||
|
)
|
||||||
OrderTransferSimulationUtils(_exchange)
|
OrderTransferSimulationUtils(_exchange)
|
||||||
LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants
|
LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants
|
||||||
{}
|
{}
|
||||||
|
|
||||||
function getOrderHash(LibOrder.Order memory order, uint256 chainId, address exchange)
|
function getOrderHash(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 chainId,
|
||||||
|
address exchange
|
||||||
|
)
|
||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
returns (bytes32 orderHash)
|
returns (bytes32 orderHash)
|
||||||
|
@@ -26,10 +26,14 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
|
|||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IChai.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibAssetData {
|
contract LibAssetData is
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
// 2^256 - 1
|
// 2^256 - 1
|
||||||
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
||||||
|
|
||||||
@@ -41,9 +45,13 @@ contract LibAssetData {
|
|||||||
address internal _ERC721_PROXY_ADDRESS;
|
address internal _ERC721_PROXY_ADDRESS;
|
||||||
address internal _ERC1155_PROXY_ADDRESS;
|
address internal _ERC1155_PROXY_ADDRESS;
|
||||||
address internal _STATIC_CALL_PROXY_ADDRESS;
|
address internal _STATIC_CALL_PROXY_ADDRESS;
|
||||||
|
address internal _CHAI_BRIDGE_ADDRESS;
|
||||||
// solhint-enable var-name-mixedcase
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
constructor (address _exchange)
|
constructor (
|
||||||
|
address _exchange,
|
||||||
|
address _chaiBridge
|
||||||
|
)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
_EXCHANGE = IExchange(_exchange);
|
_EXCHANGE = IExchange(_exchange);
|
||||||
@@ -51,6 +59,7 @@ contract LibAssetData {
|
|||||||
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
|
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
|
||||||
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
||||||
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.selector);
|
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.selector);
|
||||||
|
_CHAI_BRIDGE_ADDRESS = _chaiBridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Returns the owner's balance of the assets(s) specified in
|
/// @dev Returns the owner's balance of the assets(s) specified in
|
||||||
@@ -62,7 +71,6 @@ contract LibAssetData {
|
|||||||
/// @return Number of assets (or asset baskets) held by owner.
|
/// @return Number of assets (or asset baskets) held by owner.
|
||||||
function getBalance(address ownerAddress, bytes memory assetData)
|
function getBalance(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 balance)
|
returns (uint256 balance)
|
||||||
{
|
{
|
||||||
// Get id of AssetProxy contract
|
// Get id of AssetProxy contract
|
||||||
@@ -71,16 +79,8 @@ contract LibAssetData {
|
|||||||
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
||||||
// Get ERC20 token address
|
// Get ERC20 token address
|
||||||
address tokenAddress = assetData.readAddress(16);
|
address tokenAddress = assetData.readAddress(16);
|
||||||
|
balance = _erc20BalanceOf(tokenAddress, ownerAddress);
|
||||||
|
|
||||||
// Encode data for `balanceOf(ownerAddress)`
|
|
||||||
bytes memory balanceOfData = abi.encodeWithSelector(
|
|
||||||
IERC20Token(address(0)).balanceOf.selector,
|
|
||||||
ownerAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
// Query balance
|
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
|
||||||
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
// Get ERC721 token address and id
|
// Get ERC721 token address and id
|
||||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
@@ -94,12 +94,18 @@ contract LibAssetData {
|
|||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
||||||
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
||||||
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
|
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
// Get ERC1155 token address, array of ids, and array of values
|
// Get ERC1155 token address, array of ids, and array of values
|
||||||
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
||||||
|
|
||||||
uint256 length = tokenIds.length;
|
uint256 length = tokenIds.length;
|
||||||
for (uint256 i = 0; i != length; i++) {
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// Skip over the token if the corresponding value is 0.
|
||||||
|
if (tokenValues[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
|
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
|
||||||
bytes memory balanceOfData = abi.encodeWithSelector(
|
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||||
IERC1155(address(0)).balanceOf.selector,
|
IERC1155(address(0)).balanceOf.selector,
|
||||||
@@ -113,10 +119,14 @@ contract LibAssetData {
|
|||||||
|
|
||||||
// Scale total balance down by corresponding value in assetData
|
// Scale total balance down by corresponding value in assetData
|
||||||
uint256 scaledBalance = totalBalance / tokenValues[i];
|
uint256 scaledBalance = totalBalance / tokenValues[i];
|
||||||
|
if (scaledBalance == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (scaledBalance < balance || balance == 0) {
|
if (scaledBalance < balance || balance == 0) {
|
||||||
balance = scaledBalance;
|
balance = scaledBalance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
||||||
bytes memory transferFromData = abi.encodeWithSelector(
|
bytes memory transferFromData = abi.encodeWithSelector(
|
||||||
@@ -132,22 +142,41 @@ contract LibAssetData {
|
|||||||
|
|
||||||
// Success means that the staticcall can be made an unlimited amount of times
|
// Success means that the staticcall can be made an unlimited amount of times
|
||||||
balance = success ? _MAX_UINT256 : 0;
|
balance = success ? _MAX_UINT256 : 0;
|
||||||
|
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
|
||||||
|
// Get address of ERC20 token and bridge contract
|
||||||
|
(, address tokenAddress, address bridgeAddress,) = decodeERC20BridgeAssetData(assetData);
|
||||||
|
if (tokenAddress == _getDaiAddress() && bridgeAddress == _CHAI_BRIDGE_ADDRESS) {
|
||||||
|
uint256 chaiBalance = _erc20BalanceOf(_getChaiAddress(), ownerAddress);
|
||||||
|
// Calculate Dai balance
|
||||||
|
balance = _convertChaiToDaiAmount(chaiBalance);
|
||||||
|
}
|
||||||
|
// Balance will be 0 if bridge is not supported
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||||
// Get array of values and array of assetDatas
|
// Get array of values and array of assetDatas
|
||||||
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||||
|
|
||||||
uint256 length = nestedAssetData.length;
|
uint256 length = nestedAssetData.length;
|
||||||
for (uint256 i = 0; i != length; i++) {
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// Skip over the asset if the corresponding amount is 0.
|
||||||
|
if (assetAmounts[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Query balance of individual assetData
|
// Query balance of individual assetData
|
||||||
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
|
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
|
||||||
|
|
||||||
// Scale total balance down by corresponding value in assetData
|
// Scale total balance down by corresponding value in assetData
|
||||||
uint256 scaledBalance = totalBalance / assetAmounts[i];
|
uint256 scaledBalance = totalBalance / assetAmounts[i];
|
||||||
|
if (scaledBalance == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (scaledBalance < balance || balance == 0) {
|
if (scaledBalance < balance || balance == 0) {
|
||||||
balance = scaledBalance;
|
balance = scaledBalance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Balance will be 0 if assetProxyId is unknown
|
// Balance will be 0 if assetProxyId is unknown
|
||||||
return balance;
|
return balance;
|
||||||
@@ -160,7 +189,6 @@ contract LibAssetData {
|
|||||||
/// corresponding to the same-indexed element in the assetData input.
|
/// corresponding to the same-indexed element in the assetData input.
|
||||||
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
|
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256[] memory balances)
|
returns (uint256[] memory balances)
|
||||||
{
|
{
|
||||||
uint256 length = assetData.length;
|
uint256 length = assetData.length;
|
||||||
@@ -181,7 +209,6 @@ contract LibAssetData {
|
|||||||
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||||
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 allowance)
|
returns (uint256 allowance)
|
||||||
{
|
{
|
||||||
// Get id of AssetProxy contract
|
// Get id of AssetProxy contract
|
||||||
@@ -193,11 +220,19 @@ contract LibAssetData {
|
|||||||
|
|
||||||
uint256 length = nestedAssetData.length;
|
uint256 length = nestedAssetData.length;
|
||||||
for (uint256 i = 0; i != length; i++) {
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// Skip over the asset if the corresponding amount is 0.
|
||||||
|
if (amounts[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Query allowance of individual assetData
|
// Query allowance of individual assetData
|
||||||
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
|
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
|
||||||
|
|
||||||
// Scale total allowance down by corresponding value in assetData
|
// Scale total allowance down by corresponding value in assetData
|
||||||
uint256 scaledAllowance = totalAllowance / amounts[i];
|
uint256 scaledAllowance = totalAllowance / amounts[i];
|
||||||
|
if (scaledAllowance == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (scaledAllowance < allowance || allowance == 0) {
|
if (scaledAllowance < allowance || allowance == 0) {
|
||||||
allowance = scaledAllowance;
|
allowance = scaledAllowance;
|
||||||
}
|
}
|
||||||
@@ -219,6 +254,7 @@ contract LibAssetData {
|
|||||||
// Query allowance
|
// Query allowance
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
||||||
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
// Get ERC721 token address and id
|
// Get ERC721 token address and id
|
||||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
@@ -244,6 +280,7 @@ contract LibAssetData {
|
|||||||
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
||||||
allowance = _MAX_UINT256;
|
allowance = _MAX_UINT256;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
// Get ERC1155 token address
|
// Get ERC1155 token address
|
||||||
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
||||||
@@ -258,9 +295,26 @@ contract LibAssetData {
|
|||||||
// Query allowance
|
// Query allowance
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
||||||
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
// The StaticCallProxy does not require any approvals
|
// The StaticCallProxy does not require any approvals
|
||||||
allowance = _MAX_UINT256;
|
allowance = _MAX_UINT256;
|
||||||
|
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
|
||||||
|
// Get address of ERC20 token and bridge contract
|
||||||
|
(, address tokenAddress, address bridgeAddress,) = decodeERC20BridgeAssetData(assetData);
|
||||||
|
if (tokenAddress == _getDaiAddress() && bridgeAddress == _CHAI_BRIDGE_ADDRESS) {
|
||||||
|
bytes memory allowanceData = abi.encodeWithSelector(
|
||||||
|
IERC20Token(address(0)).allowance.selector,
|
||||||
|
ownerAddress,
|
||||||
|
_CHAI_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
(bool success, bytes memory returnData) = _getChaiAddress().staticcall(allowanceData);
|
||||||
|
uint256 chaiAllowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
// Dai allowance is unlimited if Chai allowance is unlimited
|
||||||
|
allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance);
|
||||||
|
}
|
||||||
|
// Allowance will be 0 if bridge is not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allowance will be 0 if the assetProxyId is unknown
|
// Allowance will be 0 if the assetProxyId is unknown
|
||||||
@@ -274,7 +328,6 @@ contract LibAssetData {
|
|||||||
/// element corresponding to the same-indexed element in the assetData input.
|
/// element corresponding to the same-indexed element in the assetData input.
|
||||||
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256[] memory allowances)
|
returns (uint256[] memory allowances)
|
||||||
{
|
{
|
||||||
uint256 length = assetData.length;
|
uint256 length = assetData.length;
|
||||||
@@ -292,7 +345,6 @@ contract LibAssetData {
|
|||||||
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||||
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 balance, uint256 allowance)
|
returns (uint256 balance, uint256 allowance)
|
||||||
{
|
{
|
||||||
balance = getBalance(ownerAddress, assetData);
|
balance = getBalance(ownerAddress, assetData);
|
||||||
@@ -308,7 +360,6 @@ contract LibAssetData {
|
|||||||
/// corresponding to the same-indexed element in the assetData input.
|
/// corresponding to the same-indexed element in the assetData input.
|
||||||
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256[] memory balances, uint256[] memory allowances)
|
returns (uint256[] memory balances, uint256[] memory allowances)
|
||||||
{
|
{
|
||||||
balances = getBatchBalances(ownerAddress, assetData);
|
balances = getBatchBalances(ownerAddress, assetData);
|
||||||
@@ -316,7 +367,7 @@ contract LibAssetData {
|
|||||||
return (balances, allowances);
|
return (balances, allowances);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Decode AssetProxy identifier
|
/// @dev Decode AssetProxy identifier
|
||||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-20, ERC-721, ERC1155, or MultiAsset asset.
|
/// @param assetData AssetProxy-compliant asset data describing an ERC-20, ERC-721, ERC1155, or MultiAsset asset.
|
||||||
/// @return The AssetProxy identifier
|
/// @return The AssetProxy identifier
|
||||||
function decodeAssetProxyId(bytes memory assetData)
|
function decodeAssetProxyId(bytes memory assetData)
|
||||||
@@ -353,7 +404,7 @@ contract LibAssetData {
|
|||||||
|
|
||||||
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
|
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
|
||||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
|
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
|
||||||
/// @return The AssetProxy identifier, and the address of the ERC-20
|
/// @return The AssetProxy identifier, and the address of the ERC-20
|
||||||
/// contract hosting this asset.
|
/// contract hosting this asset.
|
||||||
function decodeERC20AssetData(bytes memory assetData)
|
function decodeERC20AssetData(bytes memory assetData)
|
||||||
public
|
public
|
||||||
@@ -589,9 +640,38 @@ contract LibAssetData {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Decode ERC20Bridge asset data from the format described in the AssetProxy contract specification.
|
||||||
|
/// @param assetData AssetProxy-compliant asset data describing an ERC20Bridge asset
|
||||||
|
/// @return The ERC20BridgeProxy identifier, the address of the ERC20 token to transfer, the address
|
||||||
|
/// of the bridge contract, and extra data to be passed to the bridge contract.
|
||||||
|
function decodeERC20BridgeAssetData(bytes memory assetData)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
bytes4 assetProxyId,
|
||||||
|
address tokenAddress,
|
||||||
|
address bridgeAddress,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
require(
|
||||||
|
assetProxyId == IAssetData(address(0)).ERC20Bridge.selector,
|
||||||
|
"WRONG_PROXY_ID"
|
||||||
|
);
|
||||||
|
|
||||||
|
(tokenAddress, bridgeAddress, bridgeData) = abi.decode(
|
||||||
|
assetData.slice(4, assetData.length),
|
||||||
|
(address, address, bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Reverts if assetData is not of a valid format for its given proxy id.
|
||||||
|
/// @param assetData AssetProxy compliant asset data.
|
||||||
function revertIfInvalidAssetData(bytes memory assetData)
|
function revertIfInvalidAssetData(bytes memory assetData)
|
||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
{
|
{
|
||||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
@@ -605,8 +685,50 @@ contract LibAssetData {
|
|||||||
decodeMultiAssetData(assetData);
|
decodeMultiAssetData(assetData);
|
||||||
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
decodeStaticCallAssetData(assetData);
|
decodeStaticCallAssetData(assetData);
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
|
||||||
|
decodeERC20BridgeAssetData(assetData);
|
||||||
} else {
|
} else {
|
||||||
revert("WRONG_PROXY_ID");
|
revert("WRONG_PROXY_ID");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Queries balance of an ERC20 token. Returns 0 if call was unsuccessful.
|
||||||
|
/// @param tokenAddress Address of ERC20 token.
|
||||||
|
/// @param ownerAddress Address of owner of ERC20 token.
|
||||||
|
/// @return balance ERC20 token balance of owner.
|
||||||
|
function _erc20BalanceOf(
|
||||||
|
address tokenAddress,
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
// Encode data for `balanceOf(ownerAddress)`
|
||||||
|
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||||
|
IERC20Token(address(0)).balanceOf.selector,
|
||||||
|
ownerAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
// Query balance
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
||||||
|
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Converts an amount of Chai into its equivalent Dai amount.
|
||||||
|
/// Also accumulates Dai from DSR if called after the last time it was collected.
|
||||||
|
/// @param chaiAmount Amount of Chai to converts.
|
||||||
|
function _convertChaiToDaiAmount(uint256 chaiAmount)
|
||||||
|
internal
|
||||||
|
returns (uint256 daiAmount)
|
||||||
|
{
|
||||||
|
PotLike pot = IChai(_getChaiAddress()).pot();
|
||||||
|
// Accumulate savings if called after last time savings were collected
|
||||||
|
uint256 chiMultiplier = (now > pot.rho())
|
||||||
|
? pot.drip()
|
||||||
|
: pot.chi();
|
||||||
|
daiAmount = LibMath.getPartialAmountFloor(chiMultiplier, 10**27, chaiAmount);
|
||||||
|
return daiAmount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -41,6 +41,10 @@ contract OrderTransferSimulationUtils is
|
|||||||
TransfersSuccessful // All transfers in the order were successful
|
TransfersSuccessful // All transfers in the order were successful
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE(jalextowle): This is a random address that we use to avoid issues that addresses like `address(1)`
|
||||||
|
// may cause later.
|
||||||
|
address constant internal UNUSED_ADDRESS = address(0x377f698C4c287018D09b516F415317aEC5919332);
|
||||||
|
|
||||||
// keccak256(abi.encodeWithSignature("Error(string)", "TRANSFERS_SUCCESSFUL"));
|
// keccak256(abi.encodeWithSignature("Error(string)", "TRANSFERS_SUCCESSFUL"));
|
||||||
bytes32 constant internal _TRANSFERS_SUCCESSFUL_RESULT_HASH = 0xf43f26ea5a94b478394a975e856464913dc1a8a1ca70939d974aa7c238aa0ce0;
|
bytes32 constant internal _TRANSFERS_SUCCESSFUL_RESULT_HASH = 0xf43f26ea5a94b478394a975e856464913dc1a8a1ca70939d974aa7c238aa0ce0;
|
||||||
|
|
||||||
@@ -54,6 +58,51 @@ contract OrderTransferSimulationUtils is
|
|||||||
_EXCHANGE = IExchange(_exchange);
|
_EXCHANGE = IExchange(_exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Simulates the maker transfers within an order and returns the index of the first failed transfer.
|
||||||
|
/// @param order The order to simulate transfers for.
|
||||||
|
/// @param takerAddress The address of the taker that will fill the order.
|
||||||
|
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
|
||||||
|
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
|
||||||
|
function getSimulatedOrderMakerTransferResults(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
address takerAddress,
|
||||||
|
uint256 takerAssetFillAmount
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (OrderTransferResults orderTransferResults)
|
||||||
|
{
|
||||||
|
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResults(
|
||||||
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
_EXCHANGE.protocolFeeMultiplier(),
|
||||||
|
tx.gasprice
|
||||||
|
);
|
||||||
|
|
||||||
|
bytes[] memory assetData = new bytes[](2);
|
||||||
|
address[] memory fromAddresses = new address[](2);
|
||||||
|
address[] memory toAddresses = new address[](2);
|
||||||
|
uint256[] memory amounts = new uint256[](2);
|
||||||
|
|
||||||
|
// Transfer `makerAsset` from maker to taker
|
||||||
|
assetData[0] = order.makerAssetData;
|
||||||
|
fromAddresses[0] = order.makerAddress;
|
||||||
|
toAddresses[0] = takerAddress == address(0) ? UNUSED_ADDRESS : takerAddress;
|
||||||
|
amounts[0] = fillResults.makerAssetFilledAmount;
|
||||||
|
|
||||||
|
// Transfer `makerFeeAsset` from maker to feeRecipient
|
||||||
|
assetData[1] = order.makerFeeAssetData;
|
||||||
|
fromAddresses[1] = order.makerAddress;
|
||||||
|
toAddresses[1] = order.feeRecipientAddress == address(0) ? UNUSED_ADDRESS : order.feeRecipientAddress;
|
||||||
|
amounts[1] = fillResults.makerFeePaid;
|
||||||
|
|
||||||
|
return _simulateTransferFromCalls(
|
||||||
|
assetData,
|
||||||
|
fromAddresses,
|
||||||
|
toAddresses,
|
||||||
|
amounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Simulates all of the transfers within an order and returns the index of the first failed transfer.
|
/// @dev Simulates all of the transfers within an order and returns the index of the first failed transfer.
|
||||||
/// @param order The order to simulate transfers for.
|
/// @param order The order to simulate transfers for.
|
||||||
/// @param takerAddress The address of the taker that will fill the order.
|
/// @param takerAddress The address of the taker that will fill the order.
|
||||||
@@ -89,21 +138,69 @@ contract OrderTransferSimulationUtils is
|
|||||||
// Transfer `makerAsset` from maker to taker
|
// Transfer `makerAsset` from maker to taker
|
||||||
assetData[1] = order.makerAssetData;
|
assetData[1] = order.makerAssetData;
|
||||||
fromAddresses[1] = order.makerAddress;
|
fromAddresses[1] = order.makerAddress;
|
||||||
toAddresses[1] = takerAddress;
|
toAddresses[1] = takerAddress == address(0) ? UNUSED_ADDRESS : takerAddress;
|
||||||
amounts[1] = fillResults.makerAssetFilledAmount;
|
amounts[1] = fillResults.makerAssetFilledAmount;
|
||||||
|
|
||||||
// Transfer `takerFeeAsset` from taker to feeRecipient
|
// Transfer `takerFeeAsset` from taker to feeRecipient
|
||||||
assetData[2] = order.takerFeeAssetData;
|
assetData[2] = order.takerFeeAssetData;
|
||||||
fromAddresses[2] = takerAddress;
|
fromAddresses[2] = takerAddress;
|
||||||
toAddresses[2] = order.feeRecipientAddress;
|
toAddresses[2] = order.feeRecipientAddress == address(0) ? UNUSED_ADDRESS : order.feeRecipientAddress;
|
||||||
amounts[2] = fillResults.takerFeePaid;
|
amounts[2] = fillResults.takerFeePaid;
|
||||||
|
|
||||||
// Transfer `makerFeeAsset` from maker to feeRecipient
|
// Transfer `makerFeeAsset` from maker to feeRecipient
|
||||||
assetData[3] = order.makerFeeAssetData;
|
assetData[3] = order.makerFeeAssetData;
|
||||||
fromAddresses[3] = order.makerAddress;
|
fromAddresses[3] = order.makerAddress;
|
||||||
toAddresses[3] = order.feeRecipientAddress;
|
toAddresses[3] = order.feeRecipientAddress == address(0) ? UNUSED_ADDRESS : order.feeRecipientAddress;
|
||||||
amounts[3] = fillResults.makerFeePaid;
|
amounts[3] = fillResults.makerFeePaid;
|
||||||
|
|
||||||
|
return _simulateTransferFromCalls(
|
||||||
|
assetData,
|
||||||
|
fromAddresses,
|
||||||
|
toAddresses,
|
||||||
|
amounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
|
||||||
|
/// @param orders Array of orders to individually simulate transfers for.
|
||||||
|
/// @param takerAddresses Array of addresses of takers that will fill each order.
|
||||||
|
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
|
||||||
|
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
|
||||||
|
function getSimulatedOrdersTransferResults(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
address[] memory takerAddresses,
|
||||||
|
uint256[] memory takerAssetFillAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (OrderTransferResults[] memory orderTransferResults)
|
||||||
|
{
|
||||||
|
uint256 length = orders.length;
|
||||||
|
orderTransferResults = new OrderTransferResults[](length);
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
orderTransferResults[i] = getSimulatedOrderTransferResults(
|
||||||
|
orders[i],
|
||||||
|
takerAddresses[i],
|
||||||
|
takerAssetFillAmounts[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return orderTransferResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Makes the simulation call with information about the transfers and processes
|
||||||
|
/// the returndata.
|
||||||
|
/// @param assetData The assetdata to use to make transfers.
|
||||||
|
/// @param fromAddresses The addresses to transfer funds.
|
||||||
|
/// @param toAddresses The addresses that will receive funds
|
||||||
|
/// @param amounts The amounts involved in the transfer.
|
||||||
|
function _simulateTransferFromCalls(
|
||||||
|
bytes[] memory assetData,
|
||||||
|
address[] memory fromAddresses,
|
||||||
|
address[] memory toAddresses,
|
||||||
|
uint256[] memory amounts
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (OrderTransferResults orderTransferResults)
|
||||||
|
{
|
||||||
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
|
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
|
||||||
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
|
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
|
||||||
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
|
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
|
||||||
@@ -132,29 +229,4 @@ contract OrderTransferSimulationUtils is
|
|||||||
revert("UNKNOWN_RETURN_DATA");
|
revert("UNKNOWN_RETURN_DATA");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
|
|
||||||
/// @param orders Array of orders to individually simulate transfers for.
|
|
||||||
/// @param takerAddresses Array of addresses of takers that will fill each order.
|
|
||||||
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
|
|
||||||
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
|
|
||||||
function getSimulatedOrdersTransferResults(
|
|
||||||
LibOrder.Order[] memory orders,
|
|
||||||
address[] memory takerAddresses,
|
|
||||||
uint256[] memory takerAssetFillAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
returns (OrderTransferResults[] memory orderTransferResults)
|
|
||||||
{
|
|
||||||
uint256 length = orders.length;
|
|
||||||
orderTransferResults = new OrderTransferResults[](length);
|
|
||||||
for (uint256 i = 0; i != length; i++) {
|
|
||||||
orderTransferResults[i] = getSimulatedOrderTransferResults(
|
|
||||||
orders[i],
|
|
||||||
takerAddresses[i],
|
|
||||||
takerAssetFillAmounts[i]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return orderTransferResults;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.5;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
@@ -25,17 +25,25 @@ import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
|||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "./LibAssetData.sol";
|
import "./LibAssetData.sol";
|
||||||
|
import "./OrderTransferSimulationUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
contract OrderValidationUtils is
|
contract OrderValidationUtils is
|
||||||
LibAssetData
|
LibAssetData,
|
||||||
|
OrderTransferSimulationUtils
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
using LibSafeMath for uint256;
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
constructor (address _exchange)
|
constructor (
|
||||||
|
address _exchange,
|
||||||
|
address _chaiBridge
|
||||||
|
)
|
||||||
public
|
public
|
||||||
LibAssetData(_exchange)
|
LibAssetData(
|
||||||
|
_exchange,
|
||||||
|
_chaiBridge
|
||||||
|
)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
|
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
|
||||||
@@ -50,7 +58,6 @@ contract OrderValidationUtils is
|
|||||||
/// amount of each asset that can be filled.
|
/// amount of each asset that can be filled.
|
||||||
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
|
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (
|
returns (
|
||||||
LibOrder.OrderInfo memory orderInfo,
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
uint256 fillableTakerAssetAmount,
|
uint256 fillableTakerAssetAmount,
|
||||||
@@ -99,7 +106,6 @@ contract OrderValidationUtils is
|
|||||||
} else {
|
} else {
|
||||||
// Get the transferable amount of the `makerFeeAsset`
|
// Get the transferable amount of the `makerFeeAsset`
|
||||||
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, order.makerFeeAssetData);
|
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, order.makerFeeAssetData);
|
||||||
|
|
||||||
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
|
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
|
||||||
transferableMakerAssetAmount,
|
transferableMakerAssetAmount,
|
||||||
order.makerAssetAmount,
|
order.makerAssetAmount,
|
||||||
@@ -120,6 +126,25 @@ contract OrderValidationUtils is
|
|||||||
transferableTakerAssetAmount
|
transferableTakerAssetAmount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Execute the maker transfers.
|
||||||
|
fillableTakerAssetAmount = getSimulatedOrderMakerTransferResults(
|
||||||
|
order,
|
||||||
|
order.takerAddress,
|
||||||
|
fillableTakerAssetAmount
|
||||||
|
) == OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
|
||||||
|
|
||||||
|
if (!_isAssetDataValid(order.takerAssetData)) {
|
||||||
|
fillableTakerAssetAmount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) {
|
||||||
|
fillableTakerAssetAmount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
|
||||||
|
fillableTakerAssetAmount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
|
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +160,6 @@ contract OrderValidationUtils is
|
|||||||
/// the `takerAssetData` to get the final amount of each asset that can be filled.
|
/// the `takerAssetData` to get the final amount of each asset that can be filled.
|
||||||
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
|
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (
|
returns (
|
||||||
LibOrder.OrderInfo[] memory ordersInfo,
|
LibOrder.OrderInfo[] memory ordersInfo,
|
||||||
uint256[] memory fillableTakerAssetAmounts,
|
uint256[] memory fillableTakerAssetAmounts,
|
||||||
@@ -167,11 +191,69 @@ contract OrderValidationUtils is
|
|||||||
/// the individual asset amounts located within the `assetData`.
|
/// the individual asset amounts located within the `assetData`.
|
||||||
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
|
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 transferableAssetAmount)
|
returns (uint256 transferableAssetAmount)
|
||||||
{
|
{
|
||||||
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
||||||
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
|
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
|
||||||
return transferableAssetAmount;
|
return transferableAssetAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev This function handles the edge cases around taker validation. This function
|
||||||
|
/// currently attempts to find duplicate ERC721 token's in the taker
|
||||||
|
/// multiAssetData.
|
||||||
|
/// @param assetData The asset data that should be validated.
|
||||||
|
/// @return Whether or not the order should be considered valid.
|
||||||
|
function _isAssetDataValid(bytes memory assetData)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
// Asset data must be composed of an asset proxy Id and a bytes segment with
|
||||||
|
// a length divisible by 32.
|
||||||
|
if (assetData.length % 32 != 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only process the taker asset data if it is multiAssetData.
|
||||||
|
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||||
|
if (assetProxyId != IAssetData(address(0)).MultiAsset.selector) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get array of values and array of assetDatas
|
||||||
|
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||||
|
|
||||||
|
uint256 length = nestedAssetData.length;
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// TODO(jalextowle): Implement similar validation for non-fungible ERC1155 asset data.
|
||||||
|
bytes4 nestedAssetProxyId = nestedAssetData[i].readBytes4(0);
|
||||||
|
if (nestedAssetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
|
if (_isAssetDataDuplicated(nestedAssetData, i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether or not asset data is duplicated later in the nested asset data.
|
||||||
|
/// @param nestedAssetData The asset data to scan for duplication.
|
||||||
|
/// @param startIdx The index where the scan should begin.
|
||||||
|
/// @return A boolean reflecting whether or not the starting asset data was duplicated.
|
||||||
|
function _isAssetDataDuplicated(
|
||||||
|
bytes[] memory nestedAssetData,
|
||||||
|
uint256 startIdx
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
uint256 length = nestedAssetData.length;
|
||||||
|
for (uint256 i = startIdx + 1; i < length; i++) {
|
||||||
|
if (nestedAssetData[startIdx].equals(nestedAssetData[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-dev-utils",
|
"name": "@0x/contracts-dev-utils",
|
||||||
"version": "1.0.2",
|
"version": "1.0.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
"main": "lib/src/index.js",
|
"main": "lib/src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"test": "yarn assert_deployable && echo !!! Tests are run via @0x/contracts-tests !!!",
|
"test": "yarn assert_deployable && echo !!! Tests are run via @0x/contracts-integrations !!!",
|
||||||
"assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"",
|
"assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy",
|
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy",
|
||||||
@@ -41,10 +41,10 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/assert": "^3.0.2",
|
"@0x/assert": "^3.0.4",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2"
|
"@0x/base-contract": "^6.1.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -1,4 +1,31 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "2.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "2.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1578272714,
|
||||||
|
"version": "2.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v2.0.5 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.4 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v2.0.2 - _December 17, 2019_
|
## v2.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibAddress.sol";
|
import "@0x/contracts-utils/contracts/src/LibAddress.sol";
|
||||||
|
@@ -1,4 +1,23 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "./ERC1155.sol";
|
import "./ERC1155.sol";
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
contract MixinNonFungibleToken {
|
contract MixinNonFungibleToken {
|
||||||
@@ -64,7 +65,7 @@ contract MixinNonFungibleToken {
|
|||||||
// A base type has the NF bit but does has an index.
|
// A base type has the NF bit but does has an index.
|
||||||
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0);
|
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev returns owner of a non-fungible token
|
/// @dev returns owner of a non-fungible token
|
||||||
function ownerOf(uint256 id) public view returns (address) {
|
function ownerOf(uint256 id) public view returns (address) {
|
||||||
return nfOwners[id];
|
return nfOwners[id];
|
||||||
|
@@ -17,13 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
/// @title ERC-1155 Multi Token Standard
|
/// @title ERC-1155 Multi Token Standard
|
||||||
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
|
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
|
||||||
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
|
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
|
||||||
interface IERC1155 {
|
interface IERC1155 {
|
||||||
|
|
||||||
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
||||||
/// including zero value transfers as well as minting or burning.
|
/// including zero value transfers as well as minting or burning.
|
||||||
/// Operator will always be msg.sender.
|
/// Operator will always be msg.sender.
|
||||||
|
@@ -1,4 +1,23 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./IERC1155.sol";
|
import "./IERC1155.sol";
|
||||||
|
|
||||||
|
@@ -17,10 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
interface IERC1155Receiver {
|
interface IERC1155Receiver {
|
||||||
|
|
||||||
/// @notice Handle the receipt of a single ERC1155 token type
|
/// @notice Handle the receipt of a single ERC1155 token type
|
||||||
/// @dev The smart contract calls this function on the recipient
|
/// @dev The smart contract calls this function on the recipient
|
||||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc1155",
|
"name": "@0x/contracts-erc1155",
|
||||||
"version": "2.0.2",
|
"version": "2.0.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -52,11 +52,11 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
@@ -80,10 +80,10 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2",
|
"@0x/base-contract": "^6.1.1",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ERC1155MintableContract, ERC1155TransferSingleEventArgs } from './wrappers';
|
import { ERC1155MintableContract, ERC1155TransferSingleEventArgs } from './wrappers';
|
||||||
@@ -8,7 +8,7 @@ export class Erc1155Wrapper {
|
|||||||
private readonly _erc1155Contract: ERC1155MintableContract;
|
private readonly _erc1155Contract: ERC1155MintableContract;
|
||||||
private readonly _contractOwner: string;
|
private readonly _contractOwner: string;
|
||||||
|
|
||||||
constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) {
|
constructor(contractInstance: ERC1155MintableContract, contractOwner: string) {
|
||||||
this._erc1155Contract = contractInstance;
|
this._erc1155Contract = contractInstance;
|
||||||
this._contractOwner = contractOwner;
|
this._contractOwner = contractOwner;
|
||||||
}
|
}
|
||||||
|
@@ -5,21 +5,18 @@ export {
|
|||||||
IERC1155ReceiverContract,
|
IERC1155ReceiverContract,
|
||||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||||
ERC1155TransferSingleEventArgs,
|
ERC1155TransferSingleEventArgs,
|
||||||
|
ERC1155TransferBatchEventArgs,
|
||||||
|
ERC1155Events,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
export { artifacts } from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export { Erc1155Wrapper } from './erc1155_wrapper';
|
export { Erc1155Wrapper } from './erc1155_wrapper';
|
||||||
export {
|
export {
|
||||||
Provider,
|
|
||||||
TransactionReceiptWithDecodedLogs,
|
TransactionReceiptWithDecodedLogs,
|
||||||
JSONRPCRequestPayload,
|
|
||||||
JSONRPCResponsePayload,
|
|
||||||
JSONRPCResponseError,
|
|
||||||
JSONRPCErrorCallback,
|
|
||||||
TransactionReceiptStatus,
|
TransactionReceiptStatus,
|
||||||
ContractArtifact,
|
|
||||||
ContractChains,
|
ContractChains,
|
||||||
CompilerOpts,
|
CompilerOpts,
|
||||||
StandardContractOutput,
|
StandardContractOutput,
|
||||||
|
ContractArtifact,
|
||||||
CompilerSettings,
|
CompilerSettings,
|
||||||
ContractChainData,
|
ContractChainData,
|
||||||
ContractAbi,
|
ContractAbi,
|
||||||
|
@@ -67,7 +67,7 @@ describe('ERC1155Token', () => {
|
|||||||
);
|
);
|
||||||
receiver = erc1155Receiver.address;
|
receiver = erc1155Receiver.address;
|
||||||
// create wrapper & mint erc1155 tokens
|
// create wrapper & mint erc1155 tokens
|
||||||
erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, provider, owner);
|
erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, owner);
|
||||||
fungibleToken = await erc1155Wrapper.mintFungibleTokensAsync([spender], spenderInitialFungibleBalance);
|
fungibleToken = await erc1155Wrapper.mintFungibleTokensAsync([spender], spenderInitialFungibleBalance);
|
||||||
let nonFungibleTokens: BigNumber[];
|
let nonFungibleTokens: BigNumber[];
|
||||||
[, nonFungibleTokens] = await erc1155Wrapper.mintNonFungibleTokensAsync([spender]);
|
[, nonFungibleTokens] = await erc1155Wrapper.mintNonFungibleTokensAsync([spender]);
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -1,10 +1,44 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "1.2.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Make source IDs static on all networks, not inherited from `DeploymentConstants`.",
|
||||||
|
"pr": 2459
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1580811564
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add batch functions to query quotes",
|
||||||
|
"pr": 2427
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Early exit if a DEX sample fails",
|
||||||
|
"pr": 2427
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1579682890
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add gas limits to external quote calls.",
|
||||||
|
"pr": 2405
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1578272714
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"note": "Do not query empty/unsigned orders. Swallow revets on DEX quotes.",
|
"note": "Do not query empty/unsigned orders. Swallow revets on DEX quotes.",
|
||||||
"pr": 2365
|
"pr": 2395
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": 1576540892
|
"timestamp": 1576540892
|
||||||
|
@@ -5,9 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v1.2.0 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Make source IDs static on all networks, not inherited from `DeploymentConstants`. (#2459)
|
||||||
|
|
||||||
|
## v1.1.0 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Add batch functions to query quotes (#2427)
|
||||||
|
* Early exit if a DEX sample fails (#2427)
|
||||||
|
|
||||||
|
## v1.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Add gas limits to external quote calls. (#2405)
|
||||||
|
|
||||||
## v1.0.2 - _December 17, 2019_
|
## v1.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Do not query empty/unsigned orders. Swallow revets on DEX quotes. (#2365)
|
* Do not query empty/unsigned orders. Swallow revets on DEX quotes. (#2395)
|
||||||
|
|
||||||
## v1.0.1 - _December 9, 2019_
|
## v1.0.1 - _December 9, 2019_
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -37,6 +37,76 @@ contract ERC20BridgeSampler is
|
|||||||
DeploymentConstants
|
DeploymentConstants
|
||||||
{
|
{
|
||||||
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
||||||
|
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 1500e3;
|
||||||
|
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
|
||||||
|
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 1000e3;
|
||||||
|
address constant private UNISWAP_SOURCE = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
|
||||||
|
address constant private ETH2DAI_SOURCE = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
|
||||||
|
address constant private KYBER_SOURCE = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryBatchOrdersAndSampleSells(
|
||||||
|
LibOrder.Order[][] memory orders,
|
||||||
|
bytes[][] memory orderSignatures,
|
||||||
|
address[] memory sources,
|
||||||
|
uint256[][] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ordersAndSamples = new OrdersAndSample[](orders.length);
|
||||||
|
for (uint256 i = 0; i != orders.length; i++) {
|
||||||
|
(
|
||||||
|
uint256[] memory orderFillableAssetAmounts,
|
||||||
|
uint256[][] memory tokenAmountsBySource
|
||||||
|
) = queryOrdersAndSampleSells(orders[i], orderSignatures[i], sources, takerTokenAmounts[i]);
|
||||||
|
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
|
||||||
|
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryBatchOrdersAndSampleBuys(
|
||||||
|
LibOrder.Order[][] memory orders,
|
||||||
|
bytes[][] memory orderSignatures,
|
||||||
|
address[] memory sources,
|
||||||
|
uint256[][] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ordersAndSamples = new OrdersAndSample[](orders.length);
|
||||||
|
for (uint256 i = 0; i != orders.length; i++) {
|
||||||
|
(
|
||||||
|
uint256[] memory orderFillableAssetAmounts,
|
||||||
|
uint256[][] memory tokenAmountsBySource
|
||||||
|
) = queryOrdersAndSampleBuys(orders[i], orderSignatures[i], sources, makerTokenAmounts[i]);
|
||||||
|
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
|
||||||
|
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
@@ -144,7 +214,7 @@ contract ERC20BridgeSampler is
|
|||||||
);
|
);
|
||||||
// The fillable amount is zero if the order is not fillable or if the
|
// The fillable amount is zero if the order is not fillable or if the
|
||||||
// signature is invalid.
|
// signature is invalid.
|
||||||
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE) ||
|
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE ||
|
||||||
!isValidSignature) {
|
!isValidSignature) {
|
||||||
orderFillableTakerAssetAmounts[i] = 0;
|
orderFillableTakerAssetAmounts[i] = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -268,15 +338,18 @@ contract ERC20BridgeSampler is
|
|||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
_getKyberNetworkProxyAddress().staticcall(abi.encodeWithSelector(
|
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_SAMPLE_CALL_GAS)(
|
||||||
IKyberNetwork(0).getExpectedRate.selector,
|
abi.encodeWithSelector(
|
||||||
_takerToken,
|
IKyberNetwork(0).getExpectedRate.selector,
|
||||||
_makerToken,
|
_takerToken,
|
||||||
takerTokenAmounts[i]
|
_makerToken,
|
||||||
));
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
uint256 rate = 0;
|
uint256 rate = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
rate = abi.decode(resultData, (uint256));
|
rate = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
makerTokenAmounts[i] =
|
makerTokenAmounts[i] =
|
||||||
rate *
|
rate *
|
||||||
@@ -307,15 +380,18 @@ contract ERC20BridgeSampler is
|
|||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
|
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
|
||||||
IEth2Dai(0).getBuyAmount.selector,
|
abi.encodeWithSelector(
|
||||||
makerToken,
|
IEth2Dai(0).getBuyAmount.selector,
|
||||||
takerToken,
|
makerToken,
|
||||||
takerTokenAmounts[i]
|
takerToken,
|
||||||
));
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
uint256 buyAmount = 0;
|
uint256 buyAmount = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
buyAmount = abi.decode(resultData, (uint256));
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
makerTokenAmounts[i] = buyAmount;
|
makerTokenAmounts[i] = buyAmount;
|
||||||
}
|
}
|
||||||
@@ -341,15 +417,18 @@ contract ERC20BridgeSampler is
|
|||||||
takerTokenAmounts = new uint256[](numSamples);
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
|
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
|
||||||
IEth2Dai(0).getPayAmount.selector,
|
abi.encodeWithSelector(
|
||||||
takerToken,
|
IEth2Dai(0).getPayAmount.selector,
|
||||||
makerToken,
|
takerToken,
|
||||||
makerTokenAmounts[i]
|
makerToken,
|
||||||
));
|
makerTokenAmounts[i]
|
||||||
|
));
|
||||||
uint256 sellAmount = 0;
|
uint256 sellAmount = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
sellAmount = abi.decode(resultData, (uint256));
|
sellAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
takerTokenAmounts[i] = sellAmount;
|
takerTokenAmounts[i] = sellAmount;
|
||||||
}
|
}
|
||||||
@@ -378,26 +457,28 @@ contract ERC20BridgeSampler is
|
|||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
if (makerToken == _getWethAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWethAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethBought = _callUniswapExchangePriceFunction(
|
uint256 ethBought;
|
||||||
|
(ethBought, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
if (ethBought != 0) {
|
if (ethBought != 0) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
ethBought
|
ethBought
|
||||||
@@ -406,6 +487,9 @@ contract ERC20BridgeSampler is
|
|||||||
makerTokenAmounts[i] = 0;
|
makerTokenAmounts[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,26 +516,28 @@ contract ERC20BridgeSampler is
|
|||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
if (makerToken == _getWethAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWethAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethSold = _callUniswapExchangePriceFunction(
|
uint256 ethSold;
|
||||||
|
(ethSold, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
if (ethSold != 0) {
|
if (ethSold != 0) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
ethSold
|
ethSold
|
||||||
@@ -460,6 +546,9 @@ contract ERC20BridgeSampler is
|
|||||||
takerTokenAmounts[i] = 0;
|
takerTokenAmounts[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,16 +576,18 @@ contract ERC20BridgeSampler is
|
|||||||
)
|
)
|
||||||
private
|
private
|
||||||
view
|
view
|
||||||
returns (uint256 outputAmount)
|
returns (uint256 outputAmount, bool didSucceed)
|
||||||
{
|
{
|
||||||
if (uniswapExchangeAddress == address(0)) {
|
if (uniswapExchangeAddress == address(0)) {
|
||||||
return 0;
|
return (outputAmount, didSucceed);
|
||||||
}
|
}
|
||||||
(bool didSucceed, bytes memory resultData) =
|
bytes memory resultData;
|
||||||
uniswapExchangeAddress.staticcall(abi.encodeWithSelector(
|
(didSucceed, resultData) =
|
||||||
functionSelector,
|
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
|
||||||
inputAmount
|
abi.encodeWithSelector(
|
||||||
));
|
functionSelector,
|
||||||
|
inputAmount
|
||||||
|
));
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
outputAmount = abi.decode(resultData, (uint256));
|
outputAmount = abi.decode(resultData, (uint256));
|
||||||
}
|
}
|
||||||
@@ -518,13 +609,13 @@ contract ERC20BridgeSampler is
|
|||||||
view
|
view
|
||||||
returns (uint256[] memory makerTokenAmounts)
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
{
|
{
|
||||||
if (source == _getEth2DaiAddress()) {
|
if (source == ETH2DAI_SOURCE) {
|
||||||
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
|
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
|
||||||
}
|
}
|
||||||
if (source == _getUniswapExchangeFactoryAddress()) {
|
if (source == UNISWAP_SOURCE) {
|
||||||
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
|
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
|
||||||
}
|
}
|
||||||
if (source == _getKyberNetworkProxyAddress()) {
|
if (source == KYBER_SOURCE) {
|
||||||
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
|
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
|
||||||
}
|
}
|
||||||
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
||||||
@@ -546,10 +637,10 @@ contract ERC20BridgeSampler is
|
|||||||
view
|
view
|
||||||
returns (uint256[] memory takerTokenAmounts)
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
{
|
{
|
||||||
if (source == _getEth2DaiAddress()) {
|
if (source == ETH2DAI_SOURCE) {
|
||||||
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
|
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
|
||||||
}
|
}
|
||||||
if (source == _getUniswapExchangeFactoryAddress()) {
|
if (source == UNISWAP_SOURCE) {
|
||||||
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
|
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
|
||||||
}
|
}
|
||||||
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
||||||
|
@@ -23,6 +23,52 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|||||||
|
|
||||||
|
|
||||||
interface IERC20BridgeSampler {
|
interface IERC20BridgeSampler {
|
||||||
|
struct OrdersAndSample {
|
||||||
|
uint256[] orderFillableAssetAmounts;
|
||||||
|
uint256[][] tokenAmountsBySource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryBatchOrdersAndSampleSells(
|
||||||
|
LibOrder.Order[][] calldata orders,
|
||||||
|
bytes[][] calldata orderSignatures,
|
||||||
|
address[] calldata sources,
|
||||||
|
uint256[][] calldata takerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index
|
||||||
|
function queryBatchOrdersAndSampleBuys(
|
||||||
|
LibOrder.Order[][] calldata orders,
|
||||||
|
bytes[][] calldata orderSignatures,
|
||||||
|
address[] calldata sources,
|
||||||
|
uint256[][] calldata makerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
);
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
|
@@ -305,6 +305,8 @@ contract TestERC20BridgeSampler is
|
|||||||
TestERC20BridgeSamplerEth2Dai public eth2Dai;
|
TestERC20BridgeSamplerEth2Dai public eth2Dai;
|
||||||
TestERC20BridgeSamplerKyberNetwork public kyber;
|
TestERC20BridgeSamplerKyberNetwork public kyber;
|
||||||
|
|
||||||
|
uint8 private constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED) + 1;
|
||||||
|
|
||||||
constructor() public {
|
constructor() public {
|
||||||
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
|
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
|
||||||
eth2Dai = new TestERC20BridgeSamplerEth2Dai();
|
eth2Dai = new TestERC20BridgeSamplerEth2Dai();
|
||||||
@@ -336,9 +338,12 @@ contract TestERC20BridgeSampler is
|
|||||||
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
||||||
// Everything else is derived from the hash.
|
// Everything else is derived from the hash.
|
||||||
orderInfo.orderHash = orderHash;
|
orderInfo.orderHash = orderHash;
|
||||||
orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1));
|
if (uint256(orderHash) % 100 > 90) {
|
||||||
orderInfo.orderTakerAssetFilledAmount =
|
orderInfo.orderStatus = LibOrder.OrderStatus.FULLY_FILLED;
|
||||||
uint256(orderHash) % order.takerAssetAmount;
|
} else {
|
||||||
|
orderInfo.orderStatus = LibOrder.OrderStatus.FILLABLE;
|
||||||
|
}
|
||||||
|
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
||||||
fillableTakerAssetAmount =
|
fillableTakerAssetAmount =
|
||||||
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
||||||
isValidSignature = uint256(orderHash) % 2 == 1;
|
isValidSignature = uint256(orderHash) % 2 == 1;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc20-bridge-sampler",
|
"name": "@0x/contracts-erc20-bridge-sampler",
|
||||||
"version": "1.0.2",
|
"version": "1.2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -50,18 +50,18 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
"@0x/contracts-asset-proxy": "^3.1.2",
|
||||||
"@0x/contracts-erc20": "^3.0.2",
|
"@0x/contracts-erc20": "^3.0.5",
|
||||||
"@0x/contracts-exchange": "^3.0.2",
|
"@0x/contracts-exchange": "^3.1.1",
|
||||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
"@0x/contracts-exchange-libs": "^4.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -79,10 +79,10 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2",
|
"@0x/base-contract": "^6.1.1",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
"@0x/typescript-typings": "^5.0.1",
|
"@0x/typescript-typings": "^5.0.1",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"ethereum-types": "^3.0.0",
|
"ethereum-types": "^3.0.0",
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
},
|
},
|
||||||
|
@@ -15,7 +15,6 @@ import { TestERC20BridgeSamplerContract } from './wrappers';
|
|||||||
|
|
||||||
blockchainTests('erc20-bridge-sampler', env => {
|
blockchainTests('erc20-bridge-sampler', env => {
|
||||||
let testContract: TestERC20BridgeSamplerContract;
|
let testContract: TestERC20BridgeSamplerContract;
|
||||||
let allSources: { [name: string]: string };
|
|
||||||
const RATE_DENOMINATOR = constants.ONE_ETHER;
|
const RATE_DENOMINATOR = constants.ONE_ETHER;
|
||||||
const MIN_RATE = new BigNumber('0.01');
|
const MIN_RATE = new BigNumber('0.01');
|
||||||
const MAX_RATE = new BigNumber('100');
|
const MAX_RATE = new BigNumber('100');
|
||||||
@@ -30,6 +29,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const INVALID_ASSET_DATA = hexUtils.random(37);
|
const INVALID_ASSET_DATA = hexUtils.random(37);
|
||||||
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
|
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
|
||||||
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
|
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
|
||||||
|
const SOURCE_IDS: { [source: string]: string } = {
|
||||||
|
Uniswap: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95',
|
||||||
|
Eth2Dai: '0x39755357759ce0d7f32dc8dc45414cca409ae24e',
|
||||||
|
Kyber: '0x818e6fecd516ecc3849daf6845e3ec868087b755',
|
||||||
|
};
|
||||||
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
|
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
|
||||||
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
|
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
|
||||||
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
|
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
|
||||||
@@ -45,14 +49,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
allSources = _.zipObject(
|
|
||||||
['Uniswap', 'Eth2Dai', 'Kyber'],
|
|
||||||
[
|
|
||||||
await testContract.uniswap().callAsync(),
|
|
||||||
await testContract.eth2Dai().callAsync(),
|
|
||||||
await testContract.kyber().callAsync(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function getPackedHash(...args: string[]): string {
|
function getPackedHash(...args: string[]): string {
|
||||||
@@ -195,7 +191,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
||||||
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
|
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
|
||||||
const orderStatus = new BigNumber(hash).mod(255).toNumber();
|
const orderStatus = new BigNumber(hash).mod(100).toNumber() > 90 ? 5 : 3;
|
||||||
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
||||||
if (orderStatus !== 3 || !isValidSignature) {
|
if (orderStatus !== 3 || !isValidSignature) {
|
||||||
return constants.ZERO_AMOUNT;
|
return constants.ZERO_AMOUNT;
|
||||||
@@ -208,7 +204,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
return order.makerAssetAmount
|
return order.makerAssetAmount
|
||||||
.times(takerAmount)
|
.times(takerAmount)
|
||||||
.div(order.takerAssetAmount)
|
.div(order.takerAssetAmount)
|
||||||
.integerValue(BigNumber.ROUND_DOWN);
|
.integerValue(BigNumber.ROUND_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getERC20AssetData(tokenAddress: string): string {
|
function getERC20AssetData(tokenAddress: string): string {
|
||||||
@@ -255,7 +251,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
describe('getOrderFillableTakerAssetAmounts()', () => {
|
describe('getOrderFillableTakerAssetAmounts()', () => {
|
||||||
it('returns the expected amount for each order', async () => {
|
it('returns the expected amount for each order', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq(expected);
|
expect(actual).to.deep.eq(expected);
|
||||||
@@ -269,7 +265,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero maker asset amount', async () => {
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -277,7 +273,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero taker asset amount', async () => {
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -293,7 +289,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
describe('getOrderFillableMakerAssetAmounts()', () => {
|
describe('getOrderFillableMakerAssetAmounts()', () => {
|
||||||
it('returns the expected amount for each order', async () => {
|
it('returns the expected amount for each order', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
|
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq(expected);
|
expect(actual).to.deep.eq(expected);
|
||||||
@@ -307,7 +303,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero maker asset amount', async () => {
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -315,7 +311,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero taker asset amount', async () => {
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -330,7 +326,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
describe('queryOrdersAndSampleSells()', () => {
|
describe('queryOrdersAndSampleSells()', () => {
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
@@ -340,7 +336,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
|
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
|
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
|
||||||
const [orderInfos] = await testContract
|
const [orderInfos] = await testContract
|
||||||
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => SOURCE_IDS[n]), takerTokenAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
||||||
});
|
});
|
||||||
@@ -349,14 +345,14 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
||||||
const [, quotes] = await testContract
|
const [, quotes] = await testContract
|
||||||
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), sampleAmounts)
|
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => SOURCE_IDS[n]), sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no orders are passed in', async () => {
|
it('throws if no orders are passed in', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
|
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => SOURCE_IDS[n]), getSampleAmounts(TAKER_TOKEN))
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||||
});
|
});
|
||||||
@@ -366,7 +362,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
.queryOrdersAndSampleSells(
|
.queryOrdersAndSampleSells(
|
||||||
ORDERS,
|
ORDERS,
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
|
[...SELL_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -381,7 +377,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -396,7 +392,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -411,7 +407,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
makerAssetData: INVALID_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -426,7 +422,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
takerAssetData: INVALID_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -436,7 +432,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
describe('queryOrdersAndSampleBuys()', () => {
|
describe('queryOrdersAndSampleBuys()', () => {
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
@@ -446,7 +442,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
|
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
|
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
|
||||||
const [orderInfos] = await testContract
|
const [orderInfos] = await testContract
|
||||||
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => SOURCE_IDS[n]), takerTokenAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
||||||
});
|
});
|
||||||
@@ -455,14 +451,14 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
||||||
const [, quotes] = await testContract
|
const [, quotes] = await testContract
|
||||||
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), sampleAmounts)
|
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => SOURCE_IDS[n]), sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no orders are passed in', async () => {
|
it('throws if no orders are passed in', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
|
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => SOURCE_IDS[n]), getSampleAmounts(MAKER_TOKEN))
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||||
});
|
});
|
||||||
@@ -472,7 +468,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
ORDERS,
|
ORDERS,
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
|
[...BUY_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -485,7 +481,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
ORDERS,
|
ORDERS,
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
sources.map(n => allSources[n]),
|
sources.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -500,7 +496,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -515,7 +511,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -530,7 +526,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
makerAssetData: INVALID_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -545,7 +541,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
takerAssetData: INVALID_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
SIGNATURES,
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
@@ -561,7 +557,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns empty quotes with no sample amounts', async () => {
|
it('returns empty quotes with no sample amounts', async () => {
|
||||||
const emptyQuotes = _.times(SELL_SOURCES.length, () => []);
|
const emptyQuotes = _.times(SELL_SOURCES.length, () => []);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
.sampleSells(SELL_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(emptyQuotes);
|
expect(quotes).to.deep.eq(emptyQuotes);
|
||||||
});
|
});
|
||||||
@@ -570,7 +566,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
.sampleSells(SELL_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
@@ -580,7 +576,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const sources = _.sampleSize(SELL_SOURCES, 1);
|
const sources = _.sampleSize(SELL_SOURCES, 1);
|
||||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
.sampleSells(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
.sampleSells(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
@@ -588,7 +584,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with an unsupported source', async () => {
|
it('throws with an unsupported source', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.sampleSells(
|
.sampleSells(
|
||||||
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
|
[...SELL_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||||
TAKER_TOKEN,
|
TAKER_TOKEN,
|
||||||
MAKER_TOKEN,
|
MAKER_TOKEN,
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
@@ -606,7 +602,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns empty quotes with no sample amounts', async () => {
|
it('returns empty quotes with no sample amounts', async () => {
|
||||||
const emptyQuotes = _.times(BUY_SOURCES.length, () => []);
|
const emptyQuotes = _.times(BUY_SOURCES.length, () => []);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
.sampleBuys(BUY_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(emptyQuotes);
|
expect(quotes).to.deep.eq(emptyQuotes);
|
||||||
});
|
});
|
||||||
@@ -615,7 +611,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
.sampleBuys(BUY_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
@@ -625,7 +621,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const sources = _.sampleSize(BUY_SOURCES, 1);
|
const sources = _.sampleSize(BUY_SOURCES, 1);
|
||||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
.sampleBuys(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
@@ -633,7 +629,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with an unsupported source', async () => {
|
it('throws with an unsupported source', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.sampleBuys(
|
.sampleBuys(
|
||||||
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
|
[...BUY_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||||
TAKER_TOKEN,
|
TAKER_TOKEN,
|
||||||
MAKER_TOKEN,
|
MAKER_TOKEN,
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
@@ -645,7 +641,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws if kyber is passed in as a source', async () => {
|
it('throws if kyber is passed in as a source', async () => {
|
||||||
const sources = [...BUY_SOURCES, 'Kyber'];
|
const sources = [...BUY_SOURCES, 'Kyber'];
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, getSampleAmounts(MAKER_TOKEN))
|
.sampleBuys(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, getSampleAmounts(MAKER_TOKEN))
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||||
});
|
});
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -1,4 +1,31 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "3.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "3.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1578272714,
|
||||||
|
"version": "3.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.0.5 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.4 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v3.0.2 - _December 17, 2019_
|
## v3.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// solhint-disable
|
// solhint-disable
|
||||||
pragma solidity ^0.4.18;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
contract WETH9 {
|
contract WETH9 {
|
||||||
@@ -30,27 +30,27 @@ contract WETH9 {
|
|||||||
mapping (address => uint) public balanceOf;
|
mapping (address => uint) public balanceOf;
|
||||||
mapping (address => mapping (address => uint)) public allowance;
|
mapping (address => mapping (address => uint)) public allowance;
|
||||||
|
|
||||||
function() public payable {
|
function() external payable {
|
||||||
deposit();
|
deposit();
|
||||||
}
|
}
|
||||||
function deposit() public payable {
|
function deposit() public payable {
|
||||||
balanceOf[msg.sender] += msg.value;
|
balanceOf[msg.sender] += msg.value;
|
||||||
Deposit(msg.sender, msg.value);
|
emit Deposit(msg.sender, msg.value);
|
||||||
}
|
}
|
||||||
function withdraw(uint wad) public {
|
function withdraw(uint wad) public {
|
||||||
require(balanceOf[msg.sender] >= wad);
|
require(balanceOf[msg.sender] >= wad);
|
||||||
balanceOf[msg.sender] -= wad;
|
balanceOf[msg.sender] -= wad;
|
||||||
msg.sender.transfer(wad);
|
msg.sender.transfer(wad);
|
||||||
Withdrawal(msg.sender, wad);
|
emit Withdrawal(msg.sender, wad);
|
||||||
}
|
}
|
||||||
|
|
||||||
function totalSupply() public view returns (uint) {
|
function totalSupply() public view returns (uint) {
|
||||||
return this.balance;
|
return address(this).balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
function approve(address guy, uint wad) public returns (bool) {
|
function approve(address guy, uint wad) public returns (bool) {
|
||||||
allowance[msg.sender][guy] = wad;
|
allowance[msg.sender][guy] = wad;
|
||||||
Approval(msg.sender, guy, wad);
|
emit Approval(msg.sender, guy, wad);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ contract WETH9 {
|
|||||||
balanceOf[src] -= wad;
|
balanceOf[src] -= wad;
|
||||||
balanceOf[dst] += wad;
|
balanceOf[dst] += wad;
|
||||||
|
|
||||||
Transfer(src, dst, wad);
|
emit Transfer(src, dst, wad);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc20",
|
"name": "@0x/contracts-erc20",
|
||||||
"version": "3.0.2",
|
"version": "3.0.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -51,18 +51,18 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
"@0x/typescript-typings": "^5.0.1",
|
"@0x/typescript-typings": "^5.0.1",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2"
|
"@0x/base-contract": "^6.1.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -1,4 +1,31 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "3.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "3.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1578272714,
|
||||||
|
"version": "3.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.0.5 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.4 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v3.0.2 - _December 17, 2019_
|
## v3.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc721",
|
"name": "@0x/contracts-erc721",
|
||||||
"version": "3.0.2",
|
"version": "3.0.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -52,18 +52,18 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
"@0x/typescript-typings": "^5.0.1",
|
"@0x/typescript-typings": "^5.0.1",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2"
|
"@0x/base-contract": "^6.1.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -1,4 +1,31 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "4.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "4.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1578272714,
|
||||||
|
"version": "4.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v4.0.5 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v4.0.4 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v4.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v4.0.2 - _December 17, 2019_
|
## v4.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -21,6 +21,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "./MixinForwarderCore.sol";
|
import "./MixinForwarderCore.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "./libs/LibConstants.sol";
|
||||||
|
import "./MixinReceiver.sol";
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
@@ -28,16 +29,19 @@ import "./libs/LibConstants.sol";
|
|||||||
// MixinForwarderCore.
|
// MixinForwarderCore.
|
||||||
contract Forwarder is
|
contract Forwarder is
|
||||||
LibConstants,
|
LibConstants,
|
||||||
MixinForwarderCore
|
MixinForwarderCore,
|
||||||
|
MixinReceiver
|
||||||
{
|
{
|
||||||
constructor (
|
constructor (
|
||||||
address _exchange,
|
address _exchange,
|
||||||
|
address _exchangeV2,
|
||||||
address _weth
|
address _weth
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
Ownable()
|
Ownable()
|
||||||
LibConstants(
|
LibConstants(
|
||||||
_exchange,
|
_exchange,
|
||||||
|
_exchangeV2,
|
||||||
_weth
|
_weth
|
||||||
)
|
)
|
||||||
MixinForwarderCore()
|
MixinForwarderCore()
|
||||||
|
@@ -22,9 +22,9 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "./libs/LibConstants.sol";
|
||||||
|
import "./libs/LibAssetDataTransfer.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
import "./interfaces/IAssets.sol";
|
import "./interfaces/IAssets.sol";
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ contract MixinAssets is
|
|||||||
IAssets
|
IAssets
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
using LibAssetDataTransfer for bytes;
|
||||||
|
|
||||||
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
|
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
|
||||||
/// that were accidentally sent to this contract.
|
/// that were accidentally sent to this contract.
|
||||||
@@ -47,7 +48,7 @@ contract MixinAssets is
|
|||||||
external
|
external
|
||||||
onlyOwner
|
onlyOwner
|
||||||
{
|
{
|
||||||
_transferAssetToSender(assetData, amount);
|
assetData.transferOut(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf.
|
/// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf.
|
||||||
@@ -72,69 +73,4 @@ contract MixinAssets is
|
|||||||
LibERC20Token.approve(token, proxyAddress, MAX_UINT);
|
LibERC20Token.approve(token, proxyAddress, MAX_UINT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Transfers given amount of asset to sender.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to transfer to sender.
|
|
||||||
function _transferAssetToSender(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
bytes4 proxyId = assetData.readBytes4(0);
|
|
||||||
|
|
||||||
if (
|
|
||||||
proxyId == IAssetData(address(0)).ERC20Token.selector ||
|
|
||||||
proxyId == IAssetData(address(0)).ERC20Bridge.selector
|
|
||||||
) {
|
|
||||||
_transferERC20Token(assetData, amount);
|
|
||||||
} else if (proxyId == IAssetData(address(0)).ERC721Token.selector) {
|
|
||||||
_transferERC721Token(assetData, amount);
|
|
||||||
} else {
|
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError(
|
|
||||||
proxyId
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Decodes ERC20 or ERC20Bridge assetData and transfers given amount to sender.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to transfer to sender.
|
|
||||||
function _transferERC20Token(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
// Transfer tokens.
|
|
||||||
LibERC20Token.transfer(token, msg.sender, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Decodes ERC721 assetData and transfers given amount to sender.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to transfer to sender.
|
|
||||||
function _transferERC721Token(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
if (amount != 1) {
|
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.Erc721AmountMustEqualOneError(
|
|
||||||
amount
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Decode asset data.
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
uint256 tokenId = assetData.readUint256(36);
|
|
||||||
|
|
||||||
// Perform transfer.
|
|
||||||
IERC721Token(token).transferFrom(
|
|
||||||
address(this),
|
|
||||||
msg.sender,
|
|
||||||
tokenId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,7 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
|||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "./libs/LibConstants.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
|
import "./interfaces/IExchangeV2.sol";
|
||||||
import "./MixinAssets.sol";
|
import "./MixinAssets.sol";
|
||||||
|
|
||||||
|
|
||||||
@@ -54,23 +55,19 @@ contract MixinExchangeWrapper is
|
|||||||
internal
|
internal
|
||||||
returns (LibFillResults.FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
// ABI encode calldata for `fillOrder`
|
if (_isV2Order(order)) {
|
||||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
return _fillV2OrderNoThrow(
|
||||||
IExchange(address(0)).fillOrder.selector,
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _fillV3OrderNoThrow(
|
||||||
order,
|
order,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
signature
|
signature
|
||||||
);
|
);
|
||||||
|
|
||||||
address exchange = address(EXCHANGE);
|
|
||||||
(bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
|
|
||||||
if (didSucceed) {
|
|
||||||
assert(returnData.length == 160);
|
|
||||||
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
|
||||||
}
|
|
||||||
|
|
||||||
// fillResults values will be 0 by default if call was unsuccessful
|
|
||||||
return fillResults;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Executes a single call of fillOrder according to the wethSellAmount and
|
/// @dev Executes a single call of fillOrder according to the wethSellAmount and
|
||||||
@@ -162,8 +159,7 @@ contract MixinExchangeWrapper is
|
|||||||
uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
|
uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
|
||||||
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
|
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
|
||||||
|
|
||||||
uint256 ordersLength = orders.length;
|
for (uint256 i = 0; i != orders.length; i++) {
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
|
||||||
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
|
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
|
||||||
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
|
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
|
||||||
continue;
|
continue;
|
||||||
@@ -172,7 +168,7 @@ contract MixinExchangeWrapper is
|
|||||||
// The remaining amount of WETH to sell
|
// The remaining amount of WETH to sell
|
||||||
uint256 remainingTakerAssetFillAmount = wethSellAmount
|
uint256 remainingTakerAssetFillAmount = wethSellAmount
|
||||||
.safeSub(totalWethSpentAmount)
|
.safeSub(totalWethSpentAmount)
|
||||||
.safeSub(protocolFee);
|
.safeSub(_isV2Order(orders[i]) ? 0 : protocolFee);
|
||||||
|
|
||||||
// If the maker asset is ERC20Bridge, take a snapshot of the Forwarder contract's balance.
|
// If the maker asset is ERC20Bridge, take a snapshot of the Forwarder contract's balance.
|
||||||
bytes4 makerAssetProxyId = orders[i].makerAssetData.readBytes4(0);
|
bytes4 makerAssetProxyId = orders[i].makerAssetData.readBytes4(0);
|
||||||
@@ -201,7 +197,7 @@ contract MixinExchangeWrapper is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
|
orders[i].makerAssetData.transferOut(makerAssetAcquiredAmount);
|
||||||
|
|
||||||
totalWethSpentAmount = totalWethSpentAmount
|
totalWethSpentAmount = totalWethSpentAmount
|
||||||
.safeAdd(wethSpentAmount);
|
.safeAdd(wethSpentAmount);
|
||||||
@@ -349,7 +345,7 @@ contract MixinExchangeWrapper is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
|
orders[i].makerAssetData.transferOut(makerAssetAcquiredAmount);
|
||||||
|
|
||||||
totalWethSpentAmount = totalWethSpentAmount
|
totalWethSpentAmount = totalWethSpentAmount
|
||||||
.safeAdd(wethSpentAmount);
|
.safeAdd(wethSpentAmount);
|
||||||
@@ -370,6 +366,91 @@ contract MixinExchangeWrapper is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Fills the input ExchangeV2 order. The `makerFeeAssetData` must be
|
||||||
|
// equal to EXCHANGE_V2_ORDER_ID (0x770501f8).
|
||||||
|
/// Returns false if the transaction would otherwise revert.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
|
function _fillV2OrderNoThrow(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
// Strip v3 specific fields from order
|
||||||
|
IExchangeV2.Order memory v2Order = IExchangeV2.Order({
|
||||||
|
makerAddress: order.makerAddress,
|
||||||
|
takerAddress: order.takerAddress,
|
||||||
|
feeRecipientAddress: order.feeRecipientAddress,
|
||||||
|
senderAddress: order.senderAddress,
|
||||||
|
makerAssetAmount: order.makerAssetAmount,
|
||||||
|
takerAssetAmount: order.takerAssetAmount,
|
||||||
|
// NOTE: We assume fees are 0 for all v2 orders. Orders with non-zero fees will fail to be filled.
|
||||||
|
makerFee: 0,
|
||||||
|
takerFee: 0,
|
||||||
|
expirationTimeSeconds: order.expirationTimeSeconds,
|
||||||
|
salt: order.salt,
|
||||||
|
makerAssetData: order.makerAssetData,
|
||||||
|
takerAssetData: order.takerAssetData
|
||||||
|
});
|
||||||
|
|
||||||
|
// ABI encode calldata for `fillOrder`
|
||||||
|
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||||
|
IExchangeV2(address(0)).fillOrder.selector,
|
||||||
|
v2Order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
address exchange = address(EXCHANGE_V2);
|
||||||
|
(bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
|
||||||
|
if (didSucceed) {
|
||||||
|
assert(returnData.length == 128);
|
||||||
|
// NOTE: makerFeePaid, takerFeePaid, and protocolFeePaid will always be 0 for v2 orders
|
||||||
|
(fillResults.makerAssetFilledAmount, fillResults.takerAssetFilledAmount) = abi.decode(returnData, (uint256, uint256));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillResults values will be 0 by default if call was unsuccessful
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Fills the input ExchangeV3 order.
|
||||||
|
/// Returns false if the transaction would otherwise revert.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
|
function _fillV3OrderNoThrow(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
// ABI encode calldata for `fillOrder`
|
||||||
|
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||||
|
IExchange(address(0)).fillOrder.selector,
|
||||||
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
address exchange = address(EXCHANGE);
|
||||||
|
(bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
|
||||||
|
if (didSucceed) {
|
||||||
|
assert(returnData.length == 160);
|
||||||
|
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillResults values will be 0 by default if call was unsuccessful
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Checks whether one asset is effectively equal to another asset.
|
/// @dev Checks whether one asset is effectively equal to another asset.
|
||||||
/// This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
|
/// This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
|
||||||
/// one is the ERC20Bridge equivalent of the other.
|
/// one is the ERC20Bridge equivalent of the other.
|
||||||
@@ -398,7 +479,18 @@ contract MixinExchangeWrapper is
|
|||||||
address token2 = assetData2.readAddress(16);
|
address token2 = assetData2.readAddress(16);
|
||||||
return (token1 == token2);
|
return (token1 == token2);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return assetData1.equals(assetData2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Checks whether an order is a v2 order.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @return True if the order's `makerFeeAssetData` is set to the v2 order id.
|
||||||
|
function _isV2Order(LibOrder.Order memory order)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return order.makerFeeAssetData.length > 3 && order.makerFeeAssetData.readBytes4(0) == EXCHANGE_V2_ORDER_ID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -85,7 +85,6 @@ contract MixinForwarderCore is
|
|||||||
ethFeeAmounts,
|
ethFeeAmounts,
|
||||||
feeRecipients
|
feeRecipients
|
||||||
);
|
);
|
||||||
|
|
||||||
// Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender,
|
// Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender,
|
||||||
// and pays WETH order fees.
|
// and pays WETH order fees.
|
||||||
(
|
(
|
||||||
|
76
contracts/exchange-forwarder/contracts/src/MixinReceiver.sol
Normal file
76
contracts/exchange-forwarder/contracts/src/MixinReceiver.sol
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
contract MixinReceiver {
|
||||||
|
|
||||||
|
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||||
|
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
|
||||||
|
|
||||||
|
/// @notice Handle the receipt of a single ERC1155 token type
|
||||||
|
/// @dev The smart contract calls this function on the recipient
|
||||||
|
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||||
|
/// transfer. Return of other than the magic value MUST result in the
|
||||||
|
///transaction being reverted
|
||||||
|
/// Note: the contract address is always the message sender
|
||||||
|
/// @param operator The address which called `safeTransferFrom` function
|
||||||
|
/// @param from The address which previously owned the token
|
||||||
|
/// @param id An array containing the ids of the token being transferred
|
||||||
|
/// @param value An array containing the amount of tokens being transferred
|
||||||
|
/// @param data Additional data with no specified format
|
||||||
|
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||||
|
function onERC1155Received(
|
||||||
|
address operator,
|
||||||
|
address from,
|
||||||
|
uint256 id,
|
||||||
|
uint256 value,
|
||||||
|
bytes calldata data
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4)
|
||||||
|
{
|
||||||
|
return ERC1155_RECEIVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Handle the receipt of multiple ERC1155 token types
|
||||||
|
/// @dev The smart contract calls this function on the recipient
|
||||||
|
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||||
|
/// transfer. Return of other than the magic value MUST result in the
|
||||||
|
/// transaction being reverted
|
||||||
|
/// Note: the contract address is always the message sender
|
||||||
|
/// @param operator The address which called `safeTransferFrom` function
|
||||||
|
/// @param from The address which previously owned the token
|
||||||
|
/// @param ids An array containing ids of each token being transferred
|
||||||
|
/// @param values An array containing amounts of each token being transferred
|
||||||
|
/// @param data Additional data with no specified format
|
||||||
|
/// @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
|
||||||
|
function onERC1155BatchReceived(
|
||||||
|
address operator,
|
||||||
|
address from,
|
||||||
|
uint256[] calldata ids,
|
||||||
|
uint256[] calldata values,
|
||||||
|
bytes calldata data
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4)
|
||||||
|
{
|
||||||
|
return ERC1155_BATCH_RECEIVED;
|
||||||
|
}
|
||||||
|
}
|
@@ -109,7 +109,6 @@ contract MixinWeth is
|
|||||||
if (wethRemaining > 0) {
|
if (wethRemaining > 0) {
|
||||||
// Convert remaining WETH to ETH
|
// Convert remaining WETH to ETH
|
||||||
ETHER_TOKEN.withdraw(wethRemaining);
|
ETHER_TOKEN.withdraw(wethRemaining);
|
||||||
|
|
||||||
// Transfer remaining ETH to sender
|
// Transfer remaining ETH to sender
|
||||||
msg.sender.transfer(wethRemaining);
|
msg.sender.transfer(wethRemaining);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
contract IExchangeV2 {
|
||||||
|
|
||||||
|
// solhint-disable max-line-length
|
||||||
|
struct Order {
|
||||||
|
address makerAddress; // Address that created the order.
|
||||||
|
address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.
|
||||||
|
address feeRecipientAddress; // Address that will recieve fees when order is filled.
|
||||||
|
address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
|
||||||
|
uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0.
|
||||||
|
uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0.
|
||||||
|
uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.
|
||||||
|
uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.
|
||||||
|
uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires.
|
||||||
|
uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash.
|
||||||
|
bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy.
|
||||||
|
bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy.
|
||||||
|
}
|
||||||
|
// solhint-enable max-line-length
|
||||||
|
|
||||||
|
struct FillResults {
|
||||||
|
uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled.
|
||||||
|
uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled.
|
||||||
|
uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s).
|
||||||
|
uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s).
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OrderInfo {
|
||||||
|
uint8 orderStatus; // Status that describes order's validity and fillability.
|
||||||
|
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
|
||||||
|
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Fills the input order.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
|
function fillOrder(
|
||||||
|
Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (FillResults memory fillResults);
|
||||||
|
|
||||||
|
/// @dev Gets information about an order: status, hash, and amount filled.
|
||||||
|
/// @param order Order to gather information on.
|
||||||
|
/// @return OrderInfo Information about the order and its state.
|
||||||
|
/// See LibOrder.OrderInfo for a complete description.
|
||||||
|
function getOrderInfo(Order memory order)
|
||||||
|
public
|
||||||
|
returns (OrderInfo memory orderInfo);
|
||||||
|
}
|
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||||
|
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
|
import "./LibForwarderRichErrors.sol";
|
||||||
|
|
||||||
|
|
||||||
|
library LibAssetDataTransfer {
|
||||||
|
|
||||||
|
using LibBytes for bytes;
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
using LibAssetDataTransfer for bytes;
|
||||||
|
|
||||||
|
/// @dev Transfers given amount of asset to sender.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer asset from.
|
||||||
|
/// @param to Address to transfer asset to.
|
||||||
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
|
function transferFrom(
|
||||||
|
bytes memory assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
if (amount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes4 proxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
if (
|
||||||
|
proxyId == IAssetData(address(0)).ERC20Token.selector ||
|
||||||
|
proxyId == IAssetData(address(0)).ERC20Bridge.selector
|
||||||
|
) {
|
||||||
|
assetData.transferERC20Token(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
} else if (proxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
|
assetData.transferERC721Token(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
} else if (proxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
|
assetData.transferERC1155Assets(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
} else if (proxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||||
|
assetData.transferMultiAsset(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
} else if (proxyId != IAssetData(address(0)).StaticCall.selector) {
|
||||||
|
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError(
|
||||||
|
proxyId
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///@dev Transfer asset from sender to this contract.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
|
function transferIn(
|
||||||
|
bytes memory assetData,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
assetData.transferFrom(
|
||||||
|
msg.sender,
|
||||||
|
address(this),
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
///@dev Transfer asset from this contract to sender.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
|
function transferOut(
|
||||||
|
bytes memory assetData,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
assetData.transferFrom(
|
||||||
|
address(this),
|
||||||
|
msg.sender,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes ERC20 or ERC20Bridge assetData and transfers given amount to sender.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer asset from.
|
||||||
|
/// @param to Address to transfer asset to.
|
||||||
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
|
function transferERC20Token(
|
||||||
|
bytes memory assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
address token = assetData.readAddress(16);
|
||||||
|
// Transfer tokens.
|
||||||
|
if (from == address(this)) {
|
||||||
|
LibERC20Token.transfer(
|
||||||
|
token,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
LibERC20Token.transferFrom(
|
||||||
|
token,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes ERC721 assetData and transfers given amount to sender.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer asset from.
|
||||||
|
/// @param to Address to transfer asset to.
|
||||||
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
|
function transferERC721Token(
|
||||||
|
bytes memory assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
if (amount != 1) {
|
||||||
|
LibRichErrors.rrevert(LibForwarderRichErrors.Erc721AmountMustEqualOneError(
|
||||||
|
amount
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Decode asset data.
|
||||||
|
address token = assetData.readAddress(16);
|
||||||
|
uint256 tokenId = assetData.readUint256(36);
|
||||||
|
|
||||||
|
// Perform transfer.
|
||||||
|
IERC721Token(token).transferFrom(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
tokenId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes ERC1155 assetData and transfers given amounts to sender.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer asset from.
|
||||||
|
/// @param to Address to transfer asset to.
|
||||||
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
|
function transferERC1155Assets(
|
||||||
|
bytes memory assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
// Decode assetData
|
||||||
|
// solhint-disable
|
||||||
|
(
|
||||||
|
address token,
|
||||||
|
uint256[] memory ids,
|
||||||
|
uint256[] memory values,
|
||||||
|
bytes memory data
|
||||||
|
) = abi.decode(
|
||||||
|
assetData.slice(4, assetData.length),
|
||||||
|
(address, uint256[], uint256[], bytes)
|
||||||
|
);
|
||||||
|
// solhint-enable
|
||||||
|
|
||||||
|
// Scale up values by `amount`
|
||||||
|
uint256 length = values.length;
|
||||||
|
uint256[] memory scaledValues = new uint256[](length);
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
scaledValues[i] = values[i].safeMul(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute `safeBatchTransferFrom` call
|
||||||
|
// Either succeeds or throws
|
||||||
|
IERC1155(token).safeBatchTransferFrom(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
ids,
|
||||||
|
scaledValues,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes MultiAsset assetData and recursively transfers assets to sender.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer asset from.
|
||||||
|
/// @param to Address to transfer asset to.
|
||||||
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
|
function transferMultiAsset(
|
||||||
|
bytes memory assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
// solhint-disable indent
|
||||||
|
(uint256[] memory nestedAmounts, bytes[] memory nestedAssetData) = abi.decode(
|
||||||
|
assetData.slice(4, assetData.length),
|
||||||
|
(uint256[], bytes[])
|
||||||
|
);
|
||||||
|
// solhint-enable indent
|
||||||
|
|
||||||
|
uint256 numNestedAssets = nestedAssetData.length;
|
||||||
|
for (uint256 i = 0; i != numNestedAssets; i++) {
|
||||||
|
transferFrom(
|
||||||
|
nestedAssetData[i],
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount.safeMul(nestedAmounts[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -18,29 +18,49 @@
|
|||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
|
import "../interfaces/IExchangeV2.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibConstants {
|
contract LibConstants {
|
||||||
|
|
||||||
using LibBytes for bytes;
|
uint256 constant internal MAX_UINT = uint256(-1);
|
||||||
|
|
||||||
uint256 constant internal MAX_UINT = 2**256 - 1;
|
// The v2 order id is the first 4 bytes of the ExchangeV2 order schema hash.
|
||||||
|
// bytes4(keccak256(abi.encodePacked(
|
||||||
|
// "Order(",
|
||||||
|
// "address makerAddress,",
|
||||||
|
// "address takerAddress,",
|
||||||
|
// "address feeRecipientAddress,",
|
||||||
|
// "address senderAddress,",
|
||||||
|
// "uint256 makerAssetAmount,",
|
||||||
|
// "uint256 takerAssetAmount,",
|
||||||
|
// "uint256 makerFee,",
|
||||||
|
// "uint256 takerFee,",
|
||||||
|
// "uint256 expirationTimeSeconds,",
|
||||||
|
// "uint256 salt,",
|
||||||
|
// "bytes makerAssetData,",
|
||||||
|
// "bytes takerAssetData",
|
||||||
|
// ")"
|
||||||
|
// )));
|
||||||
|
bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8;
|
||||||
|
|
||||||
// solhint-disable var-name-mixedcase
|
// solhint-disable var-name-mixedcase
|
||||||
IExchange internal EXCHANGE;
|
IExchange internal EXCHANGE;
|
||||||
|
IExchangeV2 internal EXCHANGE_V2;
|
||||||
IEtherToken internal ETHER_TOKEN;
|
IEtherToken internal ETHER_TOKEN;
|
||||||
// solhint-enable var-name-mixedcase
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
address _exchange,
|
address _exchange,
|
||||||
|
address _exchangeV2,
|
||||||
address _weth
|
address _weth
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
EXCHANGE = IExchange(_exchange);
|
EXCHANGE = IExchange(_exchange);
|
||||||
|
EXCHANGE_V2 = IExchangeV2(_exchangeV2);
|
||||||
ETHER_TOKEN = IEtherToken(_weth);
|
ETHER_TOKEN = IEtherToken(_weth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,16 +21,22 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "../src/MixinExchangeWrapper.sol";
|
import "../src/MixinExchangeWrapper.sol";
|
||||||
import "../src/libs/LibConstants.sol";
|
import "../src/libs/LibConstants.sol";
|
||||||
|
import "../src/libs/LibAssetDataTransfer.sol";
|
||||||
|
import "../src/MixinReceiver.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestForwarder is
|
contract TestForwarder is
|
||||||
LibConstants,
|
LibConstants,
|
||||||
MixinExchangeWrapper
|
MixinExchangeWrapper,
|
||||||
|
MixinReceiver
|
||||||
{
|
{
|
||||||
|
using LibAssetDataTransfer for bytes;
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
constructor ()
|
constructor ()
|
||||||
public
|
public
|
||||||
LibConstants(
|
LibConstants(
|
||||||
|
address(0),
|
||||||
address(0),
|
address(0),
|
||||||
address(0)
|
address(0)
|
||||||
)
|
)
|
||||||
@@ -49,15 +55,12 @@ contract TestForwarder is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function transferAssetToSender(
|
function transferOut(
|
||||||
bytes memory assetData,
|
bytes memory assetData,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
_transferAssetToSender(
|
assetData.transferOut(amount);
|
||||||
assetData,
|
|
||||||
amount
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-exchange-forwarder",
|
"name": "@0x/contracts-exchange-forwarder",
|
||||||
"version": "4.0.2",
|
"version": "4.0.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "Forwarder",
|
"publicInterfaceContracts": "Forwarder,IExchangeV2",
|
||||||
"abis": "./test/generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth|TestForwarder).json",
|
"abis": "./test/generated-artifacts/@(Forwarder|IAssets|IExchangeV2|IForwarder|IForwarderCore|LibAssetDataTransfer|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinReceiver|MixinWeth|TestForwarder).json",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -52,24 +52,25 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
"@0x/contracts-asset-proxy": "^3.1.2",
|
||||||
"@0x/contracts-dev-utils": "^1.0.2",
|
"@0x/contracts-dev-utils": "^1.0.5",
|
||||||
"@0x/contracts-erc20": "^3.0.2",
|
"@0x/contracts-erc1155": "^2.0.5",
|
||||||
"@0x/contracts-erc721": "^3.0.2",
|
"@0x/contracts-erc20": "^3.0.5",
|
||||||
"@0x/contracts-exchange": "^3.0.2",
|
"@0x/contracts-erc721": "^3.0.5",
|
||||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
"@0x/contracts-exchange": "^3.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-exchange-libs": "^4.1.1",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/order-utils": "^10.0.1",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/order-utils": "^10.1.2",
|
||||||
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -89,7 +90,7 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2",
|
"@0x/base-contract": "^6.1.1",
|
||||||
"@0x/typescript-typings": "^5.0.1",
|
"@0x/typescript-typings": "^5.0.1",
|
||||||
"ethereum-types": "^3.0.0"
|
"ethereum-types": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
@@ -6,4 +6,5 @@
|
|||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
import * as Forwarder from '../generated-artifacts/Forwarder.json';
|
import * as Forwarder from '../generated-artifacts/Forwarder.json';
|
||||||
export const artifacts = { Forwarder: Forwarder as ContractArtifact };
|
import * as IExchangeV2 from '../generated-artifacts/IExchangeV2.json';
|
||||||
|
export const artifacts = { Forwarder: Forwarder as ContractArtifact, IExchangeV2: IExchangeV2 as ContractArtifact };
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
export { artifacts } from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export { ForwarderContract } from './wrappers';
|
export { ForwarderContract, IExchangeV2Contract } from './wrappers';
|
||||||
export { ExchangeForwarderRevertErrors } from '@0x/utils';
|
export { ExchangeForwarderRevertErrors } from '@0x/utils';
|
||||||
export {
|
export {
|
||||||
ContractArtifact,
|
ContractArtifact,
|
||||||
|
@@ -4,3 +4,4 @@
|
|||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
export * from '../generated-wrappers/forwarder';
|
export * from '../generated-wrappers/forwarder';
|
||||||
|
export * from '../generated-wrappers/i_exchange_v2';
|
||||||
|
@@ -7,13 +7,16 @@ import { ContractArtifact } from 'ethereum-types';
|
|||||||
|
|
||||||
import * as Forwarder from '../test/generated-artifacts/Forwarder.json';
|
import * as Forwarder from '../test/generated-artifacts/Forwarder.json';
|
||||||
import * as IAssets from '../test/generated-artifacts/IAssets.json';
|
import * as IAssets from '../test/generated-artifacts/IAssets.json';
|
||||||
|
import * as IExchangeV2 from '../test/generated-artifacts/IExchangeV2.json';
|
||||||
import * as IForwarder from '../test/generated-artifacts/IForwarder.json';
|
import * as IForwarder from '../test/generated-artifacts/IForwarder.json';
|
||||||
import * as IForwarderCore from '../test/generated-artifacts/IForwarderCore.json';
|
import * as IForwarderCore from '../test/generated-artifacts/IForwarderCore.json';
|
||||||
|
import * as LibAssetDataTransfer from '../test/generated-artifacts/LibAssetDataTransfer.json';
|
||||||
import * as LibConstants from '../test/generated-artifacts/LibConstants.json';
|
import * as LibConstants from '../test/generated-artifacts/LibConstants.json';
|
||||||
import * as LibForwarderRichErrors from '../test/generated-artifacts/LibForwarderRichErrors.json';
|
import * as LibForwarderRichErrors from '../test/generated-artifacts/LibForwarderRichErrors.json';
|
||||||
import * as MixinAssets from '../test/generated-artifacts/MixinAssets.json';
|
import * as MixinAssets from '../test/generated-artifacts/MixinAssets.json';
|
||||||
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
|
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
|
||||||
import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json';
|
import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json';
|
||||||
|
import * as MixinReceiver from '../test/generated-artifacts/MixinReceiver.json';
|
||||||
import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json';
|
import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json';
|
||||||
import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json';
|
import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
@@ -21,10 +24,13 @@ export const artifacts = {
|
|||||||
MixinAssets: MixinAssets as ContractArtifact,
|
MixinAssets: MixinAssets as ContractArtifact,
|
||||||
MixinExchangeWrapper: MixinExchangeWrapper as ContractArtifact,
|
MixinExchangeWrapper: MixinExchangeWrapper as ContractArtifact,
|
||||||
MixinForwarderCore: MixinForwarderCore as ContractArtifact,
|
MixinForwarderCore: MixinForwarderCore as ContractArtifact,
|
||||||
|
MixinReceiver: MixinReceiver as ContractArtifact,
|
||||||
MixinWeth: MixinWeth as ContractArtifact,
|
MixinWeth: MixinWeth as ContractArtifact,
|
||||||
IAssets: IAssets as ContractArtifact,
|
IAssets: IAssets as ContractArtifact,
|
||||||
|
IExchangeV2: IExchangeV2 as ContractArtifact,
|
||||||
IForwarder: IForwarder as ContractArtifact,
|
IForwarder: IForwarder as ContractArtifact,
|
||||||
IForwarderCore: IForwarderCore as ContractArtifact,
|
IForwarderCore: IForwarderCore as ContractArtifact,
|
||||||
|
LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact,
|
||||||
LibConstants: LibConstants as ContractArtifact,
|
LibConstants: LibConstants as ContractArtifact,
|
||||||
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
|
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
|
||||||
TestForwarder: TestForwarder as ContractArtifact,
|
TestForwarder: TestForwarder as ContractArtifact,
|
||||||
|
@@ -1,4 +1,11 @@
|
|||||||
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
|
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
|
||||||
|
import {
|
||||||
|
artifacts as ERC1155Artifacts,
|
||||||
|
ERC1155Events,
|
||||||
|
ERC1155MintableContract,
|
||||||
|
ERC1155TransferBatchEventArgs,
|
||||||
|
Erc1155Wrapper,
|
||||||
|
} from '@0x/contracts-erc1155';
|
||||||
import {
|
import {
|
||||||
artifacts as ERC20Artifacts,
|
artifacts as ERC20Artifacts,
|
||||||
DummyERC20TokenContract,
|
DummyERC20TokenContract,
|
||||||
@@ -20,11 +27,13 @@ import {
|
|||||||
verifyEventsFromLogs,
|
verifyEventsFromLogs,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, ExchangeForwarderRevertErrors, hexUtils } from '@0x/utils';
|
import { BigNumber, ExchangeForwarderRevertErrors, hexUtils } from '@0x/utils';
|
||||||
|
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
import { artifacts } from './artifacts';
|
||||||
import { TestForwarderContract } from './wrappers';
|
import { TestForwarderContract } from './wrappers';
|
||||||
|
|
||||||
blockchainTests('Supported asset type unit tests', env => {
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
|
blockchainTests.resets('Supported asset type unit tests', env => {
|
||||||
let forwarder: TestForwarderContract;
|
let forwarder: TestForwarderContract;
|
||||||
let assetDataEncoder: IAssetDataContract;
|
let assetDataEncoder: IAssetDataContract;
|
||||||
let bridgeAddress: string;
|
let bridgeAddress: string;
|
||||||
@@ -33,11 +42,15 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
|
|
||||||
let erc20Token: DummyERC20TokenContract;
|
let erc20Token: DummyERC20TokenContract;
|
||||||
let erc721Token: DummyERC721TokenContract;
|
let erc721Token: DummyERC721TokenContract;
|
||||||
|
let erc1155Token: ERC1155MintableContract;
|
||||||
|
let erc1155Wrapper: Erc1155Wrapper;
|
||||||
let nftId: BigNumber;
|
let nftId: BigNumber;
|
||||||
|
|
||||||
let erc20AssetData: string;
|
let erc20AssetData: string;
|
||||||
let erc721AssetData: string;
|
let erc721AssetData: string;
|
||||||
let erc20BridgeAssetData: string;
|
let erc20BridgeAssetData: string;
|
||||||
|
let staticCallAssetData: string;
|
||||||
|
let multiAssetData: string;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[receiver] = await env.getAccountAddressesAsync();
|
[receiver] = await env.getAccountAddressesAsync();
|
||||||
@@ -47,7 +60,7 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
artifacts.TestForwarder,
|
artifacts.TestForwarder,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
{ ...artifacts, ...ERC20Artifacts, ...ERC721Artifacts },
|
{ ...artifacts, ...ERC20Artifacts, ...ERC721Artifacts, ...ERC1155Artifacts },
|
||||||
);
|
);
|
||||||
|
|
||||||
erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
@@ -70,14 +83,30 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
constants.DUMMY_TOKEN_NAME,
|
constants.DUMMY_TOKEN_NAME,
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
);
|
);
|
||||||
nftId = getRandomInteger(constants.ZERO_AMOUNT, constants.MAX_UINT256);
|
nftId = getRandomInteger(0, constants.MAX_UINT256);
|
||||||
erc721AssetData = assetDataEncoder.ERC721Token(erc721Token.address, nftId).getABIEncodedTransactionData();
|
erc721AssetData = assetDataEncoder.ERC721Token(erc721Token.address, nftId).getABIEncodedTransactionData();
|
||||||
|
|
||||||
|
erc1155Token = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||||
|
ERC1155Artifacts.ERC1155Mintable,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
ERC1155Artifacts,
|
||||||
|
);
|
||||||
|
erc1155Wrapper = new Erc1155Wrapper(erc1155Token, receiver);
|
||||||
|
|
||||||
bridgeAddress = randomAddress();
|
bridgeAddress = randomAddress();
|
||||||
bridgeData = hexUtils.random();
|
bridgeData = hexUtils.random();
|
||||||
erc20BridgeAssetData = assetDataEncoder
|
erc20BridgeAssetData = assetDataEncoder
|
||||||
.ERC20Bridge(erc20Token.address, bridgeAddress, bridgeData)
|
.ERC20Bridge(erc20Token.address, bridgeAddress, bridgeData)
|
||||||
.getABIEncodedTransactionData();
|
.getABIEncodedTransactionData();
|
||||||
|
|
||||||
|
staticCallAssetData = assetDataEncoder
|
||||||
|
.StaticCall(randomAddress(), hexUtils.random(), constants.KECCAK256_NULL)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
|
||||||
|
multiAssetData = assetDataEncoder
|
||||||
|
.MultiAsset([new BigNumber(1)], [erc20AssetData])
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('_areUnderlyingAssetsEqual', () => {
|
describe('_areUnderlyingAssetsEqual', () => {
|
||||||
@@ -115,13 +144,64 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
.callAsync();
|
.callAsync();
|
||||||
expect(result).to.be.false();
|
expect(result).to.be.false();
|
||||||
});
|
});
|
||||||
it('returns false if assetData1 == assetData2 are ERC721', async () => {
|
it('returns true if assetData1 == assetData2 are ERC721', async () => {
|
||||||
const result = await forwarder.areUnderlyingAssetsEqual(erc721AssetData, erc721AssetData).callAsync();
|
const result = await forwarder.areUnderlyingAssetsEqual(erc721AssetData, erc721AssetData).callAsync();
|
||||||
|
expect(result).to.be.true();
|
||||||
|
});
|
||||||
|
it('returns false if assetData1 != assetData2 are ERC721', async () => {
|
||||||
|
const differentErc721AssetData = assetDataEncoder
|
||||||
|
.ERC721Token(randomAddress(), getRandomInteger(0, constants.MAX_UINT256))
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const result = await forwarder
|
||||||
|
.areUnderlyingAssetsEqual(erc721AssetData, differentErc721AssetData)
|
||||||
|
.callAsync();
|
||||||
|
expect(result).to.be.false();
|
||||||
|
});
|
||||||
|
it('returns true if assetData1 == assetData2 are StaticCall', async () => {
|
||||||
|
const result = await forwarder
|
||||||
|
.areUnderlyingAssetsEqual(staticCallAssetData, staticCallAssetData)
|
||||||
|
.callAsync();
|
||||||
|
expect(result).to.be.true();
|
||||||
|
});
|
||||||
|
it('returns false if assetData1 != assetData2 are StaticCall', async () => {
|
||||||
|
const differentStaticCallAssetData = assetDataEncoder
|
||||||
|
.StaticCall(randomAddress(), hexUtils.random(), constants.KECCAK256_NULL)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const result = await forwarder
|
||||||
|
.areUnderlyingAssetsEqual(staticCallAssetData, differentStaticCallAssetData)
|
||||||
|
.callAsync();
|
||||||
|
expect(result).to.be.false();
|
||||||
|
});
|
||||||
|
it('returns false if assetData1 is ERC20 and assetData2 is MultiAsset', async () => {
|
||||||
|
const result = await forwarder.areUnderlyingAssetsEqual(erc20AssetData, multiAssetData).callAsync();
|
||||||
|
expect(result).to.be.false();
|
||||||
|
});
|
||||||
|
it('returns true if assetData1 == assetData2 are MultiAsset (single nested asset)', async () => {
|
||||||
|
const result = await forwarder.areUnderlyingAssetsEqual(multiAssetData, multiAssetData).callAsync();
|
||||||
|
expect(result).to.be.true();
|
||||||
|
});
|
||||||
|
it('returns true if assetData1 == assetData2 are MultiAsset (multiple nested assets)', async () => {
|
||||||
|
const assetData = assetDataEncoder
|
||||||
|
.MultiAsset(
|
||||||
|
[getRandomInteger(0, constants.MAX_UINT256), new BigNumber(1)],
|
||||||
|
[erc20AssetData, erc721AssetData],
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const result = await forwarder.areUnderlyingAssetsEqual(assetData, assetData).callAsync();
|
||||||
|
expect(result).to.be.true();
|
||||||
|
});
|
||||||
|
it('returns false if assetData1 != assetData2 are MultiAsset', async () => {
|
||||||
|
const differentMultiAssetData = assetDataEncoder
|
||||||
|
.MultiAsset([getRandomInteger(0, constants.MAX_UINT256)], [erc721AssetData])
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const result = await forwarder
|
||||||
|
.areUnderlyingAssetsEqual(multiAssetData, differentMultiAssetData)
|
||||||
|
.callAsync();
|
||||||
expect(result).to.be.false();
|
expect(result).to.be.false();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('_transferAssetToSender', () => {
|
describe('transferOut', () => {
|
||||||
const TRANSFER_AMOUNT = new BigNumber(1);
|
const TRANSFER_AMOUNT = new BigNumber(1);
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await erc20Token
|
await erc20Token
|
||||||
@@ -132,7 +212,7 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
|
|
||||||
it('transfers an ERC20 token given ERC20 assetData', async () => {
|
it('transfers an ERC20 token given ERC20 assetData', async () => {
|
||||||
const txReceipt = await forwarder
|
const txReceipt = await forwarder
|
||||||
.transferAssetToSender(erc20AssetData, TRANSFER_AMOUNT)
|
.transferOut(erc20AssetData, TRANSFER_AMOUNT)
|
||||||
.awaitTransactionSuccessAsync({ from: receiver });
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
|
verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
|
||||||
txReceipt.logs,
|
txReceipt.logs,
|
||||||
@@ -142,7 +222,7 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
});
|
});
|
||||||
it('transfers an ERC721 token given ERC721 assetData and amount == 1', async () => {
|
it('transfers an ERC721 token given ERC721 assetData and amount == 1', async () => {
|
||||||
const txReceipt = await forwarder
|
const txReceipt = await forwarder
|
||||||
.transferAssetToSender(erc721AssetData, TRANSFER_AMOUNT)
|
.transferOut(erc721AssetData, TRANSFER_AMOUNT)
|
||||||
.awaitTransactionSuccessAsync({ from: receiver });
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
verifyEventsFromLogs<ERC721TokenTransferEventArgs>(
|
verifyEventsFromLogs<ERC721TokenTransferEventArgs>(
|
||||||
txReceipt.logs,
|
txReceipt.logs,
|
||||||
@@ -153,14 +233,120 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
it('reverts if attempting to transfer an ERC721 token with amount != 1', async () => {
|
it('reverts if attempting to transfer an ERC721 token with amount != 1', async () => {
|
||||||
const invalidAmount = new BigNumber(2);
|
const invalidAmount = new BigNumber(2);
|
||||||
const tx = forwarder
|
const tx = forwarder
|
||||||
.transferAssetToSender(erc721AssetData, invalidAmount)
|
.transferOut(erc721AssetData, invalidAmount)
|
||||||
.awaitTransactionSuccessAsync({ from: receiver });
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
const expectedError = new ExchangeForwarderRevertErrors.Erc721AmountMustEqualOneError(invalidAmount);
|
const expectedError = new ExchangeForwarderRevertErrors.Erc721AmountMustEqualOneError(invalidAmount);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
|
it('transfers a single ERC1155 token', async () => {
|
||||||
|
const values = [new BigNumber(1)];
|
||||||
|
const amount = new BigNumber(1);
|
||||||
|
const ids = [await erc1155Wrapper.mintFungibleTokensAsync([forwarder.address], values)];
|
||||||
|
const assetData = assetDataEncoder
|
||||||
|
.ERC1155Assets(erc1155Token.address, ids, values, constants.NULL_BYTES)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const txReceipt = await forwarder
|
||||||
|
.transferOut(assetData, amount)
|
||||||
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
|
verifyEventsFromLogs<ERC1155TransferBatchEventArgs>(
|
||||||
|
txReceipt.logs,
|
||||||
|
[{ operator: forwarder.address, from: forwarder.address, to: receiver, ids, values }],
|
||||||
|
ERC1155Events.TransferBatch,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('transfers multiple ids of an ERC1155 token', async () => {
|
||||||
|
const amount = new BigNumber(1);
|
||||||
|
const ids = [
|
||||||
|
await erc1155Wrapper.mintFungibleTokensAsync([forwarder.address], [amount]),
|
||||||
|
await erc1155Wrapper.mintFungibleTokensAsync([forwarder.address], [amount]),
|
||||||
|
];
|
||||||
|
const values = [amount, amount];
|
||||||
|
const assetData = assetDataEncoder
|
||||||
|
.ERC1155Assets(erc1155Token.address, ids, values, constants.NULL_BYTES)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const txReceipt = await forwarder.transferOut(assetData, amount).awaitTransactionSuccessAsync();
|
||||||
|
verifyEventsFromLogs<ERC1155TransferBatchEventArgs>(
|
||||||
|
txReceipt.logs,
|
||||||
|
[{ operator: forwarder.address, from: forwarder.address, to: receiver, ids, values }],
|
||||||
|
ERC1155Events.TransferBatch,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('scales up values when transfering ERC1155 tokens', async () => {
|
||||||
|
const amount = new BigNumber(2);
|
||||||
|
const values = [new BigNumber(1), new BigNumber(2)];
|
||||||
|
const scaledValues = values.map(value => value.times(amount));
|
||||||
|
const ids = [
|
||||||
|
await erc1155Wrapper.mintFungibleTokensAsync([forwarder.address], [scaledValues[0]]),
|
||||||
|
await erc1155Wrapper.mintFungibleTokensAsync([forwarder.address], [scaledValues[1]]),
|
||||||
|
];
|
||||||
|
const assetData = assetDataEncoder
|
||||||
|
.ERC1155Assets(erc1155Token.address, ids, values, constants.NULL_BYTES)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const txReceipt = await forwarder.transferOut(assetData, amount).awaitTransactionSuccessAsync();
|
||||||
|
verifyEventsFromLogs<ERC1155TransferBatchEventArgs>(
|
||||||
|
txReceipt.logs,
|
||||||
|
[{ operator: forwarder.address, from: forwarder.address, to: receiver, ids, values: scaledValues }],
|
||||||
|
ERC1155Events.TransferBatch,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('transfers a single ERC20 token wrapped as MultiAsset', async () => {
|
||||||
|
const nestedAmount = new BigNumber(1337);
|
||||||
|
const erc20MultiAssetData = assetDataEncoder
|
||||||
|
.MultiAsset([nestedAmount], [erc20AssetData])
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const multiAssetAmount = new BigNumber(2);
|
||||||
|
const txReceipt = await forwarder
|
||||||
|
.transferOut(erc20MultiAssetData, multiAssetAmount)
|
||||||
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
|
verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
|
||||||
|
txReceipt.logs,
|
||||||
|
[{ _from: forwarder.address, _to: receiver, _value: multiAssetAmount.times(nestedAmount) }],
|
||||||
|
ERC20TokenEvents.Transfer,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('transfers ERC20, ERC721, and StaticCall assets wrapped as MultiAsset', async () => {
|
||||||
|
const nestedAmounts = [new BigNumber(1337), TRANSFER_AMOUNT, TRANSFER_AMOUNT];
|
||||||
|
const assortedMultiAssetData = assetDataEncoder
|
||||||
|
.MultiAsset(nestedAmounts, [erc20AssetData, erc721AssetData, staticCallAssetData])
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const txReceipt = await forwarder
|
||||||
|
.transferOut(assortedMultiAssetData, TRANSFER_AMOUNT)
|
||||||
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
|
expect(txReceipt.logs.length).to.equal(2);
|
||||||
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
|
const erc20TransferEvent = (txReceipt.logs[0] as LogWithDecodedArgs<ERC20TokenTransferEventArgs>).args;
|
||||||
|
const erc721TransferEvent = (txReceipt.logs[1] as LogWithDecodedArgs<ERC721TokenTransferEventArgs>).args;
|
||||||
|
// tslint:enable:no-unnecessary-type-assertion
|
||||||
|
expect(erc20TransferEvent).to.deep.equal({
|
||||||
|
_from: forwarder.address,
|
||||||
|
_to: receiver,
|
||||||
|
_value: nestedAmounts[0],
|
||||||
|
});
|
||||||
|
expect(erc721TransferEvent).to.deep.equal({ _from: forwarder.address, _to: receiver, _tokenId: nftId });
|
||||||
|
});
|
||||||
|
it('performs nested MultiAsset transfers', async () => {
|
||||||
|
const nestedAmounts = [TRANSFER_AMOUNT, TRANSFER_AMOUNT, TRANSFER_AMOUNT];
|
||||||
|
const assortedMultiAssetData = assetDataEncoder
|
||||||
|
.MultiAsset(nestedAmounts, [multiAssetData, erc721AssetData, staticCallAssetData])
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
const txReceipt = await forwarder
|
||||||
|
.transferOut(assortedMultiAssetData, TRANSFER_AMOUNT)
|
||||||
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
|
expect(txReceipt.logs.length).to.equal(2);
|
||||||
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
|
const erc20TransferEvent = (txReceipt.logs[0] as LogWithDecodedArgs<ERC20TokenTransferEventArgs>).args;
|
||||||
|
const erc721TransferEvent = (txReceipt.logs[1] as LogWithDecodedArgs<ERC721TokenTransferEventArgs>).args;
|
||||||
|
// tslint:enable:no-unnecessary-type-assertion
|
||||||
|
expect(erc20TransferEvent).to.deep.equal({
|
||||||
|
_from: forwarder.address,
|
||||||
|
_to: receiver,
|
||||||
|
_value: TRANSFER_AMOUNT,
|
||||||
|
});
|
||||||
|
expect(erc721TransferEvent).to.deep.equal({ _from: forwarder.address, _to: receiver, _tokenId: nftId });
|
||||||
|
});
|
||||||
it('transfers an ERC20 token given ERC20Bridge assetData', async () => {
|
it('transfers an ERC20 token given ERC20Bridge assetData', async () => {
|
||||||
const txReceipt = await forwarder
|
const txReceipt = await forwarder
|
||||||
.transferAssetToSender(erc20BridgeAssetData, TRANSFER_AMOUNT)
|
.transferOut(erc20BridgeAssetData, TRANSFER_AMOUNT)
|
||||||
.awaitTransactionSuccessAsync({ from: receiver });
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
|
verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
|
||||||
txReceipt.logs,
|
txReceipt.logs,
|
||||||
@@ -168,10 +354,16 @@ blockchainTests('Supported asset type unit tests', env => {
|
|||||||
ERC20TokenEvents.Transfer,
|
ERC20TokenEvents.Transfer,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('noops (emits no events) for StaticCall assetData', async () => {
|
||||||
|
const txReceipt = await forwarder
|
||||||
|
.transferOut(staticCallAssetData, TRANSFER_AMOUNT)
|
||||||
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
|
expect(txReceipt.logs.length).to.equal(0);
|
||||||
|
});
|
||||||
it('reverts if assetData is unsupported', async () => {
|
it('reverts if assetData is unsupported', async () => {
|
||||||
const randomBytes = hexUtils.random();
|
const randomBytes = hexUtils.random();
|
||||||
const tx = forwarder
|
const tx = forwarder
|
||||||
.transferAssetToSender(randomBytes, TRANSFER_AMOUNT)
|
.transferOut(randomBytes, TRANSFER_AMOUNT)
|
||||||
.awaitTransactionSuccessAsync({ from: receiver });
|
.awaitTransactionSuccessAsync({ from: receiver });
|
||||||
const expectedError = new ExchangeForwarderRevertErrors.UnsupportedAssetProxyError(
|
const expectedError = new ExchangeForwarderRevertErrors.UnsupportedAssetProxyError(
|
||||||
hexUtils.slice(randomBytes, 0, 4),
|
hexUtils.slice(randomBytes, 0, 4),
|
||||||
|
@@ -5,12 +5,15 @@
|
|||||||
*/
|
*/
|
||||||
export * from '../test/generated-wrappers/forwarder';
|
export * from '../test/generated-wrappers/forwarder';
|
||||||
export * from '../test/generated-wrappers/i_assets';
|
export * from '../test/generated-wrappers/i_assets';
|
||||||
|
export * from '../test/generated-wrappers/i_exchange_v2';
|
||||||
export * from '../test/generated-wrappers/i_forwarder';
|
export * from '../test/generated-wrappers/i_forwarder';
|
||||||
export * from '../test/generated-wrappers/i_forwarder_core';
|
export * from '../test/generated-wrappers/i_forwarder_core';
|
||||||
|
export * from '../test/generated-wrappers/lib_asset_data_transfer';
|
||||||
export * from '../test/generated-wrappers/lib_constants';
|
export * from '../test/generated-wrappers/lib_constants';
|
||||||
export * from '../test/generated-wrappers/lib_forwarder_rich_errors';
|
export * from '../test/generated-wrappers/lib_forwarder_rich_errors';
|
||||||
export * from '../test/generated-wrappers/mixin_assets';
|
export * from '../test/generated-wrappers/mixin_assets';
|
||||||
export * from '../test/generated-wrappers/mixin_exchange_wrapper';
|
export * from '../test/generated-wrappers/mixin_exchange_wrapper';
|
||||||
export * from '../test/generated-wrappers/mixin_forwarder_core';
|
export * from '../test/generated-wrappers/mixin_forwarder_core';
|
||||||
|
export * from '../test/generated-wrappers/mixin_receiver';
|
||||||
export * from '../test/generated-wrappers/mixin_weth';
|
export * from '../test/generated-wrappers/mixin_weth';
|
||||||
export * from '../test/generated-wrappers/test_forwarder';
|
export * from '../test/generated-wrappers/test_forwarder';
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -4,15 +4,19 @@
|
|||||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/Forwarder.json",
|
"generated-artifacts/Forwarder.json",
|
||||||
|
"generated-artifacts/IExchangeV2.json",
|
||||||
"test/generated-artifacts/Forwarder.json",
|
"test/generated-artifacts/Forwarder.json",
|
||||||
"test/generated-artifacts/IAssets.json",
|
"test/generated-artifacts/IAssets.json",
|
||||||
|
"test/generated-artifacts/IExchangeV2.json",
|
||||||
"test/generated-artifacts/IForwarder.json",
|
"test/generated-artifacts/IForwarder.json",
|
||||||
"test/generated-artifacts/IForwarderCore.json",
|
"test/generated-artifacts/IForwarderCore.json",
|
||||||
|
"test/generated-artifacts/LibAssetDataTransfer.json",
|
||||||
"test/generated-artifacts/LibConstants.json",
|
"test/generated-artifacts/LibConstants.json",
|
||||||
"test/generated-artifacts/LibForwarderRichErrors.json",
|
"test/generated-artifacts/LibForwarderRichErrors.json",
|
||||||
"test/generated-artifacts/MixinAssets.json",
|
"test/generated-artifacts/MixinAssets.json",
|
||||||
"test/generated-artifacts/MixinExchangeWrapper.json",
|
"test/generated-artifacts/MixinExchangeWrapper.json",
|
||||||
"test/generated-artifacts/MixinForwarderCore.json",
|
"test/generated-artifacts/MixinForwarderCore.json",
|
||||||
|
"test/generated-artifacts/MixinReceiver.json",
|
||||||
"test/generated-artifacts/MixinWeth.json",
|
"test/generated-artifacts/MixinWeth.json",
|
||||||
"test/generated-artifacts/TestForwarder.json"
|
"test/generated-artifacts/TestForwarder.json"
|
||||||
],
|
],
|
||||||
|
@@ -1,4 +1,32 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "4.1.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "4.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Reference functions for `matchOrders` and `matchOrdersWithMaximalFill`.",
|
||||||
|
"pr": 2437
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1579682890
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1578272714,
|
||||||
|
"version": "4.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v4.1.1 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v4.1.0 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Reference functions for `matchOrders` and `matchOrdersWithMaximalFill`. (#2437)
|
||||||
|
|
||||||
|
## v4.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v4.0.2 - _December 17, 2019_
|
## v4.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -29,9 +29,11 @@ contract LibEIP712ExchangeDomain {
|
|||||||
// EIP712 Exchange Domain Version value
|
// EIP712 Exchange Domain Version value
|
||||||
string constant internal _EIP712_EXCHANGE_DOMAIN_VERSION = "3.0.0";
|
string constant internal _EIP712_EXCHANGE_DOMAIN_VERSION = "3.0.0";
|
||||||
|
|
||||||
// Hash of the EIP712 Domain Separator data
|
// solhint-disable var-name-mixedcase
|
||||||
// solhint-disable-next-line var-name-mixedcase
|
/// @dev Hash of the EIP712 Domain Separator data
|
||||||
|
/// @return 0 Domain hash.
|
||||||
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
|
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
|
||||||
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
/// @param chainId Chain ID of the network this contract is deployed on.
|
/// @param chainId Chain ID of the network this contract is deployed on.
|
||||||
/// @param verifyingContractAddressIfExists Address of the verifying contract (null if the address of this contract)
|
/// @param verifyingContractAddressIfExists Address of the verifying contract (null if the address of this contract)
|
||||||
|
@@ -60,6 +60,7 @@ library LibOrder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// solhint-disable max-line-length
|
// solhint-disable max-line-length
|
||||||
|
/// @dev Canonical order structure.
|
||||||
struct Order {
|
struct Order {
|
||||||
address makerAddress; // Address that created the order.
|
address makerAddress; // Address that created the order.
|
||||||
address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.
|
address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.
|
||||||
@@ -78,8 +79,9 @@ library LibOrder {
|
|||||||
}
|
}
|
||||||
// solhint-enable max-line-length
|
// solhint-enable max-line-length
|
||||||
|
|
||||||
|
/// @dev Order information returned by `getOrderInfo()`.
|
||||||
struct OrderInfo {
|
struct OrderInfo {
|
||||||
uint8 orderStatus; // Status that describes order's validity and fillability.
|
OrderStatus orderStatus; // Status that describes order's validity and fillability.
|
||||||
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
|
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
|
||||||
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
|
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-exchange-libs",
|
"name": "@0x/contracts-exchange-libs",
|
||||||
"version": "4.0.2",
|
"version": "4.1.1",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -52,15 +52,14 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.0.2",
|
"@0x/abi-gen": "^5.1.1",
|
||||||
"@0x/contracts-gen": "^2.0.2",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-test-utils": "^5.0.1",
|
"@0x/dev-utils": "^3.1.2",
|
||||||
"@0x/dev-utils": "^3.0.2",
|
"@0x/sol-compiler": "^4.0.5",
|
||||||
"@0x/sol-compiler": "^4.0.2",
|
"@0x/subproviders": "^6.0.5",
|
||||||
"@0x/subproviders": "^6.0.2",
|
|
||||||
"@0x/ts-doc-gen": "^0.0.22",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/tslint-config": "^4.0.0",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/web3-wrapper": "^7.0.2",
|
"@0x/web3-wrapper": "^7.0.4",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -81,12 +80,13 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.0.2",
|
"@0x/base-contract": "^6.1.1",
|
||||||
"@0x/contracts-utils": "^4.0.2",
|
"@0x/contracts-test-utils": "^5.1.2",
|
||||||
"@0x/order-utils": "^10.0.1",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
|
"@0x/order-utils": "^10.1.2",
|
||||||
"@0x/types": "^3.1.1",
|
"@0x/types": "^3.1.1",
|
||||||
"@0x/typescript-typings": "^5.0.1",
|
"@0x/typescript-typings": "^5.0.1",
|
||||||
"@0x/utils": "^5.1.1",
|
"@0x/utils": "^5.2.0",
|
||||||
"ethereum-types": "^3.0.0"
|
"ethereum-types": "^3.0.0"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
|
import { orderHashUtils } from '@0x/contracts-test-utils';
|
||||||
import { ReferenceFunctions } from '@0x/contracts-utils';
|
import { ReferenceFunctions } from '@0x/contracts-utils';
|
||||||
import { FillResults, Order } from '@0x/types';
|
import { FillResults, MatchedFillResults, Order } from '@0x/types';
|
||||||
import { BigNumber, LibMathRevertErrors } from '@0x/utils';
|
import { BigNumber, ExchangeRevertErrors, LibMathRevertErrors } from '@0x/utils';
|
||||||
|
|
||||||
const { safeAdd, safeSub, safeMul, safeDiv } = ReferenceFunctions;
|
const { safeAdd, safeSub, safeMul, safeDiv } = ReferenceFunctions;
|
||||||
|
|
||||||
@@ -114,6 +115,119 @@ export function calculateFillResults(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates amounts filled and fees paid by maker and taker.
|
||||||
|
*/
|
||||||
|
export function calculateMatchResults(
|
||||||
|
leftOrder: Order,
|
||||||
|
rightOrder: Order,
|
||||||
|
protocolFeeMultiplier: BigNumber,
|
||||||
|
gasPrice: BigNumber,
|
||||||
|
withMaximalFill: boolean = false,
|
||||||
|
): MatchedFillResults {
|
||||||
|
// Initialize empty fill results.
|
||||||
|
const leftFillResults: FillResults = {
|
||||||
|
makerAssetFilledAmount: new BigNumber(0),
|
||||||
|
takerAssetFilledAmount: new BigNumber(0),
|
||||||
|
makerFeePaid: new BigNumber(0),
|
||||||
|
takerFeePaid: new BigNumber(0),
|
||||||
|
protocolFeePaid: new BigNumber(0),
|
||||||
|
};
|
||||||
|
const rightFillResults: FillResults = {
|
||||||
|
makerAssetFilledAmount: new BigNumber(0),
|
||||||
|
takerAssetFilledAmount: new BigNumber(0),
|
||||||
|
makerFeePaid: new BigNumber(0),
|
||||||
|
takerFeePaid: new BigNumber(0),
|
||||||
|
protocolFeePaid: new BigNumber(0),
|
||||||
|
};
|
||||||
|
let profitInLeftMakerAsset = new BigNumber(0);
|
||||||
|
let profitInRightMakerAsset = new BigNumber(0);
|
||||||
|
|
||||||
|
// Assert matchable
|
||||||
|
if (
|
||||||
|
leftOrder.makerAssetAmount
|
||||||
|
.times(rightOrder.makerAssetAmount)
|
||||||
|
.lt(leftOrder.takerAssetAmount.times(rightOrder.takerAssetAmount))
|
||||||
|
) {
|
||||||
|
throw new ExchangeRevertErrors.NegativeSpreadError(
|
||||||
|
orderHashUtils.getOrderHashHex(leftOrder),
|
||||||
|
orderHashUtils.getOrderHashHex(rightOrder),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset Transfer Amounts
|
||||||
|
if (leftOrder.takerAssetAmount.gt(rightOrder.makerAssetAmount)) {
|
||||||
|
leftFillResults.makerAssetFilledAmount = safeGetPartialAmountFloor(
|
||||||
|
leftOrder.makerAssetAmount,
|
||||||
|
leftOrder.takerAssetAmount,
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
);
|
||||||
|
leftFillResults.takerAssetFilledAmount = rightOrder.makerAssetAmount;
|
||||||
|
rightFillResults.makerAssetFilledAmount = rightOrder.makerAssetAmount;
|
||||||
|
rightFillResults.takerAssetFilledAmount = rightOrder.takerAssetAmount;
|
||||||
|
} else if (withMaximalFill && leftOrder.makerAssetAmount.lt(rightOrder.takerAssetAmount)) {
|
||||||
|
leftFillResults.makerAssetFilledAmount = leftOrder.makerAssetAmount;
|
||||||
|
leftFillResults.takerAssetFilledAmount = leftOrder.takerAssetAmount;
|
||||||
|
rightFillResults.makerAssetFilledAmount = safeGetPartialAmountFloor(
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
rightOrder.takerAssetAmount,
|
||||||
|
leftOrder.makerAssetAmount,
|
||||||
|
);
|
||||||
|
rightFillResults.takerAssetFilledAmount = leftOrder.makerAssetAmount;
|
||||||
|
} else if (!withMaximalFill && leftOrder.takerAssetAmount.lt(rightOrder.makerAssetAmount)) {
|
||||||
|
leftFillResults.makerAssetFilledAmount = leftOrder.makerAssetAmount;
|
||||||
|
leftFillResults.takerAssetFilledAmount = leftOrder.takerAssetAmount;
|
||||||
|
rightFillResults.makerAssetFilledAmount = leftOrder.takerAssetAmount;
|
||||||
|
rightFillResults.takerAssetFilledAmount = safeGetPartialAmountCeil(
|
||||||
|
rightOrder.takerAssetAmount,
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
leftOrder.takerAssetAmount,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
leftFillResults.makerAssetFilledAmount = leftOrder.makerAssetAmount;
|
||||||
|
leftFillResults.takerAssetFilledAmount = leftOrder.takerAssetAmount;
|
||||||
|
rightFillResults.makerAssetFilledAmount = rightOrder.makerAssetAmount;
|
||||||
|
rightFillResults.takerAssetFilledAmount = rightOrder.takerAssetAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profit
|
||||||
|
profitInLeftMakerAsset = leftFillResults.makerAssetFilledAmount.minus(rightFillResults.makerAssetFilledAmount);
|
||||||
|
profitInRightMakerAsset = rightFillResults.makerAssetFilledAmount.minus(leftFillResults.makerAssetFilledAmount);
|
||||||
|
|
||||||
|
// Fees
|
||||||
|
leftFillResults.makerFeePaid = safeGetPartialAmountFloor(
|
||||||
|
leftFillResults.makerAssetFilledAmount,
|
||||||
|
leftOrder.makerAssetAmount,
|
||||||
|
leftOrder.makerFee,
|
||||||
|
);
|
||||||
|
leftFillResults.takerFeePaid = safeGetPartialAmountFloor(
|
||||||
|
leftFillResults.takerAssetFilledAmount,
|
||||||
|
leftOrder.takerAssetAmount,
|
||||||
|
leftOrder.takerFee,
|
||||||
|
);
|
||||||
|
rightFillResults.makerFeePaid = safeGetPartialAmountFloor(
|
||||||
|
rightFillResults.makerAssetFilledAmount,
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
rightOrder.makerFee,
|
||||||
|
);
|
||||||
|
rightFillResults.takerFeePaid = safeGetPartialAmountFloor(
|
||||||
|
rightFillResults.takerAssetFilledAmount,
|
||||||
|
rightOrder.takerAssetAmount,
|
||||||
|
rightOrder.takerFee,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Protocol Fee
|
||||||
|
leftFillResults.protocolFeePaid = safeMul(protocolFeeMultiplier, gasPrice);
|
||||||
|
rightFillResults.protocolFeePaid = safeMul(protocolFeeMultiplier, gasPrice);
|
||||||
|
|
||||||
|
return {
|
||||||
|
left: leftFillResults,
|
||||||
|
right: rightFillResults,
|
||||||
|
profitInLeftMakerAsset,
|
||||||
|
profitInRightMakerAsset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const LibFractions = {
|
export const LibFractions = {
|
||||||
add: (n1: BigNumber, d1: BigNumber, n2: BigNumber, d2: BigNumber): [BigNumber, BigNumber] => {
|
add: (n1: BigNumber, d1: BigNumber, n2: BigNumber, d2: BigNumber): [BigNumber, BigNumber] => {
|
||||||
if (n1.isZero()) {
|
if (n1.isZero()) {
|
||||||
|
@@ -84,7 +84,7 @@ module.exports = {
|
|||||||
solc: {
|
solc: {
|
||||||
version: '0.5.9',
|
version: '0.5.9',
|
||||||
settings: {
|
settings: {
|
||||||
evmVersion: 'constantinople',
|
evmVersion: 'istanbul',
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 1000000,
|
runs: 1000000,
|
||||||
|
@@ -1,4 +1,32 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "3.1.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "3.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Uses updated event decoding to properly decodes arrays and objects.",
|
||||||
|
"pr": 2443
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1579682890
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1578272714,
|
||||||
|
"version": "3.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1576540892,
|
"timestamp": 1576540892,
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.1.1 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.1.0 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Uses updated event decoding to properly decodes arrays and objects. (#2443)
|
||||||
|
|
||||||
|
## v3.0.3 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v3.0.2 - _December 17, 2019_
|
## v3.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
|
@@ -29,6 +29,7 @@ import "./MixinTransferSimulator.sol";
|
|||||||
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinSignatureValidator,
|
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinSignatureValidator,
|
||||||
// and MixinTransactions are all inherited via the other Mixins that are
|
// and MixinTransactions are all inherited via the other Mixins that are
|
||||||
// used.
|
// used.
|
||||||
|
/// @dev The 0x Exchange contract.
|
||||||
contract Exchange is
|
contract Exchange is
|
||||||
LibEIP712ExchangeDomain,
|
LibEIP712ExchangeDomain,
|
||||||
MixinMatchOrders,
|
MixinMatchOrders,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user