Merge branch '3.0' into feat/3.0/testnet-migrations

This commit is contained in:
Amir Bandeali 2019-10-03 23:04:07 -07:00
commit ac1063dd68
295 changed files with 68140 additions and 46805 deletions

View File

@ -72,18 +72,6 @@ jobs:
# - run: yarn wsrun test:circleci @0x/contracts-extensions
# TODO(abandeali): Re-enable after this package is complete.
# - run: yarn wsrun test:circleci @0x/contracts-coordinator
test-contracts-geth:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/devnet
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
# HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
# initialized
- run: sleep 10 && TEST_PROVIDER=geth 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-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils @0x/contracts-staking
test-publish:
resource_class: medium+
docker:
@ -94,7 +82,9 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn test:publish:circleci
- run:
command: yarn test:publish:circleci
no_output_timeout: 1800
test-doc-generation:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
@ -103,7 +93,9 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn test:generate_docs:circleci
- run:
command: yarn test:generate_docs:circleci
no_output_timeout: 1200
test-rest:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
@ -405,22 +397,18 @@ workflows:
- test-contracts-rest-ganache-3.0:
requires:
- build
# Disabled until geth docker image is fixed.
# - test-contracts-geth:
# requires:
# - build
- test-rest:
requires:
- build
- static-tests:
requires:
- build
# - test-publish:
# requires:
# - build
# - test-doc-generation:
# requires:
# - build
- test-publish:
requires:
- build
- test-doc-generation:
requires:
- build
- submit-coverage:
requires:
- test-contracts-rest-ganache-3.0

View File

@ -32,6 +32,5 @@ contracts: ['contracts']
@0x/json-schemas: ['packages/json-schemas']
@0x/ethereum-types: ['ethereum-types']
@0x/connect: ['packages/connect']
@0x/fill-scenarios: ['packages/fill-scenarios']
@0x/testnet-faucets: ['packages/testnet-faucets']
@0x/monorepo-scripts: ['packages/monorepo-scripts']

View File

@ -93,7 +93,6 @@ These packages are all under development. See [/contracts/README.md](/contracts/
| [`@0x/assert`](/packages/assert) | [![npm](https://img.shields.io/npm/v/@0x/assert.svg)](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
| [`@0x/base-contract`](/packages/base-contract) | [![npm](https://img.shields.io/npm/v/@0x/base-contract.svg)](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
| [`@0x/dev-utils`](/packages/dev-utils) | [![npm](https://img.shields.io/npm/v/@0x/dev-utils.svg)](https://www.npmjs.com/package/@0x/dev-utils) | Dev utils to be shared across 0x packages |
| [`@0x/fill-scenarios`](/packages/fill-scenarios) | [![npm](https://img.shields.io/npm/v/@0x/fill-scenarios.svg)](https://www.npmjs.com/package/@0x/fill-scenarios) | 0x order fill scenario generator |
#### Private Packages

View File

@ -1,6 +1,6 @@
[
{
"version": "3.0.0",
"version": "2.3.0-beta.0",
"changes": [
{
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
@ -17,8 +17,17 @@
{
"note": "Remove unused dependency on IAuthorizable in IAssetProxy",
"pr": 1910
},
{
"note": "Add `ERC20BridgeProxy`",
"pr": 2220
},
{
"note": "Add `Eth2DaiBridge`",
"pr": 2221
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.3.0-beta.0 - _October 3, 2019_
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
* Remove `LibAssetProxyIds` contract (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
* Add `ERC20BridgeProxy` (#2220)
* Add `Eth2DaiBridge` (#2221)
## v2.2.8 - _September 17, 2019_
* Dependencies updated
@ -58,6 +67,9 @@ CHANGELOG
## v2.1.2 - _May 10, 2019_
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797)
* Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID (#1819)
* Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()` (#1819)
* Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`. (#1819)
## v2.1.1 - _April 11, 2019_

View File

@ -0,0 +1,126 @@
/*
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/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/Authorizable.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "./interfaces/IAssetProxy.sol";
import "./interfaces/IERC20Bridge.sol";
contract ERC20BridgeProxy is
IAssetProxy,
Authorizable
{
using LibBytes for bytes;
using LibSafeMath for uint256;
// @dev Id of this proxy. Also the result of a successful bridge call.
// bytes4(keccak256("ERC20Bridge(address,address,bytes)"))
bytes4 constant private PROXY_ID = 0xdc1600f3;
/// @dev Calls a bridge contract to transfer `amount` of ERC20 from `from`
/// to `to`. Asserts that the balance of `to` has increased by `amount`.
/// @param assetData Abi-encoded data for this asset proxy encoded as:
/// abi.encodeWithSelector(
/// bytes4 PROXY_ID,
/// address tokenAddress,
/// address bridgeAddress,
/// bytes bridgeData
/// )
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFrom(
bytes calldata assetData,
address from,
address to,
uint256 amount
)
external
onlyAuthorized
{
// Extract asset data fields.
(
address tokenAddress,
address bridgeAddress,
bytes memory bridgeData
) = abi.decode(
assetData.sliceDestructive(4, assetData.length),
(address, address, bytes)
);
// Remember the balance of `to` before calling the bridge.
uint256 balanceBefore = balanceOf(tokenAddress, to);
// Call the bridge, who should transfer `amount` of `tokenAddress` to
// `to`.
bytes4 success = IERC20Bridge(bridgeAddress).withdrawTo(
tokenAddress,
from,
to,
amount,
bridgeData
);
// Bridge must return the proxy ID to indicate success.
require(success == PROXY_ID, "BRIDGE_FAILED");
// Ensure that the balance of `to` has increased by at least `amount`.
require(
balanceBefore.safeAdd(amount) <= balanceOf(tokenAddress, to),
"BRIDGE_UNDERPAY"
);
}
/// @dev Gets the proxy id associated with this asset proxy.
/// @return proxyId The proxy id.
function getProxyId()
external
pure
returns (bytes4 proxyId)
{
return PROXY_ID;
}
/// @dev Retrieves the balance of `owner` for this asset.
/// @return balance The balance of the ERC20 token being transferred by this
/// asset proxy.
function balanceOf(bytes calldata assetData, address owner)
external
view
returns (uint256 balance)
{
(address tokenAddress) = abi.decode(
assetData.sliceDestructive(4, assetData.length),
(address)
);
return balanceOf(tokenAddress, owner);
}
/// @dev Retrieves the balance of `owner` given an ERC20 address.
/// @return balance The balance of the ERC20 token for `owner`.
function balanceOf(address tokenAddress, address owner)
private
view
returns (uint256 balance)
{
return IERC20Token(tokenAddress).balanceOf(owner);
}
}

View File

@ -0,0 +1,142 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IEth2Dai.sol";
import "../interfaces/IWallet.sol";
// solhint-disable space-after-comma
contract Eth2DaiBridge is
IERC20Bridge,
IWallet
{
/* Mainnet addresses */
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoeded "from" token address.
/// @return success The magic bytes if successful.
function withdrawTo(
address toTokenAddress,
address /* from */,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success)
{
// Decode the bridge data to get the `fromTokenAddress`.
(address fromTokenAddress) = abi.decode(bridgeData, (address));
IEth2Dai exchange = _getEth2DaiContract();
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
IERC20Token(fromTokenAddress).approve(address(exchange), uint256(-1));
// Try to sell all of this contract's `fromTokenAddress` token balance.
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
address(fromTokenAddress),
IERC20Token(fromTokenAddress).balanceOf(address(this)),
toTokenAddress,
amount
);
// Transfer the converted `toToken`s to `to`.
_transferERC20Token(toTokenAddress, to, boughtAmount);
return BRIDGE_SUCCESS;
}
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
/// and sign for itself in orders. Always succeeds.
/// @return magicValue Magic success bytes, always.
function isValidSignature(
bytes32,
bytes calldata
)
external
view
returns (bytes4 magicValue)
{
return LEGACY_WALLET_MAGIC_VALUE;
}
/// @dev Overridable way to get the eth2dai contract.
/// @return exchange The Eth2Dai exchange contract.
function _getEth2DaiContract()
internal
view
returns (IEth2Dai exchange)
{
return IEth2Dai(ETH2DAI_ADDRESS);
}
/// @dev Permissively transfers an ERC20 token that may not adhere to
/// specs.
/// @param tokenAddress The token contract address.
/// @param to The token recipient.
/// @param amount The amount of tokens to transfer.
function _transferERC20Token(
address tokenAddress,
address to,
uint256 amount
)
private
{
// Transfer tokens.
// We do a raw call so we can check the success separate
// from the return data.
(bool didSucceed, bytes memory returnData) = tokenAddress.call(
abi.encodeWithSelector(
IERC20Token(0).transfer.selector,
to,
amount
)
);
if (!didSucceed) {
assembly { revert(add(returnData, 0x20), mload(returnData)) }
}
// Check return data.
// If there is no return data, we assume the token incorrectly
// does not return a bool. In this case we expect it to revert
// on failure, which was handled above.
// If the token does return data, we require that it is a single
// value that evaluates to true.
assembly {
if returndatasize {
didSucceed := 0
if eq(returndatasize, 32) {
// First 64 bytes of memory are reserved scratch space
returndatacopy(0, 0, 32)
didSucceed := mload(0)
}
}
}
require(didSucceed, "ERC20_TRANSFER_FAILED");
}
}

View File

@ -74,4 +74,15 @@ interface IAssetData {
bytes32 expectedReturnDataHash
)
external;
/// @dev Function signature for encoding ERC20Bridge assetData.
/// @param tokenAddress Address of token to transfer.
/// @param bridgeAddress Address of the bridge contract.
/// @param bridgeData Arbitrary data to be passed to the bridge contract.
function ERC20Bridge(
address tokenAddress,
address bridgeAddress,
bytes calldata bridgeData
)
external;
}

View File

@ -0,0 +1,43 @@
/*
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 IERC20Bridge {
// @dev Result of a successful bridge call.
bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3;
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
/// @param tokenAddress The address of the ERC20 token to transfer.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
/// @return success The magic bytes `0x37708e9b` if successful.
function withdrawTo(
address tokenAddress,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success);
}

View File

@ -0,0 +1,38 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
interface IEth2Dai {
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
/// @param fromToken The token being sold.
/// @param sellAmount The amount of `fromToken` token being sold.
/// @param toToken The token being bought.
/// @param minFillAmount Minimum amount of `toToken` token to buy.
/// @return fillAmount Amount of `toToken` bought.
function sellAllAmount(
address fromToken,
uint256 sellAmount,
address toToken,
uint256 minFillAmount
)
external
returns (uint256 fillAmount);
}

View File

@ -0,0 +1,38 @@
/*
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 IWallet {
bytes4 internal constant LEGACY_WALLET_MAGIC_VALUE = 0xb0671381;
/// @dev Validates a hash with the `Wallet` signature type.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return magicValue `bytes4(0xb0671381)` if the signature check succeeds.
function isValidSignature(
bytes32 hash,
bytes calldata signature
)
external
view
returns (bytes4 magicValue);
}

View File

@ -0,0 +1,108 @@
/*
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 "../src/interfaces/IERC20Bridge.sol";
/// @dev Test bridge token
contract TestERC20BridgeToken {
mapping (address => uint256) private _balances;
function addBalance(address owner, int256 amount)
external
{
setBalance(owner, uint256(int256(balanceOf(owner)) + amount));
}
function setBalance(address owner, uint256 balance)
public
{
_balances[owner] = balance;
}
function balanceOf(address owner)
public
view
returns (uint256)
{
return _balances[owner];
}
}
/// @dev Test bridge contract.
contract TestERC20Bridge is
IERC20Bridge
{
TestERC20BridgeToken public testToken;
event BridgeWithdrawTo(
address tokenAddress,
address from,
address to,
uint256 amount,
bytes bridgeData
);
constructor() public {
testToken = new TestERC20BridgeToken();
}
function setTestTokenBalance(address owner, uint256 balance)
external
{
testToken.setBalance(owner, balance);
}
function withdrawTo(
address tokenAddress,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4)
{
emit BridgeWithdrawTo(
tokenAddress,
from,
to,
amount,
bridgeData
);
// Unpack the bridgeData.
(
int256 transferAmount,
bytes memory revertData,
bytes memory returnData
) = abi.decode(bridgeData, (int256, bytes, bytes));
// If `revertData` is set, revert.
if (revertData.length != 0) {
assembly { revert(add(revertData, 0x20), mload(revertData)) }
}
// Increase `to`'s balance by `transferAmount`.
TestERC20BridgeToken(tokenAddress).addBalance(to, transferAmount);
// Return `returnData`.
assembly { return(add(returnData, 0x20), mload(returnData)) }
}
}

View File

@ -0,0 +1,202 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "../src/bridges/Eth2DaiBridge.sol";
import "../src/interfaces/IEth2Dai.sol";
// solhint-disable no-simple-event-func-name
contract TestEvents {
event TokenTransfer(
address token,
address from,
address to,
uint256 amount
);
event TokenApprove(
address token,
address spender,
uint256 allowance
);
function raiseTokenTransfer(
address from,
address to,
uint256 amount
)
external
{
emit TokenTransfer(
msg.sender,
from,
to,
amount
);
}
function raiseTokenApprove(address spender, uint256 allowance)
external
{
emit TokenApprove(msg.sender, spender, allowance);
}
}
/// @dev A minimalist ERC20 token.
contract TestToken {
mapping (address => uint256) public balances;
string private _nextTransferRevertReason;
bytes private _nextTransferReturnData;
/// @dev Just calls `raiseTokenTransfer()` on the caller.
function transfer(address to, uint256 amount)
external
returns (bool)
{
TestEvents(msg.sender).raiseTokenTransfer(msg.sender, to, amount);
if (bytes(_nextTransferRevertReason).length != 0) {
revert(_nextTransferRevertReason);
}
bytes memory returnData = _nextTransferReturnData;
assembly { return(add(returnData, 0x20), mload(returnData)) }
}
/// @dev Set the balance for `owner`.
function setBalance(address owner, uint256 balance)
external
{
balances[owner] = balance;
}
/// @dev Set the behavior of the `transfer()` call.
function setTransferBehavior(
string calldata revertReason,
bytes calldata returnData
)
external
{
_nextTransferRevertReason = revertReason;
_nextTransferReturnData = returnData;
}
/// @dev Just calls `raiseTokenApprove()` on the caller.
function approve(address spender, uint256 allowance)
external
returns (bool)
{
TestEvents(msg.sender).raiseTokenApprove(spender, allowance);
return true;
}
/// @dev Retrieve the balance for `owner`.
function balanceOf(address owner)
external
view
returns (uint256)
{
return balances[owner];
}
}
/// @dev Eth2DaiBridge overridden to mock tokens and
/// implement IEth2Dai.
contract TestEth2DaiBridge is
TestEvents,
IEth2Dai,
Eth2DaiBridge
{
event SellAllAmount(
address sellToken,
uint256 sellTokenAmount,
address buyToken,
uint256 minimumFillAmount
);
mapping (address => TestToken) public testTokens;
string private _nextRevertReason;
uint256 private _nextFillAmount;
/// @dev Create a token and set this contract's balance.
function createToken(uint256 balance)
external
returns (address tokenAddress)
{
TestToken token = new TestToken();
testTokens[address(token)] = token;
token.setBalance(address(this), balance);
return address(token);
}
/// @dev Set the behavior for `IEth2Dai.sellAllAmount()`.
function setFillBehavior(string calldata revertReason, uint256 fillAmount)
external
{
_nextRevertReason = revertReason;
_nextFillAmount = fillAmount;
}
/// @dev Set the behavior of a token's `transfer()`.
function setTransferBehavior(
address tokenAddress,
string calldata revertReason,
bytes calldata returnData
)
external
{
testTokens[tokenAddress].setTransferBehavior(revertReason, returnData);
}
/// @dev Implementation of `IEth2Dai.sellAllAmount()`
function sellAllAmount(
address sellTokenAddress,
uint256 sellTokenAmount,
address buyTokenAddress,
uint256 minimumFillAmount
)
external
returns (uint256 fillAmount)
{
emit SellAllAmount(
sellTokenAddress,
sellTokenAmount,
buyTokenAddress,
minimumFillAmount
);
if (bytes(_nextRevertReason).length != 0) {
revert(_nextRevertReason);
}
return _nextFillAmount;
}
// @dev This contract will double as the Eth2Dai contract.
function _getEth2DaiContract()
internal
view
returns (IEth2Dai)
{
return IEth2Dai(address(this));
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "2.2.8",
"version": "2.3.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -35,7 +35,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestStaticCallTarget).json",
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IWallet|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@ -48,11 +48,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -71,17 +71,17 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-erc1155": "^1.1.15",
"@0x/contracts-erc20": "^2.2.14",
"@0x/contracts-erc721": "^2.1.15",
"@0x/contracts-utils": "^3.2.4",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-erc1155": "^1.2.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},

View File

@ -6,30 +6,44 @@
import { ContractArtifact } from 'ethereum-types';
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
import * as IAssetData from '../generated-artifacts/IAssetData.json';
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json';
import * as IWallet from '../generated-artifacts/IWallet.json';
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
import * as Ownable from '../generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
export const artifacts = {
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
Ownable: Ownable as ContractArtifact,
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IWallet: IWallet as ContractArtifact,
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
};

View File

@ -4,15 +4,22 @@
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_bridge_proxy';
export * from '../generated-wrappers/erc20_proxy';
export * from '../generated-wrappers/erc721_proxy';
export * from '../generated-wrappers/eth2_dai_bridge';
export * from '../generated-wrappers/i_asset_data';
export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
export * from '../generated-wrappers/i_authorizable';
export * from '../generated-wrappers/i_erc20_bridge';
export * from '../generated-wrappers/i_eth2_dai';
export * from '../generated-wrappers/i_wallet';
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../generated-wrappers/mixin_authorizable';
export * from '../generated-wrappers/multi_asset_proxy';
export * from '../generated-wrappers/ownable';
export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_erc20_bridge';
export * from '../generated-wrappers/test_eth2_dai_bridge';
export * from '../generated-wrappers/test_static_call_target';

View File

@ -0,0 +1,299 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
hexLeftPad,
hexRightPad,
hexSlice,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { AbiEncoder, AuthorizableRevertErrors, BigNumber, StringRevertError } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import {
artifacts,
ERC20BridgeProxyContract,
TestERC20BridgeBridgeWithdrawToEventArgs,
TestERC20BridgeContract,
} from '../src';
blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
const PROXY_ID = AssetProxyId.ERC20Bridge;
const BRIDGE_SUCCESS_RETURN_DATA = hexRightPad(PROXY_ID);
let owner: string;
let badCaller: string;
let assetProxy: ERC20BridgeProxyContract;
let bridgeContract: TestERC20BridgeContract;
let testTokenAddress: string;
before(async () => {
[owner, badCaller] = await env.getAccountAddressesAsync();
assetProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
artifacts.ERC20BridgeProxy,
env.provider,
env.txDefaults,
artifacts,
);
bridgeContract = await TestERC20BridgeContract.deployFrom0xArtifactAsync(
artifacts.TestERC20Bridge,
env.provider,
env.txDefaults,
artifacts,
);
testTokenAddress = await bridgeContract.testToken.callAsync();
await assetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(owner);
});
interface AssetDataOpts {
tokenAddress: string;
bridgeAddress: string;
bridgeData: BridgeDataOpts;
}
interface BridgeDataOpts {
transferAmount: Numberish;
revertError?: string;
returnData: string;
}
function createAssetData(opts?: Partial<AssetDataOpts>): AssetDataOpts {
return _.merge(
{
tokenAddress: testTokenAddress,
bridgeAddress: bridgeContract.address,
bridgeData: createBridgeData(),
},
opts,
);
}
function createBridgeData(opts?: Partial<BridgeDataOpts>): BridgeDataOpts {
return _.merge(
{
transferAmount: constants.ZERO_AMOUNT,
returnData: BRIDGE_SUCCESS_RETURN_DATA,
},
opts,
);
}
function encodeAssetData(opts: AssetDataOpts): string {
const encoder = AbiEncoder.createMethod('ERC20BridgeProxy', [
{ name: 'tokenAddress', type: 'address' },
{ name: 'bridgeAddress', type: 'address' },
{ name: 'bridgeData', type: 'bytes' },
]);
return encoder.encode([opts.tokenAddress, opts.bridgeAddress, encodeBridgeData(opts.bridgeData)]);
}
function encodeBridgeData(opts: BridgeDataOpts): string {
const encoder = AbiEncoder.create([
{ name: 'transferAmount', type: 'int256' },
{ name: 'revertData', type: 'bytes' },
{ name: 'returnData', type: 'bytes' },
]);
const revertErrorBytes =
opts.revertError !== undefined ? new StringRevertError(opts.revertError).encode() : '0x';
return encoder.encode([new BigNumber(opts.transferAmount), revertErrorBytes, opts.returnData]);
}
async function setTestTokenBalanceAsync(_owner: string, balance: Numberish): Promise<void> {
await bridgeContract.setTestTokenBalance.awaitTransactionSuccessAsync(_owner, new BigNumber(balance));
}
describe('transferFrom()', () => {
interface TransferFromOpts {
assetData: AssetDataOpts;
from: string;
to: string;
amount: Numberish;
}
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
const transferAmount = _.get(opts, ['amount'], getRandomInteger(1, 100e18)) as BigNumber;
return _.merge(
{
assetData: createAssetData({
bridgeData: createBridgeData({
transferAmount,
}),
}),
from: randomAddress(),
to: randomAddress(),
amount: transferAmount,
},
opts,
);
}
async function transferFromAsync(opts?: Partial<TransferFromOpts>, caller?: string): Promise<DecodedLogs> {
const _opts = createTransferFromOpts(opts);
const { logs } = await assetProxy.transferFrom.awaitTransactionSuccessAsync(
encodeAssetData(_opts.assetData),
_opts.from,
_opts.to,
new BigNumber(_opts.amount),
{ from: caller },
);
return (logs as any) as DecodedLogs;
}
it('succeeds if the bridge succeeds and balance increases by `amount`', async () => {
const tx = transferFromAsync();
return expect(tx).to.be.fulfilled('');
});
it('succeeds if balance increases more than `amount`', async () => {
const amount = getRandomInteger(1, 100e18);
const tx = transferFromAsync({
amount,
assetData: createAssetData({
bridgeData: createBridgeData({
transferAmount: amount.plus(1),
}),
}),
});
return expect(tx).to.be.fulfilled('');
});
it('passes the correct arguments to the bridge contract', async () => {
const opts = createTransferFromOpts();
const logs = await transferFromAsync(opts);
expect(logs.length).to.eq(1);
const args = logs[0].args as TestERC20BridgeBridgeWithdrawToEventArgs;
expect(args.tokenAddress).to.eq(opts.assetData.tokenAddress);
expect(args.from).to.eq(opts.from);
expect(args.to).to.eq(opts.to);
expect(args.amount).to.bignumber.eq(opts.amount);
expect(args.bridgeData).to.eq(encodeBridgeData(opts.assetData.bridgeData));
});
it('fails if not called by an authorized address', async () => {
const tx = transferFromAsync({}, badCaller);
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(badCaller));
});
it('fails if asset data is truncated', async () => {
const opts = createTransferFromOpts();
const truncatedAssetData = hexSlice(encodeAssetData(opts.assetData), 0, -1);
const tx = assetProxy.transferFrom.awaitTransactionSuccessAsync(
truncatedAssetData,
opts.from,
opts.to,
new BigNumber(opts.amount),
);
return expect(tx).to.be.rejected();
});
it('fails if bridge returns nothing', async () => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
returnData: '0x',
}),
}),
});
// This will actually revert when the AP tries to decode the return
// value.
return expect(tx).to.be.rejected();
});
it('fails if bridge returns true', async () => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
returnData: hexLeftPad('0x1'),
}),
}),
});
// This will actually revert when the AP tries to decode the return
// value.
return expect(tx).to.be.rejected();
});
it('fails if bridge returns 0x1', async () => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
returnData: hexRightPad('0x1'),
}),
}),
});
return expect(tx).to.revertWith('BRIDGE_FAILED');
});
it('fails if bridge is an EOA', async () => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeAddress: randomAddress(),
}),
});
// This will actually revert when the AP tries to decode the return
// value.
return expect(tx).to.be.rejected();
});
it('fails if bridge reverts', async () => {
const revertError = 'FOOBAR';
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
revertError,
}),
}),
});
return expect(tx).to.revertWith(revertError);
});
it('fails if balance of `to` increases by less than `amount`', async () => {
const amount = getRandomInteger(1, 100e18);
const tx = transferFromAsync({
amount,
assetData: createAssetData({
bridgeData: createBridgeData({
transferAmount: amount.minus(1),
}),
}),
});
return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
});
it('fails if balance of `to` decreases', async () => {
const toAddress = randomAddress();
await setTestTokenBalanceAsync(toAddress, 1e18);
const tx = transferFromAsync({
to: toAddress,
assetData: createAssetData({
bridgeData: createBridgeData({
transferAmount: -1,
}),
}),
});
return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
});
});
describe('balanceOf()', () => {
it('retrieves the balance of the encoded token', async () => {
const _owner = randomAddress();
const balance = getRandomInteger(1, 100e18);
await bridgeContract.setTestTokenBalance.awaitTransactionSuccessAsync(_owner, balance);
const assetData = createAssetData({
tokenAddress: testTokenAddress,
});
const actualBalance = await assetProxy.balanceOf.callAsync(encodeAssetData(assetData), _owner);
expect(actualBalance).to.bignumber.eq(balance);
});
});
describe('getProxyId()', () => {
it('returns the correct proxy ID', async () => {
const proxyId = await assetProxy.getProxyId.callAsync();
expect(proxyId).to.eq(PROXY_ID);
});
});
});

View File

@ -0,0 +1,192 @@
import {
blockchainTests,
constants,
expect,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
TransactionHelper,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import {
artifacts,
TestEth2DaiBridgeContract,
TestEth2DaiBridgeEvents,
TestEth2DaiBridgeSellAllAmountEventArgs,
TestEth2DaiBridgeTokenApproveEventArgs,
TestEth2DaiBridgeTokenTransferEventArgs,
} from '../src';
blockchainTests.resets('Eth2DaiBridge unit tests', env => {
const txHelper = new TransactionHelper(env.web3Wrapper, artifacts);
let testContract: TestEth2DaiBridgeContract;
before(async () => {
testContract = await TestEth2DaiBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestEth2DaiBridge,
env.provider,
env.txDefaults,
artifacts,
);
});
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract.isValidSignature.callAsync(hexRandom(), hexRandom(_.random(0, 32)));
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
describe('withdrawTo()', () => {
interface WithdrawToOpts {
toTokenAddress?: string;
fromTokenAddress?: string;
toAddress: string;
amount: Numberish;
fromTokenBalance: Numberish;
revertReason: string;
fillAmount: Numberish;
toTokentransferRevertReason: string;
toTokenTransferReturnData: string;
}
interface WithdrawToResult {
opts: WithdrawToOpts;
result: string;
logs: DecodedLogs;
}
function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts {
return {
toAddress: randomAddress(),
amount: getRandomInteger(1, 100e18),
revertReason: '',
fillAmount: getRandomInteger(1, 100e18),
fromTokenBalance: getRandomInteger(1, 100e18),
toTokentransferRevertReason: '',
toTokenTransferReturnData: hexLeftPad(1),
...opts,
};
}
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
const _opts = createWithdrawToOpts(opts);
// Set the fill behavior.
await testContract.setFillBehavior.awaitTransactionSuccessAsync(
_opts.revertReason,
new BigNumber(_opts.fillAmount),
);
// Create tokens and balances.
if (_opts.fromTokenAddress === undefined) {
[_opts.fromTokenAddress] = await txHelper.getResultAndReceiptAsync(
testContract.createToken,
new BigNumber(_opts.fromTokenBalance),
);
}
if (_opts.toTokenAddress === undefined) {
[_opts.toTokenAddress] = await txHelper.getResultAndReceiptAsync(
testContract.createToken,
constants.ZERO_AMOUNT,
);
}
// Set the transfer behavior of `toTokenAddress`.
await testContract.setTransferBehavior.awaitTransactionSuccessAsync(
_opts.toTokenAddress,
_opts.toTokentransferRevertReason,
_opts.toTokenTransferReturnData,
);
// Call withdrawTo().
const [result, { logs }] = await txHelper.getResultAndReceiptAsync(
testContract.withdrawTo,
// "to" token address
_opts.toTokenAddress,
// Random from address.
randomAddress(),
// To address.
_opts.toAddress,
new BigNumber(_opts.amount),
// ABI-encode the "from" token address as the bridge data.
hexLeftPad(_opts.fromTokenAddress as string),
);
return {
opts: _opts,
result,
logs: (logs as any) as DecodedLogs,
};
}
it('returns magic bytes on success', async () => {
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
const { result } = await withdrawToAsync();
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
});
it('calls `Eth2Dai.sellAllAmount()`', async () => {
const { opts, logs } = await withdrawToAsync();
const transfers = filterLogsToArguments<TestEth2DaiBridgeSellAllAmountEventArgs>(
logs,
TestEth2DaiBridgeEvents.SellAllAmount,
);
expect(transfers.length).to.eq(1);
expect(transfers[0].sellToken).to.eq(opts.fromTokenAddress);
expect(transfers[0].buyToken).to.eq(opts.toTokenAddress);
expect(transfers[0].sellTokenAmount).to.bignumber.eq(opts.fromTokenBalance);
expect(transfers[0].minimumFillAmount).to.bignumber.eq(opts.amount);
});
it('sets an unlimited allowance on the `fromTokenAddress` token', async () => {
const { opts, logs } = await withdrawToAsync();
const approvals = filterLogsToArguments<TestEth2DaiBridgeTokenApproveEventArgs>(
logs,
TestEth2DaiBridgeEvents.TokenApprove,
);
expect(approvals.length).to.eq(1);
expect(approvals[0].token).to.eq(opts.fromTokenAddress);
expect(approvals[0].spender).to.eq(testContract.address);
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
});
it('transfers filled amount to `to`', async () => {
const { opts, logs } = await withdrawToAsync();
const transfers = filterLogsToArguments<TestEth2DaiBridgeTokenTransferEventArgs>(
logs,
TestEth2DaiBridgeEvents.TokenTransfer,
);
expect(transfers.length).to.eq(1);
expect(transfers[0].token).to.eq(opts.toTokenAddress);
expect(transfers[0].from).to.eq(testContract.address);
expect(transfers[0].to).to.eq(opts.toAddress);
expect(transfers[0].amount).to.bignumber.eq(opts.fillAmount);
});
it('fails if `Eth2Dai.sellAllAmount()` reverts', async () => {
const opts = createWithdrawToOpts({ revertReason: 'FOOBAR' });
const tx = withdrawToAsync(opts);
return expect(tx).to.revertWith(opts.revertReason);
});
it('fails if `toTokenAddress.transfer()` reverts', async () => {
const opts = createWithdrawToOpts({ toTokentransferRevertReason: 'FOOBAR' });
const tx = withdrawToAsync(opts);
return expect(tx).to.revertWith(opts.toTokentransferRevertReason);
});
it('fails if `toTokenAddress.transfer()` returns falsey', async () => {
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexLeftPad(0) });
const tx = withdrawToAsync(opts);
return expect(tx).to.revertWith('ERC20_TRANSFER_FAILED');
});
it('succeeds if `toTokenAddress.transfer()` returns truthy', async () => {
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(100) });
});
});
});

View File

@ -4,17 +4,24 @@
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20BridgeProxy.json",
"generated-artifacts/ERC20Proxy.json",
"generated-artifacts/ERC721Proxy.json",
"generated-artifacts/Eth2DaiBridge.json",
"generated-artifacts/IAssetData.json",
"generated-artifacts/IAssetProxy.json",
"generated-artifacts/IAssetProxyDispatcher.json",
"generated-artifacts/IAuthorizable.json",
"generated-artifacts/IERC20Bridge.json",
"generated-artifacts/IEth2Dai.json",
"generated-artifacts/IWallet.json",
"generated-artifacts/MixinAssetProxyDispatcher.json",
"generated-artifacts/MixinAuthorizable.json",
"generated-artifacts/MultiAssetProxy.json",
"generated-artifacts/Ownable.json",
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestERC20Bridge.json",
"generated-artifacts/TestEth2DaiBridge.json",
"generated-artifacts/TestStaticCallTarget.json"
],
"exclude": ["./deploy/solc/solc_bin"]

View File

@ -1,6 +1,6 @@
[
{
"version": "3.0.0",
"version": "2.1.0-beta.0",
"changes": [
{
"note": "Add chainId to domain separator",
@ -42,7 +42,8 @@
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,19 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.0-beta.0 - _October 3, 2019_
* Add chainId to domain separator (#1742)
* Inherit Exchange domain constants from `exchange-libs` to reduce code duplication (#1742)
* Update domain separator (#1742)
* Refactor contract to use new ITransactions interface (#1753)
* Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor (#1753)
* Remove LibZeroExTransaction contract (#1753)
* Update tests for arbitrary fee tokens (ZEIP-28). (#1819)
* Update for new `marketXOrders` consolidation. (#2042)
* Use built in selectors instead of hard coded constants (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
## v2.0.13 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "2.0.13",
"version": "2.1.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -48,11 +48,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -71,18 +71,18 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-asset-proxy": "^2.2.8",
"@0x/contracts-erc20": "^2.2.14",
"@0x/contracts-exchange": "^2.1.14",
"@0x/contracts-exchange-libs": "^3.0.8",
"@0x/contracts-utils": "^3.2.4",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-exchange": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},

View File

@ -1,6 +1,6 @@
[
{
"version": "1.0.0",
"version": "0.1.0-beta.0",
"changes": [
{
"note": "Use built in selectors instead of hard coded constants",
@ -18,7 +18,8 @@
"note": "`run_mocha` package script runs with `UNLIMITED_CONTRACT_SIZE=true` environment variable.",
"pr": 2075
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,13 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.1.0-beta.0 - _October 3, 2019_
* Use built in selectors instead of hard coded constants (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Add `marketBuy/SellOrdersNoThrow` and `marketBuy/SellOrdersFillOrKill` to `LibTransactionDecoder`. (#2075)
* `run_mocha` package script runs with `UNLIMITED_CONTRACT_SIZE=true` environment variable. (#2075)
## v0.0.10 - _September 17, 2019_
* Dependencies updated
@ -53,3 +60,5 @@ CHANGELOG
* Refactor `LibAssetData` balance/allowance checks to never revert (#1848)
* Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount` (#1848)
* Add support for StaticCallProxy (#1863)
* Add `OrderTransferSimulationUtils` contract for simulating order transfers on-chain (#1868)
* Updated to use the new rich error pattern from @0x/contracts-exchange (#1913)

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "0.0.10",
"version": "0.1.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -49,11 +49,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -72,20 +72,20 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-asset-proxy": "^2.2.8",
"@0x/contracts-erc1155": "^1.1.15",
"@0x/contracts-erc20": "^2.2.14",
"@0x/contracts-erc721": "^2.1.15",
"@0x/contracts-exchange": "^2.1.14",
"@0x/contracts-exchange-libs": "^3.0.8",
"@0x/contracts-utils": "^3.2.4",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc1155": "^1.2.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-exchange": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1"
},
"publishConfig": {

View File

@ -94,7 +94,7 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
txDefaults,
artifacts,
);
const exchangeWrapper = new ExchangeWrapper(exchange, provider);
const exchangeWrapper = new ExchangeWrapper(exchange);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner);

View File

@ -1,12 +1,13 @@
[
{
"version": "1.1.16",
"version": "1.2.0-beta.0",
"changes": [
{
"note": "Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`.",
"pr": 1819
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.2.0-beta.0 - _October 3, 2019_
* Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`. (#1819)
## v1.1.15 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "1.1.15",
"version": "1.2.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -48,10 +48,10 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -70,14 +70,14 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/contracts-utils": "^3.2.4",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@ -1,4 +1,13 @@
[
{
"version": "2.3.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "2.2.14",

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.3.0-beta.0 - _October 3, 2019_
* Dependencies updated
## v2.2.14 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "2.2.14",
"version": "2.3.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -47,11 +47,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -69,13 +69,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-utils": "^3.2.4",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@ -1,4 +1,13 @@
[
{
"version": "2.2.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "2.1.15",

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.2.0-beta.0 - _October 3, 2019_
* Dependencies updated
## v2.1.15 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "2.1.15",
"version": "2.2.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -48,11 +48,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -71,13 +71,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-utils": "^3.2.4",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@ -1,4 +1,13 @@
[
{
"version": "3.1.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "3.0.12",

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.0-beta.0 - _October 3, 2019_
* Dependencies updated
## v3.0.12 - _September 17, 2019_
* Dependencies updated

View File

@ -27,10 +27,12 @@ import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
import "./MixinAssets.sol";
contract MixinExchangeWrapper is
LibConstants
LibConstants,
MixinAssets
{
using LibSafeMath for uint256;
@ -57,25 +59,12 @@ contract MixinExchangeWrapper is
);
address exchange = address(EXCHANGE);
// Call `fillOrder` and handle any exceptions gracefully
assembly {
let success := call(
gas, // forward all gas
exchange, // call address of Exchange contract
0, // transfer 0 wei
add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)
mload(fillOrderCalldata), // length of input
fillOrderCalldata, // write output over input
128 // output size is 128 bytes
)
if success {
mstore(fillResults, mload(fillOrderCalldata))
mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))
}
(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;
}
@ -98,7 +87,7 @@ contract MixinExchangeWrapper is
uint256 makerAssetAcquiredAmount
)
{
// No fee or percentage fee
// No taker fee or percentage fee
if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.makerAssetData)) {
// Attempt to sell the remaining amount of WETH
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
@ -107,16 +96,17 @@ contract MixinExchangeWrapper is
signature
);
wethSpentAmount = singleFillResults.takerAssetFilledAmount;
wethSpentAmount = singleFillResults.takerAssetFilledAmount
.safeAdd(singleFillResults.protocolFeePaid);
// Subtract fee from makerAssetFilledAmount for the net amount acquired.
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount.safeSub(
singleFillResults.takerFeePaid
);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
.safeSub(singleFillResults.takerFeePaid);
// WETH fee
} else if (order.takerFeeAssetData.equals(order.takerAssetData)) {
// We will first sell WETH as the takerAsset, then use it to pay the takerFee.
// This ensures that we reserve enough to pay the fee.
// This ensures that we reserve enough to pay the taker and protocol fees.
uint256 takerAssetFillAmount = LibMath.getPartialAmountCeil(
order.takerAssetAmount,
order.takerAssetAmount.safeAdd(order.takerFee),
@ -130,9 +120,9 @@ contract MixinExchangeWrapper is
);
// WETH is also spent on the taker fee, so we add it here.
wethSpentAmount = singleFillResults.takerAssetFilledAmount.safeAdd(
singleFillResults.takerFeePaid
);
wethSpentAmount = singleFillResults.takerAssetFilledAmount
.safeAdd(singleFillResults.takerFeePaid)
.safeAdd(singleFillResults.protocolFeePaid);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
// Unsupported fee
@ -161,22 +151,18 @@ contract MixinExchangeWrapper is
)
{
uint256 ordersLength = orders.length;
uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
for (uint256 i = 0; i != ordersLength; i++) {
if (!orders[i].makerAssetData.equals(orders[0].makerAssetData)) {
LibRichErrors.rrevert(LibForwarderRichErrors.MakerAssetMismatchError(
orders[0].makerAssetData,
orders[i].makerAssetData
));
}
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
continue;
}
// The remaining amount of WETH to sell
uint256 remainingTakerAssetFillAmount = wethSellAmount.safeSub(totalWethSpentAmount);
uint256 remainingTakerAssetFillAmount = wethSellAmount
.safeSub(totalWethSpentAmount)
.safeSub(protocolFee);
(
uint256 wethSpentAmount,
@ -187,8 +173,12 @@ contract MixinExchangeWrapper is
remainingTakerAssetFillAmount
);
totalWethSpentAmount = totalWethSpentAmount.safeAdd(wethSpentAmount);
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount.safeAdd(makerAssetAcquiredAmount);
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
totalWethSpentAmount = totalWethSpentAmount
.safeAdd(wethSpentAmount);
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount
.safeAdd(makerAssetAcquiredAmount);
// Stop execution if the entire amount of WETH has been sold
if (totalWethSpentAmount >= wethSellAmount) {
@ -215,7 +205,7 @@ contract MixinExchangeWrapper is
uint256 makerAssetAcquiredAmount
)
{
// No fee or WETH fee
// No taker fee or WETH fee
if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.takerAssetData)) {
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
@ -231,10 +221,10 @@ contract MixinExchangeWrapper is
signature
);
// WETH is also spent on the taker fee, so we add it here.
wethSpentAmount = singleFillResults.takerAssetFilledAmount.safeAdd(
singleFillResults.takerFeePaid
);
// WETH is also spent on the protocol and taker fees, so we add it here.
wethSpentAmount = singleFillResults.takerAssetFilledAmount
.safeAdd(singleFillResults.takerFeePaid)
.safeAdd(singleFillResults.protocolFeePaid);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
// Percentage fee
@ -253,12 +243,12 @@ contract MixinExchangeWrapper is
signature
);
wethSpentAmount = singleFillResults.takerAssetFilledAmount;
wethSpentAmount = singleFillResults.takerAssetFilledAmount
.safeAdd(singleFillResults.protocolFeePaid);
// Subtract fee from makerAssetFilledAmount for the net amount acquired.
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount.safeSub(
singleFillResults.takerFeePaid
);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
.safeSub(singleFillResults.takerFeePaid);
// Unsupported fee
} else {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedFeeError(order.takerFeeAssetData));
@ -289,19 +279,13 @@ contract MixinExchangeWrapper is
{
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
if (!orders[i].makerAssetData.equals(orders[0].makerAssetData)) {
LibRichErrors.rrevert(LibForwarderRichErrors.MakerAssetMismatchError(
orders[0].makerAssetData,
orders[i].makerAssetData
));
}
// Preemptively skip to avoid division by zero in _marketBuySingleOrder
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
continue;
}
uint256 remainingMakerAssetFillAmount = makerAssetBuyAmount.safeSub(totalMakerAssetAcquiredAmount);
uint256 remainingMakerAssetFillAmount = makerAssetBuyAmount
.safeSub(totalMakerAssetAcquiredAmount);
(
uint256 wethSpentAmount,
@ -312,8 +296,12 @@ contract MixinExchangeWrapper is
remainingMakerAssetFillAmount
);
totalWethSpentAmount = totalWethSpentAmount.safeAdd(wethSpentAmount);
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount.safeAdd(makerAssetAcquiredAmount);
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
totalWethSpentAmount = totalWethSpentAmount
.safeAdd(wethSpentAmount);
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount
.safeAdd(makerAssetAcquiredAmount);
// Stop execution if the entire amount of makerAsset has been bought
if (totalMakerAssetAcquiredAmount >= makerAssetBuyAmount) {

View File

@ -28,7 +28,6 @@ import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
import "./interfaces/IAssets.sol";
import "./interfaces/IForwarderCore.sol";
import "./MixinAssets.sol";
import "./MixinExchangeWrapper.sol";
import "./MixinWeth.sol";
@ -38,7 +37,6 @@ contract MixinForwarderCore is
IAssets,
IForwarderCore,
MixinWeth,
MixinAssets,
MixinExchangeWrapper
{
using LibBytes for bytes;
@ -53,6 +51,11 @@ contract MixinForwarderCore is
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
}
ETHER_TOKEN.approve(proxyAddress, MAX_UINT);
address protocolFeeCollector = EXCHANGE.protocolFeeCollector();
if (protocolFeeCollector != address(0)) {
ETHER_TOKEN.approve(protocolFeeCollector, MAX_UINT);
}
}
/// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent
@ -88,7 +91,8 @@ contract MixinForwarderCore is
msg.value
);
// Spends up to wethSellAmount to fill orders and pay WETH order fees.
// Spends up to wethSellAmount to fill orders, transfers purchased assets to msg.sender,
// and pays WETH order fees.
(
wethSpentAmount,
makerAssetAcquiredAmount
@ -105,12 +109,6 @@ contract MixinForwarderCore is
feePercentage,
feeRecipient
);
// Transfer purchased assets to msg.sender.
_transferAssetToSender(
orders[0].makerAssetData,
makerAssetAcquiredAmount
);
}
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
@ -143,9 +141,7 @@ contract MixinForwarderCore is
// Convert ETH to WETH.
_convertEthToWeth();
// Attempt to fill the desired amount of makerAsset. Note that makerAssetAcquiredAmount < makerAssetBuyAmount
// if any of the orders filled have an takerFee denominated in makerAsset, since these fees will be paid out
// from the Forwarder's temporary makerAsset balance.
// Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender.
(
wethSpentAmount,
makerAssetAcquiredAmount
@ -162,11 +158,5 @@ contract MixinForwarderCore is
feePercentage,
feeRecipient
);
// Transfer acquired assets to msg.sender.
_transferAssetToSender(
orders[0].makerAssetData,
makerAssetAcquiredAmount
);
}
}

View File

@ -47,19 +47,19 @@ contract MixinWeth is
internal
{
if (msg.value == 0) {
LibRichErrors.rrevert(LibForwarderRichErrors.MsgValueCantEqualZeroError());
LibRichErrors.rrevert(LibForwarderRichErrors.MsgValueCannotEqualZeroError());
}
ETHER_TOKEN.deposit.value(msg.value)();
}
/// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
/// Refunds any excess ETH to msg.sender.
/// @param wethSold Amount of WETH sold when filling primary orders.
/// @param wethSpent Amount of WETH spent when filling orders.
/// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
/// @param feeRecipient Address that will receive ETH when orders are filled.
/// @return ethFee Amount paid to feeRecipient as a percentage fee on the total WETH sold.
function _transferEthFeeAndRefund(
uint256 wethSold,
uint256 wethSpent,
uint256 feePercentage,
address payable feeRecipient
)
@ -73,22 +73,22 @@ contract MixinWeth is
));
}
// Ensure that no extra WETH owned by this contract has been sold.
if (wethSold > msg.value) {
LibRichErrors.rrevert(LibForwarderRichErrors.OversoldWethError(
wethSold,
// Ensure that no extra WETH owned by this contract has been spent.
if (wethSpent > msg.value) {
LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
wethSpent,
msg.value
));
}
// Calculate amount of WETH that hasn't been sold.
uint256 wethRemaining = msg.value.safeSub(wethSold);
// Calculate amount of WETH that hasn't been spent.
uint256 wethRemaining = msg.value.safeSub(wethSpent);
// Calculate ETH fee to pay to feeRecipient.
ethFee = LibMath.getPartialAmountFloor(
feePercentage,
PERCENTAGE_DENOMINATOR,
wethSold
wethSpent
);
// Ensure fee is less than amount of WETH remaining.

View File

@ -21,7 +21,6 @@ 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-erc20/contracts/src/interfaces/IEtherToken.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
contract LibConstants {

View File

@ -35,10 +35,6 @@ library LibForwarderRichErrors {
bytes4 internal constant COMPLETE_BUY_FAILED_ERROR_SELECTOR =
0x91353a0c;
// bytes4(keccak256("MakerAssetMismatchError(bytes,bytes)"))
bytes4 internal constant MAKER_ASSET_MISMATCH_ERROR_SELECTOR =
0x56677f2c;
// bytes4(keccak256("UnsupportedFeeError(bytes)"))
bytes4 internal constant UNSUPPORTED_FEE_ERROR_SELECTOR =
0x31360af1;
@ -51,9 +47,9 @@ library LibForwarderRichErrors {
bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR =
0xecf40fd9;
// bytes4(keccak256("OversoldWethError(uint256,uint256)"))
bytes4 internal constant OVERSOLD_WETH_ERROR_SELECTOR =
0x5cc555c8;
// bytes4(keccak256("OverspentWethError(uint256,uint256)"))
bytes4 internal constant OVERSPENT_WETH_ERROR_SELECTOR =
0xcdcbed5d;
// bytes4(keccak256("TransferFailedError(bytes)"))
bytes4 internal constant TRANSFER_FAILED_ERROR_SELECTOR =
@ -63,9 +59,9 @@ library LibForwarderRichErrors {
bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
0x08b18698;
// bytes4(keccak256("MsgValueCantEqualZeroError()"))
bytes4 internal constant MSG_VALUE_CANT_EQUAL_ZERO_ERROR_SELECTOR =
0x1213e1d6;
// bytes4(keccak256("MsgValueCannotEqualZeroError()"))
bytes4 internal constant MSG_VALUE_CANNOT_EQUAL_ZERO_ERROR_SELECTOR =
0x8c0e562b;
// bytes4(keccak256("Erc721AmountMustEqualOneError(uint256)"))
bytes4 internal constant ERC721_AMOUNT_MUST_EQUAL_ONE_ERROR_SELECTOR =
@ -108,21 +104,6 @@ library LibForwarderRichErrors {
);
}
function MakerAssetMismatchError(
bytes memory firstOrderMakerAssetData,
bytes memory mismatchedMakerAssetData
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
MAKER_ASSET_MISMATCH_ERROR_SELECTOR,
firstOrderMakerAssetData,
mismatchedMakerAssetData
);
}
function UnsupportedFeeError(
bytes memory takerFeeAssetData
)
@ -164,8 +145,8 @@ library LibForwarderRichErrors {
);
}
function OversoldWethError(
uint256 wethSold,
function OverspentWethError(
uint256 wethSpent,
uint256 msgValue
)
internal
@ -173,8 +154,8 @@ library LibForwarderRichErrors {
returns (bytes memory)
{
return abi.encodeWithSelector(
OVERSOLD_WETH_ERROR_SELECTOR,
wethSold,
OVERSPENT_WETH_ERROR_SELECTOR,
wethSpent,
msgValue
);
}
@ -205,12 +186,12 @@ library LibForwarderRichErrors {
);
}
function MsgValueCantEqualZeroError()
function MsgValueCannotEqualZeroError()
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(MSG_VALUE_CANT_EQUAL_ZERO_ERROR_SELECTOR);
return abi.encodeWithSelector(MSG_VALUE_CANNOT_EQUAL_ZERO_ERROR_SELECTOR);
}
function Erc721AmountMustEqualOneError(

View File

@ -0,0 +1,59 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
// solhint-disable no-unused-vars
contract TestProtocolFeeCollector {
address private _wethAddress;
constructor (
address wethAddress
)
public
{
_wethAddress = wethAddress;
}
/// @dev Pays a protocol fee in WETH (Forwarder orders will always pay protocol fees in WETH).
/// @param makerAddress The address of the order's maker.
/// @param payerAddress The address of the protocol fee payer.
/// @param protocolFeePaid The protocol fee that should be paid.
function payProtocolFee(
address makerAddress,
address payerAddress,
uint256 protocolFeePaid
)
external
payable
{
assert(msg.value == 0);
// Transfer the protocol fee to this address in WETH.
IEtherToken(_wethAddress).transferFrom(
payerAddress,
address(this),
protocolFeePaid
);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "3.0.12",
"version": "3.1.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -11,6 +11,7 @@
},
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ts": "tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
@ -34,7 +35,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth).json",
"abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth|TestProtocolFeeCollector).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@ -47,11 +48,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -70,19 +71,19 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-asset-proxy": "^2.2.8",
"@0x/contracts-erc20": "^2.2.14",
"@0x/contracts-erc721": "^2.1.15",
"@0x/contracts-exchange": "^2.1.14",
"@0x/contracts-exchange-libs": "^3.0.8",
"@0x/contracts-utils": "^3.2.4",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-exchange": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@ -15,6 +15,7 @@ import * as MixinAssets from '../generated-artifacts/MixinAssets.json';
import * as MixinExchangeWrapper from '../generated-artifacts/MixinExchangeWrapper.json';
import * as MixinForwarderCore from '../generated-artifacts/MixinForwarderCore.json';
import * as MixinWeth from '../generated-artifacts/MixinWeth.json';
import * as TestProtocolFeeCollector from '../generated-artifacts/TestProtocolFeeCollector.json';
export const artifacts = {
Forwarder: Forwarder as ContractArtifact,
MixinAssets: MixinAssets as ContractArtifact,
@ -26,4 +27,5 @@ export const artifacts = {
IForwarderCore: IForwarderCore as ContractArtifact,
LibConstants: LibConstants as ContractArtifact,
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
TestProtocolFeeCollector: TestProtocolFeeCollector as ContractArtifact,
};

View File

@ -13,3 +13,4 @@ export * from '../generated-wrappers/mixin_assets';
export * from '../generated-wrappers/mixin_exchange_wrapper';
export * from '../generated-wrappers/mixin_forwarder_core';
export * from '../generated-wrappers/mixin_weth';
export * from '../generated-wrappers/test_protocol_fee_collector';

View File

@ -14,21 +14,24 @@ import {
import { assetDataUtils, ForwarderRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { artifacts, ForwarderContract, ForwarderTestFactory, ForwarderWrapper } from '../src';
import {
artifacts,
ForwarderContract,
ForwarderTestFactory,
ForwarderWrapper,
TestProtocolFeeCollectorContract,
} from '../src';
const DECIMALS_DEFAULT = 18;
blockchainTests(ContractName.Forwarder, env => {
let chainId: number;
let makerAddress: string;
let owner: string;
let makerAddress: string;
let takerAddress: string;
let orderFeeRecipientAddress: string;
let forwarderFeeRecipientAddress: string;
let defaultMakerAssetAddress: string;
let wethAssetData: string;
let weth: DummyERC20TokenContract;
let erc20Token: DummyERC20TokenContract;
@ -36,22 +39,26 @@ blockchainTests(ContractName.Forwarder, env => {
let erc721Token: DummyERC721TokenContract;
let forwarderContract: ForwarderContract;
let wethContract: WETH9Contract;
let exchangeContract: ExchangeContract;
let protocolFeeCollector: TestProtocolFeeCollectorContract;
let forwarderWrapper: ForwarderWrapper;
let exchangeWrapper: ExchangeWrapper;
let erc20Wrapper: ERC20Wrapper;
let orderFactory: OrderFactory;
let forwarderTestFactory: ForwarderTestFactory;
let erc20Wrapper: ERC20Wrapper;
let tx: TransactionReceiptWithDecodedLogs;
let chainId: number;
let wethAssetData: string;
let erc721MakerAssetIds: BigNumber[];
const gasPrice = new BigNumber(constants.DEFAULT_GAS_PRICE);
const GAS_PRICE = new BigNumber(env.txDefaults.gasPrice || constants.DEFAULT_GAS_PRICE);
const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150);
const PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
before(async () => {
await env.blockchainLifecycle.startAsync();
chainId = await env.getChainIdAsync();
// Set up addresses
const accounts = await env.getAccountAddressesAsync();
const usedAddresses = ([
owner,
@ -61,24 +68,27 @@ blockchainTests(ContractName.Forwarder, env => {
forwarderFeeRecipientAddress,
] = accounts);
const erc721Wrapper = new ERC721Wrapper(env.provider, usedAddresses, owner);
erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
const numDummyErc20ToDeploy = 2;
[erc20Token, secondErc20Token] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy,
constants.DUMMY_TOKEN_DECIMALS,
// Set up Exchange
chainId = await env.getChainIdAsync();
exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
env.provider,
env.txDefaults,
{},
new BigNumber(chainId),
);
exchangeWrapper = new ExchangeWrapper(exchangeContract);
// Set up ERC20
erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
[erc20Token, secondErc20Token] = await erc20Wrapper.deployDummyTokensAsync(2, constants.DUMMY_TOKEN_DECIMALS);
const erc20Proxy = await erc20Wrapper.deployProxyAsync();
await erc20Wrapper.setBalancesAndAllowancesAsync();
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
const erc721Proxy = await erc721Wrapper.deployProxyAsync();
await erc721Wrapper.setBalancesAndAllowancesAsync();
const erc721Balances = await erc721Wrapper.getBalancesAsync();
erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
from: owner,
});
// Set up WETH
wethContract = await WETH9Contract.deployFrom0xArtifactAsync(
erc20Artifacts.WETH9,
env.provider,
@ -86,59 +96,72 @@ blockchainTests(ContractName.Forwarder, env => {
{},
);
weth = new DummyERC20TokenContract(wethContract.address, env.provider);
erc20Wrapper.addDummyTokenContract(weth);
wethAssetData = assetDataUtils.encodeERC20AssetData(wethContract.address);
const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
erc20Wrapper.addDummyTokenContract(weth);
await erc20Wrapper.setBalancesAndAllowancesAsync();
// Set up ERC721
const erc721Wrapper = new ERC721Wrapper(env.provider, usedAddresses, owner);
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
const erc721Proxy = await erc721Wrapper.deployProxyAsync();
await erc721Wrapper.setBalancesAndAllowancesAsync();
const erc721Balances = await erc721Wrapper.getBalancesAsync();
erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
from: owner,
});
// Set up Protocol Fee Collector
protocolFeeCollector = await TestProtocolFeeCollectorContract.deployFrom0xArtifactAsync(
artifacts.TestProtocolFeeCollector,
env.provider,
env.txDefaults,
{},
new BigNumber(chainId),
wethContract.address,
);
exchangeWrapper = new ExchangeWrapper(exchangeInstance, env.provider);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
from: owner,
});
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
from: owner,
});
await exchangeContract.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(PROTOCOL_FEE_MULTIPLIER);
await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(
protocolFeeCollector.address,
);
erc20Wrapper.addTokenOwnerAddress(protocolFeeCollector.address);
// Set defaults
defaultMakerAssetAddress = erc20Token.address;
const defaultTakerAssetAddress = wethContract.address;
const defaultOrderParams = {
makerAddress,
feeRecipientAddress: orderFeeRecipientAddress,
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(200, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, DECIMALS_DEFAULT),
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
makerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
takerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
exchangeAddress: exchangeInstance.address,
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
exchangeAddress: exchangeContract.address,
chainId,
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
// Set up Forwarder
forwarderContract = await ForwarderContract.deployFrom0xArtifactAsync(
artifacts.Forwarder,
env.provider,
env.txDefaults,
{},
exchangeInstance.address,
exchangeContract.address,
wethAssetData,
);
forwarderWrapper = new ForwarderWrapper(forwarderContract, env.provider);
await forwarderWrapper.approveMakerAssetProxyAsync(defaultOrderParams.makerAssetData, { from: takerAddress });
await forwarderWrapper.approveMakerAssetProxyAsync(defaultOrderParams.makerAssetData, {
from: takerAddress,
});
erc20Wrapper.addTokenOwnerAddress(forwarderContract.address);
// Set up factories
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
forwarderTestFactory = new ForwarderTestFactory(
exchangeWrapper,
forwarderWrapper,
@ -146,16 +169,18 @@ blockchainTests(ContractName.Forwarder, env => {
forwarderContract.address,
makerAddress,
takerAddress,
protocolFeeCollector.address,
orderFeeRecipientAddress,
forwarderFeeRecipientAddress,
weth.address,
gasPrice,
GAS_PRICE,
PROTOCOL_FEE_MULTIPLIER,
);
});
blockchainTests.resets('constructor', () => {
it('should revert if assetProxy is unregistered', async () => {
const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
env.provider,
env.txDefaults,
@ -168,7 +193,7 @@ blockchainTests(ContractName.Forwarder, env => {
env.provider,
env.txDefaults,
{},
exchangeInstance.address,
exchange.address,
wethAssetData,
) as any) as sendTransactionResult;
@ -178,7 +203,7 @@ blockchainTests(ContractName.Forwarder, env => {
blockchainTests.resets('marketSellOrdersWithEth without extra fees', () => {
it('should fill a single order without a taker fee', async () => {
const orderWithoutFee = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([orderWithoutFee], 0.78, erc20Token);
await forwarderTestFactory.marketSellTestAsync([orderWithoutFee], 0.78, [erc20Token]);
});
it('should fill multiple orders without taker fees', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync();
@ -187,14 +212,14 @@ blockchainTests(ContractName.Forwarder, env => {
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(21, DECIMALS_DEFAULT),
});
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketSellTestAsync(orders, 1.51, erc20Token);
await forwarderTestFactory.marketSellTestAsync(orders, 1.51, [erc20Token]);
});
it('should fill a single order with a percentage fee', async () => {
const orderWithPercentageFee = await orderFactory.newSignedOrderAsync({
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
await forwarderTestFactory.marketSellTestAsync([orderWithPercentageFee], 0.58, erc20Token);
await forwarderTestFactory.marketSellTestAsync([orderWithPercentageFee], 0.58, [erc20Token]);
});
it('should fill multiple orders with percentage fees', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync({
@ -208,7 +233,7 @@ blockchainTests(ContractName.Forwarder, env => {
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketSellTestAsync(orders, 1.34, erc20Token);
await forwarderTestFactory.marketSellTestAsync(orders, 1.34, [erc20Token]);
});
it('should fail to fill an order with a percentage fee if the asset proxy is not yet approved', async () => {
const unapprovedAsset = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
@ -223,7 +248,7 @@ blockchainTests(ContractName.Forwarder, env => {
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
// Execute test case
tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
value: ethValue,
from: takerAddress,
});
@ -231,7 +256,7 @@ blockchainTests(ContractName.Forwarder, env => {
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalEthSpent = gasPrice.times(tx.gasUsed);
const totalEthSpent = GAS_PRICE.times(tx.gasUsed);
// Validate test case
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
@ -255,7 +280,7 @@ blockchainTests(ContractName.Forwarder, env => {
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketSellTestAsync([orderWithWethFee], 0.13, erc20Token);
await forwarderTestFactory.marketSellTestAsync([orderWithWethFee], 0.13, [erc20Token]);
});
it('should fill multiple orders with WETH fees', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync({
@ -269,23 +294,23 @@ blockchainTests(ContractName.Forwarder, env => {
takerFeeAssetData: wethAssetData,
});
const orders = [firstOrder, secondOrderWithWethFee];
await forwarderTestFactory.marketSellTestAsync(orders, 1.25, erc20Token);
await forwarderTestFactory.marketSellTestAsync(orders, 1.25, [erc20Token]);
});
it('should refund remaining ETH if amount is greater than takerAssetAmount', async () => {
const order = await orderFactory.newSignedOrderAsync();
const ethValue = order.takerAssetAmount.plus(2);
const ethValue = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(2);
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
value: ethValue,
from: takerAddress,
});
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const totalEthSpent = order.takerAssetAmount.plus(gasPrice.times(tx.gasUsed));
const totalEthSpent = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(GAS_PRICE.times(tx.gasUsed));
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
});
it('should fail to fill orders with mismatched makerAssetData', async () => {
it('should fill orders with different makerAssetData', async () => {
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
const firstOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: firstOrderMakerAssetData,
@ -295,16 +320,10 @@ blockchainTests(ContractName.Forwarder, env => {
const secondOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: secondOrderMakerAssetData,
});
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
const orders = [firstOrder, secondOrder];
const revertError = new ForwarderRevertErrors.MakerAssetMismatchError(
firstOrderMakerAssetData,
secondOrderMakerAssetData,
);
await forwarderTestFactory.marketSellTestAsync(orders, 2, erc20Token, {
revertError,
});
await forwarderTestFactory.marketSellTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
});
it('should fail to fill an order with a fee denominated in an asset other than makerAsset or WETH', async () => {
const makerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
@ -317,14 +336,14 @@ blockchainTests(ContractName.Forwarder, env => {
});
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
await forwarderTestFactory.marketSellTestAsync([order], 0.5, erc20Token, {
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
revertError,
});
});
it('should fill a partially-filled order without a taker fee', async () => {
const order = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([order], 0.3, erc20Token);
await forwarderTestFactory.marketSellTestAsync([order], 0.8, erc20Token);
await forwarderTestFactory.marketSellTestAsync([order], 0.3, [erc20Token]);
await forwarderTestFactory.marketSellTestAsync([order], 0.8, [erc20Token]);
});
it('should skip over an order with an invalid maker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
@ -332,7 +351,7 @@ blockchainTests(ContractName.Forwarder, env => {
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an order with an invalid taker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
@ -340,7 +359,7 @@ blockchainTests(ContractName.Forwarder, env => {
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an expired order', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
@ -349,21 +368,21 @@ blockchainTests(ContractName.Forwarder, env => {
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([expiredOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketSellTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a fully filled order', async () => {
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder], 1, erc20Token);
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder], 1, [erc20Token]);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a cancelled order', async () => {
const cancelledOrder = await orderFactory.newSignedOrderAsync();
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([cancelledOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketSellTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
});
});
blockchainTests.resets('marketSellOrdersWithEth with extra fees', () => {
@ -372,7 +391,7 @@ blockchainTests(ContractName.Forwarder, env => {
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(157, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(36, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketSellTestAsync([order], 0.67, erc20Token, {
await forwarderTestFactory.marketSellTestAsync([order], 0.67, [erc20Token], {
forwarderFeePercentage: new BigNumber(2),
});
});
@ -383,7 +402,7 @@ blockchainTests(ContractName.Forwarder, env => {
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage),
);
await forwarderTestFactory.marketSellTestAsync([order], 0.5, erc20Token, {
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
forwarderFeePercentage,
revertError,
});
@ -395,7 +414,7 @@ blockchainTests(ContractName.Forwarder, env => {
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(131, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(20, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.62, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([order], 0.62, [erc20Token]);
});
it('should buy the exact amount of makerAsset in multiple orders', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync();
@ -404,14 +423,29 @@ blockchainTests(ContractName.Forwarder, env => {
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
});
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketBuyTestAsync(orders, 1.96, erc20Token);
await forwarderTestFactory.marketBuyTestAsync(orders, 1.96, [erc20Token]);
});
it('should buy exactly makerAssetBuyAmount in orders with different makerAssetData', async () => {
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
const firstOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: firstOrderMakerAssetData,
});
const secondOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
const secondOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: secondOrderMakerAssetData,
});
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketBuyTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
});
it('should buy the exact amount of makerAsset and return excess ETH', async () => {
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(80, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.57, erc20Token, {
await forwarderTestFactory.marketBuyTestAsync([order], 0.57, [erc20Token], {
ethValueAdjustment: 2,
});
});
@ -422,7 +456,7 @@ blockchainTests(ContractName.Forwarder, env => {
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.38, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([order], 0.38, [erc20Token]);
});
it('should buy the exact amount of makerAsset from a single order with a percentage fee', async () => {
const order = await orderFactory.newSignedOrderAsync({
@ -431,7 +465,7 @@ blockchainTests(ContractName.Forwarder, env => {
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.52, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([order], 0.52, [erc20Token]);
});
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount', async () => {
const order = await orderFactory.newSignedOrderAsync();
@ -440,7 +474,7 @@ blockchainTests(ContractName.Forwarder, env => {
constants.ZERO_AMOUNT,
);
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
ethValueAdjustment: -2,
revertError,
});
@ -452,7 +486,7 @@ blockchainTests(ContractName.Forwarder, env => {
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketBuyTestAsync([erc721Order], 1, erc721Token, {
await forwarderTestFactory.marketBuyTestAsync([erc721Order], 1, [erc721Token], {
makerAssetId,
});
});
@ -464,7 +498,7 @@ blockchainTests(ContractName.Forwarder, env => {
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketBuyTestAsync([erc721orderWithWethFee], 1, erc721Token, {
await forwarderTestFactory.marketBuyTestAsync([erc721orderWithWethFee], 1, [erc721Token], {
makerAssetId,
});
});
@ -479,14 +513,14 @@ blockchainTests(ContractName.Forwarder, env => {
});
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
revertError,
});
});
it('should fill a partially-filled order without a taker fee', async () => {
const order = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([order], 0.3, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([order], 0.8, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([order], 0.3, [erc20Token]);
await forwarderTestFactory.marketBuyTestAsync([order], 0.8, [erc20Token]);
});
it('should skip over an order with an invalid maker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
@ -494,7 +528,7 @@ blockchainTests(ContractName.Forwarder, env => {
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an order with an invalid taker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
@ -502,7 +536,7 @@ blockchainTests(ContractName.Forwarder, env => {
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an expired order', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
@ -511,23 +545,23 @@ blockchainTests(ContractName.Forwarder, env => {
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([expiredOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a fully filled order', async () => {
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder], 1, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder], 1, [erc20Token]);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a cancelled order', async () => {
const cancelledOrder = await orderFactory.newSignedOrderAsync();
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([cancelledOrder, fillableOrder], 1.5, erc20Token);
await forwarderTestFactory.marketBuyTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
});
it('Should buy slightly greater MakerAsset when exchange rate is rounded', async () => {
it('Should buy slightly greater makerAsset when exchange rate is rounded', async () => {
// The 0x Protocol contracts round the exchange rate in favor of the Maker.
// In this case, the taker must round up how much they're going to spend, which
// in turn increases the amount of MakerAsset being purchased.
@ -553,13 +587,14 @@ blockchainTests(ContractName.Forwarder, env => {
});
const desiredMakerAssetFillAmount = new BigNumber('5');
const makerAssetFillAmount = new BigNumber('6');
const ethValue = new BigNumber('4');
const primaryTakerAssetFillAmount = new BigNumber('4');
const ethValue = primaryTakerAssetFillAmount.plus(PROTOCOL_FEE);
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
// Execute test case
tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
value: ethValue,
from: takerAddress,
});
@ -567,8 +602,7 @@ blockchainTests(ContractName.Forwarder, env => {
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
const newBalances = await erc20Wrapper.getBalancesAsync();
const primaryTakerAssetFillAmount = ethValue;
const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
const totalEthSpent = ethValue.plus(GAS_PRICE.times(tx.gasUsed));
// Validate test case
expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
@ -588,6 +622,8 @@ blockchainTests(ContractName.Forwarder, env => {
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
it('Should buy slightly greater MakerAsset when exchange rate is rounded (Regression Test)', async () => {
// Disable protocol fees for regression test
await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(constants.NULL_ADDRESS);
// Order taken from a transaction on mainnet that failed due to a rounding error.
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: new BigNumber('268166666666666666666'),
@ -608,7 +644,7 @@ blockchainTests(ContractName.Forwarder, env => {
.times(order.makerAssetAmount)
.dividedToIntegerBy(order.takerAssetAmount);
// Execute test case
tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
value: ethValue,
from: takerAddress,
});
@ -617,7 +653,7 @@ blockchainTests(ContractName.Forwarder, env => {
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
const newBalances = await erc20Wrapper.getBalancesAsync();
const primaryTakerAssetFillAmount = ethValue;
const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
const totalEthSpent = primaryTakerAssetFillAmount.plus(GAS_PRICE.times(tx.gasUsed));
// Validate test case
expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
@ -643,7 +679,7 @@ blockchainTests(ContractName.Forwarder, env => {
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(125, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.33, erc20Token, {
await forwarderTestFactory.marketBuyTestAsync([order], 0.33, [erc20Token], {
forwarderFeePercentage: new BigNumber(2),
});
});
@ -652,7 +688,7 @@ blockchainTests(ContractName.Forwarder, env => {
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, new BigNumber(6)),
);
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
forwarderFeePercentage: new BigNumber(6),
revertError,
});
@ -661,14 +697,14 @@ blockchainTests(ContractName.Forwarder, env => {
const order = await orderFactory.newSignedOrderAsync();
const forwarderFeePercentage = new BigNumber(2);
const ethFee = ForwarderTestFactory.getPercentageOfValue(
order.takerAssetAmount.times(0.5),
order.takerAssetAmount.times(0.5).plus(PROTOCOL_FEE),
forwarderFeePercentage,
);
const revertError = new ForwarderRevertErrors.InsufficientEthForFeeError(ethFee, ethFee.minus(1));
// -2 to compensate for the extra 1 wei added in ForwarderTestFactory to account for rounding
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
ethValueAdjustment: -2,
forwarderFeePercentage,
revertError,

View File

@ -2,118 +2,36 @@ import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
import { ExchangeWrapper } from '@0x/contracts-exchange';
import { chaiSetup, constants, ERC20BalancesByOwner, OrderStatus, web3Wrapper } from '@0x/contracts-test-utils';
import { constants, ERC20BalancesByOwner, expect, OrderStatus, web3Wrapper } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { OrderInfo, SignedOrder } from '@0x/types';
import { BigNumber, RevertError } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { ForwarderWrapper } from './forwarder_wrapper';
chaiSetup.configure();
const expect = chai.expect;
// Necessary bookkeeping to validate Forwarder results
interface ForwarderFillState {
takerAssetFillAmount: BigNumber;
makerAssetFillAmount: BigNumber;
makerAssetFillAmount: {
[makerAssetData: string]: BigNumber;
};
protocolFees: BigNumber;
wethFees: BigNumber;
percentageFees: BigNumber;
percentageFees: {
[makerAssetData: string]: BigNumber;
};
maxOversoldWeth: BigNumber;
maxOverboughtMakerAsset: BigNumber;
}
// Simulates filling some orders via the Forwarder contract. For example, if
// orders = [A, B, C, D] and fractionalNumberOfOrdersToFill = 2.3, then
// we simulate A and B being completely filled, and 0.3 * C being filled.
function computeExpectedResults(
orders: SignedOrder[],
ordersInfoBefore: OrderInfo[],
fractionalNumberOfOrdersToFill: number,
): ForwarderFillState {
const currentState = {
takerAssetFillAmount: constants.ZERO_AMOUNT,
makerAssetFillAmount: constants.ZERO_AMOUNT,
wethFees: constants.ZERO_AMOUNT,
percentageFees: constants.ZERO_AMOUNT,
maxOversoldWeth: constants.ZERO_AMOUNT,
maxOverboughtMakerAsset: constants.ZERO_AMOUNT,
};
let remainingOrdersToFill = fractionalNumberOfOrdersToFill;
for (const [i, order] of orders.entries()) {
if (remainingOrdersToFill === 0) {
break;
}
if (ordersInfoBefore[i].orderStatus !== OrderStatus.Fillable) {
// If the order is not fillable, skip over it but still count it towards fractionalNumberOfOrdersToFill
remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
continue;
}
let makerAssetAmount;
let takerAssetAmount;
let takerFee;
if (remainingOrdersToFill < 1) {
makerAssetAmount = order.makerAssetAmount.times(remainingOrdersToFill).integerValue();
takerAssetAmount = order.takerAssetAmount.times(remainingOrdersToFill).integerValue();
takerFee = order.takerFee.times(remainingOrdersToFill).integerValue();
// Up to 1 wei worth of WETH will be oversold on the last order due to rounding
currentState.maxOversoldWeth = new BigNumber(1);
// Equivalently, up to 1 wei worth of maker asset will be overbought
currentState.maxOverboughtMakerAsset = currentState.maxOversoldWeth
.times(order.makerAssetAmount)
.dividedToIntegerBy(order.takerAssetAmount);
} else {
makerAssetAmount = order.makerAssetAmount;
takerAssetAmount = order.takerAssetAmount;
takerFee = order.takerFee;
}
// Accounting for partially filled orders
// As with unfillable orders, these still count as 1 towards fractionalNumberOfOrdersToFill
const takerAssetFilled = ordersInfoBefore[i].orderTakerAssetFilledAmount;
const makerAssetFilled = takerAssetFilled
.times(order.makerAssetAmount)
.dividedToIntegerBy(order.takerAssetAmount);
takerAssetAmount = BigNumber.max(takerAssetAmount.minus(takerAssetFilled), constants.ZERO_AMOUNT);
makerAssetAmount = BigNumber.max(makerAssetAmount.minus(makerAssetFilled), constants.ZERO_AMOUNT);
currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
currentState.makerAssetFillAmount = currentState.makerAssetFillAmount.plus(makerAssetAmount);
if (order.takerFeeAssetData === order.makerAssetData) {
currentState.percentageFees = currentState.percentageFees.plus(takerFee);
} else if (order.takerFeeAssetData === order.takerAssetData) {
currentState.wethFees = currentState.wethFees.plus(takerFee);
}
remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
}
return currentState;
}
// Since bignumber is not compatible with chai's within
function expectBalanceWithin(balance: BigNumber, low: BigNumber, high: BigNumber): void {
expect(balance).to.be.bignumber.gte(low);
expect(balance).to.be.bignumber.lte(high);
function expectBalanceWithin(balance: BigNumber, low: BigNumber, high: BigNumber, message?: string): void {
expect(balance, message).to.be.bignumber.gte(low);
expect(balance, message).to.be.bignumber.lte(high);
}
export class ForwarderTestFactory {
private readonly _exchangeWrapper: ExchangeWrapper;
private readonly _forwarderWrapper: ForwarderWrapper;
private readonly _erc20Wrapper: ERC20Wrapper;
private readonly _forwarderAddress: string;
private readonly _makerAddress: string;
private readonly _takerAddress: string;
private readonly _orderFeeRecipientAddress: string;
private readonly _forwarderFeeRecipientAddress: string;
private readonly _wethAddress: string;
private readonly _gasPrice: BigNumber;
public static getPercentageOfValue(value: BigNumber, percentage: BigNumber): BigNumber {
const numerator = constants.PERCENTAGE_DENOMINATOR.times(percentage).dividedToIntegerBy(100);
const newValue = value.times(numerator).dividedToIntegerBy(constants.PERCENTAGE_DENOMINATOR);
@ -121,33 +39,24 @@ export class ForwarderTestFactory {
}
constructor(
exchangeWrapper: ExchangeWrapper,
forwarderWrapper: ForwarderWrapper,
erc20Wrapper: ERC20Wrapper,
forwarderAddress: string,
makerAddress: string,
takerAddress: string,
orderFeeRecipientAddress: string,
forwarderFeeRecipientAddress: string,
wethAddress: string,
gasPrice: BigNumber,
) {
this._exchangeWrapper = exchangeWrapper;
this._forwarderWrapper = forwarderWrapper;
this._erc20Wrapper = erc20Wrapper;
this._forwarderAddress = forwarderAddress;
this._makerAddress = makerAddress;
this._takerAddress = takerAddress;
this._orderFeeRecipientAddress = orderFeeRecipientAddress;
this._forwarderFeeRecipientAddress = forwarderFeeRecipientAddress;
this._wethAddress = wethAddress;
this._gasPrice = gasPrice;
}
private readonly _exchangeWrapper: ExchangeWrapper,
private readonly _forwarderWrapper: ForwarderWrapper,
private readonly _erc20Wrapper: ERC20Wrapper,
private readonly _forwarderAddress: string,
private readonly _makerAddress: string,
private readonly _takerAddress: string,
private readonly _protocolFeeCollectorAddress: string,
private readonly _orderFeeRecipientAddress: string,
private readonly _forwarderFeeRecipientAddress: string,
private readonly _wethAddress: string,
private readonly _gasPrice: BigNumber,
private readonly _protocolFeeMultiplier: BigNumber,
) {}
public async marketBuyTestAsync(
orders: SignedOrder[],
fractionalNumberOfOrdersToFill: number,
makerAssetContract: DummyERC20TokenContract | DummyERC721TokenContract,
makerAssetContracts: Array<DummyERC20TokenContract | DummyERC721TokenContract>,
options: {
ethValueAdjustment?: number; // Used to provided insufficient/excess ETH
forwarderFeePercentage?: BigNumber;
@ -167,25 +76,28 @@ export class ForwarderTestFactory {
const ordersInfoBefore = await Promise.all(orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)));
const orderStatusesBefore = ordersInfoBefore.map(orderInfo => orderInfo.orderStatus);
const expectedResults = computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(
expectedResults.takerAssetFillAmount,
forwarderFeePercentage,
);
const expectedResults = this._computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
const wethSpent = expectedResults.takerAssetFillAmount
.plus(expectedResults.protocolFees)
.plus(expectedResults.wethFees)
.plus(expectedResults.maxOversoldWeth);
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(wethSpent, forwarderFeePercentage);
const ethValue = wethSpent.plus(ethSpentOnForwarderFee).plus(ethValueAdjustment);
const feePercentage = ForwarderTestFactory.getPercentageOfValue(
constants.PERCENTAGE_DENOMINATOR,
forwarderFeePercentage,
);
const ethValue = expectedResults.takerAssetFillAmount
.plus(expectedResults.wethFees)
.plus(expectedResults.maxOversoldWeth)
.plus(ethSpentOnForwarderFee)
.plus(ethValueAdjustment);
const totalMakerAssetFillAmount = Object.values(expectedResults.makerAssetFillAmount).reduce((prev, current) =>
prev.plus(current),
);
const totalPercentageFees = Object.values(expectedResults.percentageFees).reduce((prev, current) =>
prev.plus(current),
);
const tx = this._forwarderWrapper.marketBuyOrdersWithEthAsync(
orders,
expectedResults.makerAssetFillAmount.minus(expectedResults.percentageFees),
totalMakerAssetFillAmount.minus(totalPercentageFees),
{
value: ethValue,
from: this._takerAddress,
@ -210,7 +122,7 @@ export class ForwarderTestFactory {
expectedResults,
takerEthBalanceBefore,
erc20Balances,
makerAssetContract,
makerAssetContracts,
{
forwarderFeePercentage,
forwarderFeeRecipientEthBalanceBefore,
@ -223,7 +135,7 @@ export class ForwarderTestFactory {
public async marketSellTestAsync(
orders: SignedOrder[],
fractionalNumberOfOrdersToFill: number,
makerAssetContract: DummyERC20TokenContract,
makerAssetContracts: DummyERC20TokenContract[],
options: {
forwarderFeePercentage?: BigNumber;
revertError?: RevertError;
@ -240,21 +152,20 @@ export class ForwarderTestFactory {
const ordersInfoBefore = await Promise.all(orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)));
const orderStatusesBefore = ordersInfoBefore.map(orderInfo => orderInfo.orderStatus);
const expectedResults = computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(
expectedResults.takerAssetFillAmount,
forwarderFeePercentage,
);
const expectedResults = this._computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
const wethSpent = expectedResults.takerAssetFillAmount
.plus(expectedResults.protocolFees)
.plus(expectedResults.wethFees)
.plus(expectedResults.maxOversoldWeth);
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(wethSpent, forwarderFeePercentage);
const ethValue = wethSpent.plus(ethSpentOnForwarderFee);
const feePercentage = ForwarderTestFactory.getPercentageOfValue(
constants.PERCENTAGE_DENOMINATOR,
forwarderFeePercentage,
);
const ethValue = expectedResults.takerAssetFillAmount
.plus(expectedResults.wethFees)
.plus(expectedResults.maxOversoldWeth)
.plus(ethSpentOnForwarderFee);
const tx = this._forwarderWrapper.marketSellOrdersWithEthAsync(
orders,
{
@ -268,10 +179,9 @@ export class ForwarderTestFactory {
await expect(tx).to.revertWith(options.revertError);
} else {
const gasUsed = (await tx).gasUsed;
const ordersInfoAfter = await Promise.all(
orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)),
const orderStatusesAfter = await Promise.all(
orders.map(async order => (await this._exchangeWrapper.getOrderInfoAsync(order)).orderStatus),
);
const orderStatusesAfter = ordersInfoAfter.map(orderInfo => orderInfo.orderStatus);
await this._checkResultsAsync(
fractionalNumberOfOrdersToFill,
@ -281,7 +191,7 @@ export class ForwarderTestFactory {
expectedResults,
takerEthBalanceBefore,
erc20Balances,
makerAssetContract,
makerAssetContracts,
{
forwarderFeePercentage,
forwarderFeeRecipientEthBalanceBefore,
@ -297,27 +207,39 @@ export class ForwarderTestFactory {
makerAssetContract: DummyERC20TokenContract,
): void {
const makerAssetAddress = makerAssetContract.address;
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerAssetAddress);
const {
maxOverboughtMakerAsset,
makerAssetFillAmount: { [makerAssetData]: makerAssetFillAmount },
percentageFees: { [makerAssetData]: percentageFees },
} = expectedResults;
expectBalanceWithin(
newBalances[this._makerAddress][makerAssetAddress],
oldBalances[this._makerAddress][makerAssetAddress]
.minus(expectedResults.makerAssetFillAmount)
.minus(expectedResults.maxOverboughtMakerAsset),
oldBalances[this._makerAddress][makerAssetAddress].minus(expectedResults.makerAssetFillAmount),
.minus(makerAssetFillAmount)
.minus(maxOverboughtMakerAsset),
oldBalances[this._makerAddress][makerAssetAddress].minus(makerAssetFillAmount),
'Maker makerAsset balance',
);
expectBalanceWithin(
newBalances[this._takerAddress][makerAssetAddress],
oldBalances[this._takerAddress][makerAssetAddress].plus(makerAssetFillAmount).minus(percentageFees),
oldBalances[this._takerAddress][makerAssetAddress]
.plus(expectedResults.makerAssetFillAmount)
.minus(expectedResults.percentageFees),
oldBalances[this._takerAddress][makerAssetAddress]
.plus(expectedResults.makerAssetFillAmount)
.minus(expectedResults.percentageFees)
.plus(expectedResults.maxOverboughtMakerAsset),
.plus(makerAssetFillAmount)
.minus(percentageFees)
.plus(maxOverboughtMakerAsset),
'Taker makerAsset balance',
);
expect(newBalances[this._orderFeeRecipientAddress][makerAssetAddress]).to.be.bignumber.equal(
oldBalances[this._orderFeeRecipientAddress][makerAssetAddress].plus(expectedResults.percentageFees),
);
expect(newBalances[this._forwarderAddress][makerAssetAddress]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(
newBalances[this._orderFeeRecipientAddress][makerAssetAddress],
'Order fee recipient makerAsset balance',
).to.be.bignumber.equal(oldBalances[this._orderFeeRecipientAddress][makerAssetAddress].plus(percentageFees));
expect(
newBalances[this._forwarderAddress][makerAssetAddress],
'Forwarder contract makerAsset balance',
).to.be.bignumber.equal(constants.ZERO_AMOUNT);
}
private async _checkResultsAsync(
@ -328,7 +250,7 @@ export class ForwarderTestFactory {
expectedResults: ForwarderFillState,
takerEthBalanceBefore: BigNumber,
erc20Balances: ERC20BalancesByOwner,
makerAssetContract: DummyERC20TokenContract | DummyERC721TokenContract,
makerAssetContracts: Array<DummyERC20TokenContract | DummyERC721TokenContract>,
options: {
forwarderFeePercentage?: BigNumber;
forwarderFeeRecipientEthBalanceBefore?: BigNumber;
@ -340,18 +262,17 @@ export class ForwarderTestFactory {
if (fractionalNumberOfOrdersToFill >= i + 1 && orderStatusesBefore[i] === OrderStatus.Fillable) {
expectedOrderStatus = OrderStatus.FullyFilled;
}
expect(orderStatus).to.equal(expectedOrderStatus);
expect(orderStatus, ` Order ${i} status`).to.equal(expectedOrderStatus);
}
const wethSpent = expectedResults.takerAssetFillAmount
.plus(expectedResults.protocolFees)
.plus(expectedResults.wethFees);
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(
expectedResults.takerAssetFillAmount,
wethSpent,
options.forwarderFeePercentage || constants.ZERO_AMOUNT,
);
const totalEthSpent = expectedResults.takerAssetFillAmount
.plus(expectedResults.wethFees)
.plus(ethSpentOnForwarderFee)
.plus(this._gasPrice.times(gasUsed));
const totalEthSpent = wethSpent.plus(ethSpentOnForwarderFee).plus(this._gasPrice.times(gasUsed));
const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(this._takerAddress);
const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(this._forwarderAddress);
@ -361,21 +282,24 @@ export class ForwarderTestFactory {
takerEthBalanceAfter,
takerEthBalanceBefore.minus(totalEthSpent).minus(expectedResults.maxOversoldWeth),
takerEthBalanceBefore.minus(totalEthSpent),
'Taker ETH balance',
);
if (options.forwarderFeeRecipientEthBalanceBefore !== undefined) {
const fowarderFeeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(
this._forwarderFeeRecipientAddress,
);
expect(fowarderFeeRecipientEthBalanceAfter).to.be.bignumber.equal(
expect(fowarderFeeRecipientEthBalanceAfter, 'Forwarder fee recipient ETH balance').to.be.bignumber.equal(
options.forwarderFeeRecipientEthBalanceBefore.plus(ethSpentOnForwarderFee),
);
}
if (makerAssetContract instanceof DummyERC20TokenContract) {
this._checkErc20Balances(erc20Balances, newBalances, expectedResults, makerAssetContract);
} else if (options.makerAssetId !== undefined) {
const newOwner = await makerAssetContract.ownerOf.callAsync(options.makerAssetId);
expect(newOwner).to.be.bignumber.equal(this._takerAddress);
for (const makerAssetContract of makerAssetContracts) {
if (makerAssetContract instanceof DummyERC20TokenContract) {
this._checkErc20Balances(erc20Balances, newBalances, expectedResults, makerAssetContract);
} else if (options.makerAssetId !== undefined) {
const newOwner = await makerAssetContract.ownerOf.callAsync(options.makerAssetId);
expect(newOwner, 'New ERC721 owner').to.be.bignumber.equal(this._takerAddress);
}
}
expectBalanceWithin(
@ -384,12 +308,108 @@ export class ForwarderTestFactory {
erc20Balances[this._makerAddress][this._wethAddress]
.plus(expectedResults.takerAssetFillAmount)
.plus(expectedResults.maxOversoldWeth),
'Maker WETH balance',
);
expect(newBalances[this._orderFeeRecipientAddress][this._wethAddress]).to.be.bignumber.equal(
expect(
newBalances[this._orderFeeRecipientAddress][this._wethAddress],
'Order fee recipient WETH balance',
).to.be.bignumber.equal(
erc20Balances[this._orderFeeRecipientAddress][this._wethAddress].plus(expectedResults.wethFees),
);
expect(newBalances[this._forwarderAddress][this._wethAddress]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(
newBalances[this._forwarderAddress][this._wethAddress],
'Forwarder contract WETH balance',
).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
}
// Simulates filling some orders via the Forwarder contract. For example, if
// orders = [A, B, C, D] and fractionalNumberOfOrdersToFill = 2.3, then
// we simulate A and B being completely filled, and 0.3 * C being filled.
private _computeExpectedResults(
orders: SignedOrder[],
ordersInfoBefore: OrderInfo[],
fractionalNumberOfOrdersToFill: number,
): ForwarderFillState {
const currentState: ForwarderFillState = {
takerAssetFillAmount: constants.ZERO_AMOUNT,
makerAssetFillAmount: {},
protocolFees: constants.ZERO_AMOUNT,
wethFees: constants.ZERO_AMOUNT,
percentageFees: {},
maxOversoldWeth: constants.ZERO_AMOUNT,
maxOverboughtMakerAsset: constants.ZERO_AMOUNT,
};
let remainingOrdersToFill = fractionalNumberOfOrdersToFill;
for (const [i, order] of orders.entries()) {
if (currentState.makerAssetFillAmount[order.makerAssetData] === undefined) {
currentState.makerAssetFillAmount[order.makerAssetData] = new BigNumber(0);
}
if (currentState.percentageFees[order.makerAssetData] === undefined) {
currentState.percentageFees[order.makerAssetData] = new BigNumber(0);
}
if (remainingOrdersToFill === 0) {
break;
}
if (ordersInfoBefore[i].orderStatus !== OrderStatus.Fillable) {
// If the order is not fillable, skip over it but still count it towards fractionalNumberOfOrdersToFill
remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
continue;
}
let makerAssetAmount;
let takerAssetAmount;
let takerFee;
if (remainingOrdersToFill < 1) {
makerAssetAmount = order.makerAssetAmount.times(remainingOrdersToFill).integerValue();
takerAssetAmount = order.takerAssetAmount.times(remainingOrdersToFill).integerValue();
takerFee = order.takerFee.times(remainingOrdersToFill).integerValue();
// Up to 1 wei worth of WETH will be oversold on the last order due to rounding
currentState.maxOversoldWeth = new BigNumber(1);
// Equivalently, up to 1 wei worth of maker asset will be overbought
currentState.maxOverboughtMakerAsset = currentState.maxOversoldWeth
.times(order.makerAssetAmount)
.dividedToIntegerBy(order.takerAssetAmount);
} else {
makerAssetAmount = order.makerAssetAmount;
takerAssetAmount = order.takerAssetAmount;
takerFee = order.takerFee;
}
// Accounting for partially filled orders
// As with unfillable orders, these still count as 1 towards fractionalNumberOfOrdersToFill
const takerAssetFilled = ordersInfoBefore[i].orderTakerAssetFilledAmount;
const makerAssetFilled = takerAssetFilled
.times(order.makerAssetAmount)
.dividedToIntegerBy(order.takerAssetAmount);
takerAssetAmount = BigNumber.max(takerAssetAmount.minus(takerAssetFilled), constants.ZERO_AMOUNT);
makerAssetAmount = BigNumber.max(makerAssetAmount.minus(makerAssetFilled), constants.ZERO_AMOUNT);
currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
currentState.makerAssetFillAmount[order.makerAssetData] = currentState.makerAssetFillAmount[
order.makerAssetData
].plus(makerAssetAmount);
if (this._protocolFeeCollectorAddress !== constants.NULL_ADDRESS) {
currentState.protocolFees = currentState.protocolFees.plus(
this._gasPrice.times(this._protocolFeeMultiplier),
);
}
if (order.takerFeeAssetData === order.makerAssetData) {
currentState.percentageFees[order.makerAssetData] = currentState.percentageFees[
order.makerAssetData
].plus(takerFee);
} else if (order.takerFeeAssetData === order.takerAssetData) {
currentState.wethFees = currentState.wethFees.plus(takerFee);
}
remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
}
return currentState;
}
}

View File

@ -12,7 +12,8 @@
"generated-artifacts/MixinAssets.json",
"generated-artifacts/MixinExchangeWrapper.json",
"generated-artifacts/MixinForwarderCore.json",
"generated-artifacts/MixinWeth.json"
"generated-artifacts/MixinWeth.json",
"generated-artifacts/TestProtocolFeeCollector.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -1,6 +1,6 @@
[
{
"version": "3.1.0",
"version": "3.1.0-beta.0",
"changes": [
{
"note": "Break up `LibEIP712` into reusable components",
@ -106,7 +106,8 @@
"note": "Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields.",
"pr": 2075
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,35 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.0-beta.0 - _October 3, 2019_
* Break up `LibEIP712` into reusable components (#1742)
* Add `chainId` to EIP712 domain schema (#1742)
* Rename `verifyingContract` to `verifyingContractAddress` in domain schema (#1742)
* Add LibZeroExTransaction contract (#1753)
* Add verifyingContractIfExists arg to LibEIP712ExchangeDomain constructor (#1753)
* Remove LibEIP712ExchangeDomainConstants and LibEIP712 contracts (#1753)
* Add `LibExchangeRichErrorDecoder` contract. (#1790)
* Break out types/interaces from `MExchangeRichErrors` into `MExchangeRichErrorTypes`. (#1790)
* Reorder some revert error parameters for consistency (#1790)
* Add new `Order` fields for arbitrary fee tokens (ZEIP-28). (#1819)
* Remove `LibAbiEncoder` and `LibConstants`. (#1819)
* Add `generate-exchange-selectors` package script. (#1819)
* Add `expirationTimeSeconds` to `ZeroExTransaction` struct (#1823)
* Add reference functions for `LibMath` and `LibFillResults` (#2031)
* Move in revamped `LibMath` tests from the `contracts-exchange` package. (#2031)
* Move in revamped `LibFillResults` tests from the `contracts-exchange` package. (#2031)
* Remove unecessary zero-denominator checks in `LibMath`. (#2031)
* Fix coverage hooks. (#2031)
* Regenerate selectors. (#2042)
* Convert `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries (#2055)
* Remove `LibExchangeSelectors` (#2055)
* Add `LibExchangeRichErrors` (#2055)
* Add `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults` (#2055)
* Remove `_hashEIP712ExchangeMessage` from `LibEIP712ExchangeDomain` (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields. (#2075)
## v3.0.8 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "3.0.8",
"version": "3.1.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -48,12 +48,12 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
"devDependencies": {
"@0x/subproviders": "^5.0.1",
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/subproviders": "^5.1.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -73,14 +73,14 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-utils": "^3.2.4",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@ -1,6 +1,6 @@
[
{
"version": "3.0.0",
"version": "2.2.0-beta.0",
"changes": [
{
"note": "Use new/cheaper reentrancy guard/mutex",
@ -190,7 +190,8 @@
"note": "Overridden functions in `ReentrancyTester` now return sane values.",
"pr": 2075
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,56 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.2.0-beta.0 - _October 3, 2019_
* Use new/cheaper reentrancy guard/mutex (#1699)
* Update domain separator (#1742)
* Refactor `executeTransaction` to take `ZeroExTransaction` struct as input (#1753)
* Refactor example contracts that use `executeTransaction` (#1753)
* Upgrade all string reverts to rich reverts (#1761)
* Add support for `SignatureType.OrderValidator` for orders (#1774)
* Add support for `SignatureType.WalletOrderValidator` for orders (#1774)
* Add a `bytes` return value to `executeTransaction`, which is equal to the encoded return data of the underlying Exchange function call (#1793)
* Implement `batchExecuteTransactions` (#1793)
* Refactor preSign to be compatible with `executeTransaction` (#1793)
* Remove ZRX fees in lieu of arbitrary maker and taker fee tokens. (#1819)
* Incorporate Multi-asset and ERC1155 tests into `fillOrder` and `matchOrders` tests (#1819)
* Swap fill order from maker -> taker to taker -> maker (#1819)
* Avoid redundant transfer in `fillOrder()` and `matchOrders()` when maker/taker is the same as feeRecipient and assets are the same (#1819)
* Implement `cancelOrderNoThrow` and `batchCancelOrdersNoThrow` functions (#1827)
* `executeTransaction` will now revert if the input transaction is expired (#1832)
* Log an `TransactionExecuted` event when an `executeTransaction` call is successful (#1832)
* Return a FillResults array for batch fill variants (#1834)
* Add `MixinTransferSimulator` contract for simulating multiple transfers on-chain (#1868)
* Add `EIP1271Wallet` signature type (#1885)
* Remove `WalletOrderValidator` and `OrderValidator` signature types (#1885)
* Make the regular `Validator` signature type have EIP1271 behavior (#1885)
* Always check signature types that are validated via contract (not just on first fill). (#1885)
* Remove unecessary rich revert error types. (#1885)
* Add `IEIP1271Wallet` interface (#1885)
* Add `validatorAddress` field to `SignatureValidatorError` rich reverts (#1885)
* Make `calculateMatchedFillResults` public (#1885)
* Updated RichErrors to the library pattern (#1913)
* Rewrote _dispatchTransferFrom in Solidity (#2020)
* Add `TestIsolatedExchange` contract and `IsolatedExchangeWrapper` test class (#2031)
* Add `ReferenceFunctions` as package export. (#2031)
* Remove `TestExchangeMath.sol`. Exchange math functions are now tested in the `exchange-libs` package and reference implementations are available there as well. (#2031)
* Remove functions from `TestExchangeInternals.sol` that are no longer tested in this package. (#2031)
* Remove `_assertValidFill()` (#2031)
* Add `wrapper_unit_tests` tests and `TestWrapperFunctions` contract (#2042)
* Disallow `signerAddress == 0` in signature validation functions. (#2042)
* Update `Wallet` signature type behavior to be in line with v2.1. (#2042)
* Add (semi) automated reentrancy tests and remove manual ones (#2042)
* Refactor to use new `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries (#2055)
* Remove `LibExchangeRichErrors` and `IExchangeRichErrors` (#2055)
* Use built in selectors instead of `LibExchangeSelectors` constants (#2055)
* Move `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults` in `exchange-libs` package (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Rename `marketSellOrders` and `marketBuyOrders` back to `marketSellOrdersNoThrow` and `marketBuyOrdersNoThrow`. (#2075)
* Introduce new `marketSellOrdersFillOrKill` and `marketBuyOrdersFillOrKill` functions. (#2075)
* Use `abi.decode()` in `LibExchangeRichErrorDecoder` over `LibBytes`. (#2075)
* Overridden functions in `ReentrancyTester` now return sane values. (#2075)
## v2.1.14 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange",
"version": "2.1.14",
"version": "2.2.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -48,11 +48,13 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-multisig": "^3.2.0-beta.0",
"@0x/contracts-staking": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -71,19 +73,19 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-asset-proxy": "^2.2.8",
"@0x/contracts-erc1155": "^1.1.15",
"@0x/contracts-erc20": "^2.2.14",
"@0x/contracts-erc721": "^2.1.15",
"@0x/contracts-exchange-libs": "^3.0.8",
"@0x/contracts-utils": "^3.2.4",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc1155": "^1.2.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},

View File

@ -1,3 +1,4 @@
export * from './artifacts';
export * from './wrappers';
export * from '../test/utils';
export * from './wrapper_interfaces';

View File

@ -0,0 +1,53 @@
import { PromiseWithTransactionHash } from '@0x/base-contract';
import { BlockParam, CallData, TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
// Generated Wrapper Interfaces
export interface AssetProxyDispatcher {
registerAssetProxy: {
awaitTransactionSuccessAsync: (
assetProxy: string,
txData?: Partial<TxData>,
pollingIntervalMs?: number,
timeoutMs?: number,
) => PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>;
};
getAssetProxy: {
callAsync(assetProxyId: string, callData?: Partial<CallData>, defaultBlock?: BlockParam): Promise<string>;
};
}
export interface Authorizable extends Ownable {
addAuthorizedAddress: {
awaitTransactionSuccessAsync: (
target: string,
txData?: Partial<TxData>,
pollingIntervalMs?: number,
timeoutMs?: number,
) => PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>;
};
removeAuthorizedAddress: {
awaitTransactionSuccessAsync: (
target: string,
txData?: Partial<TxData>,
pollingIntervalMs?: number,
timeoutMs?: number,
) => PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>;
};
authorized: {
callAsync(authority: string, callData?: Partial<CallData>, defaultBlock?: BlockParam): Promise<boolean>;
};
}
export interface Ownable {
transferOwnership: {
awaitTransactionSuccessAsync: (
newOwner: string,
txData?: Partial<TxData>,
pollingIntervalMs?: number,
timeoutMs?: number,
) => PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>;
};
owner: {
callAsync(callData?: Partial<CallData>, defaultBlock?: BlockParam): Promise<string>;
};
}

View File

@ -172,7 +172,7 @@ blockchainTests.resets('Exchange core', () => {
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(staticCallProxy.address, { from: owner });
// Configure Exchange
exchangeWrapper = new ExchangeWrapper(exchange, provider);
exchangeWrapper = new ExchangeWrapper(exchange);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc1155Proxy.address, owner);

View File

@ -0,0 +1,439 @@
import {
artifacts as assetProxyArtifacts,
ERC1155ProxyContract,
ERC20ProxyContract,
ERC721ProxyContract,
MultiAssetProxyContract,
StaticCallProxyContract,
} from '@0x/contracts-asset-proxy';
import { artifacts as multisigArtifacts, AssetProxyOwnerContract } from '@0x/contracts-multisig';
import {
artifacts as stakingArtifacts,
ReadOnlyProxyContract,
StakingContract,
StakingEvents,
StakingExchangeAddedEventArgs,
StakingProxyContract,
} from '@0x/contracts-staking';
import { blockchainTests, constants, expect, filterLogsToArguments } from '@0x/contracts-test-utils';
import {
AuthorizableAuthorizedAddressAddedEventArgs,
AuthorizableAuthorizedAddressRemovedEventArgs,
AuthorizableEvents,
} from '@0x/contracts-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { TxData } from 'ethereum-types';
import {
artifacts as exchangeArtifacts,
AssetProxyDispatcher,
Authorizable,
ExchangeAssetProxyRegisteredEventArgs,
ExchangeContract,
ExchangeEvents,
ExchangeProtocolFeeCollectorAddressEventArgs,
ExchangeProtocolFeeMultiplierEventArgs,
Ownable,
} from '../../src';
// tslint:disable:no-unnecessary-type-assertion
blockchainTests('Deployment and Configuration End to End Tests', env => {
// Available Addresses
let owner: string;
// Contract Instances
let assetProxyOwner: AssetProxyOwnerContract;
let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract;
let erc1155Proxy: ERC1155ProxyContract;
let exchange: ExchangeContract;
let multiAssetProxy: MultiAssetProxyContract;
let readOnlyProxy: ReadOnlyProxyContract;
let staking: StakingContract;
let staticCallProxy: StaticCallProxyContract;
let stakingProxy: StakingProxyContract;
let stakingWrapper: StakingContract;
// TxDefaults
let txDefaults: Partial<TxData>;
// ChainId of the Exchange
let chainId: number;
// Protocol Fees
const protocolFeeMultiplier = new BigNumber(150000);
before(async () => {
// Get the chain ID.
chainId = await env.getChainIdAsync();
// Create accounts and tx defaults
[owner] = await env.getAccountAddressesAsync();
txDefaults = {
...env.txDefaults,
from: owner,
};
// Deploy AssetProxyOwner. For the purposes of this test, we will assume that
// the AssetProxyOwner does not know what destinations will be needed during
// construction.
assetProxyOwner = await AssetProxyOwnerContract.deployFrom0xArtifactAsync(
multisigArtifacts.AssetProxyOwner,
env.provider,
txDefaults,
multisigArtifacts,
[],
[],
[],
[owner],
new BigNumber(1),
constants.ZERO_AMOUNT,
);
// Deploy Exchange.
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
env.provider,
txDefaults,
exchangeArtifacts,
new BigNumber(chainId),
);
// Deploy ReadOnlyProxy.
readOnlyProxy = await ReadOnlyProxyContract.deployFrom0xArtifactAsync(
stakingArtifacts.ReadOnlyProxy,
env.provider,
txDefaults,
stakingArtifacts,
);
// Deploy Staking.
staking = await StakingContract.deployFrom0xArtifactAsync(
stakingArtifacts.Staking,
env.provider,
txDefaults,
stakingArtifacts,
);
// Deploy the staking proxy.
stakingProxy = await StakingProxyContract.deployFrom0xArtifactAsync(
stakingArtifacts.StakingProxy,
env.provider,
txDefaults,
stakingArtifacts,
staking.address,
readOnlyProxy.address,
);
// Authorize owner in the staking proxy.
await stakingProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(owner);
// Deploy the asset proxy contracts.
erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.ERC20Proxy,
env.provider,
txDefaults,
assetProxyArtifacts,
);
erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.ERC721Proxy,
env.provider,
txDefaults,
assetProxyArtifacts,
);
erc1155Proxy = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.ERC1155Proxy,
env.provider,
txDefaults,
assetProxyArtifacts,
);
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.MultiAssetProxy,
env.provider,
txDefaults,
assetProxyArtifacts,
);
staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.StaticCallProxy,
env.provider,
txDefaults,
assetProxyArtifacts,
);
// Set up the staking wrapper so that the entire staking interface can be accessed
// easily through the proxy.
stakingWrapper = new StakingContract(stakingProxy.address, env.provider);
});
describe('deployment and configuration', () => {
describe('exchange specific', () => {
// Registers an asset proxy in the exchange contract and ensure that the correct state changes occurred.
async function registerAssetProxyAndAssertSuccessAsync(
registrationContract: AssetProxyDispatcher,
assetProxyAddress: string,
assetProxyId: string,
): Promise<void> {
// Register the asset proxy.
const receipt = await registrationContract.registerAssetProxy.awaitTransactionSuccessAsync(
assetProxyAddress,
{
from: owner,
},
);
// Ensure that the correct event was logged.
const logs = filterLogsToArguments<ExchangeAssetProxyRegisteredEventArgs>(
receipt.logs,
ExchangeEvents.AssetProxyRegistered,
);
expect(logs).to.be.deep.eq([{ id: assetProxyId, assetProxy: assetProxyAddress }]);
// Ensure that the asset proxy was actually registered.
const proxyAddress = await registrationContract.getAssetProxy.callAsync(assetProxyId);
expect(proxyAddress).to.be.eq(assetProxyAddress);
}
// Authorizes an address for a given asset proxy using the owner address.
async function authorizeAddressAndAssertSuccessAsync(
authorizable: Authorizable,
newAuthorityAddress: string,
): Promise<void> {
// Authorize the address.
const receipt = await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
newAuthorityAddress,
{ from: owner },
);
// Ensure that the correct log was emitted.
const logs = filterLogsToArguments<AuthorizableAuthorizedAddressAddedEventArgs>(
receipt.logs,
AuthorizableEvents.AuthorizedAddressAdded,
);
expect(logs).to.be.deep.eq([{ target: newAuthorityAddress, caller: owner }]);
// Ensure that the address was actually authorized.
const wasAuthorized = await authorizable.authorized.callAsync(newAuthorityAddress);
expect(wasAuthorized).to.be.true();
}
it('should successfully register the asset proxies in the exchange', async () => {
// Register the asset proxies in the exchange.
await registerAssetProxyAndAssertSuccessAsync(exchange, erc20Proxy.address, AssetProxyId.ERC20);
await registerAssetProxyAndAssertSuccessAsync(exchange, erc721Proxy.address, AssetProxyId.ERC721);
await registerAssetProxyAndAssertSuccessAsync(exchange, erc1155Proxy.address, AssetProxyId.ERC1155);
await registerAssetProxyAndAssertSuccessAsync(
exchange,
multiAssetProxy.address,
AssetProxyId.MultiAsset,
);
await registerAssetProxyAndAssertSuccessAsync(
exchange,
staticCallProxy.address,
AssetProxyId.StaticCall,
);
});
it('should successfully register the asset proxies in the multi-asset proxy', async () => {
// Register the asset proxies in the multi-asset proxy.
await registerAssetProxyAndAssertSuccessAsync(multiAssetProxy, erc20Proxy.address, AssetProxyId.ERC20);
await registerAssetProxyAndAssertSuccessAsync(
multiAssetProxy,
erc721Proxy.address,
AssetProxyId.ERC721,
);
await registerAssetProxyAndAssertSuccessAsync(
multiAssetProxy,
erc1155Proxy.address,
AssetProxyId.ERC1155,
);
await registerAssetProxyAndAssertSuccessAsync(
multiAssetProxy,
staticCallProxy.address,
AssetProxyId.StaticCall,
);
});
it('should successfully add the exchange as an authority in the appropriate asset proxies', async () => {
// Authorize the exchange in all of the asset proxies, except for the static call proxy.
await authorizeAddressAndAssertSuccessAsync(erc20Proxy, exchange.address);
await authorizeAddressAndAssertSuccessAsync(erc721Proxy, exchange.address);
await authorizeAddressAndAssertSuccessAsync(erc1155Proxy, exchange.address);
await authorizeAddressAndAssertSuccessAsync(multiAssetProxy, exchange.address);
});
it('should successfully add the multi asset proxy as an authority in the appropriate asset proxies', async () => {
// Authorize the multi-asset proxy in the token asset proxies.
await authorizeAddressAndAssertSuccessAsync(erc20Proxy, multiAssetProxy.address);
await authorizeAddressAndAssertSuccessAsync(erc721Proxy, multiAssetProxy.address);
await authorizeAddressAndAssertSuccessAsync(erc1155Proxy, multiAssetProxy.address);
});
});
describe('staking specific', () => {
it('should have properly configured the staking proxy with the logic contract and read-only proxy', async () => {
// Ensure that the registered read-only proxy is correct.
const readOnlyProxyAddress = await stakingProxy.readOnlyProxy.callAsync();
expect(readOnlyProxyAddress).to.be.eq(readOnlyProxy.address);
// Ensure that the registered read-only proxy callee is correct.
const readOnlyProxyCalleeAddress = await stakingProxy.readOnlyProxyCallee.callAsync();
expect(readOnlyProxyCalleeAddress).to.be.eq(staking.address);
// Ensure that the registered staking contract is correct.
const stakingAddress = await stakingProxy.stakingContract.callAsync();
expect(stakingAddress).to.be.eq(staking.address);
});
it('should have initialized the correct parameters in the staking proxy', async () => {
// Ensure that the correct parameters were set.
const params = await stakingWrapper.getParams.callAsync();
expect(params).to.be.deep.eq(
[
864000, // epochDurationInSeconds
900000, // rewardDelegatedStakeWeight
100000000000000000000, // minimumPoolStake
10, // maximumMakerInPool
1, // cobbDouglasAlphaNumerator
2, // cobbDouglasAlphaDenominator
].map(value => new BigNumber(value)),
);
});
});
describe('exchange and staking integration', () => {
it('should successfully register the exchange in the staking contract', async () => {
// Register the exchange.
const receipt = await stakingWrapper.addExchangeAddress.awaitTransactionSuccessAsync(exchange.address, {
from: owner,
});
// Ensure that the correct events were logged.
const logs = filterLogsToArguments<StakingExchangeAddedEventArgs>(
receipt.logs,
StakingEvents.ExchangeAdded,
);
expect(logs).to.be.deep.eq([{ exchangeAddress: exchange.address }]);
// Ensure that the exchange was registered.
const wasRegistered = await stakingWrapper.validExchanges.callAsync(exchange.address);
expect(wasRegistered).to.be.true();
});
it('should successfully register the staking contract in the exchange', async () => {
// Register the staking contract.
const receipt = await exchange.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(
stakingProxy.address,
{
from: owner,
},
);
// Ensure that the correct events were logged.
const logs = filterLogsToArguments<ExchangeProtocolFeeCollectorAddressEventArgs>(
receipt.logs,
ExchangeEvents.ProtocolFeeCollectorAddress,
);
expect(logs).to.be.deep.eq([
{
oldProtocolFeeCollector: constants.NULL_ADDRESS,
updatedProtocolFeeCollector: stakingProxy.address,
},
]);
// Ensure that the staking contract was registered.
const feeCollector = await exchange.protocolFeeCollector.callAsync();
expect(feeCollector).to.be.eq(stakingProxy.address);
});
it('should successfully update the protocol fee multiplier in the staking contract', async () => {
// Update the protocol fee multiplier.
const receipt = await exchange.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(
protocolFeeMultiplier,
);
// Ensure that the correct events were logged.
const logs = filterLogsToArguments<ExchangeProtocolFeeMultiplierEventArgs>(
receipt.logs,
ExchangeEvents.ProtocolFeeMultiplier,
);
expect(logs).to.be.deep.eq([
{
oldProtocolFeeMultiplier: constants.ZERO_AMOUNT,
updatedProtocolFeeMultiplier: protocolFeeMultiplier,
},
]);
// Ensure that the protocol fee multiplier was set correctly.
const multiplier = await exchange.protocolFeeMultiplier.callAsync();
expect(multiplier).bignumber.to.be.eq(protocolFeeMultiplier);
});
});
});
describe('transferring ownership', () => {
// Removes authorization of the "externally owned address" owner and transfers the authorization
// to the asset proxy owner.
async function transferAuthorizationAndAssertSuccessAsync(contract: Authorizable): Promise<void> {
// Remove authorization from the old owner.
let receipt = await contract.removeAuthorizedAddress.awaitTransactionSuccessAsync(owner, { from: owner });
// Ensure that the correct log was recorded.
let logs = filterLogsToArguments<AuthorizableAuthorizedAddressRemovedEventArgs>(
receipt.logs,
AuthorizableEvents.AuthorizedAddressRemoved,
);
expect(logs).to.be.deep.eq([{ target: owner, caller: owner }]);
// Ensure that the owner was actually removed.
let isAuthorized = await contract.authorized.callAsync(owner);
expect(isAuthorized).to.be.false();
// Authorize the asset-proxy owner.
receipt = await contract.addAuthorizedAddress.awaitTransactionSuccessAsync(assetProxyOwner.address, {
from: owner,
});
// Ensure that the correct log was recorded.
logs = filterLogsToArguments<AuthorizableAuthorizedAddressAddedEventArgs>(
receipt.logs,
AuthorizableEvents.AuthorizedAddressAdded,
);
expect(logs).to.be.deep.eq([{ target: assetProxyOwner.address, caller: owner }]);
// Ensure that the asset-proxy owner was actually authorized.
isAuthorized = await contract.authorized.callAsync(assetProxyOwner.address);
expect(isAuthorized).to.be.true();
}
// Transfers ownership of a contract to the asset-proxy owner, and ensures that the change was actually made.
async function transferOwnershipAndAssertSuccessAsync(contract: Ownable): Promise<void> {
// Transfer ownership to the new owner.
await contract.transferOwnership.awaitTransactionSuccessAsync(assetProxyOwner.address, { from: owner });
// Ensure that the owner address has been updated.
const ownerAddress = await contract.owner.callAsync();
expect(ownerAddress).to.be.eq(assetProxyOwner.address);
}
it('should transfer authorization of the owner to the asset-proxy owner in the staking contracts', async () => {
// Transfer authorization of the staking system. We intentionally neglect
// to add the asset-proxy owner as an authorized address in the asset proxies
// as a security precaution.
await transferAuthorizationAndAssertSuccessAsync(stakingProxy);
});
it('should transfer ownership of all appropriate contracts to the asset-proxy owner', async () => {
// Transfer ownership of most contracts (we exclude contracts that are not ownable).
await transferOwnershipAndAssertSuccessAsync(exchange);
await transferOwnershipAndAssertSuccessAsync(readOnlyProxy);
await transferOwnershipAndAssertSuccessAsync(staking);
await transferOwnershipAndAssertSuccessAsync(stakingProxy);
await transferOwnershipAndAssertSuccessAsync(erc20Proxy);
await transferOwnershipAndAssertSuccessAsync(erc721Proxy);
await transferOwnershipAndAssertSuccessAsync(erc1155Proxy);
await transferOwnershipAndAssertSuccessAsync(multiAssetProxy);
});
});
});
// tslint:enable:no-unnecessary-type-assertion

View File

@ -135,7 +135,7 @@ describe('matchOrders', () => {
{},
new BigNumber(chainId),
);
exchangeWrapper = new ExchangeWrapper(exchange, provider);
exchangeWrapper = new ExchangeWrapper(exchange);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc1155Proxy.address, owner);

View File

@ -96,7 +96,7 @@ blockchainTests.resets('Exchange transactions', env => {
{},
new BigNumber(chainId),
);
exchangeWrapper = new ExchangeWrapper(exchangeInstance, env.provider);
exchangeWrapper = new ExchangeWrapper(exchangeInstance);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchangeInstance.address, { from: owner });

View File

@ -1,4 +1,4 @@
import { BatchMatchOrder, orderUtils, Web3ProviderEngine } from '@0x/contracts-test-utils';
import { BatchMatchOrder, orderUtils } from '@0x/contracts-test-utils';
import {
BatchMatchedFillResults,
FillResults,
@ -8,7 +8,7 @@ import {
SignedZeroExTransaction,
} from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
import { MethodAbi, TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
import { MethodAbi, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { ExchangeContract } from '../../src';
@ -16,18 +16,15 @@ import { ExchangeContract } from '../../src';
import { AbiDecodedFillOrderData } from './types';
export class ExchangeWrapper {
private readonly _exchange: ExchangeContract;
// tslint:disable no-unused-variable
constructor(exchangeContract: ExchangeContract, provider: Web3ProviderEngine | ZeroExProvider) {
this._exchange = exchangeContract;
}
constructor(public readonly exchangeContract: ExchangeContract) {}
public async fillOrderAsync(
signedOrder: SignedOrder,
from: string,
opts: { takerAssetFillAmount?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const txReceipt = await this._exchange.fillOrder.awaitTransactionSuccessAsync(
const txReceipt = await this.exchangeContract.fillOrder.awaitTransactionSuccessAsync(
params.order,
params.takerAssetFillAmount,
params.signature,
@ -37,7 +34,7 @@ export class ExchangeWrapper {
}
public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createCancel(signedOrder);
const txReceipt = await this._exchange.cancelOrder.awaitTransactionSuccessAsync(params.order, { from });
const txReceipt = await this.exchangeContract.cancelOrder.awaitTransactionSuccessAsync(params.order, { from });
return txReceipt;
}
public async fillOrKillOrderAsync(
@ -46,7 +43,7 @@ export class ExchangeWrapper {
opts: { takerAssetFillAmount?: BigNumber; gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const txReceipt = await this._exchange.fillOrKillOrder.awaitTransactionSuccessAsync(
const txReceipt = await this.exchangeContract.fillOrKillOrder.awaitTransactionSuccessAsync(
params.order,
params.takerAssetFillAmount,
params.signature,
@ -59,7 +56,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerAssetFillAmounts?: BigNumber[]; gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.batchFillOrders.awaitTransactionSuccessAsync(
return this.exchangeContract.batchFillOrders.awaitTransactionSuccessAsync(
orders,
opts.takerAssetFillAmounts === undefined
? orders.map(signedOrder => signedOrder.takerAssetAmount)
@ -73,7 +70,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerAssetFillAmounts?: BigNumber[]; gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.batchFillOrKillOrders.awaitTransactionSuccessAsync(
return this.exchangeContract.batchFillOrKillOrders.awaitTransactionSuccessAsync(
orders,
opts.takerAssetFillAmounts === undefined
? orders.map(signedOrder => signedOrder.takerAssetAmount)
@ -87,7 +84,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number; gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.batchFillOrdersNoThrow.awaitTransactionSuccessAsync(
return this.exchangeContract.batchFillOrdersNoThrow.awaitTransactionSuccessAsync(
orders,
opts.takerAssetFillAmounts === undefined
? orders.map(signedOrder => signedOrder.takerAssetAmount)
@ -101,7 +98,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerAssetFillAmount: BigNumber; gas?: number; gasPrice?: BigNumber },
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.marketSellOrdersNoThrow.awaitTransactionSuccessAsync(
return this.exchangeContract.marketSellOrdersNoThrow.awaitTransactionSuccessAsync(
orders,
opts.takerAssetFillAmount,
orders.map(signedOrder => signedOrder.signature),
@ -113,7 +110,7 @@ export class ExchangeWrapper {
from: string,
opts: { makerAssetFillAmount: BigNumber; gas?: number; gasPrice?: BigNumber },
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.marketBuyOrdersNoThrow.awaitTransactionSuccessAsync(
return this.exchangeContract.marketBuyOrdersNoThrow.awaitTransactionSuccessAsync(
orders,
opts.makerAssetFillAmount,
orders.map(signedOrder => signedOrder.signature),
@ -125,7 +122,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerAssetFillAmount: BigNumber; gas?: number },
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.marketSellOrdersFillOrKill.awaitTransactionSuccessAsync(
return this.exchangeContract.marketSellOrdersFillOrKill.awaitTransactionSuccessAsync(
orders,
opts.takerAssetFillAmount,
orders.map(signedOrder => signedOrder.signature),
@ -137,7 +134,7 @@ export class ExchangeWrapper {
from: string,
opts: { makerAssetFillAmount: BigNumber; gas?: number },
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.marketBuyOrdersFillOrKill.awaitTransactionSuccessAsync(
return this.exchangeContract.marketBuyOrdersFillOrKill.awaitTransactionSuccessAsync(
orders,
opts.makerAssetFillAmount,
orders.map(signedOrder => signedOrder.signature),
@ -148,19 +145,22 @@ export class ExchangeWrapper {
orders: SignedOrder[],
from: string,
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.batchCancelOrders.awaitTransactionSuccessAsync(orders, { from });
return this.exchangeContract.batchCancelOrders.awaitTransactionSuccessAsync(orders, { from });
}
public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
const txReceipt = await this._exchange.cancelOrdersUpTo.awaitTransactionSuccessAsync(salt, { from });
const txReceipt = await this.exchangeContract.cancelOrdersUpTo.awaitTransactionSuccessAsync(salt, { from });
return txReceipt;
}
public async registerAssetProxyAsync(
assetProxyAddress: string,
from: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const txReceipt = await this._exchange.registerAssetProxy.awaitTransactionSuccessAsync(assetProxyAddress, {
from,
});
const txReceipt = await this.exchangeContract.registerAssetProxy.awaitTransactionSuccessAsync(
assetProxyAddress,
{
from,
},
);
return txReceipt;
}
public async executeTransactionAsync(
@ -168,7 +168,7 @@ export class ExchangeWrapper {
from: string,
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.executeTransaction.awaitTransactionSuccessAsync(
return this.exchangeContract.executeTransaction.awaitTransactionSuccessAsync(
signedTransaction,
signedTransaction.signature,
{ from, gasPrice: opts.gasPrice },
@ -180,25 +180,29 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const signatures = signedTransactions.map(signedTransaction => signedTransaction.signature);
return this._exchange.batchExecuteTransactions.awaitTransactionSuccessAsync(signedTransactions, signatures, {
from,
gasPrice: opts.gasPrice,
});
return this.exchangeContract.batchExecuteTransactions.awaitTransactionSuccessAsync(
signedTransactions,
signatures,
{
from,
gasPrice: opts.gasPrice,
},
);
}
public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> {
const filledAmount = await this._exchange.filled.callAsync(orderHashHex);
const filledAmount = await this.exchangeContract.filled.callAsync(orderHashHex);
return filledAmount;
}
public async isCancelledAsync(orderHashHex: string): Promise<boolean> {
const isCancelled = await this._exchange.cancelled.callAsync(orderHashHex);
const isCancelled = await this.exchangeContract.cancelled.callAsync(orderHashHex);
return isCancelled;
}
public async getOrderEpochAsync(makerAddress: string, senderAddress: string): Promise<BigNumber> {
const orderEpoch = await this._exchange.orderEpoch.callAsync(makerAddress, senderAddress);
const orderEpoch = await this.exchangeContract.orderEpoch.callAsync(makerAddress, senderAddress);
return orderEpoch;
}
public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> {
const orderInfo = await this._exchange.getOrderInfo.callAsync(signedOrder);
const orderInfo = await this.exchangeContract.getOrderInfo.callAsync(signedOrder);
return orderInfo;
}
public async batchMatchOrdersAsync(
@ -208,7 +212,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createBatchMatchOrders(signedOrdersLeft, signedOrdersRight);
return this._exchange.batchMatchOrders.awaitTransactionSuccessAsync(
return this.exchangeContract.batchMatchOrders.awaitTransactionSuccessAsync(
params.leftOrders,
params.rightOrders,
params.leftSignatures,
@ -221,7 +225,7 @@ export class ExchangeWrapper {
from: string,
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.batchMatchOrders.awaitTransactionSuccessAsync(
return this.exchangeContract.batchMatchOrders.awaitTransactionSuccessAsync(
params.leftOrders,
params.rightOrders,
params.leftSignatures,
@ -236,7 +240,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<BatchMatchedFillResults> {
const params = orderUtils.createBatchMatchOrders(signedOrdersLeft, signedOrdersRight);
const batchMatchedFillResults = await this._exchange.batchMatchOrders.callAsync(
const batchMatchedFillResults = await this.exchangeContract.batchMatchOrders.callAsync(
params.leftOrders,
params.rightOrders,
params.leftSignatures,
@ -252,7 +256,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createBatchMatchOrders(signedOrdersLeft, signedOrdersRight);
return this._exchange.batchMatchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
return this.exchangeContract.batchMatchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
params.leftOrders,
params.rightOrders,
params.leftSignatures,
@ -265,7 +269,7 @@ export class ExchangeWrapper {
from: string,
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
return this._exchange.batchMatchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
return this.exchangeContract.batchMatchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
params.leftOrders,
params.rightOrders,
params.leftSignatures,
@ -280,7 +284,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<BatchMatchedFillResults> {
const params = orderUtils.createBatchMatchOrders(signedOrdersLeft, signedOrdersRight);
const batchMatchedFillResults = await this._exchange.batchMatchOrdersWithMaximalFill.callAsync(
const batchMatchedFillResults = await this.exchangeContract.batchMatchOrdersWithMaximalFill.callAsync(
params.leftOrders,
params.rightOrders,
params.leftSignatures,
@ -296,7 +300,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight);
const txReceipt = await this._exchange.matchOrders.awaitTransactionSuccessAsync(
const txReceipt = await this.exchangeContract.matchOrders.awaitTransactionSuccessAsync(
params.left,
params.right,
params.leftSignature,
@ -312,7 +316,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<MatchedFillResults> {
const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight);
const matchedFillResults = await this._exchange.matchOrders.callAsync(
const matchedFillResults = await this.exchangeContract.matchOrders.callAsync(
params.left,
params.right,
params.leftSignature,
@ -328,7 +332,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight);
return this._exchange.matchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
return this.exchangeContract.matchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
params.left,
params.right,
params.leftSignature,
@ -343,7 +347,7 @@ export class ExchangeWrapper {
opts: { gasPrice?: BigNumber },
): Promise<MatchedFillResults> {
const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight);
const matchedFillResults = await this._exchange.matchOrdersWithMaximalFill.callAsync(
const matchedFillResults = await this.exchangeContract.matchOrdersWithMaximalFill.callAsync(
params.left,
params.right,
params.leftSignature,
@ -358,7 +362,7 @@ export class ExchangeWrapper {
opts: { takerAssetFillAmount?: BigNumber; gasPrice?: BigNumber } = {},
): Promise<FillResults> {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const fillResults = await this._exchange.fillOrder.callAsync(
const fillResults = await this.exchangeContract.fillOrder.callAsync(
params.order,
params.takerAssetFillAmount,
params.signature,
@ -368,7 +372,7 @@ export class ExchangeWrapper {
}
public abiEncodeFillOrder(signedOrder: SignedOrder, opts: { takerAssetFillAmount?: BigNumber } = {}): string {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const data = this._exchange.fillOrder.getABIEncodedTransactionData(
const data = this.exchangeContract.fillOrder.getABIEncodedTransactionData(
params.order,
params.takerAssetFillAmount,
params.signature,
@ -377,13 +381,10 @@ export class ExchangeWrapper {
}
public abiDecodeFillOrder(data: string): AbiDecodedFillOrderData {
// Lookup fillOrder ABI in exchange abi
const fillOrderAbi = _.find(this._exchange.abi, { name: 'fillOrder' }) as MethodAbi;
const fillOrderAbi = _.find(this.exchangeContract.abi, { name: 'fillOrder' }) as MethodAbi;
// Decode input data
const abiEncoder = new AbiEncoder.Method(fillOrderAbi);
const decodedData = abiEncoder.decode(data) as AbiDecodedFillOrderData;
return decodedData;
}
public getExchangeAddress(): string {
return this._exchange.address;
}
}

View File

@ -117,7 +117,7 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
);
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
const exchangeWrapper = new ExchangeWrapper(exchangeContract, provider);
const exchangeWrapper = new ExchangeWrapper(exchangeContract);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, ownerAddress);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, ownerAddress);
await exchangeWrapper.registerAssetProxyAsync(erc1155Proxy.address, ownerAddress);
@ -646,7 +646,7 @@ export class FillOrderCombinatorialUtils {
const exchangeLogs = _.filter(
txReceipt.logs,
txLog => txLog.address === this.exchangeWrapper.getExchangeAddress(),
txLog => txLog.address === this.exchangeWrapper.exchangeContract.address,
);
expect(exchangeLogs.length).to.be.equal(1, 'logs length');
// tslint:disable-next-line:no-unnecessary-type-assertion

View File

@ -78,7 +78,7 @@ blockchainTests.resets('Exchange wrappers', env => {
from: owner,
});
exchangeWrapper = new ExchangeWrapper(exchange, env.provider);
exchangeWrapper = new ExchangeWrapper(exchange);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, {

View File

@ -1,4 +1,13 @@
[
{
"version": "4.1.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "4.0.8",

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.1.0-beta.0 - _October 3, 2019_
* Dependencies updated
## v4.0.8 - _September 17, 2019_
* Dependencies updated
@ -40,6 +44,9 @@ CHANGELOG
## v4.0.0 - _July 13, 2019_
* Move `OrderValidator` to contracts/dev-utils package as `OrderValidationUtils` (#1848)
* Remove unused `LibOrder` inheritance (#1742)
* Refactor BalanceThresholdFilter to use new ITransactions interface (#1753)
* Update tests for rich reverts (#1761)
## v3.1.5 - _May 24, 2019_

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "4.0.8",
"version": "4.1.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -48,11 +48,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -71,19 +71,19 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-asset-proxy": "^2.2.8",
"@0x/contracts-erc20": "^2.2.14",
"@0x/contracts-erc721": "^2.1.15",
"@0x/contracts-exchange": "^2.1.14",
"@0x/contracts-exchange-libs": "^3.0.8",
"@0x/contracts-utils": "^3.2.4",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-exchange": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@ -136,7 +136,7 @@ describe(ContractName.BalanceThresholdFilter, () => {
zrxAssetData,
new BigNumber(chainId),
);
exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider);
exchangeWrapper = new ExchangeWrapper(exchangeInstance);
// Register proxies
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {

View File

@ -95,7 +95,7 @@ describe(ContractName.DutchAuction, () => {
zrxAssetData,
new BigNumber(chainId),
);
const exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider);
const exchangeWrapper = new ExchangeWrapper(exchangeInstance);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);

View File

@ -115,7 +115,7 @@ describe('OrderMatcher', () => {
assetDataUtils.encodeERC20AssetData(zrxToken.address),
new BigNumber(chainId),
);
exchangeWrapper = new ExchangeWrapper(exchange, provider);
exchangeWrapper = new ExchangeWrapper(exchange);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
// Authorize ERC20 trades by exchange

View File

@ -1,4 +1,13 @@
[
{
"version": "3.2.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "3.1.14",

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.2.0-beta.0 - _October 3, 2019_
* Dependencies updated
## v3.1.14 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-multisig",
"version": "3.1.14",
"version": "3.2.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -47,11 +47,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/multisig/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@ -69,15 +69,15 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contracts-asset-proxy": "^2.2.8",
"@0x/contracts-erc20": "^2.2.14",
"@0x/contracts-utils": "^3.2.4",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@ -1,6 +1,6 @@
[
{
"version": "1.0.0",
"version": "1.1.0-beta.0",
"changes": [
{
"note": "Created package",
@ -70,6 +70,7 @@
"note": "Removed explicit dependency on epoch+1 when delegating.",
"pr": 2188
}
]
],
"timestamp": 1570135330
}
]

View File

@ -4,3 +4,23 @@ Edit the package's CHANGELOG.json file only.
-->
CHANGELOG
## v1.1.0-beta.0 - _October 3, 2019_
* Created package (#1821)
* First implementation (#1910)
* Replace `LibFeeMath` with `LibFixedMath`. (#2109)
* Use a more precise cobb-douglas implementation. (#2109)
* Change the way operator stake is computed. (#2109)
* Denominate pool operator shares in parts-per-million. (#2109)
* New stake management mechanics. Delay before delegation. Nixed shadow rewards. (#2118)
* Tests for new stake management mechanics. (#2126)
* Add `init()` pattern to contracts. (#2131)
* Replace `MixinDeploymentConstants` with `MixinParams`. (#2131)
* Reference counting for cumulative rewards. (#2154)
* Refactored Staking Reward Vault. Moved pool management logic into staking contract. (#2156)
* Removed MixinStakingPoolRewardVault.sol (#2156)
* Refactored out `_cobbDouglas()` into its own library (#2179)
* Introduce multi-block finalization. (#2155)
* Removed reference counting for cumulative rewards. (#2188)
* Removed explicit dependency on epoch+1 when delegating. (#2188)

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-staking",
"version": "1.0.0",
"version": "1.1.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -49,13 +49,13 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.1.0",
"@0x/contracts-gen": "^1.0.13",
"@0x/contracts-test-utils": "^3.1.2",
"@0x/dev-utils": "^2.2.1",
"@0x/sol-compiler": "^3.1.6",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/utils": "^4.3.1",
"@0x/utils": "^4.5.2",
"@types/lodash": "4.14.104",
"@types/node": "*",
"chai": "^4.0.1",
@ -74,16 +74,16 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.1.0",
"@0x/contracts-asset-proxy": "^2.2.5",
"@0x/contracts-erc20": "^2.2.0",
"@0x/contracts-utils": "^3.2.1",
"@0x/order-utils": "^8.1.0",
"@0x/types": "^2.2.2",
"@0x/typescript-typings": "^4.2.2",
"@0x/utils": "^4.3.1",
"@0x/web3-wrapper": "^6.0.6",
"ethereum-types": "^2.1.2",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},

View File

@ -1,6 +1,6 @@
[
{
"version": "4.0.0",
"version": "3.2.0-beta.0",
"changes": [
{
"note": "Add `chainId` to `TransactionFactory` constructor",
@ -93,8 +93,13 @@
{
"note": "Add `shortZip()` to `lang_utils.ts`",
"pr": 2155
},
{
"note": "Add `number_utils.ts` and `hexSize()`",
"pr": 2220
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,33 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.2.0-beta.0 - _October 3, 2019_
* Add `chainId` to `TransactionFactory` constructor (#1742)
* Use new `Order` structure with `domain` field (#1742)
* Inherit `chaiSetup` from `@0x/dev-utils` (#1761)
* Add `generatePseudoRandomOrderHash()` to `orderUtils` (#1761)
* Inherit `OrderStatus` from `@0x/types` (#1761)
* Update types for arbitrary fee tokens (#1819)
* Remove formatters (#1834)
* Add `hexConcat()` in `hex_utils.ts` (#1885)
* Introduce Mocha blockchain extensions (#2007)
* Move `*FillResults`, `OrderInfo` types to `@0x/types` (#2031)
* Add `log_utils.ts` (#2031)
* Add `haxRandom()` to `hex_utils.ts` (#2031)
* Add the constants: `MAX_UINT256`, `ADDRESS_LENGTH`, `MAX_UINT256_ROOT`, `ONE_ETHER` (#2031)
* Make `testCombinatoriallyWithReferenceFuncAsync` non-async (#2031)
* Update `testWithReferenceFuncAsync` to work with `RevertErrors` (#2031)
* `web3Wrapper` is created with `shouldAllowUnlimitedContractSize` if `UNLIMITED_CONTRACT_SIZE` environment variable is set. (#2075)
* Add `toHex()`, `hexLeftPad()`, `hexRightPad()`, and 'hexInvert()' hex utils (#2109)
* Add `PPM_DENOMINATOR` and `PPM_100_PERCENT` constants. (#2109)
* Increase the number of ganache accounts to 20 (#2109)
* Add `Numberish` type. (#2131)
* Tweaks/Upgrades to `hex_utils`, most notably `hexSlice()` (#2155)
* Add `hexHash()` to `hex_utils` (#2155)
* Add `shortZip()` to `lang_utils.ts` (#2155)
* Add `number_utils.ts` and `hexSize()` (#2220)
## v3.1.16 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "3.1.16",
"version": "3.2.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -34,41 +34,41 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/test-utils/README.md",
"devDependencies": {
"@types/mocha": "^5.2.7",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
"tslint": "5.11.0",
"typescript": "3.0.1"
},
"dependencies": {
"@0x/dev-utils": "^2.3.3",
"@0x/order-utils": "^8.4.0",
"@0x/sol-compiler": "^3.1.15",
"@0x/sol-coverage": "^3.0.12",
"@0x/sol-profiler": "^3.1.14",
"@0x/sol-trace": "^2.0.20",
"@0x/subproviders": "^5.0.4",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/sol-coverage": "^3.1.0-beta.0",
"@0x/sol-profiler": "^3.2.0-beta.0",
"@0x/sol-trace": "^2.1.0-beta.0",
"@0x/subproviders": "^5.1.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"@types/bn.js": "^4.11.0",
"@types/js-combinatorics": "^0.5.29",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
"bn.js": "^4.11.8",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^2.1.6",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1",
"ethers": "~4.0.4",
"js-combinatorics": "^0.5.3",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0"
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0"
},
"publishConfig": {
"access": "public"

View File

@ -65,6 +65,13 @@ export function hexHash(n: Numberish): string {
return ethUtil.bufferToHex(ethUtil.sha3(ethUtil.toBuffer(toHex(n))));
}
/**
* Get the length, in bytes, of a hex string.
*/
export function hexSize(hex: string): number {
return Math.ceil((hex.length - 2) / 2);
}
/**
* Convert a string, a number, or a BigNumber into a hex string.
* Works with negative numbers, as well.

View File

@ -28,7 +28,17 @@ export { bytes32Values, testCombinatoriallyWithReferenceFunc, uint256Values } fr
export { TransactionFactory } from './transaction_factory';
export { MutatorContractFunction, TransactionHelper } from './transaction_helper';
export { testWithReferenceFuncAsync } from './test_with_reference';
export { hexConcat, hexHash, hexLeftPad, hexInvert, hexSlice, hexRandom, hexRightPad, toHex } from './hex_utils';
export {
hexConcat,
hexHash,
hexLeftPad,
hexInvert,
hexSlice,
hexRandom,
hexRightPad,
hexSize,
toHex,
} from './hex_utils';
export {
BatchMatchOrder,
ContractName,
@ -51,3 +61,12 @@ export { blockchainTests, BlockchainTestsEnvironment, describe } from './mocha_b
export { chaiSetup, expect } from './chai_setup';
export { getCodesizeFromArtifact } from './codesize';
export { shortZip } from './lang_utils';
export {
assertIntegerRoughlyEquals,
assertRoughlyEquals,
getRandomFloat,
getRandomInteger,
getRandomPortion,
getNumericalDivergence,
toBaseUnitAmount,
} from './number_utils';

View File

@ -3,21 +3,20 @@ import { Web3ProviderEngine } from '@0x/subproviders';
import { providerUtils } from '@0x/utils';
import { TxData, Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
// Import ambient declarations (and clobber Jest).
import 'mocha';
import * as mocha from 'mocha';
import * as process from 'process';
import { provider, txDefaults, web3Wrapper } from './web3_wrapper';
// tslint:disable: no-namespace only-arrow-functions no-unbound-method
export type ISuite = Mocha.ISuite;
export type ISuiteCallbackContext = Mocha.ISuiteCallbackContext;
export type ISuite = mocha.ISuite;
export type ISuiteCallbackContext = mocha.ISuiteCallbackContext;
export type SuiteCallback = (this: ISuiteCallbackContext) => void;
export type ContextDefinitionCallback<T> = (description: string, callback: SuiteCallback) => T;
export type BlockchainSuiteCallback = (this: ISuiteCallbackContext, env: BlockchainTestsEnvironment) => void;
export type BlockchainContextDefinitionCallback<T> = (description: string, callback: BlockchainSuiteCallback) => T;
export interface ContextDefinition extends Mocha.IContextDefinition {
export interface ContextDefinition extends mocha.IContextDefinition {
optional: ContextDefinitionCallback<ISuite | void>;
}
@ -88,10 +87,10 @@ export class BlockchainTestsEnvironmentSingleton {
}
// The original `describe()` global provided by mocha.
const mochaDescribe = (global as any).describe as Mocha.IContextDefinition;
const mochaDescribe = (global as any).describe as mocha.IContextDefinition;
/**
* An augmented version of Mocha's `describe()`.
* An augmented version of mocha's `describe()`.
*/
export const describe = _.assign(mochaDescribe, {
optional(description: string, callback: SuiteCallback): ISuite | void {

View File

@ -0,0 +1,88 @@
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as crypto from 'crypto';
import { expect } from './chai_setup';
import { Numberish } from './types';
/**
* Generate a random integer between `min` and `max`, inclusive.
*/
export function getRandomInteger(min: Numberish, max: Numberish): BigNumber {
const range = new BigNumber(max).minus(min);
return getRandomPortion(range).plus(min);
}
/**
* Generate a random integer between `0` and `total`, inclusive.
*/
export function getRandomPortion(total: Numberish): BigNumber {
return new BigNumber(total).times(getRandomFloat(0, 1)).integerValue(BigNumber.ROUND_HALF_UP);
}
/**
* Generate a random, high-precision decimal between `min` and `max`, inclusive.
*/
export function getRandomFloat(min: Numberish, max: Numberish): BigNumber {
// Generate a really high precision number between [0, 1]
const r = new BigNumber(crypto.randomBytes(32).toString('hex'), 16).dividedBy(new BigNumber(2).pow(256).minus(1));
return new BigNumber(max)
.minus(min)
.times(r)
.plus(min);
}
/**
* Converts two decimal numbers to integers with `precision` digits, then returns
* the absolute difference.
*/
export function getNumericalDivergence(a: Numberish, b: Numberish, precision: number = 18): number {
const _a = new BigNumber(a);
const _b = new BigNumber(b);
const maxIntegerDigits = Math.max(
_a.integerValue(BigNumber.ROUND_DOWN).sd(true),
_b.integerValue(BigNumber.ROUND_DOWN).sd(true),
);
const _toInteger = (n: BigNumber) => {
const base = 10 ** (precision - maxIntegerDigits);
return n.times(base).integerValue(BigNumber.ROUND_DOWN);
};
return _toInteger(_a)
.minus(_toInteger(_b))
.abs()
.toNumber();
}
/**
* Asserts that two numbers are equal up to `precision` digits.
*/
export function assertRoughlyEquals(actual: Numberish, expected: Numberish, precision: number = 18): void {
if (getNumericalDivergence(actual, expected, precision) <= 1) {
return;
}
expect(actual).to.bignumber.eq(expected);
}
/**
* Asserts that two numbers are equal with up to `maxError` difference between them.
*/
export function assertIntegerRoughlyEquals(actual: Numberish, expected: Numberish, maxError: number = 1): void {
const diff = new BigNumber(actual)
.minus(expected)
.abs()
.toNumber();
if (diff <= maxError) {
return;
}
expect(actual).to.bignumber.eq(expected);
}
/**
* Converts `amount` into a base unit amount with 18 digits.
*/
export function toBaseUnitAmount(amount: Numberish): BigNumber {
const decimals = 18;
const amountAsBigNumber = new BigNumber(amount);
const baseUnitAmount = Web3Wrapper.toBaseUnitAmount(amountAsBigNumber, decimals);
return baseUnitAmount;
}

View File

@ -20,10 +20,10 @@ export class OrderFactory {
const tenMinutesInSeconds = 10 * 60;
const currentBlockTimestamp = await getLatestBlockTimestampAsync();
const order = ({
takerAddress: constants.NULL_ADDRESS,
senderAddress: constants.NULL_ADDRESS,
expirationTimeSeconds: new BigNumber(currentBlockTimestamp).plus(tenMinutesInSeconds),
salt: generatePseudoRandomSalt(),
takerAddress: constants.NULL_ADDRESS,
...this._defaultOrderParams,
...customOrderParams,
} as any) as Order;

View File

@ -1,6 +1,6 @@
[
{
"version": "3.2.5",
"version": "3.3.0-beta.0",
"changes": [
{
"note": "Change ReentrancyGuard implementation to cheaper one",
@ -62,7 +62,8 @@
"note": "Introduce automatic normalization and some zero-value shortcuts in `LibFractions`.",
"pr": 2155
}
]
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,

View File

@ -5,6 +5,24 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.0-beta.0 - _October 3, 2019_
* Change ReentrancyGuard implementation to cheaper one (#1699)
* Add LibEIP712 contract (#1753)
* Add `RichErrors` and `mixins/MRichErrors` (#1761)
* Break out types/interaces from `MRichErrors` into `MRichErrorTypes`. (#1790)
* Add LibEIP1271.sol (#1885)
* Updated RichErrors to the library pattern, and implemented RichErrors for all remaining reverts and requires (#1913)
* Added unit tests for all of the internal functions in the package (#2014)
* Updated Ownable to revert when the owner attempts to transfer ownership to the zero address (#2019)
* Add reference functions for `SafeMath` functions. (#2031)
* Throw a `SafeMathError` in `SafeMath._safeDiv()` when denominator is zero. (#2031)
* Create `LibSafeMath` (#2055)
* Rename `_rrevert` to `rrevert` in `LibRichErrors` contract (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Added LibFractions (#2118)
* Introduce automatic normalization and some zero-value shortcuts in `LibFractions`. (#2155)
## v3.2.4 - _September 17, 2019_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "3.2.4",
"version": "3.3.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -48,11 +48,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/utils/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/contracts-gen": "^1.0.15",
"@0x/contracts-test-utils": "^3.1.16",
"@0x/dev-utils": "^2.3.3",
"@0x/sol-compiler": "^3.1.15",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
@ -72,14 +72,14 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/order-utils": "^8.4.0",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"bn.js": "^4.11.8",
"ethereum-types": "^2.1.6",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},

View File

@ -21,11 +21,11 @@
"run:publish": "run-s install:all build:monorepo_scripts script:prepublish_checks rebuild script:publish",
"run:publish:local": "IS_LOCAL_PUBLISH=true yarn run:publish",
"script:prepublish_checks": "node ./packages/monorepo-scripts/lib/prepublish_checks.js",
"script:publish": "node ./packages/monorepo-scripts/lib/publish.js",
"script:publish": "DIST_TAG=protocolV3 node ./packages/monorepo-scripts/lib/publish.js",
"install:all": "yarn install",
"wsrun": "wsrun",
"lerna": "lerna",
"build": "lerna link && wsrun build $PKG -r --stages --fast-exit --exclude-missing",
"build": "lerna link && wsrun build $PKG -r --stages --fast-exit --exclude-missing --exclude @0x/contracts-extensions --exclude @0x/contracts-coordinator",
"build:ci": "lerna link && wsrun build:ci $PKG --fast-exit -r --stages --exclude-missing --exclude @0x/contracts-extensions --exclude @0x/contracts-coordinator",
"build:contracts": "lerna link && wsrun build -p ${npm_package_config_contractsPackages} -c --fast-exit -r --stages --exclude-missing",
"build:monorepo_scripts": "PKG=@0x/monorepo-scripts yarn build",
@ -65,7 +65,7 @@
},
{
"path": "packages/instant/umd/instant.js",
"maxSize": "1460kB"
"maxSize": "1960kB"
}
],
"ci": {

View File

@ -1,4 +1,13 @@
[
{
"version": "7.1.0-beta.0",
"changes": [
{
"note": "Updated to work with 0x v3"
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "7.0.2",

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v7.1.0-beta.0 - _October 3, 2019_
* Updated to work with 0x v3
## v7.0.2 - _September 17, 2019_
* Dependencies updated

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "0x.js",
"version": "7.0.2",
"version": "7.1.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -46,9 +46,9 @@
},
"license": "Apache-2.0",
"devDependencies": {
"@0x/contract-addresses": "^3.2.0",
"@0x/dev-utils": "^2.3.3",
"@0x/migrations": "^4.3.2",
"@0x/contract-addresses": "^3.3.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/migrations": "^4.4.0-beta.0",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
@ -77,19 +77,19 @@
"webpack": "^4.20.2"
},
"dependencies": {
"@0x/abi-gen-wrappers": "^5.3.2",
"@0x/assert": "^2.1.6",
"@0x/asset-swapper": "^2.0.0",
"@0x/base-contract": "^5.4.0",
"@0x/contract-wrappers": "^12.1.0",
"@0x/order-utils": "^8.4.0",
"@0x/subproviders": "^5.0.4",
"@0x/types": "^2.4.3",
"@0x/typescript-typings": "^4.3.0",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"@0x/abi-gen-wrappers": "^5.4.0-beta.0",
"@0x/assert": "^2.2.0-beta.0",
"@0x/asset-swapper": "^2.1.0-beta.0",
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contract-wrappers": "^12.2.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/subproviders": "^5.1.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"@types/web3-provider-engine": "^14.0.0",
"ethereum-types": "^2.1.6",
"ethereum-types": "^2.2.0-beta.0",
"ethers": "~4.0.4",
"lodash": "^4.17.11",
"web3-provider-engine": "14.0.6"

View File

@ -1,4 +1,4 @@
export { ContractAddresses } from '@0x/contract-addresses';
export { getContractAddressesForNetworkOrThrow, NetworkId, ContractAddresses } from '@0x/contract-addresses';
export {
assetDataUtils,
@ -8,21 +8,6 @@ export {
transactionHashUtils,
} from '@0x/order-utils';
export {
ContractWrappers,
CoordinatorWrapper,
CoordinatorServerCancellationResponse,
CoordinatorServerError,
IndexedFilterValues,
ContractWrappersConfig,
OrderTransactionOpts,
TransactionOpts,
OrderInfo,
EventCallback,
DecodedLogEvent,
OrderStatus,
} from '@0x/contract-wrappers';
export {
ExchangeEventArgs,
ExchangeEvents,
@ -33,13 +18,6 @@ export {
ExchangeAssetProxyRegisteredEventArgs,
ExchangeContract,
DevUtilsContract,
ForwarderContract,
DutchAuctionContract,
CoordinatorContract,
CoordinatorRegistryEventArgs,
CoordinatorRegistryEvents,
CoordinatorRegistryCoordinatorEndpointSetEventArgs,
CoordinatorRegistryContract,
IValidatorContract,
IWalletContract,
WETH9EventArgs,
@ -60,38 +38,14 @@ export {
ERC721TokenApprovalEventArgs,
ERC721TokenApprovalForAllEventArgs,
ERC721TokenContract,
ERC1155ProxyEventArgs,
ERC1155ProxyEvents,
ERC1155ProxyAuthorizedAddressAddedEventArgs,
ERC1155ProxyAuthorizedAddressRemovedEventArgs,
ERC1155ProxyContract,
ZRXTokenEventArgs,
ZRXTokenEvents,
ZRXTokenTransferEventArgs,
ZRXTokenApprovalEventArgs,
ZRXTokenContract,
DummyERC20TokenEventArgs,
DummyERC20TokenEvents,
DummyERC20TokenTransferEventArgs,
DummyERC20TokenApprovalEventArgs,
DummyERC20TokenContract,
DummyERC721TokenEventArgs,
DummyERC721TokenEvents,
DummyERC721TokenTransferEventArgs,
DummyERC721TokenApprovalEventArgs,
DummyERC721TokenApprovalForAllEventArgs,
DummyERC721TokenContract,
ERC20ProxyEventArgs,
ERC20ProxyEvents,
ERC20ProxyContract,
ERC20ProxyAuthorizedAddressAddedEventArgs,
ERC20ProxyAuthorizedAddressRemovedEventArgs,
ERC721ProxyEventArgs,
ERC721ProxyEvents,
ERC721ProxyAuthorizedAddressAddedEventArgs,
ERC721ProxyAuthorizedAddressRemovedEventArgs,
ERC721ProxyContract,
OrderValidatorContract,
ExchangeProtocolFeeCollectorAddressEventArgs,
ExchangeProtocolFeeMultiplierEventArgs,
ExchangeTransactionExecutionEventArgs,
} from '@0x/abi-gen-wrappers';
export import Web3ProviderEngine = require('web3-provider-engine');
@ -104,7 +58,7 @@ export {
MetamaskSubprovider,
} from '@0x/subproviders';
export { AbiDecoder, DecodedCalldata, BigNumber } from '@0x/utils';
export { DecodedCalldata, BigNumber } from '@0x/utils';
export {
Order,
@ -128,23 +82,20 @@ export {
SimpleStandardContractOutput,
SimpleEvmOutput,
SimpleEvmBytecodeOutput,
EIP712DomainWithDefaultSchema,
EventCallback,
IndexedFilterValues,
DecodedLogEvent,
} from '@0x/types';
export {
BlockRange,
ContractAbi,
LogWithDecodedArgs,
ContractEventArg,
SupportedProvider,
JSONRPCRequestPayload,
JSONRPCResponsePayload,
JSONRPCResponseError,
LogEntry,
DecodedLogArgs,
LogEntryEvent,
DecodedLogEntry,
DecodedLogEntryEvent,
RawLog,
AbiDefinition,
FunctionAbi,
EventAbi,
@ -183,4 +134,7 @@ export {
OutputField,
ParamDescription,
EvmBytecodeOutput,
RevertErrorAbi,
DecodedLogArgs,
LogWithDecodedArgs,
} from 'ethereum-types';

View File

@ -1,6 +1,6 @@
[
{
"version": "6.0.0",
"version": "5.4.0-beta.0",
"changes": [
{
"note": "Use V3 contracts",
@ -10,7 +10,8 @@
"note": "Hardcode bytecode for local EVM execution",
"pr": 2198
}
]
],
"timestamp": 1570135330
},
{
"version": "5.3.2",

View File

@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.0-beta.0 - _October 3, 2019_
* Use V3 contracts (#2181)
* Hardcode bytecode for local EVM execution (#2198)
## v5.3.2 - _September 17, 2019_
* Redirect `callAsync` to use local EVM instead of eth_call for pure functions (#2108)
@ -47,7 +52,9 @@ CHANGELOG
## v4.3.0 - _May 10, 2019_
* Update Coordinator and Exchange wrappers (#1742)
* Update wrapper functions to expose `awaitTransactionSuccessAsync()` methods (#1797)
* Update wrappers to automatically throw `RevertError` types when possible. (#1819)
## v4.2.0 - _April 11, 2019_

View File

@ -1,6 +1,6 @@
{
"name": "@0x/abi-gen-wrappers",
"version": "5.3.2",
"version": "5.4.0-beta.0",
"engines": {
"node": ">=6.12"
},
@ -33,22 +33,22 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen-wrappers/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.2.1",
"@0x/assert": "^2.1.6",
"@0x/json-schemas": "^4.0.2",
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/assert": "^2.2.0-beta.0",
"@0x/json-schemas": "^4.1.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/types": "^2.4.3",
"@0x/utils": "^4.5.2",
"@0x/web3-wrapper": "^6.0.13",
"ethereum-types": "^2.1.6",
"@0x/types": "^2.5.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethers": "~4.0.4",
"lodash": "^4.17.11",
"shx": "^0.2.2"
},
"dependencies": {
"@0x/base-contract": "^5.4.0",
"@0x/contract-addresses": "^3.2.0",
"@0x/contract-artifacts": "^2.2.2"
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contract-addresses": "^3.3.0-beta.0",
"@0x/contract-artifacts": "^2.3.0-beta.0"
},
"publishConfig": {
"access": "public"

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@ import {
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { SimpleContractArtifact } from '@0x/types';
import { SimpleContractArtifact, EventCallback, IndexedFilterValues } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';

View File

@ -1,13 +1,7 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
// tslint:disable:no-unused-variable
import {
BaseContract,
EventCallback,
IndexedFilterValues,
SubscriptionManager,
PromiseWithTransactionHash,
} from '@0x/base-contract';
import { BaseContract, SubscriptionManager, PromiseWithTransactionHash } from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
BlockParam,
@ -25,7 +19,7 @@ import {
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { SimpleContractArtifact } from '@0x/types';
import { SimpleContractArtifact, EventCallback, IndexedFilterValues } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More