Compare commits

...

44 Commits

Author SHA1 Message Date
xianny
6b0f3570b9 Publish
- @0x/contracts-asset-proxy@3.0.1
 - @0x/contracts-coordinator@3.0.1
 - @0x/contracts-dev-utils@1.0.1
 - @0x/contracts-erc1155@2.0.1
 - @0x/contracts-erc20-bridge-sampler@1.0.1
 - @0x/contracts-erc20@3.0.1
 - @0x/contracts-erc721@3.0.1
 - @0x/contracts-exchange-forwarder@4.0.1
 - @0x/contracts-exchange-libs@4.0.1
 - @0x/contracts-exchange@3.0.1
 - @0x/contracts-extensions@5.1.0
 - @0x/contracts-integrations@2.0.1
 - @0x/contracts-multisig@4.0.1
 - @0x/contracts-staking@2.0.1
 - @0x/contracts-test-utils@5.0.0
 - @0x/contracts-tests@0.0.7
 - @0x/contracts-utils@4.0.1
 - 0x.js@9.0.1
 - @0x/abi-gen@5.0.1
 - @0x/assert@3.0.1
 - @0x/asset-swapper@3.0.1
 - @0x/base-contract@6.0.1
 - @0x/connect@6.0.1
 - @0x/contract-artifacts@3.1.0
 - @0x/contract-wrappers-test@12.2.2
 - @0x/contract-wrappers@13.1.0
 - @0x/contracts-gen@2.0.1
 - @0x/dev-utils@3.0.1
 - @0x/instant@1.0.38
 - @0x/json-schemas@5.0.1
 - @0x/migrations@5.0.1
 - @0x/monorepo-scripts@1.0.44
 - @0x/order-utils@10.0.0
 - @0x/orderbook@1.0.1
 - @0x/sol-compiler@4.0.1
 - @0x/sol-coverage@4.0.1
 - @0x/sol-doc@3.0.1
 - @0x/sol-profiler@4.0.1
 - @0x/sol-resolver@3.0.1
 - @0x/sol-trace@3.0.1
 - @0x/sol-tracing-utils@7.0.1
 - @0x/sra-spec@3.0.1
 - @0x/subproviders@6.0.1
 - @0x/types@3.1.0
 - @0x/utils@5.1.0
 - @0x/web3-wrapper@7.0.1
2019-12-09 14:53:19 -08:00
xianny
71de0d04f3 Updated CHANGELOGS & MD docs 2019-12-09 14:53:05 -08:00
Xianny
99debff5d2 Add syntactic sugar for assetDataUtils (#2388)
* add syntactic sugar for assetDataUtils
2019-12-09 13:55:58 -08:00
Amir Bandeali
3bac6fcb27 Merge pull request #2377 from 0xProject/feat/forwarder/multi-affiliate-fees
Forwarder affiliate fee usability improvements
2019-12-08 19:13:28 -08:00
Amir Bandeali
4b842b81a0 Address PR feedback 2019-12-08 16:28:00 -08:00
Amir Bandeali
e2e4d048ab Update tests to use new Forwarder interface 2019-12-04 21:23:55 -08:00
Amir Bandeali
5574c368cd Allow different ETh fees to be specified for different feeRecipient addresses 2019-12-04 21:23:55 -08:00
Amir Bandeali
0d34f7b92e Add EthFeeLengthMismatchError 2019-12-04 21:23:55 -08:00
Amir Bandeali
5be0632e01 Add tests with multiple fee recipients 2019-12-04 21:23:55 -08:00
Amir Bandeali
79ea0bf9f4 Allow affiliate fee to be split between multiple fee recipient addresses 2019-12-04 21:23:54 -08:00
Amir Bandeali
b1929cb688 Update affiliate fee tests 2019-12-04 21:23:54 -08:00
Amir Bandeali
5ad98700e5 Remove FeePercentageTooLargeError rich revert 2019-12-04 21:22:54 -08:00
Amir Bandeali
a54624b697 Do not return ethFeePaid 2019-12-04 21:22:54 -08:00
Amir Bandeali
ca34c865af Remove max fee percentage for affiliate fees 2019-12-04 21:22:54 -08:00
Amir Bandeali
dde57b1eca Make affiliate fee a flat amount 2019-12-04 21:22:54 -08:00
Amir Bandeali
264b06938e Merge pull request #2378 from 0xProject/feat/bridges/chai-bridge
Implement ChaiBridge
2019-12-04 16:25:46 -08:00
John Johnson
99edb303e2 Merge pull request #2386 from 0xProject/feature/fix-unbound-provider
Fix unbound method in provider standardizer
2019-12-04 15:44:22 -08:00
John Johnson
104cc24dfc Fixing unbound provider (metamask v7.7 incorrectly binds this) 2019-12-04 15:09:00 -08:00
Xianny
fcbcbac889 Remove assetDataUtils everywhere (#2373)
* remove assetDataUtils everywhere

* export IAssetDataContract from @0x/contract-wrappers to allow @0x/instant to decode asset data  synchronously

* export generic function `decodeAssetDataOrThrow` and add ERC20Bridge support

* export `hexUtils` from order-utils instead of contracts-test-utils
2019-12-04 13:08:08 -08:00
mzhu25
b86d19028c Merge pull request #2366 from 0xProject/feature/fuzz/makers-and-takers
Pool Member Fuzz Tests
2019-12-04 11:12:55 -08:00
F. Eugene Aumson
4f17a251d3 Python publish for v3 (#2383)
* Remove pre-release suffixes from version numbers

* For wrapper test, pull latest ganache image first

* For wrapper test, unpin ganache, use beta snapshot

* In docs, advise using beta ganache snapshot

Because we haven't yet published the non-beta snapshot

* Unpin package interdependencies

* unpin tests from beta 0xorg/ganache-cli version

* use beta ganache snapshot

* Set release date in CHANGELOGs

* In testing deployment, stop testing pre-releases

* Include rmtree("build") in all clean commands

* Fix clean not cleaning what it thought it was

* In monorepo script, install pkgs 1st then dev deps

* Stop pinning ganache snapshot version

* In test setup, wait longer for mesh to start up

* Fix broken hyperlinks in docs

* fix missing \n that was breaking doc rendering

* In monorepo script comment, fix typo, and clarify
2019-12-04 08:42:00 -08:00
Michael Zhu
3d79fe2bf4 post-rebase lockfile update 2019-12-03 15:34:59 -08:00
Alex Towle
474399154f Addressed last review comment 2019-12-03 14:41:53 -08:00
Alex Towle
19f5153d0e Addressed some review feedback 2019-12-03 14:41:53 -08:00
Alex Towle
ce11271866 Appease the linter 2019-12-03 14:40:18 -08:00
Alex Towle
86cf353296 Improved the fuzz test 2019-12-03 14:40:07 -08:00
Alex Towle
36df5dc721 Implemented a hacky version of the fillOrder fuzz tests 2019-12-03 14:40:07 -08:00
Alex Towle
1e44a9c942 Made function assertions work with the new wrappers 2019-12-03 14:39:29 -08:00
mzhu25
8685cf9036 Merge pull request #2357 from 0xProject/refactor/integrations/transaction-tests
`@0x/contracts-integrations`: Transaction integration tests
2019-12-03 11:04:11 -08:00
Michael Zhu
2232870b09 address comments 2019-12-03 10:35:59 -08:00
Amir Bandeali
b68acd101e Fix failing tests 2019-12-03 08:46:47 -08:00
Amir Bandeali
173ba9b2b5 Add ChaiBridge unit tests 2019-12-02 16:42:17 -08:00
Amir Bandeali
64ed1f87d3 Rethrow custom error string if draw call fails 2019-12-02 16:42:17 -08:00
Michael Zhu
1ca085ec4a address comments 2019-12-02 15:39:03 -08:00
Michael Zhu
e332b7535c prettier 2019-12-02 15:39:02 -08:00
Michael Zhu
79eb613b3e Use AbiEncoder for methodAbiToFunctionSignature 2019-12-02 15:39:02 -08:00
Michael Zhu
5a79ec28d1 transaction protocol fee integration tests 2019-12-02 15:39:02 -08:00
Michael Zhu
97e65a02c0 fix test nesting 2019-12-02 15:39:02 -08:00
Michael Zhu
e87c786b77 fix dataItemsToABIString 2019-12-02 15:39:02 -08:00
Michael Zhu
251d30d47f refactor transaction integration tests to use new framework 2019-12-02 15:39:02 -08:00
Amir Bandeali
7a3f878c11 Add ChaiBridge to boilerplate 2019-12-01 15:57:01 -08:00
Amir Bandeali
b8439598bc Remove redundant getters from bridges 2019-12-01 15:55:27 -08:00
Amir Bandeali
7fb0818923 Implement ChaiBridge 2019-12-01 15:54:58 -08:00
Amir Bandeali
a7c435adc4 Add mainnet deployment addresses for Dai, Chai, and ERC20BridgeProxy 2019-12-01 15:40:29 -08:00
274 changed files with 7037 additions and 5160 deletions

View File

@@ -193,10 +193,7 @@ jobs:
working_directory: ~/repo
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/ganache-cli:4.4.0-beta.1
environment:
VERSION: latest
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
- image: 0xorg/ganache-cli
- image: 0xorg/mesh:0xV3
environment:
ETHEREUM_RPC_URL: 'http://localhost:8545'
@@ -219,7 +216,7 @@ jobs:
TAKER_FEE_UNIT_AMOUNT: 0
MESH_ENDPOINT: 'ws://localhost:60557'
command: |
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
sh -c "waitForMesh () { sleep 30; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
steps:
- checkout
- restore_cache:

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Implement `KyberBridge`. (#2352)

View File

@@ -0,0 +1,75 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IChai.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
// solhint-disable space-after-comma
contract ChaiBridge is
IERC20Bridge,
DeploymentConstants
{
/// @dev Withdraws `amount` of `from` address's Dai from the Chai contract.
/// Transfers `amount` of Dai to `to` address.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @return success The magic bytes `0x37708e9b` if successful.
function bridgeTransferFrom(
address /* tokenAddress */,
address from,
address to,
uint256 amount,
bytes calldata /* bridgeData */
)
external
returns (bytes4 success)
{
// Ensure that only the `ERC20BridgeProxy` can call this function.
require(
msg.sender == _getERC20BridgeProxyAddress(),
"ChaiBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY"
);
// Withdraw `from` address's Dai.
// NOTE: This contract must be approved to spend Chai on behalf of `from`.
bytes memory drawCalldata = abi.encodeWithSelector(
IChai(address(0)).draw.selector,
from,
amount
);
(bool success,) = _getChaiAddress().call(drawCalldata);
require(
success,
"ChaiBridge/DRAW_DAI_FAILED"
);
// Transfer Dai to `to`
// This will never fail if the `draw` call was successful
IERC20Token(_getDaiAddress()).transfer(to, amount);
return BRIDGE_SUCCESS;
}
}

View File

@@ -55,7 +55,7 @@ contract Eth2DaiBridge is
// Decode the bridge data to get the `fromTokenAddress`.
(address fromTokenAddress) = abi.decode(bridgeData, (address));
IEth2Dai exchange = _getEth2DaiContract();
IEth2Dai exchange = IEth2Dai(_getEth2DaiAddress());
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
@@ -84,14 +84,4 @@ contract Eth2DaiBridge is
{
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(_getEth2DaiAddress());
}
}

View File

@@ -77,8 +77,8 @@ contract KyberBridge is
returns (bytes4 success)
{
TradeState memory state;
state.kyber = _getKyberContract();
state.weth = _getWETHContract();
state.kyber = IKyberNetworkProxy(_getKyberNetworkProxyAddress());
state.weth = IEtherToken(_getWethAddress());
// Decode the bridge data to get the `fromTokenAddress`.
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
@@ -143,24 +143,4 @@ contract KyberBridge is
{
return LEGACY_WALLET_MAGIC_VALUE;
}
/// @dev Overridable way to get the `KyberNetworkProxy` contract.
/// @return kyber The `IKyberNetworkProxy` contract.
function _getKyberContract()
internal
view
returns (IKyberNetworkProxy kyber)
{
return IKyberNetworkProxy(_getKyberNetworkProxyAddress());
}
/// @dev Overridable way to get the WETH contract.
/// @return weth The WETH contract.
function _getWETHContract()
internal
view
returns (IEtherToken weth)
{
return IEtherToken(_getWETHAddress());
}
}

View File

@@ -88,7 +88,7 @@ contract UniswapBridge is
// Get our balance of `fromTokenAddress` token.
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Get the weth contract.
state.weth = getWethContract();
state.weth = IEtherToken(_getWethAddress());
// Convert from WETH to a token.
if (fromTokenAddress == address(state.weth)) {
@@ -161,26 +161,6 @@ contract UniswapBridge is
return LEGACY_WALLET_MAGIC_VALUE;
}
/// @dev Overridable way to get the weth contract.
/// @return token The WETH contract.
function getWethContract()
public
view
returns (IEtherToken token)
{
return IEtherToken(_getWETHAddress());
}
/// @dev Overridable way to get the uniswap exchange factory contract.
/// @return factory The exchange factory contract.
function getUniswapExchangeFactoryContract()
public
view
returns (IUniswapExchangeFactory factory)
{
return IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress());
}
/// @dev Grants an unlimited allowance to the exchange for its token
/// on behalf of this contract.
/// @param exchange The Uniswap token exchange.
@@ -207,11 +187,12 @@ contract UniswapBridge is
{
address exchangeTokenAddress = fromTokenAddress;
// Whichever isn't WETH is the exchange token.
if (fromTokenAddress == address(getWethContract())) {
if (fromTokenAddress == _getWethAddress()) {
exchangeTokenAddress = toTokenAddress;
}
exchange = IUniswapExchange(
getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress)
IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
.getExchange(exchangeTokenAddress)
);
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
return exchange;

View File

@@ -0,0 +1,33 @@
/*
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;
// The actual Chai contract can be found here: https://github.com/dapphub/chai
contract IChai {
/// @dev Withdraws Dai owned by `src`
/// @param src Address that owns Dai.
/// @param wad Amount of Dai to withdraw.
function draw(
address src,
uint256 wad
)
external;
}

View File

@@ -0,0 +1,80 @@
/*
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/bridges/ChaiBridge.sol";
import "@0x/contracts-erc20/contracts/src/ERC20Token.sol";
contract TestChaiDai is
ERC20Token
{
address private constant ALWAYS_REVERT_ADDRESS = address(1);
function draw(
address from,
uint256 amount
)
external
{
if (from == ALWAYS_REVERT_ADDRESS) {
revert();
}
balances[msg.sender] += amount;
}
}
contract TestChaiBridge is
ChaiBridge
{
address public testChaiDai;
address private constant ALWAYS_REVERT_ADDRESS = address(1);
constructor()
public
{
testChaiDai = address(new TestChaiDai());
}
function _getDaiAddress()
internal
view
returns (address)
{
return testChaiDai;
}
function _getChaiAddress()
internal
view
returns (address)
{
return testChaiDai;
}
function _getERC20BridgeProxyAddress()
internal
view
returns (address)
{
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
}
}

View File

@@ -192,11 +192,11 @@ contract TestEth2DaiBridge is
}
// @dev This contract will double as the Eth2Dai contract.
function _getEth2DaiContract()
function _getEth2DaiAddress()
internal
view
returns (IEth2Dai)
returns (address)
{
return IEth2Dai(address(this));
return address(this);
}
}

View File

@@ -303,20 +303,20 @@ contract TestKyberBridge is
}
// @dev overridden to point to this contract.
function _getKyberContract()
function _getKyberNetworkProxyAddress()
internal
view
returns (IKyberNetworkProxy kyber)
returns (address)
{
return IKyberNetworkProxy(address(this));
return address(this);
}
// @dev overridden to point to test WETH.
function _getWETHContract()
function _getWethAddress()
internal
view
returns (IEtherToken weth_)
returns (address)
{
return weth;
return address(weth);
}
}

View File

@@ -413,20 +413,20 @@ contract TestUniswapBridge is
}
// @dev Use `wethToken`.
function getWethContract()
public
function _getWethAddress()
internal
view
returns (IEtherToken)
returns (address)
{
return IEtherToken(address(wethToken));
return address(wethToken);
}
// @dev This contract will double as the Uniswap contract.
function getUniswapExchangeFactoryContract()
public
function _getUniswapExchangeFactoryAddress()
internal
view
returns (IUniswapExchangeFactory)
returns (address)
{
return IUniswapExchangeFactory(address(this));
return address(this);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "3.0.0",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -38,8 +38,8 @@
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,TestStaticCallTarget",
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,ChaiBridge,TestStaticCallTarget",
"abis": "./test/generated-artifacts/@(ChaiBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -52,15 +52,15 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/types": "^3.1.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -80,15 +80,15 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc1155": "^2.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-erc721": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc1155": "^2.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

@@ -5,6 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
@@ -29,5 +30,6 @@ export const artifacts = {
IAssetProxy: IAssetProxy as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
};

View File

@@ -3,6 +3,7 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/chai_bridge';
export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_bridge_proxy';
export * from '../generated-wrappers/erc20_proxy';

View File

@@ -5,6 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.json';
@@ -14,6 +15,7 @@ import * as IAssetData from '../test/generated-artifacts/IAssetData.json';
import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
import * as IChai from '../test/generated-artifacts/IChai.json';
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
@@ -25,6 +27,7 @@ import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizabl
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
import * as Ownable from '../test/generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json';
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json';
@@ -41,6 +44,7 @@ export const artifacts = {
ERC721Proxy: ERC721Proxy as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
@@ -48,11 +52,13 @@ export const artifacts = {
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IChai: IChai as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
IUniswapExchange: IUniswapExchange as ContractArtifact,
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
TestChaiBridge: TestChaiBridge as ContractArtifact,
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestKyberBridge: TestKyberBridge as ContractArtifact,

View File

@@ -0,0 +1,60 @@
import { ERC20TokenContract } from '@0x/contracts-erc20';
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { artifacts } from './artifacts';
import { TestChaiBridgeContract } from './wrappers';
blockchainTests.resets('ChaiBridge unit tests', env => {
let chaiBridgeContract: TestChaiBridgeContract;
let testDaiContract: ERC20TokenContract;
let fromAddress: string;
let toAddress: string;
const alwaysRevertAddress = '0x0000000000000000000000000000000000000001';
const amount = new BigNumber(1);
before(async () => {
[fromAddress, toAddress] = await env.getAccountAddressesAsync();
chaiBridgeContract = await TestChaiBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestChaiBridge,
env.provider,
env.txDefaults,
artifacts,
);
const testChaiDaiAddress = await chaiBridgeContract.testChaiDai().callAsync();
testDaiContract = new ERC20TokenContract(testChaiDaiAddress, env.provider, env.txDefaults);
});
describe('bridgeTransferFrom()', () => {
it('fails if not called by ERC20BridgeProxy', async () => {
return expect(
chaiBridgeContract
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
.awaitTransactionSuccessAsync({ from: alwaysRevertAddress }),
).to.revertWith(RevertReason.ChaiBridgeOnlyCallableByErc20BridgeProxy);
});
it('returns magic bytes upon success', async () => {
const magicBytes = await chaiBridgeContract
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
.callAsync();
expect(magicBytes).to.eq(AssetProxyId.ERC20Bridge);
});
it('should increase the Dai balance of `toAddress` by `amount` if successful', async () => {
const initialBalance = await testDaiContract.balanceOf(toAddress).callAsync();
await chaiBridgeContract
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
.awaitTransactionSuccessAsync();
const endBalance = await testDaiContract.balanceOf(toAddress).callAsync();
expect(endBalance).to.bignumber.eq(initialBalance.plus(amount));
});
it('fails if the `chai.draw` call fails', async () => {
return expect(
chaiBridgeContract
.bridgeTransferFrom(randomAddress(), alwaysRevertAddress, toAddress, amount, constants.NULL_BYTES)
.awaitTransactionSuccessAsync(),
).to.revertWith(RevertReason.ChaiBridgeDrawDaiFailed);
});
});
});

View File

@@ -3,15 +3,12 @@ import {
constants,
expect,
getRandomInteger,
hexLeftPad,
hexRightPad,
hexSlice,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
import { AssetProxyId } from '@0x/types';
import { AbiEncoder, BigNumber, StringRevertError } from '@0x/utils';
import { AbiEncoder, BigNumber, hexUtils, StringRevertError } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -21,7 +18,7 @@ import { ERC20BridgeProxyContract, TestERC20BridgeContract } from './wrappers';
blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
const PROXY_ID = AssetProxyId.ERC20Bridge;
const BRIDGE_SUCCESS_RETURN_DATA = hexRightPad(PROXY_ID);
const BRIDGE_SUCCESS_RETURN_DATA = hexUtils.rightPad(PROXY_ID);
let owner: string;
let badCaller: string;
let assetProxy: ERC20BridgeProxyContract;
@@ -173,7 +170,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
it('fails if asset data is truncated', async () => {
const opts = createTransferFromOpts();
const truncatedAssetData = hexSlice(encodeAssetData(opts.assetData), 0, -1);
const truncatedAssetData = hexUtils.slice(encodeAssetData(opts.assetData), 0, -1);
const tx = assetProxy
.transferFrom(truncatedAssetData, opts.from, opts.to, new BigNumber(opts.amount))
.awaitTransactionSuccessAsync();
@@ -197,7 +194,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
returnData: hexLeftPad('0x1'),
returnData: hexUtils.leftPad('0x1'),
}),
}),
});
@@ -210,7 +207,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
returnData: hexRightPad('0x1'),
returnData: hexUtils.rightPad('0x1'),
}),
}),
});

View File

@@ -4,13 +4,11 @@ import {
expect,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber, RawRevertError } from '@0x/utils';
import { BigNumber, hexUtils, RawRevertError } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -39,7 +37,9 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract.isValidSignature(hexRandom(), hexRandom(_.random(0, 32))).callAsync();
const result = await testContract
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
.callAsync();
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
@@ -71,7 +71,7 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
fillAmount: getRandomInteger(1, 100e18),
fromTokenBalance: getRandomInteger(1, 100e18),
toTokentransferRevertReason: '',
toTokenTransferReturnData: hexLeftPad(1),
toTokenTransferReturnData: hexUtils.leftPad(1),
...opts,
};
}
@@ -111,7 +111,7 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
_opts.toAddress,
new BigNumber(_opts.amount),
// ABI-encode the "from" token address as the bridge data.
hexLeftPad(_opts.fromTokenAddress as string),
hexUtils.leftPad(_opts.fromTokenAddress as string),
);
const result = await bridgeTransferFromFn.callAsync();
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
@@ -179,13 +179,13 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
});
it('fails if `toTokenAddress.transfer()` returns false', async () => {
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexLeftPad(0) });
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexUtils.leftPad(0) });
const tx = withdrawToAsync(opts);
return expect(tx).to.revertWith(new RawRevertError(hexLeftPad(0)));
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0)));
});
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(1) });
await withdrawToAsync({ toTokenTransferReturnData: hexUtils.leftPad(1) });
});
});
});

View File

@@ -3,13 +3,11 @@ import {
constants,
expect,
getRandomInteger,
hexLeftPad,
hexRandom,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BigNumber, hexUtils } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -33,7 +31,9 @@ blockchainTests.resets('KyberBridge unit tests', env => {
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract.isValidSignature(hexRandom(), hexRandom(_.random(0, 32))).callAsync();
const result = await testContract
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
.callAsync();
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
@@ -107,7 +107,7 @@ blockchainTests.resets('KyberBridge unit tests', env => {
// Transfer amount.
_opts.amount,
// ABI-encode the input token address as the bridge data.
hexLeftPad(_opts.fromTokenAddress),
hexUtils.leftPad(_opts.fromTokenAddress),
);
const result = await bridgeTransferFromFn.callAsync();
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();

View File

@@ -5,13 +5,11 @@ import {
filterLogs,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BigNumber, hexUtils } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -46,7 +44,9 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract.isValidSignature(hexRandom(), hexRandom(_.random(0, 32))).callAsync();
const result = await testContract
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
.callAsync();
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
@@ -126,7 +126,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
// The amount to transfer to "to"
new BigNumber(_opts.amount),
// ABI-encoded "from" token address.
hexLeftPad(_opts.fromTokenAddress),
hexUtils.leftPad(_opts.fromTokenAddress),
);
const result = await bridgeTransferFromFn.callAsync();
const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
@@ -208,7 +208,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(randomAddress()),
hexUtils.leftPad(randomAddress()),
)
.awaitTransactionSuccessAsync();
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
@@ -282,7 +282,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(wethTokenAddress),
hexUtils.leftPad(wethTokenAddress),
)
.awaitTransactionSuccessAsync();
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
@@ -342,7 +342,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(randomAddress()),
hexUtils.leftPad(randomAddress()),
)
.awaitTransactionSuccessAsync();
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');

View File

@@ -3,6 +3,7 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/chai_bridge';
export * from '../test/generated-wrappers/erc1155_proxy';
export * from '../test/generated-wrappers/erc20_bridge_proxy';
export * from '../test/generated-wrappers/erc20_proxy';
@@ -12,6 +13,7 @@ export * from '../test/generated-wrappers/i_asset_data';
export * from '../test/generated-wrappers/i_asset_proxy';
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/i_authorizable';
export * from '../test/generated-wrappers/i_chai';
export * from '../test/generated-wrappers/i_erc20_bridge';
export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network_proxy';
@@ -23,6 +25,7 @@ export * from '../test/generated-wrappers/mixin_authorizable';
export * from '../test/generated-wrappers/multi_asset_proxy';
export * from '../test/generated-wrappers/ownable';
export * from '../test/generated-wrappers/static_call_proxy';
export * from '../test/generated-wrappers/test_chai_bridge';
export * from '../test/generated-wrappers/test_erc20_bridge';
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
export * from '../test/generated-wrappers/test_kyber_bridge';

View File

@@ -3,6 +3,7 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/ChaiBridge.json",
"generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20BridgeProxy.json",
"generated-artifacts/ERC20Proxy.json",
@@ -15,6 +16,7 @@
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestStaticCallTarget.json",
"generated-artifacts/UniswapBridge.json",
"test/generated-artifacts/ChaiBridge.json",
"test/generated-artifacts/ERC1155Proxy.json",
"test/generated-artifacts/ERC20BridgeProxy.json",
"test/generated-artifacts/ERC20Proxy.json",
@@ -24,6 +26,7 @@
"test/generated-artifacts/IAssetProxy.json",
"test/generated-artifacts/IAssetProxyDispatcher.json",
"test/generated-artifacts/IAuthorizable.json",
"test/generated-artifacts/IChai.json",
"test/generated-artifacts/IERC20Bridge.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetworkProxy.json",
@@ -35,6 +38,7 @@
"test/generated-artifacts/MultiAssetProxy.json",
"test/generated-artifacts/Ownable.json",
"test/generated-artifacts/StaticCallProxy.json",
"test/generated-artifacts/TestChaiBridge.json",
"test/generated-artifacts/TestERC20Bridge.json",
"test/generated-artifacts/TestEth2DaiBridge.json",
"test/generated-artifacts/TestKyberBridge.json",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "3.0.0",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,19 +52,19 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -84,14 +84,14 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/assert": "^3.0.0",
"@0x/base-contract": "^6.0.0",
"@0x/assert": "^3.0.1",
"@0x/base-contract": "^6.0.1",
"@0x/contract-addresses": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/json-schemas": "^5.0.0",
"@0x/types": "^3.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/json-schemas": "^5.0.1",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/utils": "^5.1.0",
"ethereum-types": "^3.0.0",
"http-status-codes": "^1.3.2"
},

View File

@@ -1,5 +1,6 @@
import { hexConcat, signingUtils } from '@0x/contracts-test-utils';
import { signingUtils } from '@0x/contracts-test-utils';
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
import { hexUtils } from '@0x/utils';
import { hashUtils } from './hash_utils';
import { SignedCoordinatorApproval } from './types';
@@ -27,7 +28,7 @@ export class ApprovalFactory {
const signedApproval = {
txOrigin,
transaction,
signature: hexConcat(signatureBuff),
signature: hexUtils.concat(signatureBuff),
};
return signedApproval;
}

View File

@@ -1,7 +1,6 @@
import { hexConcat } from '@0x/contracts-test-utils';
import { eip712Utils } from '@0x/order-utils';
import { SignedZeroExTransaction } from '@0x/types';
import { signTypedDataUtils } from '@0x/utils';
import { hexUtils, signTypedDataUtils } from '@0x/utils';
export const hashUtils = {
async getApprovalHashBufferAsync(
@@ -22,7 +21,9 @@ export const hashUtils = {
verifyingContract: string,
txOrigin: string,
): Promise<string> {
const hashHex = hexConcat(await hashUtils.getApprovalHashBufferAsync(transaction, verifyingContract, txOrigin));
const hashHex = hexUtils.concat(
await hashUtils.getApprovalHashBufferAsync(transaction, verifyingContract, txOrigin),
);
return hashHex;
},
};

View File

@@ -4,15 +4,13 @@ import {
constants,
ExchangeFunctionName,
expect,
hexConcat,
hexSlice,
randomAddress,
TransactionFactory,
transactionHashUtils,
} from '@0x/contracts-test-utils';
import { LibBytesRevertErrors } from '@0x/contracts-utils';
import { SignatureType, SignedOrder } from '@0x/types';
import { BigNumber, CoordinatorRevertErrors } from '@0x/utils';
import { BigNumber, CoordinatorRevertErrors, hexUtils } from '@0x/utils';
import { ApprovalFactory } from '../src/approval_factory';
@@ -89,8 +87,8 @@ blockchainTests.resets('Mixins tests', env => {
it('should revert with with the Illegal signature type', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
transaction.signature = hexUtils.concat(
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.Illegal,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
@@ -105,7 +103,7 @@ blockchainTests.resets('Mixins tests', env => {
it('should revert with with the Invalid signature type', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(SignatureType.Invalid);
transaction.signature = hexUtils.concat(SignatureType.Invalid);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress(transactionHash, transaction.signature).callAsync()).to.revertWith(
new CoordinatorRevertErrors.SignatureError(
@@ -118,8 +116,8 @@ blockchainTests.resets('Mixins tests', env => {
it('should revert with with a signature type that equals `NSignatureTypes`', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
transaction.signature = hexUtils.concat(
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.NSignatureTypes,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
@@ -134,8 +132,8 @@ blockchainTests.resets('Mixins tests', env => {
it("should revert with with a signature type that isn't supported", async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
transaction.signature = hexUtils.concat(
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.Wallet,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
@@ -296,10 +294,10 @@ blockchainTests.resets('Mixins tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress);
const signature = hexConcat(
hexSlice(approval.signature, 0, 2),
const signature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
hexUtils.slice(approval.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -432,10 +430,10 @@ blockchainTests.resets('Mixins tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress);
const signature = hexConcat(
hexSlice(approval.signature, 0, 2),
const signature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
hexUtils.slice(approval.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -454,10 +452,10 @@ blockchainTests.resets('Mixins tests', env => {
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval1 = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress);
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress);
const approvalSignature2 = hexConcat(
hexSlice(approval2.signature, 0, 2),
const approvalSignature2 = hexUtils.concat(
hexUtils.slice(approval2.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval2.signature, 6),
hexUtils.slice(approval2.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -476,10 +474,10 @@ blockchainTests.resets('Mixins tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress);
const approvalSignature2 = hexConcat(
hexSlice(approval2.signature, 0, 2),
const approvalSignature2 = hexUtils.concat(
hexUtils.slice(approval2.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval2.signature, 6),
hexUtils.slice(approval2.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.1 - _December 9, 2019_
* Dependencies updated
## v1.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "1.0.0",
"version": "1.0.1",
"engines": {
"node": ">=6.12"
},
@@ -41,10 +41,10 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/assert": "^3.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/assert": "^3.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@types/node": "*",
@@ -59,7 +59,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0"
"@0x/base-contract": "^6.0.1"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "2.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.1 - _December 9, 2019_
* Dependencies updated
## v2.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "2.0.0",
"version": "2.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,14 +52,14 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -80,10 +80,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.1 - _December 9, 2019_
* Dependencies updated
## v1.0.0 - _December 2, 2019_
* Created package. (#2344)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20-bridge-sampler",
"version": "1.0.0",
"version": "1.0.1",
"engines": {
"node": ">=6.12"
},
@@ -50,18 +50,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-exchange-libs": "^4.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -79,10 +79,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/types": "^3.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/utils": "^5.1.0",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

@@ -4,15 +4,10 @@ import {
expect,
getRandomInteger,
getRandomPortion,
hexConcat,
hexHash,
hexLeftPad,
hexRandom,
randomAddress,
toHex,
} from '@0x/contracts-test-utils';
import { Order, OrderInfo } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
@@ -31,8 +26,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
const ERC20_PROXY_ID = '0xf47261b0';
const INVALID_ASSET_PROXY_ASSET_DATA = hexConcat('0xf47261b1', hexLeftPad(randomAddress()));
const INVALID_ASSET_DATA = hexRandom(37);
const INVALID_ASSET_PROXY_ASSET_DATA = hexUtils.concat('0xf47261b1', hexUtils.leftPad(randomAddress()));
const INVALID_ASSET_DATA = hexUtils.random(37);
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
const EMPTY_ORDERS_ERROR = 'EMPTY_ORDERS';
@@ -60,7 +55,7 @@ blockchainTests('erc20-bridge-sampler', env => {
});
function getPackedHash(...args: string[]): string {
return hexHash(hexConcat(...args.map(a => toHex(a))));
return hexUtils.hash(hexUtils.concat(...args.map(a => hexUtils.toHex(a))));
}
function getUniswapExchangeSalt(tokenAddress: string): string {
@@ -198,7 +193,7 @@ blockchainTests('erc20-bridge-sampler', env => {
}
function getDeterministicOrderInfo(order: Order): OrderInfo {
const hash = getPackedHash(toHex(order.salt, 32));
const hash = getPackedHash(hexUtils.leftPad(order.salt, 32));
return {
orderHash: hash,
orderStatus: new BigNumber(hash).mod(255).toNumber(),
@@ -207,7 +202,7 @@ blockchainTests('erc20-bridge-sampler', env => {
}
function getERC20AssetData(tokenAddress: string): string {
return hexConcat(ERC20_PROXY_ID, hexLeftPad(tokenAddress));
return hexUtils.concat(ERC20_PROXY_ID, hexUtils.leftPad(tokenAddress));
}
function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
@@ -234,7 +229,7 @@ blockchainTests('erc20-bridge-sampler', env => {
takerAssetData: getERC20AssetData(takerToken),
makerFeeAssetData: getERC20AssetData(randomAddress()),
takerFeeAssetData: getERC20AssetData(randomAddress()),
salt: new BigNumber(hexRandom()),
salt: new BigNumber(hexUtils.random()),
expirationTimeSeconds: getRandomInteger(0, 2 ** 32),
};
}

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.0.0",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -51,18 +51,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -82,7 +82,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0"
"@0x/base-contract": "^6.0.1"
},
"publishConfig": {
"access": "public"

View File

@@ -3,11 +3,10 @@ import {
constants,
expect,
getRandomInteger,
hexLeftPad,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { RawRevertError, StringRevertError } from '@0x/utils';
import { hexUtils, RawRevertError, StringRevertError } from '@0x/utils';
import { TestLibERC20TokenContract, TestLibERC20TokenTargetEvents } from './wrappers';
@@ -17,11 +16,11 @@ blockchainTests('LibERC20Token', env => {
let testContract: TestLibERC20TokenContract;
const REVERT_STRING = 'WHOOPSIE';
const ENCODED_REVERT = new StringRevertError(REVERT_STRING).encode();
const ENCODED_TRUE = hexLeftPad(1);
const ENCODED_FALSE = hexLeftPad(0);
const ENCODED_TWO = hexLeftPad(2);
const ENCODED_SHORT_TRUE = hexLeftPad(2, 31);
const ENCODED_LONG_TRUE = hexLeftPad(2, 33);
const ENCODED_TRUE = hexUtils.leftPad(1);
const ENCODED_FALSE = hexUtils.leftPad(0);
const ENCODED_TWO = hexUtils.leftPad(2);
const ENCODED_SHORT_TRUE = hexUtils.leftPad(2, 31);
const ENCODED_LONG_TRUE = hexUtils.leftPad(2, 33);
before(async () => {
testContract = await TestLibERC20TokenContract.deployFrom0xArtifactAsync(
@@ -301,14 +300,14 @@ blockchainTests('LibERC20Token', env => {
describe('decimals()', () => {
const DEFAULT_DECIMALS = 18;
const ENCODED_ZERO = hexLeftPad(0);
const ENCODED_SHORT_ZERO = hexLeftPad(0, 31);
const ENCODED_LONG_ZERO = hexLeftPad(0, 33);
const ENCODED_ZERO = hexUtils.leftPad(0);
const ENCODED_SHORT_ZERO = hexUtils.leftPad(0, 31);
const ENCODED_LONG_ZERO = hexUtils.leftPad(0, 33);
const randomDecimals = () => Math.floor(Math.random() * 256) + 1;
it('returns the number of decimals defined by the token', async () => {
const decimals = randomDecimals();
const encodedDecimals = hexLeftPad(decimals);
const encodedDecimals = hexUtils.leftPad(decimals);
const result = await testContract.testDecimals(false, ENCODED_REVERT, encodedDecimals).callAsync();
return expect(result).to.bignumber.eq(decimals);
});

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "3.0.0",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,18 +52,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -84,7 +84,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0"
"@0x/base-contract": "^6.0.1"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "4.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.1 - _December 9, 2019_
* Dependencies updated
## v4.0.0 - _December 2, 2019_
* Added buy support for ERC20Bridge (#2356)

View File

@@ -109,6 +109,7 @@ contract MixinExchangeWrapper is
// Subtract fee from makerAssetFilledAmount for the net amount acquired.
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
.safeSub(singleFillResults.takerFeePaid);
// WETH fee
} else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)) {
@@ -132,6 +133,7 @@ contract MixinExchangeWrapper is
.safeAdd(singleFillResults.protocolFeePaid);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
// Unsupported fee
} else {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedFeeError(order.takerFeeAssetData));
@@ -146,7 +148,7 @@ contract MixinExchangeWrapper is
/// @param signatures Proofs that orders have been signed by makers.
/// @return totalWethSpentAmount Total amount of WETH spent on the given orders.
/// @return totalMakerAssetAcquiredAmount Total amount of maker asset acquired from the given orders.
function _marketSellWeth(
function _marketSellNoThrow(
LibOrder.Order[] memory orders,
uint256 wethSellAmount,
bytes[] memory signatures
@@ -256,6 +258,7 @@ contract MixinExchangeWrapper is
.safeAdd(singleFillResults.protocolFeePaid);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
// Percentage fee
} else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)) {
// Calculate the remaining amount of takerAsset to sell
@@ -278,6 +281,7 @@ contract MixinExchangeWrapper is
// Subtract fee from makerAssetFilledAmount for the net amount acquired.
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
.safeSub(singleFillResults.takerFeePaid);
// Unsupported fee
} else {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedFeeError(order.takerFeeAssetData));
@@ -295,7 +299,7 @@ contract MixinExchangeWrapper is
/// @param signatures Proofs that orders have been signed by makers.
/// @return totalWethSpentAmount Total amount of WETH spent on the given orders.
/// @return totalMakerAssetAcquiredAmount Total amount of maker asset acquired from the given orders.
function _marketBuyExactAmountWithWeth(
function _marketBuyFillOrKill(
LibOrder.Order[] memory orders,
uint256 makerAssetBuyAmount,
bytes[] memory signatures

View File

@@ -63,53 +63,42 @@ contract MixinForwarderCore is
/// as possible, accounting for order and forwarder fees.
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param signatures Proofs that orders have been created by makers.
/// @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.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
)
{
// Convert ETH to WETH.
_convertEthToWeth();
// Calculate amount of WETH that won't be spent on the forwarder fee.
uint256 wethSellAmount = LibMath.getPartialAmountFloor(
PERCENTAGE_DENOMINATOR,
feePercentage.safeAdd(PERCENTAGE_DENOMINATOR),
msg.value
// Pay ETH affiliate fees to all feeRecipient addresses
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
ethFeeAmounts,
feeRecipients
);
// Spends up to wethSellAmount to fill orders, transfers purchased assets to msg.sender,
// Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender,
// and pays WETH order fees.
(
wethSpentAmount,
makerAssetAcquiredAmount
) = _marketSellWeth(
) = _marketSellNoThrow(
orders,
wethSellAmount,
wethRemaining,
signatures
);
// Transfer feePercentage of total ETH spent on orders to feeRecipient.
// Refund remaining ETH to msg.sender.
ethFeePaid = _transferEthFeeAndRefund(
wethSpentAmount,
feePercentage,
feeRecipient
);
_transferEthRefund(wethRemaining, wethSpentAmount);
}
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
@@ -119,45 +108,41 @@ contract MixinForwarderCore is
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
/// @param signatures Proofs that orders have been created by makers.
/// @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.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketBuyOrdersWithEth(
LibOrder.Order[] memory orders,
uint256 makerAssetBuyAmount,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
)
{
// Convert ETH to WETH.
_convertEthToWeth();
// Pay ETH affiliate fees to all feeRecipient addresses
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
ethFeeAmounts,
feeRecipients
);
// Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender.
(
wethSpentAmount,
makerAssetAcquiredAmount
) = _marketBuyExactAmountWithWeth(
) = _marketBuyFillOrKill(
orders,
makerAssetBuyAmount,
signatures
);
// Transfer feePercentage of total ETH spent on orders to feeRecipient.
// Refund remaining ETH to msg.sender.
ethFeePaid = _transferEthFeeAndRefund(
wethSpentAmount,
feePercentage,
feeRecipient
);
_transferEthRefund(wethRemaining, wethSpentAmount);
}
}

View File

@@ -20,7 +20,6 @@ pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
@@ -42,39 +41,61 @@ contract MixinWeth is
}
}
/// @dev Converts message call's ETH value into WETH.
function _convertEthToWeth()
internal
{
if (msg.value == 0) {
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 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 wethSpent,
uint256 feePercentage,
address payable feeRecipient
/// @dev Transfers ETH denominated fees to all feeRecipient addresses
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return ethRemaining msg.value minus the amount of ETH spent on affiliate fees.
function _transferEthFeesAndWrapRemaining(
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
internal
returns (uint256 ethFee)
returns (uint256 ethRemaining)
{
// Ensure feePercentage is less than 5%.
if (feePercentage > MAX_FEE_PERCENTAGE) {
LibRichErrors.rrevert(LibForwarderRichErrors.FeePercentageTooLargeError(
feePercentage
uint256 feesLen = ethFeeAmounts.length;
// ethFeeAmounts len must equal feeRecipients len
if (feesLen != feeRecipients.length) {
LibRichErrors.rrevert(LibForwarderRichErrors.EthFeeLengthMismatchError(
feesLen,
feeRecipients.length
));
}
// This function is always called before any other function, so we assume that
// the ETH remaining is the entire msg.value.
ethRemaining = msg.value;
for (uint256 i = 0; i != feesLen; i++) {
uint256 ethFeeAmount = ethFeeAmounts[i];
// Ensure there is enough ETH to pay the fee
if (ethRemaining < ethFeeAmount) {
LibRichErrors.rrevert(LibForwarderRichErrors.InsufficientEthForFeeError(
ethFeeAmount,
ethRemaining
));
}
// Decrease ethRemaining and transfer fee to corresponding feeRecipient
ethRemaining = ethRemaining.safeSub(ethFeeAmount);
feeRecipients[i].transfer(ethFeeAmount);
}
// Convert remaining ETH to WETH.
ETHER_TOKEN.deposit.value(ethRemaining)();
return ethRemaining;
}
/// @dev Refunds any excess ETH to msg.sender.
/// @param initialWethAmount Amount of WETH available after transferring affiliate fees.
/// @param wethSpent Amount of WETH spent when filling orders.
function _transferEthRefund(
uint256 initialWethAmount,
uint256 wethSpent
)
internal
{
// Ensure that no extra WETH owned by this contract has been spent.
if (wethSpent > msg.value) {
if (wethSpent > initialWethAmount) {
LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
wethSpent,
msg.value
@@ -82,38 +103,15 @@ contract MixinWeth is
}
// 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,
wethSpent
);
// Ensure fee is less than amount of WETH remaining.
if (ethFee > wethRemaining) {
LibRichErrors.rrevert(LibForwarderRichErrors.InsufficientEthForFeeError(
ethFee,
wethRemaining
));
}
uint256 wethRemaining = initialWethAmount.safeSub(wethSpent);
// Do nothing if no WETH remaining
if (wethRemaining > 0) {
// Convert remaining WETH to ETH
ETHER_TOKEN.withdraw(wethRemaining);
// Pay ETH to feeRecipient
if (ethFee > 0) {
feeRecipient.transfer(ethFee);
}
// Refund remaining ETH to msg.sender.
uint256 ethRefund = wethRemaining.safeSub(ethFee);
if (ethRefund > 0) {
msg.sender.transfer(ethRefund);
}
// Transfer remaining ETH to sender
msg.sender.transfer(wethRemaining);
}
}
}

View File

@@ -29,23 +29,21 @@ contract IForwarderCore {
/// as possible, accounting for order and forwarder fees.
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param signatures Proofs that orders have been created by makers.
/// @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.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
);
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
@@ -55,23 +53,21 @@ contract IForwarderCore {
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
/// @param signatures Proofs that orders have been created by makers.
/// @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.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketBuyOrdersWithEth(
LibOrder.Order[] memory orders,
uint256 makerAssetBuyAmount,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
);
}

View File

@@ -28,8 +28,6 @@ contract LibConstants {
using LibBytes for bytes;
uint256 constant internal MAX_UINT = 2**256 - 1;
uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;

View File

@@ -37,10 +37,6 @@ library LibForwarderRichErrors {
bytes4 internal constant UNSUPPORTED_FEE_ERROR_SELECTOR =
0x31360af1;
// bytes4(keccak256("FeePercentageTooLargeError(uint256)"))
bytes4 internal constant FEE_PERCENTAGE_TOO_LARGE_ERROR_SELECTOR =
0x1174fb80;
// bytes4(keccak256("InsufficientEthForFeeError(uint256,uint256)"))
bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR =
0xecf40fd9;
@@ -53,13 +49,13 @@ library LibForwarderRichErrors {
bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
0x08b18698;
// 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 =
0xbaffa474;
// bytes4(keccak256("EthFeeLengthMismatchError(uint256,uint256)"))
bytes4 internal constant ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR =
0x3ecb6ceb;
// solhint-disable func-name-mixedcase
function UnregisteredAssetProxyError()
@@ -111,19 +107,6 @@ library LibForwarderRichErrors {
);
}
function FeePercentageTooLargeError(
uint256 feePercentage
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
FEE_PERCENTAGE_TOO_LARGE_ERROR_SELECTOR,
feePercentage
);
}
function InsufficientEthForFeeError(
uint256 ethFeeRequired,
uint256 ethAvailable
@@ -167,14 +150,6 @@ library LibForwarderRichErrors {
);
}
function MsgValueCannotEqualZeroError()
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(MSG_VALUE_CANNOT_EQUAL_ZERO_ERROR_SELECTOR);
}
function Erc721AmountMustEqualOneError(
uint256 amount
)
@@ -188,4 +163,18 @@ library LibForwarderRichErrors {
);
}
function EthFeeLengthMismatchError(
uint256 ethFeesLength,
uint256 feeRecipientsLength
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR,
ethFeesLength,
feeRecipientsLength
);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "4.0.0",
"version": "4.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,24 +52,24 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-erc721": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-exchange-libs": "^4.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/types": "^3.1.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -89,7 +89,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/typescript-typings": "^5.0.0",
"ethereum-types": "^3.0.0"
},

View File

@@ -16,12 +16,10 @@ import {
constants,
expect,
getRandomInteger,
hexRandom,
hexSlice,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { BigNumber, ExchangeForwarderRevertErrors } from '@0x/utils';
import { BigNumber, ExchangeForwarderRevertErrors, hexUtils } from '@0x/utils';
import { artifacts } from './artifacts';
import { TestForwarderContract } from './wrappers';
@@ -76,7 +74,7 @@ blockchainTests('Supported asset type unit tests', env => {
erc721AssetData = assetDataEncoder.ERC721Token(erc721Token.address, nftId).getABIEncodedTransactionData();
bridgeAddress = randomAddress();
bridgeData = hexRandom();
bridgeData = hexUtils.random();
erc20BridgeAssetData = assetDataEncoder
.ERC20Bridge(erc20Token.address, bridgeAddress, bridgeData)
.getABIEncodedTransactionData();
@@ -171,12 +169,12 @@ blockchainTests('Supported asset type unit tests', env => {
);
});
it('reverts if assetData is unsupported', async () => {
const randomBytes = hexRandom();
const randomBytes = hexUtils.random();
const tx = forwarder
.transferAssetToSender(randomBytes, TRANSFER_AMOUNT)
.awaitTransactionSuccessAsync({ from: receiver });
const expectedError = new ExchangeForwarderRevertErrors.UnsupportedAssetProxyError(
hexSlice(randomBytes, 0, 4),
hexUtils.slice(randomBytes, 0, 4),
);
return expect(tx).to.revertWith(expectedError);
});

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "4.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.1 - _December 9, 2019_
* Dependencies updated
## v4.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "4.0.0",
"version": "4.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,15 +52,15 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/subproviders": "^6.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/subproviders": "^6.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -81,12 +81,12 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/types": "^3.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/contracts-utils": "^4.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/utils": "^5.1.0",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

View File

@@ -3,13 +3,12 @@ import {
constants,
describe,
expect,
hexRandom,
testCombinatoriallyWithReferenceFunc,
uint256Values,
} from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { FillResults, MatchedFillResults, Order } from '@0x/types';
import { BigNumber, LibMathRevertErrors } from '@0x/utils';
import { BigNumber, hexUtils, LibMathRevertErrors } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
@@ -46,9 +45,9 @@ blockchainTests('LibFillResults', env => {
exchangeAddress: constants.NULL_ADDRESS,
};
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomAssetData = () => hexRandom(36);
const randomUint256 = () => new BigNumber(hexRandom(constants.WORD_LENGTH));
const randomAddress = () => hexUtils.random(constants.ADDRESS_LENGTH);
const randomAssetData = () => hexUtils.random(36);
const randomUint256 = () => new BigNumber(hexUtils.random(constants.WORD_LENGTH));
let libsContract: TestLibFillResultsContract;
let makerAddressLeft: string;

View File

@@ -1,7 +1,7 @@
import { blockchainTests, constants, describe, expect, hexRandom, orderHashUtils } from '@0x/contracts-test-utils';
import { blockchainTests, constants, describe, expect, orderHashUtils } from '@0x/contracts-test-utils';
import { eip712Utils } from '@0x/order-utils';
import { Order } from '@0x/types';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import { BigNumber, hexUtils, signTypedDataUtils } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -12,10 +12,10 @@ import { artifacts } from './artifacts';
blockchainTests('LibOrder', env => {
let libOrderContract: TestLibOrderContract;
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomHash = () => hexRandom(constants.WORD_LENGTH);
const randomAddress = () => hexUtils.random(constants.ADDRESS_LENGTH);
const randomHash = () => hexUtils.random(constants.WORD_LENGTH);
const randomUint256 = () => new BigNumber(randomHash());
const randomAssetData = () => hexRandom(36);
const randomAssetData = () => hexUtils.random(36);
const EMPTY_ORDER: Order = {
exchangeAddress: constants.NULL_ADDRESS,

View File

@@ -1,14 +1,7 @@
import {
blockchainTests,
constants,
describe,
expect,
hexRandom,
transactionHashUtils,
} from '@0x/contracts-test-utils';
import { blockchainTests, constants, describe, expect, transactionHashUtils } from '@0x/contracts-test-utils';
import { eip712Utils } from '@0x/order-utils';
import { ZeroExTransaction } from '@0x/types';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import { BigNumber, hexUtils, signTypedDataUtils } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -19,10 +12,10 @@ import { artifacts } from './artifacts';
blockchainTests('LibZeroExTransaction', env => {
let libZeroExTransactionContract: TestLibZeroExTransactionContract;
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomHash = () => hexRandom(constants.WORD_LENGTH);
const randomAddress = () => hexUtils.random(constants.ADDRESS_LENGTH);
const randomHash = () => hexUtils.random(constants.WORD_LENGTH);
const randomUint256 = () => new BigNumber(randomHash());
const randomAssetData = () => hexRandom(36);
const randomAssetData = () => hexUtils.random(36);
const EMPTY_TRANSACTION: ZeroExTransaction = {
salt: constants.ZERO_AMOUNT,

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange",
"version": "3.0.0",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,21 +52,21 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-exchange-libs": "^4.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-multisig": "^4.0.0",
"@0x/contracts-staking": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-multisig": "^4.0.1",
"@0x/contracts-staking": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -88,13 +88,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc1155": "^2.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-erc721": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/utils": "^5.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc1155": "^2.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/utils": "^5.1.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,8 +1,8 @@
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
import { blockchainTests, constants, expect, hexRandom, orderHashUtils } from '@0x/contracts-test-utils';
import { blockchainTests, constants, expect, orderHashUtils } from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { Order } from '@0x/types';
import { BigNumber, ExchangeRevertErrors } from '@0x/utils';
import { BigNumber, ExchangeRevertErrors, hexUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -16,9 +16,9 @@ import {
blockchainTests('Exchange core internal functions', env => {
const CHAIN_ID = 1337;
const ONE_ETHER = constants.ONE_ETHER;
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomHash = () => hexRandom(constants.WORD_LENGTH);
const randomAssetData = () => hexRandom(36);
const randomAddress = () => hexUtils.random(constants.ADDRESS_LENGTH);
const randomHash = () => hexUtils.random(constants.WORD_LENGTH);
const randomAssetData = () => hexUtils.random(36);
let testExchange: TestExchangeInternalsContract;
let senderAddress: string;
const DEFAULT_PROTOCOL_MULTIPLIER = new BigNumber(150000);

View File

@@ -1,8 +1,8 @@
import { LibMathRevertErrors, ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
import { blockchainTests, constants, expect, hexRandom } from '@0x/contracts-test-utils';
import { blockchainTests, constants, expect } from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { FillResults, OrderInfo, OrderStatus, SignatureType } from '@0x/types';
import { BigNumber, ExchangeRevertErrors } from '@0x/utils';
import { BigNumber, ExchangeRevertErrors, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import {
@@ -16,7 +16,7 @@ import {
} from './utils/isolated_exchange_wrapper';
blockchainTests('Isolated fillOrder() tests', env => {
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomAddress = () => hexUtils.random(constants.ADDRESS_LENGTH);
const getCurrentTime = () => Math.floor(_.now() / 1000);
const { ZERO_AMOUNT, ONE_ETHER, MAX_UINT256_ROOT } = constants;
const ONE_DAY = 60 * 60 * 24;

View File

@@ -1,14 +1,6 @@
import {
blockchainTests,
constants,
expect,
hexRandom,
OrderStatus,
orderUtils,
randomAddress,
} from '@0x/contracts-test-utils';
import { blockchainTests, constants, expect, OrderStatus, orderUtils, randomAddress } from '@0x/contracts-test-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { BigNumber, ExchangeRevertErrors, RevertError } from '@0x/utils';
import { BigNumber, ExchangeRevertErrors, hexUtils, RevertError } from '@0x/utils';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
@@ -63,9 +55,9 @@ blockchainTests.resets('LibExchangeRichErrorDecoder', ({ provider, txDefaults })
const orderHash = orderUtils.generatePseudoRandomOrderHash();
const signer = randomAddress();
const validator = randomAddress();
const data = hexRandom(ERROR_DATA_LENGTH);
const signature = hexRandom(SIGNATURE_LENGTH);
const errorData = hexRandom(ERROR_DATA_LENGTH);
const data = hexUtils.random(ERROR_DATA_LENGTH);
const signature = hexUtils.random(SIGNATURE_LENGTH);
const errorData = hexUtils.random(ERROR_DATA_LENGTH);
createDecodeTest(ExchangeRevertErrors.SignatureError, [errorCode, orderHash, signer, signature]);
createDecodeTest(ExchangeRevertErrors.SignatureValidatorNotApprovedError, [signer, validator]);
createDecodeTest(ExchangeRevertErrors.EIP1271SignatureError, [validator, data, signature, errorData]);
@@ -114,7 +106,7 @@ blockchainTests.resets('LibExchangeRichErrorDecoder', ({ provider, txDefaults })
(() => {
const assetProxyAddress = randomAddress();
createDecodeTest(ExchangeRevertErrors.AssetProxyExistsError, [
hexRandom(ASSET_PROXY_ID_LENGTH),
hexUtils.random(ASSET_PROXY_ID_LENGTH),
assetProxyAddress,
]);
})();
@@ -122,14 +114,14 @@ blockchainTests.resets('LibExchangeRichErrorDecoder', ({ provider, txDefaults })
(() => {
const errorCode = ExchangeRevertErrors.AssetProxyDispatchErrorCode.UnknownAssetProxy;
const orderHash = orderUtils.generatePseudoRandomOrderHash();
const assetData = hexRandom(ASSET_DATA_LENGTH);
const assetData = hexUtils.random(ASSET_DATA_LENGTH);
createDecodeTest(ExchangeRevertErrors.AssetProxyDispatchError, [errorCode, orderHash, assetData]);
})();
(() => {
const orderHash = orderUtils.generatePseudoRandomOrderHash();
const assetData = hexRandom(ASSET_DATA_LENGTH);
const errorData = hexRandom(ERROR_DATA_LENGTH);
const assetData = hexUtils.random(ASSET_DATA_LENGTH);
const errorData = hexUtils.random(ERROR_DATA_LENGTH);
createDecodeTest(ExchangeRevertErrors.AssetProxyTransferError, [orderHash, assetData, errorData]);
})();
@@ -147,14 +139,14 @@ blockchainTests.resets('LibExchangeRichErrorDecoder', ({ provider, txDefaults })
(() => {
const transactionHash = orderUtils.generatePseudoRandomOrderHash();
const errorData = hexRandom(ERROR_DATA_LENGTH);
const errorData = hexUtils.random(ERROR_DATA_LENGTH);
createDecodeTest(ExchangeRevertErrors.TransactionExecutionError, [transactionHash, errorData]);
})();
(() => {
const errorCode = ExchangeRevertErrors.IncompleteFillErrorCode.IncompleteMarketSellOrders;
const expectedAmount = new BigNumber(hexRandom(WORD_LENGTH));
const actualAmount = new BigNumber(hexRandom(WORD_LENGTH));
const expectedAmount = new BigNumber(hexUtils.random(WORD_LENGTH));
const actualAmount = new BigNumber(hexUtils.random(WORD_LENGTH));
createDecodeTest(ExchangeRevertErrors.IncompleteFillError, [errorCode, expectedAmount, actualAmount]);
})();
});

View File

@@ -1,5 +1,5 @@
import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { blockchainTests, constants, describe, expect } from '@0x/contracts-test-utils';
import { BigNumber, hexUtils } from '@0x/utils';
import { DataItem, MethodAbi, TupleDataItem } from 'ethereum-types';
import * as _ from 'lodash';
@@ -47,11 +47,11 @@ blockchainTests.resets('Reentrancy Tests', env => {
}
// Handle bytes.
if (item.type === 'bytes') {
return hexRandom(36);
return hexUtils.random(36);
}
// Handle addresses.
if (item.type === 'address') {
return hexRandom(constants.ADDRESS_LENGTH);
return hexUtils.random(constants.ADDRESS_LENGTH);
}
// Handle bools.
if (item.type === 'bool') {
@@ -84,7 +84,7 @@ blockchainTests.resets('Reentrancy Tests', env => {
m = /^bytes(\d+)$/.exec(item.type);
if (m) {
const size = parseInt(m[1], 10) || 32;
return hexRandom(size);
return hexUtils.random(size);
}
throw new Error(`Unhandled input type: ${item.type}`);
}

View File

@@ -5,8 +5,6 @@ import {
blockchainTests,
constants,
expect,
hexConcat,
hexRandom,
LogDecoder,
OrderFactory,
orderHashUtils,
@@ -16,7 +14,7 @@ import {
transactionHashUtils,
} from '@0x/contracts-test-utils';
import { SignatureType, SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber, ExchangeRevertErrors, StringRevertError } from '@0x/utils';
import { BigNumber, ExchangeRevertErrors, hexUtils, StringRevertError } from '@0x/utils';
import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
@@ -89,11 +87,11 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
const SIGNATURE_LENGTH = 65;
const generateRandomSignature = (): string => hexRandom(SIGNATURE_LENGTH);
const generateRandomSignature = (): string => hexUtils.random(SIGNATURE_LENGTH);
const hashBytes = (bytesHex: string): string => ethUtil.bufferToHex(ethUtil.sha3(ethUtil.toBuffer(bytesHex)));
const signDataHex = (dataHex: string, privateKey: Buffer): string => {
const ecSignature = ethUtil.ecsign(ethUtil.toBuffer(dataHex), privateKey);
return hexConcat(ecSignature.v, ecSignature.r, ecSignature.s);
return hexUtils.concat(ecSignature.v, ecSignature.r, ecSignature.s);
};
type ValidateHashSignatureAsync = (
@@ -123,7 +121,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when signature type is unsupported', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(SignatureType.NSignatureTypes);
const signatureHex = hexUtils.concat(SignatureType.NSignatureTypes);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.Unsupported,
hashHex,
@@ -136,7 +134,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when SignatureType=Illegal', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(SignatureType.Illegal);
const signatureHex = hexUtils.concat(SignatureType.Illegal);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.Illegal,
hashHex,
@@ -149,14 +147,14 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return false when SignatureType=Invalid and signature has a length of zero', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(SignatureType.Invalid);
const signatureHex = hexUtils.concat(SignatureType.Invalid);
const isValidSignature = await validateAsync(hashHex, signerAddress, signatureHex);
expect(isValidSignature).to.be.false();
});
it('should revert when SignatureType=Invalid and signature length is non-zero', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat('0xdeadbeef', SignatureType.Invalid);
const signatureHex = hexUtils.concat('0xdeadbeef', SignatureType.Invalid);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InvalidLength,
hashHex,
@@ -169,14 +167,14 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return true when SignatureType=EIP712 and signature is valid', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(signDataHex(hashHex, signerPrivateKey), SignatureType.EIP712);
const signatureHex = hexUtils.concat(signDataHex(hashHex, signerPrivateKey), SignatureType.EIP712);
const isValidSignature = await validateAsync(hashHex, signerAddress, signatureHex);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=EIP712 and signature is invalid', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(generateRandomSignature(), SignatureType.EIP712);
const signatureHex = hexUtils.concat(generateRandomSignature(), SignatureType.EIP712);
const isValidSignature = await validateAsync(hashHex, signerAddress, signatureHex);
expect(isValidSignature).to.be.false();
});
@@ -187,7 +185,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
const orderHashWithEthSignPrefixHex = ethUtil.bufferToHex(
ethUtil.hashPersonalMessage(ethUtil.toBuffer(hashHex)),
);
const signatureHex = hexConcat(
const signatureHex = hexUtils.concat(
signDataHex(orderHashWithEthSignPrefixHex, signerPrivateKey),
SignatureType.EthSign,
);
@@ -198,7 +196,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return false when SignatureType=EthSign and signature is invalid', async () => {
const hashHex = getCurrentHashHex();
// Create EthSign signature
const signatureHex = hexConcat(generateRandomSignature(), SignatureType.EthSign);
const signatureHex = hexUtils.concat(generateRandomSignature(), SignatureType.EthSign);
const isValidSignature = await validateAsync(hashHex, signerAddress, signatureHex);
expect(isValidSignature).to.be.false();
});
@@ -208,7 +206,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, SignatureType.Wallet);
const signatureHex = hexUtils.concat(signatureDataHex, SignatureType.Wallet);
const isValidSignature = await validateAsync(
hashHex,
validatorWallet.address,
@@ -225,7 +223,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const notSignatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(notSignatureDataHex, SignatureType.Wallet);
const signatureHex = hexUtils.concat(notSignatureDataHex, SignatureType.Wallet);
// Validate signature
const isValidSignature = await validateAsync(
hashHex,
@@ -241,7 +239,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
const hashHex = getCurrentHashHex(validatorWallet.address);
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureHex = hexConcat(generateRandomSignature(), SignatureType.Wallet);
const signatureHex = hexUtils.concat(generateRandomSignature(), SignatureType.Wallet);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
hashHex,
validatorWallet.address,
@@ -254,7 +252,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when signer is an EOA and SignatureType=Wallet', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(SignatureType.Wallet);
const signatureHex = hexUtils.concat(SignatureType.Wallet);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
hashHex,
signerAddress,
@@ -267,7 +265,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return false when validator returns `true` and SignatureType=Wallet', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(SignatureType.Wallet);
const signatureHex = hexUtils.concat(SignatureType.Wallet);
const isValidSignature = await validateAsync(
hashHex,
validatorWallet.address,
@@ -279,7 +277,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when validator returns nothing and SignatureType=Wallet', async () => {
const hashHex = getCurrentHashHex(validatorWallet.address);
const signatureHex = hexConcat(SignatureType.Wallet);
const signatureHex = hexUtils.concat(SignatureType.Wallet);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
hashHex,
validatorWallet.address,
@@ -299,7 +297,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
const hashHex = getCurrentHashHex(validatorWallet.address);
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureHex = hexConcat(generateRandomSignature(), SignatureType.Wallet);
const signatureHex = hexUtils.concat(generateRandomSignature(), SignatureType.Wallet);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
hashHex,
validatorWallet.address,
@@ -315,14 +313,14 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Presign the hash
await exchange.preSign(hashHex).awaitTransactionSuccessAsync({ from: signerAddress });
// Validate presigned signature
const signatureHex = hexConcat(SignatureType.PreSigned);
const signatureHex = hexUtils.concat(SignatureType.PreSigned);
const isValidSignature = await validateAsync(hashHex, signerAddress, signatureHex);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=Presigned and signer has not presigned hash', async () => {
const hashHex = getCurrentHashHex();
const signatureHex = hexConcat(SignatureType.PreSigned);
const signatureHex = hexUtils.concat(SignatureType.PreSigned);
const isValidSignature = await validateAsync(hashHex, signerAddress, signatureHex);
expect(isValidSignature).to.be.false();
});
@@ -355,7 +353,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
};
it('should revert when signerAddress == 0', async () => {
const signatureHex = hexConcat(SignatureType.EIP712);
const signatureHex = hexUtils.concat(SignatureType.EIP712);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InvalidSigner,
hashHex,
@@ -367,7 +365,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert when SignatureType=Validator', async () => {
const signatureHex = hexConcat(SignatureType.Validator);
const signatureHex = hexUtils.concat(SignatureType.Validator);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InappropriateSignatureType,
hashHex,
@@ -379,7 +377,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert when SignatureType=EIP1271Wallet', async () => {
const signatureHex = hexConcat(SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(SignatureType.EIP1271Wallet);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InappropriateSignatureType,
hashHex,
@@ -466,7 +464,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
};
it('should revert when signerAddress == 0', async () => {
const signatureHex = hexConcat(SignatureType.EIP712);
const signatureHex = hexUtils.concat(SignatureType.EIP712);
const nullMakerOrder = {
...signedOrder,
makerAddress: constants.NULL_ADDRESS,
@@ -486,7 +484,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const isValidSignature = await validateAsync(
signedOrder,
signatureHex,
@@ -501,7 +499,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const notSignatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(notSignatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(notSignatureDataHex, validatorWallet.address, SignatureType.Validator);
const isValidSignature = await validateAsync(
signedOrder,
signatureHex,
@@ -513,7 +511,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return false when validator returns `true` and SignatureType=Validator', async () => {
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const isValidSignature = await validateAsync(
signedOrder,
signatureHex,
@@ -525,7 +523,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when validator returns nothing and SignatureType=Validator', async () => {
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
const expectedError = new ExchangeRevertErrors.EIP1271SignatureError(
@@ -542,7 +540,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
const expectedError = new ExchangeRevertErrors.EIP1271SignatureError(
@@ -559,7 +557,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
const expectedError = new ExchangeRevertErrors.EIP1271SignatureError(
@@ -579,7 +577,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
.awaitTransactionSuccessAsync({ from: signedOrder.makerAddress });
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureHex = hexConcat(SignatureType.Validator);
const signatureHex = hexUtils.concat(SignatureType.Validator);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InvalidLength,
@@ -599,7 +597,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const expectedError = new ExchangeRevertErrors.SignatureValidatorNotApprovedError(
signedOrder.makerAddress,
validatorWallet.address,
@@ -613,7 +611,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(signatureDataHex, SignatureType.EIP1271Wallet);
// Validate signature
const isValidSignature = await validateAsync(
signedOrder,
@@ -630,7 +628,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const notSignatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(notSignatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(notSignatureDataHex, SignatureType.EIP1271Wallet);
// Validate signature
const isValidSignature = await validateAsync(
signedOrder,
@@ -644,7 +642,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return false when validator returns `true` and SignatureType=EIP1271Wallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(signatureDataHex, SignatureType.EIP1271Wallet);
// Validate signature
const isValidSignature = await validateAsync(
signedOrder,
@@ -658,7 +656,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when validator returns nothing and SignatureType=EIP1271Wallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(signatureDataHex, SignatureType.EIP1271Wallet);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
const expectedError = new ExchangeRevertErrors.EIP1271SignatureError(
@@ -675,7 +673,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
signedOrder.makerAddress = validatorWallet.address;
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureHex = hexConcat(generateRandomSignature(), SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(generateRandomSignature(), SignatureType.EIP1271Wallet);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
const expectedError = new ExchangeRevertErrors.EIP1271SignatureError(
@@ -690,7 +688,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when validator reverts and SignatureType=EIP1271Wallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signatureHex = hexConcat(SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(SignatureType.EIP1271Wallet);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
const expectedError = new ExchangeRevertErrors.EIP1271SignatureError(
@@ -704,7 +702,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert when signer is an EOA and SignatureType=EIP1271Wallet', async () => {
const signatureHex = hexConcat(SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(SignatureType.EIP1271Wallet);
signedOrder.makerAddress = notSignerAddress;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
@@ -719,7 +717,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert when signer is an EOA and SignatureType=Validator', async () => {
const signatureHex = hexConcat(notSignerAddress, SignatureType.Validator);
const signatureHex = hexUtils.concat(notSignerAddress, SignatureType.Validator);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const data = eip1271Data.OrderWithHash(signedOrder, orderHashHex).getABIEncodedTransactionData();
const expectedError = new ExchangeRevertErrors.EIP1271SignatureError(
@@ -766,7 +764,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// We don't actually do anything with the transaction so we can just
// fill it with random data.
signedTransaction = await transactionFactory.newSignedTransactionAsync({
data: hexRandom(TRANSACTION_DATA_LENGTH),
data: hexUtils.random(TRANSACTION_DATA_LENGTH),
});
});
@@ -790,7 +788,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
};
it('should revert when signerAddress == 0', async () => {
const signatureHex = hexConcat(SignatureType.EIP712);
const signatureHex = hexUtils.concat(SignatureType.EIP712);
const nullSignerTransaction = {
...signedTransaction,
signerAddress: constants.NULL_ADDRESS,
@@ -810,7 +808,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const isValidSignature = await validateAsync(
signedTransaction,
signatureHex,
@@ -825,7 +823,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const notSignatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(notSignatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(notSignatureDataHex, validatorWallet.address, SignatureType.Validator);
const isValidSignature = await validateAsync(
signedTransaction,
signatureHex,
@@ -837,7 +835,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return false when validator returns `true` and SignatureType=Validator', async () => {
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const isValidSignature = await validateAsync(
signedTransaction,
signatureHex,
@@ -854,7 +852,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
.awaitTransactionSuccessAsync({ from: signedTransaction.signerAddress });
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureHex = hexConcat(SignatureType.Validator);
const signatureHex = hexUtils.concat(SignatureType.Validator);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InvalidLength,
@@ -868,7 +866,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when validator returns nothing and SignatureType=Validator', async () => {
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -892,7 +890,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -911,7 +909,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -934,7 +932,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(signatureDataHex, validatorWallet.address, SignatureType.Validator);
const expectedError = new ExchangeRevertErrors.SignatureValidatorNotApprovedError(
signedTransaction.signerAddress,
validatorWallet.address,
@@ -948,7 +946,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(signatureDataHex, SignatureType.EIP1271Wallet);
// Validate signature
const isValidSignature = await validateAsync(
signedTransaction,
@@ -965,7 +963,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
// just does a hash comparison.
const signatureDataHex = generateRandomSignature();
const notSignatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(notSignatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(notSignatureDataHex, SignatureType.EIP1271Wallet);
// Validate signature
const isValidSignature = await validateAsync(
signedTransaction,
@@ -979,7 +977,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should return false when validator returns `true` and SignatureType=EIP1271Wallet', async () => {
signedTransaction.signerAddress = validatorWallet.address;
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(signatureDataHex, SignatureType.EIP1271Wallet);
// Validate signature
const isValidSignature = await validateAsync(
signedTransaction,
@@ -993,7 +991,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
it('should revert when validator returns nothing and SignatureType=EIP1271Wallet', async () => {
signedTransaction.signerAddress = validatorWallet.address;
const signatureDataHex = generateRandomSignature();
const signatureHex = hexConcat(signatureDataHex, SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(signatureDataHex, SignatureType.EIP1271Wallet);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -1017,7 +1015,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
signedTransaction.signerAddress = validatorWallet.address;
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureHex = hexConcat(generateRandomSignature(), SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(generateRandomSignature(), SignatureType.EIP1271Wallet);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -1036,7 +1034,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
signedTransaction.signerAddress = validatorWallet.address;
// Doesn't have to contain a real signature since our wallet contract
// just does a hash comparison.
const signatureHex = hexConcat(generateRandomSignature(), SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(generateRandomSignature(), SignatureType.EIP1271Wallet);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -1052,7 +1050,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert when signer is an EOA and SignatureType=EIP1271Wallet', async () => {
const signatureHex = hexConcat(SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(SignatureType.EIP1271Wallet);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -1068,7 +1066,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert when signer is an EOA and SignatureType=Validator', async () => {
const signatureHex = hexConcat(notSignerAddress, SignatureType.Validator);
const signatureHex = hexUtils.concat(notSignerAddress, SignatureType.Validator);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTransaction);
const data = eip1271Data
.ZeroExTransactionWithHash(signedTransaction, transactionHashHex)
@@ -1206,7 +1204,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert if `Validator` signature type rejects during a second fill', async () => {
const signatureHex = hexConcat(validatorWallet.address, SignatureType.Validator);
const signatureHex = hexUtils.concat(validatorWallet.address, SignatureType.Validator);
signedOrder.signature = signatureHex;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
// Allow the signature check for the first fill.
@@ -1234,7 +1232,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert if `Wallet` signature type rejects during a second fill', async () => {
const signatureHex = hexConcat(SignatureType.Wallet);
const signatureHex = hexUtils.concat(SignatureType.Wallet);
signedOrder.makerAddress = validatorWallet.address;
signedOrder.signature = signatureHex;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
@@ -1263,7 +1261,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
});
it('should revert if `EIP1271Wallet` signature type rejects during a second fill', async () => {
const signatureHex = hexConcat(SignatureType.EIP1271Wallet);
const signatureHex = hexUtils.concat(SignatureType.EIP1271Wallet);
signedOrder.makerAddress = validatorWallet.address;
signedOrder.signature = signatureHex;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);

View File

@@ -1,996 +0,0 @@
// tslint:disable: max-file-line-count
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import {
blockchainTests,
constants,
describe,
ExchangeFunctionName,
expect,
getLatestBlockTimestampAsync,
OrderFactory,
orderHashUtils,
TransactionFactory,
transactionHashUtils,
} from '@0x/contracts-test-utils';
import { FillResults, OrderStatus } from '@0x/types';
import { AbiEncoder, BigNumber, ExchangeRevertErrors } from '@0x/utils';
import { LogWithDecodedArgs, MethodAbi } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { exchangeDataEncoder } from '../src/exchange_data_encoder';
import { artifacts as localArtifacts } from './artifacts';
import { ExchangeWrapper } from './utils/exchange_wrapper';
import {
ExchangeCancelEventArgs,
ExchangeCancelUpToEventArgs,
ExchangeContract,
ExchangeFillEventArgs,
ExchangeSignatureValidatorApprovalEventArgs,
ExchangeTransactionExecutionEventArgs,
} from './wrappers';
const artifacts = { ...erc20Artifacts, ...localArtifacts };
// tslint:disable:no-unnecessary-type-assertion
blockchainTests.resets('Exchange transactions', env => {
let chainId: number;
let senderAddress: string;
let owner: string;
let makerAddress: string;
let takerAddress: string;
let feeRecipientAddress: string;
let validatorAddress: string;
let taker2Address: string;
let erc20TokenA: DummyERC20TokenContract;
let erc20TokenB: DummyERC20TokenContract;
let takerFeeToken: DummyERC20TokenContract;
let makerFeeToken: DummyERC20TokenContract;
let exchangeInstance: ExchangeContract;
let erc20Proxy: ERC20ProxyContract;
let orderFactory: OrderFactory;
let makerTransactionFactory: TransactionFactory;
let takerTransactionFactory: TransactionFactory;
let taker2TransactionFactory: TransactionFactory;
let exchangeWrapper: ExchangeWrapper;
let erc20Wrapper: ERC20Wrapper;
let defaultMakerTokenAddress: string;
let defaultTakerTokenAddress: string;
let defaultMakerFeeTokenAddress: string;
let defaultTakerFeeTokenAddress: string;
let makerPrivateKey: Buffer;
let takerPrivateKey: Buffer;
let taker2PrivateKey: Buffer;
const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, env.provider, env.txDefaults);
before(async () => {
chainId = await env.getChainIdAsync();
const accounts = await env.getAccountAddressesAsync();
const usedAddresses = ([
owner,
senderAddress,
makerAddress,
takerAddress,
feeRecipientAddress,
validatorAddress,
taker2Address,
] = _.slice(accounts, 0, 7));
erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
const numDummyErc20ToDeploy = 4;
[erc20TokenA, erc20TokenB, takerFeeToken, makerFeeToken] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy,
constants.DUMMY_TOKEN_DECIMALS,
);
erc20Proxy = await erc20Wrapper.deployProxyAsync();
await erc20Wrapper.setBalancesAndAllowancesAsync();
exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
artifacts.Exchange,
env.provider,
env.txDefaults,
{},
new BigNumber(chainId),
);
exchangeWrapper = new ExchangeWrapper(exchangeInstance);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress(exchangeInstance.address).awaitTransactionSuccessAsync({ from: owner });
defaultMakerTokenAddress = erc20TokenA.address;
defaultTakerTokenAddress = erc20TokenB.address;
defaultMakerFeeTokenAddress = makerFeeToken.address;
defaultTakerFeeTokenAddress = takerFeeToken.address;
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
makerAddress,
feeRecipientAddress,
makerAssetData: await devUtils.encodeERC20AssetData(defaultMakerTokenAddress).callAsync(),
takerAssetData: await devUtils.encodeERC20AssetData(defaultTakerTokenAddress).callAsync(),
makerFeeAssetData: await devUtils.encodeERC20AssetData(defaultMakerFeeTokenAddress).callAsync(),
takerFeeAssetData: await devUtils.encodeERC20AssetData(defaultTakerFeeTokenAddress).callAsync(),
exchangeAddress: exchangeInstance.address,
chainId,
};
makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
taker2PrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(taker2Address)];
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchangeInstance.address, chainId);
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchangeInstance.address, chainId);
taker2TransactionFactory = new TransactionFactory(taker2PrivateKey, exchangeInstance.address, chainId);
});
describe('executeTransaction', () => {
describe('general functionality', () => {
it('should log the correct transactionHash if successfully executed', async () => {
const order = await orderFactory.newSignedOrderAsync();
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, takerAddress);
const transactionExecutionLogs = transactionReceipt.logs.filter(
log =>
(log as LogWithDecodedArgs<ExchangeTransactionExecutionEventArgs>).event ===
'TransactionExecution',
);
expect(transactionExecutionLogs.length).to.eq(1);
const executionLogArgs = (transactionExecutionLogs[0] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(executionLogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction),
);
});
it('should revert if the transaction is expired', async () => {
const order = await orderFactory.newSignedOrderAsync();
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, orders);
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction = await takerTransactionFactory.newSignedTransactionAsync({
data,
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
});
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.Expired,
transactionHashHex,
);
const tx = exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the actual gasPrice is greater than expected', async () => {
const order = await orderFactory.newSignedOrderAsync();
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({
data,
});
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const actualGasPrice = transaction.gasPrice.plus(1);
const expectedError = new ExchangeRevertErrors.TransactionGasPriceError(
transactionHashHex,
actualGasPrice,
transaction.gasPrice,
);
const tx = exchangeInstance
.executeTransaction(transaction, transaction.signature)
.sendTransactionAsync({ gasPrice: actualGasPrice, from: senderAddress });
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the actual gasPrice is less than expected', async () => {
const order = await orderFactory.newSignedOrderAsync();
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({
data,
});
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const actualGasPrice = transaction.gasPrice.minus(1);
const expectedError = new ExchangeRevertErrors.TransactionGasPriceError(
transactionHashHex,
actualGasPrice,
transaction.gasPrice,
);
const tx = exchangeInstance
.executeTransaction(transaction, transaction.signature)
.sendTransactionAsync({ gasPrice: actualGasPrice, from: senderAddress });
return expect(tx).to.revertWith(expectedError);
});
});
describe('fill methods', () => {
for (const fnName of [
...constants.SINGLE_FILL_FN_NAMES,
...constants.BATCH_FILL_FN_NAMES,
...constants.MARKET_FILL_FN_NAMES,
]) {
it(`${fnName} should revert if signature is invalid and not called by signer`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const v = ethUtil.toBuffer(transaction.signature.slice(0, 4));
const invalidR = ethUtil.sha3('invalidR');
const invalidS = ethUtil.sha3('invalidS');
const signatureType = ethUtil.toBuffer(`0x${transaction.signature.slice(-2)}`);
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
transaction.signature = invalidSigHex;
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadTransactionSignature,
transactionHashHex,
transaction.signerAddress,
transaction.signature,
);
const tx = exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
it(`${fnName} should be successful if signed by taker and called by sender`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(
transaction,
senderAddress,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(senderAddress);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it(`${fnName} should be successful if called by taker without a transaction signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
transaction.signature = constants.NULL_BYTES;
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, takerAddress);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(takerAddress);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it(`${fnName} should return the correct data if successful`, async () => {
const order = await orderFactory.newSignedOrderAsync();
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const returnData = await exchangeInstance
.executeTransaction(transaction, transaction.signature)
.callAsync({
from: senderAddress,
});
const abi = artifacts.Exchange.compilerOutput.abi;
const methodAbi = abi.filter(abiItem => (abiItem as MethodAbi).name === fnName)[0] as MethodAbi;
const abiEncoder = new AbiEncoder.Method(methodAbi);
const decodedReturnData = abiEncoder.decodeReturnValues(returnData);
const fillResults =
constants.BATCH_FILL_FN_NAMES.indexOf(fnName) !== -1
? decodedReturnData.fillResults[0]
: decodedReturnData.fillResults;
expect(fillResults.makerAssetFilledAmount).to.be.bignumber.eq(order.makerAssetAmount);
expect(fillResults.takerAssetFilledAmount).to.be.bignumber.eq(order.takerAssetAmount);
expect(fillResults.makerFeePaid).to.be.bignumber.eq(order.makerFee);
expect(fillResults.takerFeePaid).to.be.bignumber.eq(order.takerFee);
});
it(`${fnName} should revert if transaction has already been executed`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
await exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.AlreadyExecuted,
transactionHashHex,
);
const tx = exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
it(`${fnName} should revert and rethrow error if executeTransaction is called recursively with a signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const recursiveData = exchangeInstance
.executeTransaction(transaction, transaction.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await takerTransactionFactory.newSignedTransactionAsync({
data: recursiveData,
});
const recursiveTransactionHashHex = transactionHashUtils.getTransactionHashHex(
recursiveTransaction,
);
const noReentrancyError = new ExchangeRevertErrors.TransactionInvalidContextError(
transactionHashHex,
transaction.signerAddress,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
recursiveTransactionHashHex,
noReentrancyError,
);
const tx = exchangeWrapper.executeTransactionAsync(recursiveTransaction, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
it(`${fnName} should be successful if executeTransaction is called recursively by taker without a signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const recursiveData = exchangeInstance
.executeTransaction(transaction, constants.NULL_BYTES)
.getABIEncodedTransactionData();
const recursiveTransaction = await takerTransactionFactory.newSignedTransactionAsync({
data: recursiveData,
});
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(
recursiveTransaction,
takerAddress,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(takerAddress);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
if (
[
ExchangeFunctionName.FillOrderNoThrow,
ExchangeFunctionName.BatchFillOrdersNoThrow,
ExchangeFunctionName.MarketBuyOrdersNoThrow,
ExchangeFunctionName.MarketSellOrdersNoThrow,
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
ExchangeFunctionName.MarketSellOrdersFillOrKill,
].indexOf(fnName) === -1
) {
it(`${fnName} should revert and rethrow error if the underlying function reverts`, async () => {
const order = await orderFactory.newSignedOrderAsync();
order.signature = constants.NULL_BYTES;
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const nestedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InvalidLength,
orderHashUtils.getOrderHashHex(order),
order.makerAddress,
order.signature,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashHex,
nestedError,
);
const tx = exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
}
}
});
describe('cancelOrder', () => {
it('should revert if not signed by or called by maker', async () => {
const order = await orderFactory.newSignedOrderAsync();
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const nestedError = new ExchangeRevertErrors.ExchangeInvalidContextError(
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
orderHashUtils.getOrderHashHex(order),
takerAddress,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashHex,
nestedError,
);
const tx = exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should be successful if signed by maker and called by sender', async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(senderAddress);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it('should be successful if called by maker without a signature', async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
transaction.signature = constants.NULL_BYTES;
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, makerAddress);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(makerAddress);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
});
describe('batchCancelOrders', () => {
it('should revert if not signed by or called by maker', async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchCancelOrders,
orders,
);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const nestedError = new ExchangeRevertErrors.ExchangeInvalidContextError(
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
orderHashUtils.getOrderHashHex(orders[0]),
takerAddress,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashHex,
nestedError,
);
const tx = exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should be successful if signed by maker and called by sender', async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchCancelOrders,
orders,
);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(senderAddress);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it('should be successful if called by maker without a signature', async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchCancelOrders,
orders,
);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
transaction.signature = constants.NULL_BYTES;
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, makerAddress);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(makerAddress);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
});
describe('cancelOrdersUpTo', () => {
it('should be successful if signed by maker and called by sender', async () => {
const targetEpoch = constants.ZERO_AMOUNT;
const data = exchangeInstance.cancelOrdersUpTo(targetEpoch).getABIEncodedTransactionData();
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.orderSenderAddress).to.eq(senderAddress);
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(targetEpoch.plus(1));
});
it('should be successful if called by maker without a signature', async () => {
const targetEpoch = constants.ZERO_AMOUNT;
const data = exchangeInstance.cancelOrdersUpTo(targetEpoch).getABIEncodedTransactionData();
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, makerAddress);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.orderSenderAddress).to.eq(constants.NULL_ADDRESS);
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(targetEpoch.plus(1));
});
});
describe('preSign', () => {
it('should preSign a hash for the signer', async () => {
const order = await orderFactory.newSignedOrderAsync();
const orderHash = orderHashUtils.getOrderHashHex(order);
const data = exchangeInstance.preSign(orderHash).getABIEncodedTransactionData();
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
let isPreSigned = await exchangeInstance.preSigned(orderHash, takerAddress).callAsync();
expect(isPreSigned).to.be.eq(false);
await exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
isPreSigned = await exchangeInstance.preSigned(orderHash, takerAddress).callAsync();
expect(isPreSigned).to.be.eq(true);
});
it('should preSign a hash for the caller if called without a signature', async () => {
const order = await orderFactory.newSignedOrderAsync();
const orderHash = orderHashUtils.getOrderHashHex(order);
const data = exchangeInstance.preSign(orderHash).getABIEncodedTransactionData();
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
transaction.signature = constants.NULL_BYTES;
let isPreSigned = await exchangeInstance.preSigned(orderHash, takerAddress).callAsync();
expect(isPreSigned).to.be.eq(false);
await exchangeWrapper.executeTransactionAsync(transaction, takerAddress);
isPreSigned = await exchangeInstance.preSigned(orderHash, takerAddress).callAsync();
expect(isPreSigned).to.be.eq(true);
});
});
describe('setSignatureValidatorApproval', () => {
it('should approve a validator for the signer', async () => {
const shouldApprove = true;
const data = exchangeInstance
.setSignatureValidatorApproval(validatorAddress, shouldApprove)
.getABIEncodedTransactionData();
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, senderAddress);
const validatorApprovalLogs = transactionReceipt.logs.filter(
log =>
(log as LogWithDecodedArgs<ExchangeSignatureValidatorApprovalEventArgs>).event ===
'SignatureValidatorApproval',
);
expect(validatorApprovalLogs.length).to.eq(1);
const validatorApprovalLogArgs = (validatorApprovalLogs[0] as LogWithDecodedArgs<
ExchangeSignatureValidatorApprovalEventArgs
>).args;
expect(validatorApprovalLogArgs.signerAddress).to.eq(takerAddress);
expect(validatorApprovalLogArgs.validatorAddress).to.eq(validatorAddress);
expect(validatorApprovalLogArgs.isApproved).to.eq(shouldApprove);
});
it('should approve a validator for the caller if called with no signature', async () => {
const shouldApprove = true;
const data = exchangeInstance
.setSignatureValidatorApproval(validatorAddress, shouldApprove)
.getABIEncodedTransactionData();
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
transaction.signature = constants.NULL_BYTES;
const transactionReceipt = await exchangeWrapper.executeTransactionAsync(transaction, takerAddress);
const validatorApprovalLogs = transactionReceipt.logs.filter(
log =>
(log as LogWithDecodedArgs<ExchangeSignatureValidatorApprovalEventArgs>).event ===
'SignatureValidatorApproval',
);
expect(validatorApprovalLogs.length).to.eq(1);
const validatorApprovalLogArgs = (validatorApprovalLogs[0] as LogWithDecodedArgs<
ExchangeSignatureValidatorApprovalEventArgs
>).args;
expect(validatorApprovalLogArgs.signerAddress).to.eq(takerAddress);
expect(validatorApprovalLogArgs.validatorAddress).to.eq(validatorAddress);
expect(validatorApprovalLogArgs.isApproved).to.eq(shouldApprove);
});
});
describe('batchExecuteTransactions', () => {
it('should successfully call fillOrder via 2 transactions with different taker signatures', async () => {
const order1 = await orderFactory.newSignedOrderAsync();
const order2 = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await taker2TransactionFactory.newSignedTransactionAsync({ data: data2 });
const transactionReceipt = await exchangeWrapper.batchExecuteTransactionsAsync(
[transaction1, transaction2],
senderAddress,
);
const transactionExecutionLogs = transactionReceipt.logs.filter(
log =>
(log as LogWithDecodedArgs<ExchangeTransactionExecutionEventArgs>).event ===
'TransactionExecution',
);
expect(transactionExecutionLogs.length).to.eq(2);
const execution1LogArgs = (transactionExecutionLogs[0] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution1LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction1),
);
const execution2LogArgs = (transactionExecutionLogs[1] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution2LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction2),
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(2);
const fill1LogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fill1LogArgs.makerAddress).to.eq(makerAddress);
expect(fill1LogArgs.takerAddress).to.eq(takerAddress);
expect(fill1LogArgs.senderAddress).to.eq(senderAddress);
expect(fill1LogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fill1LogArgs.makerAssetData).to.eq(order1.makerAssetData);
expect(fill1LogArgs.takerAssetData).to.eq(order1.takerAssetData);
expect(fill1LogArgs.makerAssetFilledAmount).to.bignumber.eq(order1.makerAssetAmount);
expect(fill1LogArgs.takerAssetFilledAmount).to.bignumber.eq(order1.takerAssetAmount);
expect(fill1LogArgs.makerFeePaid).to.bignumber.eq(order1.makerFee);
expect(fill1LogArgs.takerFeePaid).to.bignumber.eq(order1.takerFee);
expect(fill1LogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order1));
const fill2LogArgs = (fillLogs[1] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fill2LogArgs.makerAddress).to.eq(makerAddress);
expect(fill2LogArgs.takerAddress).to.eq(taker2Address);
expect(fill2LogArgs.senderAddress).to.eq(senderAddress);
expect(fill2LogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fill2LogArgs.makerAssetData).to.eq(order2.makerAssetData);
expect(fill2LogArgs.takerAssetData).to.eq(order2.takerAssetData);
expect(fill2LogArgs.makerAssetFilledAmount).to.bignumber.eq(order2.makerAssetAmount);
expect(fill2LogArgs.takerAssetFilledAmount).to.bignumber.eq(order2.takerAssetAmount);
expect(fill2LogArgs.makerFeePaid).to.bignumber.eq(order2.makerFee);
expect(fill2LogArgs.takerFeePaid).to.bignumber.eq(order2.takerFee);
expect(fill2LogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order2));
});
it('should successfully call fillOrder via 2 transactions when called by taker with no signatures', async () => {
const order1 = await orderFactory.newSignedOrderAsync();
const order2 = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await takerTransactionFactory.newSignedTransactionAsync({ data: data2 });
transaction1.signature = constants.NULL_BYTES;
transaction2.signature = constants.NULL_BYTES;
const transactionReceipt = await exchangeWrapper.batchExecuteTransactionsAsync(
[transaction1, transaction2],
takerAddress,
);
const transactionExecutionLogs = transactionReceipt.logs.filter(
log =>
(log as LogWithDecodedArgs<ExchangeTransactionExecutionEventArgs>).event ===
'TransactionExecution',
);
expect(transactionExecutionLogs.length).to.eq(2);
const execution1LogArgs = (transactionExecutionLogs[0] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution1LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction1),
);
const execution2LogArgs = (transactionExecutionLogs[1] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution2LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction2),
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(2);
const fill1LogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fill1LogArgs.makerAddress).to.eq(makerAddress);
expect(fill1LogArgs.takerAddress).to.eq(takerAddress);
expect(fill1LogArgs.senderAddress).to.eq(takerAddress);
expect(fill1LogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fill1LogArgs.makerAssetData).to.eq(order1.makerAssetData);
expect(fill1LogArgs.takerAssetData).to.eq(order1.takerAssetData);
expect(fill1LogArgs.makerAssetFilledAmount).to.bignumber.eq(order1.makerAssetAmount);
expect(fill1LogArgs.takerAssetFilledAmount).to.bignumber.eq(order1.takerAssetAmount);
expect(fill1LogArgs.makerFeePaid).to.bignumber.eq(order1.makerFee);
expect(fill1LogArgs.takerFeePaid).to.bignumber.eq(order1.takerFee);
expect(fill1LogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order1));
const fill2LogArgs = (fillLogs[1] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fill2LogArgs.makerAddress).to.eq(makerAddress);
expect(fill2LogArgs.takerAddress).to.eq(takerAddress);
expect(fill2LogArgs.senderAddress).to.eq(takerAddress);
expect(fill2LogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fill2LogArgs.makerAssetData).to.eq(order2.makerAssetData);
expect(fill2LogArgs.takerAssetData).to.eq(order2.takerAssetData);
expect(fill2LogArgs.makerAssetFilledAmount).to.bignumber.eq(order2.makerAssetAmount);
expect(fill2LogArgs.takerAssetFilledAmount).to.bignumber.eq(order2.takerAssetAmount);
expect(fill2LogArgs.makerFeePaid).to.bignumber.eq(order2.makerFee);
expect(fill2LogArgs.takerFeePaid).to.bignumber.eq(order2.takerFee);
expect(fill2LogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order2));
});
it('should successfully call fillOrder via 2 transactions when one is signed by taker1 and executeTransaction is called by taker2', async () => {
const order1 = await orderFactory.newSignedOrderAsync();
const order2 = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await taker2TransactionFactory.newSignedTransactionAsync({ data: data2 });
transaction2.signature = constants.NULL_BYTES;
const transactionReceipt = await exchangeWrapper.batchExecuteTransactionsAsync(
[transaction1, transaction2],
taker2Address,
);
const transactionExecutionLogs = transactionReceipt.logs.filter(
log =>
(log as LogWithDecodedArgs<ExchangeTransactionExecutionEventArgs>).event ===
'TransactionExecution',
);
expect(transactionExecutionLogs.length).to.eq(2);
const execution1LogArgs = (transactionExecutionLogs[0] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution1LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction1),
);
const execution2LogArgs = (transactionExecutionLogs[1] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution2LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction2),
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(2);
const fill1LogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fill1LogArgs.makerAddress).to.eq(makerAddress);
expect(fill1LogArgs.takerAddress).to.eq(takerAddress);
expect(fill1LogArgs.senderAddress).to.eq(taker2Address);
expect(fill1LogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fill1LogArgs.makerAssetData).to.eq(order1.makerAssetData);
expect(fill1LogArgs.takerAssetData).to.eq(order1.takerAssetData);
expect(fill1LogArgs.makerAssetFilledAmount).to.bignumber.eq(order1.makerAssetAmount);
expect(fill1LogArgs.takerAssetFilledAmount).to.bignumber.eq(order1.takerAssetAmount);
expect(fill1LogArgs.makerFeePaid).to.bignumber.eq(order1.makerFee);
expect(fill1LogArgs.takerFeePaid).to.bignumber.eq(order1.takerFee);
expect(fill1LogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order1));
const fill2LogArgs = (fillLogs[1] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fill2LogArgs.makerAddress).to.eq(makerAddress);
expect(fill2LogArgs.takerAddress).to.eq(taker2Address);
expect(fill2LogArgs.senderAddress).to.eq(taker2Address);
expect(fill2LogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fill2LogArgs.makerAssetData).to.eq(order2.makerAssetData);
expect(fill2LogArgs.takerAssetData).to.eq(order2.takerAssetData);
expect(fill2LogArgs.makerAssetFilledAmount).to.bignumber.eq(order2.makerAssetAmount);
expect(fill2LogArgs.takerAssetFilledAmount).to.bignumber.eq(order2.takerAssetAmount);
expect(fill2LogArgs.makerFeePaid).to.bignumber.eq(order2.makerFee);
expect(fill2LogArgs.takerFeePaid).to.bignumber.eq(order2.takerFee);
expect(fill2LogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order2));
});
it('should return the correct data for 2 different fillOrder calls', async () => {
const order1 = await orderFactory.newSignedOrderAsync();
const order2 = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await taker2TransactionFactory.newSignedTransactionAsync({ data: data2 });
const returnData = await exchangeInstance
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.callAsync({ from: senderAddress });
const abi = artifacts.Exchange.compilerOutput.abi;
const methodAbi = abi.filter(
abiItem => (abiItem as MethodAbi).name === ExchangeFunctionName.FillOrder,
)[0] as MethodAbi;
const abiEncoder = new AbiEncoder.Method(methodAbi);
const fillResults1: FillResults = abiEncoder.decodeReturnValues(returnData[0]).fillResults;
const fillResults2: FillResults = abiEncoder.decodeReturnValues(returnData[1]).fillResults;
expect(fillResults1.makerAssetFilledAmount).to.be.bignumber.eq(order1.makerAssetAmount);
expect(fillResults1.takerAssetFilledAmount).to.be.bignumber.eq(order1.takerAssetAmount);
expect(fillResults1.makerFeePaid).to.be.bignumber.eq(order1.makerFee);
expect(fillResults1.takerFeePaid).to.be.bignumber.eq(order1.takerFee);
expect(fillResults2.makerAssetFilledAmount).to.be.bignumber.eq(order2.makerAssetAmount);
expect(fillResults2.takerAssetFilledAmount).to.be.bignumber.eq(order2.takerAssetAmount);
expect(fillResults2.makerFeePaid).to.be.bignumber.eq(order2.makerFee);
expect(fillResults2.takerFeePaid).to.be.bignumber.eq(order2.takerFee);
});
it('should successfully call fillOrder and cancelOrder via 2 transactions', async () => {
const order1 = await orderFactory.newSignedOrderAsync();
const order2 = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [
order2,
]);
const transaction1 = await takerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await makerTransactionFactory.newSignedTransactionAsync({ data: data2 });
const transactionReceipt = await exchangeWrapper.batchExecuteTransactionsAsync(
[transaction1, transaction2],
senderAddress,
);
const transactionExecutionLogs = transactionReceipt.logs.filter(
log =>
(log as LogWithDecodedArgs<ExchangeTransactionExecutionEventArgs>).event ===
'TransactionExecution',
);
expect(transactionExecutionLogs.length).to.eq(2);
const execution1LogArgs = (transactionExecutionLogs[0] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution1LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction1),
);
const execution2LogArgs = (transactionExecutionLogs[1] as LogWithDecodedArgs<
ExchangeTransactionExecutionEventArgs
>).args;
expect(execution2LogArgs.transactionHash).to.equal(
transactionHashUtils.getTransactionHashHex(transaction2),
);
let fillLogIndex: number = 0;
let cancelLogIndex: number = 0;
const fillLogs = transactionReceipt.logs.filter((log, index) => {
if ((log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill') {
fillLogIndex = index;
return true;
}
return false;
});
const cancelLogs = transactionReceipt.logs.filter((log, index) => {
if ((log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel') {
cancelLogIndex = index;
return true;
}
return false;
});
expect(fillLogs.length).to.eq(1);
expect(cancelLogs.length).to.eq(1);
expect(cancelLogIndex).to.greaterThan(fillLogIndex);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(senderAddress);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(order1.makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(order1.takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order1.makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order1.takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order1.makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order1.takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order1));
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(senderAddress);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(order2.makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(order2.takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order2));
});
it('should return the correct data for a fillOrder and cancelOrder call', async () => {
const order1 = await orderFactory.newSignedOrderAsync();
const order2 = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [
order2,
]);
const transaction1 = await takerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await makerTransactionFactory.newSignedTransactionAsync({ data: data2 });
const returnData = await exchangeInstance
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.callAsync({ from: senderAddress });
const abi = artifacts.Exchange.compilerOutput.abi;
const methodAbi = abi.filter(
abiItem => (abiItem as MethodAbi).name === ExchangeFunctionName.FillOrder,
)[0] as MethodAbi;
const abiEncoder = new AbiEncoder.Method(methodAbi);
const fillResults: FillResults = abiEncoder.decodeReturnValues(returnData[0]).fillResults;
expect(fillResults.makerAssetFilledAmount).to.be.bignumber.eq(order1.makerAssetAmount);
expect(fillResults.takerAssetFilledAmount).to.be.bignumber.eq(order1.takerAssetAmount);
expect(fillResults.makerFeePaid).to.be.bignumber.eq(order1.makerFee);
expect(fillResults.takerFeePaid).to.be.bignumber.eq(order1.takerFee);
expect(returnData[1]).to.eq(constants.NULL_BYTES);
});
it('should revert if a single transaction reverts', async () => {
const order = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const transaction1 = await makerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await takerTransactionFactory.newSignedTransactionAsync({ data: data2 });
const tx = exchangeWrapper.batchExecuteTransactionsAsync([transaction1, transaction2], senderAddress);
const nestedError = new ExchangeRevertErrors.OrderStatusError(
orderHashUtils.getOrderHashHex(order),
OrderStatus.Cancelled,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashUtils.getTransactionHashHex(transaction2),
nestedError,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if a single transaction is expired', async () => {
const order1 = await orderFactory.newSignedOrderAsync();
const order2 = await orderFactory.newSignedOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction1 = await takerTransactionFactory.newSignedTransactionAsync({ data: data1 });
const transaction2 = await taker2TransactionFactory.newSignedTransactionAsync({
data: data2,
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
});
const tx = exchangeWrapper.batchExecuteTransactionsAsync([transaction1, transaction2], senderAddress);
const expiredTransactionHash = transactionHashUtils.getTransactionHashHex(transaction2);
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.Expired,
expiredTransactionHash,
);
return expect(tx).to.revertWith(expectedError);
});
});
});
});

View File

@@ -1,13 +1,6 @@
import {
blockchainTests,
constants,
describe,
expect,
hexRandom,
transactionHashUtils,
} from '@0x/contracts-test-utils';
import { blockchainTests, constants, describe, expect, transactionHashUtils } from '@0x/contracts-test-utils';
import { EIP712DomainWithDefaultSchema, ZeroExTransaction } from '@0x/types';
import { BigNumber, ExchangeRevertErrors, StringRevertError } from '@0x/utils';
import { BigNumber, ExchangeRevertErrors, hexUtils, StringRevertError } from '@0x/utils';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -19,7 +12,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
let accounts: string[];
let domain: EIP712DomainWithDefaultSchema;
const randomSignature = () => hexRandom(66);
const randomSignature = () => hexUtils.random(66);
const EMPTY_ZERO_EX_TRANSACTION = {
salt: constants.ZERO_AMOUNT,
@@ -668,7 +661,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
describe('setCurrentContextAddressIfRequired', () => {
it('should set the currentContextAddress if signer not equal to sender', async () => {
const randomAddress = hexRandom(20);
const randomAddress = hexUtils.random(20);
await transactionsContract
.setCurrentContextAddressIfRequired(randomAddress, randomAddress)
.awaitTransactionSuccessAsync();
@@ -676,7 +669,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
expect(currentContextAddress).to.eq(randomAddress);
});
it('should not set the currentContextAddress if signer equal to sender', async () => {
const randomAddress = hexRandom(20);
const randomAddress = hexUtils.random(20);
await transactionsContract
.setCurrentContextAddressIfRequired(accounts[0], randomAddress)
.awaitTransactionSuccessAsync({

View File

@@ -6,12 +6,11 @@ import {
describe,
expect,
getRandomPortion,
hexRandom,
orderHashUtils,
} from '@0x/contracts-test-utils';
import { ReferenceFunctions as UtilReferenceFunctions, SafeMathRevertErrors } from '@0x/contracts-utils';
import { FillResults, Order } from '@0x/types';
import { AnyRevertError, BigNumber, ExchangeRevertErrors, StringRevertError } from '@0x/utils';
import { AnyRevertError, BigNumber, ExchangeRevertErrors, hexUtils, StringRevertError } from '@0x/utils';
import { LogEntry, LogWithDecodedArgs } from 'ethereum-types';
import * as ethjs from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -30,11 +29,11 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
const { addFillResults, getPartialAmountCeil } = LibReferenceFunctions;
const { safeSub } = UtilReferenceFunctions;
const protocolFeeMultiplier = new BigNumber(150000);
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomAssetData = () => hexRandom(34);
const randomAddress = () => hexUtils.random(constants.ADDRESS_LENGTH);
const randomAssetData = () => hexUtils.random(34);
const randomAmount = (maxAmount: BigNumber = ONE_ETHER) => maxAmount.times(_.random(0, 100, true).toFixed(12));
const randomTimestamp = () => new BigNumber(Math.floor(_.now() / 1000) + _.random(0, 34560));
const randomSalt = () => new BigNumber(hexRandom(constants.WORD_LENGTH).substr(2), 16);
const randomSalt = () => new BigNumber(hexUtils.random(constants.WORD_LENGTH).substr(2), 16);
const ALWAYS_FAILING_SALT = constants.MAX_UINT256;
const ALWAYS_FAILING_SALT_REVERT_ERROR = new StringRevertError('ALWAYS_FAILING_SALT');
const EMPTY_FILL_RESULTS = {
@@ -1090,7 +1089,7 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
takerAssetAmount: new BigNumber(20000),
}),
];
const signatures = [hexRandom()];
const signatures = [hexUtils.random()];
const fillAmount = new BigNumber(10000);
const fillResults = await roundingTestContract
.marketBuyOrdersNoThrow(orders, fillAmount, signatures)
@@ -1105,7 +1104,7 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
takerAssetAmount: new BigNumber('6300000000000000000'),
}),
];
const signatures = [hexRandom()];
const signatures = [hexUtils.random()];
const fillAmount = new BigNumber('2400000000000000000');
const fillResults = await roundingTestContract
.marketBuyOrdersNoThrow(orders, fillAmount, signatures)
@@ -1120,7 +1119,7 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
takerAssetAmount: new BigNumber('103048102885858024121'),
}),
];
const signatures = [hexRandom()];
const signatures = [hexUtils.random()];
const fillAmount = new BigNumber('84819838457312347759');
const fillResults = await roundingTestContract
.marketBuyOrdersNoThrow(orders, fillAmount, signatures)
@@ -1134,7 +1133,7 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
it(`${i + 1}/${FUZZ_COUNT}`, async () => {
const ordersCount = _.random(1, 10);
const orders = _.times(ordersCount, () => randomOrder());
const signatures = orders.map(() => hexRandom());
const signatures = orders.map(() => hexUtils.random());
const totalMakerAssetAmount = BigNumber.sum(...orders.map(o => o.makerAssetAmount));
const fillAmount = getRandomPortion(totalMakerAssetAmount);
const fillResults = await roundingTestContract

View File

@@ -1,4 +1,14 @@
[
{
"version": "5.1.0",
"changes": [
{
"note": "Export function `encodeDutchAuctionAssetData`",
"pr": 2373
}
],
"timestamp": 1575931811
},
{
"version": "5.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.1.0 - _December 9, 2019_
* Export function `encodeDutchAuctionAssetData` (#2373)
## v5.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "5.0.0",
"version": "5.1.0",
"engines": {
"node": ">=6.12"
},
@@ -52,24 +52,24 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-erc721": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-exchange-libs": "^4.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/types": "^3.1.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -77,6 +77,8 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereumjs-abi": "0.6.5",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
@@ -89,7 +91,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/typescript-typings": "^5.0.0",
"ethereum-types": "^3.0.0"
},

View File

@@ -15,7 +15,7 @@ import {
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { RevertReason, SignedOrder } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
@@ -26,6 +26,8 @@ import { DutchAuctionContract, DutchAuctionTestWrapper, WETH9Contract } from './
import { artifacts } from './artifacts';
import { encodeDutchAuctionAssetData } from './utils';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
@@ -158,7 +160,7 @@ describe(ContractName.DutchAuction, () => {
feeRecipientAddress,
// taker address or sender address should be set to the ducth auction contract
takerAddress: dutchAuctionContract.address,
makerAssetData: assetDataUtils.encodeDutchAuctionAssetData(
makerAssetData: encodeDutchAuctionAssetData(
await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(),
auctionBeginTimeSeconds,
auctionBeginAmount,
@@ -202,7 +204,7 @@ describe(ContractName.DutchAuction, () => {
describe('matchOrders', () => {
it('should be worth the begin price at the begining of the auction', async () => {
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData(
const makerAssetData = encodeDutchAuctionAssetData(
defaultERC20MakerAssetData,
auctionBeginTimeSeconds,
auctionBeginAmount,
@@ -216,7 +218,7 @@ describe(ContractName.DutchAuction, () => {
it('should be be worth the end price at the end of the auction', async () => {
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData(
const makerAssetData = encodeDutchAuctionAssetData(
defaultERC20MakerAssetData,
auctionBeginTimeSeconds,
auctionBeginAmount,
@@ -282,7 +284,7 @@ describe(ContractName.DutchAuction, () => {
it('should revert when auction expires', async () => {
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData(
const makerAssetData = encodeDutchAuctionAssetData(
defaultERC20MakerAssetData,
auctionBeginTimeSeconds,
auctionBeginAmount,
@@ -310,7 +312,7 @@ describe(ContractName.DutchAuction, () => {
});
it('begin time is less than end time', async () => {
auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData(
const makerAssetData = encodeDutchAuctionAssetData(
defaultERC20MakerAssetData,
auctionBeginTimeSeconds,
auctionBeginAmount,
@@ -336,7 +338,7 @@ describe(ContractName.DutchAuction, () => {
const erc721MakerAssetData = await devUtils
.encodeERC721AssetData(erc721Token.address, makerAssetId)
.callAsync();
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData(
const makerAssetData = encodeDutchAuctionAssetData(
erc721MakerAssetData,
auctionBeginTimeSeconds,
auctionBeginAmount,

View File

@@ -1,2 +1,23 @@
import { BigNumber } from '@0x/utils';
import * as ethAbi from 'ethereumjs-abi';
import * as ethUtil from 'ethereumjs-util';
export * from './balance_threshold_wrapper';
export * from './dutch_auction_test_wrapper';
// tslint:disable-next-line:completed-docs
export function encodeDutchAuctionAssetData(
assetData: string,
beginTimeSeconds: BigNumber,
beginAmount: BigNumber,
): string {
const assetDataBuffer = ethUtil.toBuffer(assetData);
const abiEncodedAuctionData = (ethAbi as any).rawEncode(
['uint256', 'uint256'],
[beginTimeSeconds.toString(), beginAmount.toString()],
);
const abiEncodedAuctionDataBuffer = ethUtil.toBuffer(abiEncodedAuctionData);
const dutchAuctionDataBuffer = Buffer.concat([assetDataBuffer, abiEncodedAuctionDataBuffer]);
const dutchAuctionData = ethUtil.bufferToHex(dutchAuctionDataBuffer);
return dutchAuctionData;
}

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1575931811,
"version": "2.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.0.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.1 - _December 9, 2019_
* Dependencies updated
## v2.0.0 - _December 2, 2019_
* Forwader <> ERC20Bridge integration tests (#2356)

View File

@@ -20,7 +20,6 @@ pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/bridges/Eth2DaiBridge.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IEth2Dai.sol";
contract TestEth2DaiBridge is
@@ -35,11 +34,11 @@ contract TestEth2DaiBridge is
TEST_ETH2DAI_ADDRESS = testEth2Dai;
}
function _getEth2DaiContract()
function _getEth2DaiAddress()
internal
view
returns (IEth2Dai exchange)
returns (address exchange)
{
return IEth2Dai(TEST_ETH2DAI_ADDRESS);
return TEST_ETH2DAI_ADDRESS;
}
}

View File

@@ -20,8 +20,6 @@ pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/bridges/UniswapBridge.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
contract TestUniswapBridge is
@@ -41,19 +39,19 @@ contract TestUniswapBridge is
TEST_UNISWAP_EXCHANGE_FACTORY_ADDRESS = testUniswapExchangeFactory;
}
function getWethContract()
public
function _getWethAddress()
internal
view
returns (IEtherToken token)
returns (address token)
{
return IEtherToken(TEST_WETH_ADDRESS);
return TEST_WETH_ADDRESS;
}
function getUniswapExchangeFactoryContract()
public
function _getUniswapExchangeFactoryAddress()
internal
view
returns (IUniswapExchangeFactory factory)
returns (address factory)
{
return IUniswapExchangeFactory(TEST_UNISWAP_EXCHANGE_FACTORY_ADDRESS);
return TEST_UNISWAP_EXCHANGE_FACTORY_ADDRESS;
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.0.0",
"version": "2.0.1",
"engines": {
"node": ">=6.12"
},
@@ -50,21 +50,21 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@0x/abi-gen": "^5.0.1",
"@0x/contract-addresses": "^4.0.0",
"@0x/contracts-coordinator": "^3.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-exchange-forwarder": "^4.0.0",
"@0x/contracts-exchange-libs": "^4.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/coordinator-server": "^1.0.3",
"@0x/dev-utils": "^3.0.0",
"@0x/migrations": "^5.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/contracts-coordinator": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-exchange-forwarder": "^4.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-utils": "^4.0.1",
"@0x/coordinator-server": "^1.0.4",
"@0x/dev-utils": "^3.0.1",
"@0x/migrations": "^5.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -85,18 +85,18 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-erc1155": "^2.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-erc721": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-multisig": "^4.0.0",
"@0x/contracts-staking": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/base-contract": "^6.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-erc1155": "^2.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-multisig": "^4.0.1",
"@0x/contracts-staking": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/utils": "^5.1.0",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

@@ -11,15 +11,12 @@ import {
constants,
ExchangeFunctionName,
expect,
hexConcat,
hexSlice,
orderHashUtils,
transactionHashUtils,
verifyEvents,
} from '@0x/contracts-test-utils';
import { SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { BigNumber, hexUtils } from '@0x/utils';
import { Actor } from '../framework/actors/base';
import { FeeRecipient } from '../framework/actors/fee_recipient';
@@ -96,72 +93,9 @@ blockchainTests.resets('Coordinator integration tests', env => {
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
async function simulateFillsAsync(
orders: SignedOrder[],
txReceipt: TransactionReceiptWithDecodedLogs,
msgValue?: BigNumber,
): Promise<LocalBalanceStore> {
let remainingValue = msgValue || constants.ZERO_AMOUNT;
const localBalanceStore = LocalBalanceStore.create(balanceStore);
// Transaction gas cost
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
for (const order of orders) {
// Taker -> Maker
await localBalanceStore.transferAssetAsync(
taker.address,
maker.address,
order.takerAssetAmount,
order.takerAssetData,
);
// Maker -> Taker
await localBalanceStore.transferAssetAsync(
maker.address,
taker.address,
order.makerAssetAmount,
order.makerAssetData,
);
// Taker -> Fee Recipient
await localBalanceStore.transferAssetAsync(
taker.address,
feeRecipient.address,
order.takerFee,
order.takerFeeAssetData,
);
// Maker -> Fee Recipient
await localBalanceStore.transferAssetAsync(
maker.address,
feeRecipient.address,
order.makerFee,
order.makerFeeAssetData,
);
// Protocol fee
if (remainingValue.isGreaterThanOrEqualTo(DeploymentManager.protocolFee)) {
localBalanceStore.sendEth(
txReceipt.from,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
);
remainingValue = remainingValue.minus(DeploymentManager.protocolFee);
} else {
await localBalanceStore.transferAssetAsync(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
deployment.assetDataEncoder
.ERC20Token(deployment.tokens.weth.address)
.getABIEncodedTransactionData(),
);
}
}
return localBalanceStore;
}
function expectedFillEvent(order: SignedOrder): ExchangeFillEventArgs {
return {
makerAddress: order.makerAddress,
@@ -191,10 +125,7 @@ blockchainTests.resets('Coordinator integration tests', env => {
before(async () => {
order = await maker.signOrderAsync();
data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
transaction = await taker.signTransactionAsync({
data,
gasPrice: DeploymentManager.gasPrice,
});
transaction = await taker.signTransactionAsync({ data });
approval = await feeRecipient.signCoordinatorApprovalAsync(transaction, taker.address);
});
@@ -204,7 +135,14 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, taker.address, transaction.signature, [approval.signature])
.awaitTransactionSuccessAsync({ from: taker.address, value: DeploymentManager.protocolFee });
const expectedBalances = await simulateFillsAsync([order], txReceipt, DeploymentManager.protocolFee);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(
[order],
taker.address,
txReceipt,
deployment,
DeploymentManager.protocolFee,
);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -215,7 +153,14 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value: DeploymentManager.protocolFee });
const expectedBalances = await simulateFillsAsync([order], txReceipt, DeploymentManager.protocolFee);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(
[order],
taker.address,
txReceipt,
deployment,
DeploymentManager.protocolFee,
);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -229,9 +174,12 @@ blockchainTests.resets('Coordinator integration tests', env => {
value: DeploymentManager.protocolFee.plus(1),
});
const expectedBalances = await simulateFillsAsync(
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(
[order],
taker.address,
txReceipt,
deployment,
DeploymentManager.protocolFee.plus(1),
);
await balanceStore.updateBalancesAsync();
@@ -244,7 +192,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address });
const expectedBalances = await simulateFillsAsync([order], txReceipt);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, txReceipt, deployment);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -255,7 +204,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value: new BigNumber(1) });
const expectedBalances = await simulateFillsAsync([order], txReceipt, new BigNumber(1));
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, txReceipt, deployment, new BigNumber(1));
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -273,10 +223,10 @@ blockchainTests.resets('Coordinator integration tests', env => {
return expect(tx).to.revertWith(expectedError);
});
it(`${fnName} should revert with an invalid approval signature`, async () => {
const approvalSignature = hexConcat(
hexSlice(approval.signature, 0, 2),
const approvalSignature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
hexUtils.slice(approval.signature, 6),
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
const tx = coordinator
@@ -309,10 +259,7 @@ blockchainTests.resets('Coordinator integration tests', env => {
before(async () => {
orders = [await maker.signOrderAsync(), await maker.signOrderAsync()];
data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
transaction = await taker.signTransactionAsync({
data,
gasPrice: DeploymentManager.gasPrice,
});
transaction = await taker.signTransactionAsync({ data });
approval = await feeRecipient.signCoordinatorApprovalAsync(transaction, taker.address);
});
@@ -323,7 +270,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, taker.address, transaction.signature, [approval.signature])
.awaitTransactionSuccessAsync({ from: taker.address, value });
const expectedBalances = await simulateFillsAsync(orders, txReceipt, value);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
@@ -335,7 +283,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value });
const expectedBalances = await simulateFillsAsync(orders, txReceipt, value);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
@@ -347,16 +296,17 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value });
const expectedBalances = await simulateFillsAsync(orders, txReceipt, value);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
});
it(`${fnName} should revert with an invalid approval signature`, async () => {
const approvalSignature = hexConcat(
hexSlice(approval.signature, 0, 2),
const approvalSignature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
hexUtils.slice(approval.signature, 6),
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
const tx = coordinator
@@ -400,7 +350,6 @@ blockchainTests.resets('Coordinator integration tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]);
const transaction = await maker.signTransactionAsync({
data,
gasPrice: DeploymentManager.gasPrice,
});
const txReceipt = await coordinator
.executeTransaction(transaction, maker.address, transaction.signature, [])
@@ -413,7 +362,6 @@ blockchainTests.resets('Coordinator integration tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
const transaction = await maker.signTransactionAsync({
data,
gasPrice: DeploymentManager.gasPrice,
});
const txReceipt = await coordinator
.executeTransaction(transaction, maker.address, transaction.signature, [])
@@ -425,7 +373,6 @@ blockchainTests.resets('Coordinator integration tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrdersUpTo, []);
const transaction = await maker.signTransactionAsync({
data,
gasPrice: DeploymentManager.gasPrice,
});
const txReceipt = await coordinator
.executeTransaction(transaction, maker.address, transaction.signature, [])

View File

@@ -146,7 +146,7 @@ blockchainTests.resets('matchOrders integration tests', env => {
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
describe('batchMatchOrders and batchMatchOrdersWithMaximalFill rich errors', async () => {

View File

@@ -124,7 +124,7 @@ blockchainTests.resets('Exchange wrappers', env => {
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
interface SignedOrderWithValidity {
@@ -132,13 +132,9 @@ blockchainTests.resets('Exchange wrappers', env => {
isValid: boolean;
}
async function simulateFillAsync(
signedOrder: SignedOrder,
expectedFillResults: FillResults,
shouldUseWeth: boolean,
): Promise<void> {
function simulateFill(signedOrder: SignedOrder, expectedFillResults: FillResults, shouldUseWeth: boolean): void {
// taker -> maker
await localBalances.transferAssetAsync(
localBalances.transferAsset(
taker.address,
maker.address,
expectedFillResults.takerAssetFilledAmount,
@@ -146,7 +142,7 @@ blockchainTests.resets('Exchange wrappers', env => {
);
// maker -> taker
await localBalances.transferAssetAsync(
localBalances.transferAsset(
maker.address,
taker.address,
expectedFillResults.makerAssetFilledAmount,
@@ -154,7 +150,7 @@ blockchainTests.resets('Exchange wrappers', env => {
);
// maker -> feeRecipient
await localBalances.transferAssetAsync(
localBalances.transferAsset(
maker.address,
feeRecipient,
expectedFillResults.makerFeePaid,
@@ -162,7 +158,7 @@ blockchainTests.resets('Exchange wrappers', env => {
);
// taker -> feeRecipient
await localBalances.transferAssetAsync(
localBalances.transferAsset(
taker.address,
feeRecipient,
expectedFillResults.takerFeePaid,
@@ -171,7 +167,7 @@ blockchainTests.resets('Exchange wrappers', env => {
// taker -> protocol fees
if (shouldUseWeth) {
await localBalances.transferAssetAsync(
localBalances.transferAsset(
taker.address,
deployment.staking.stakingProxy.address,
expectedFillResults.protocolFeePaid,
@@ -343,7 +339,7 @@ blockchainTests.resets('Exchange wrappers', env => {
const shouldPayWethFees = DeploymentManager.protocolFee.gt(value);
// Simulate filling the order
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
// Ensure that the correct logs were emitted and that the balances are accurate.
await assertResultsAsync(receipt, [{ signedOrder, expectedFillResults, shouldPayWethFees }]);
@@ -444,7 +440,7 @@ blockchainTests.resets('Exchange wrappers', env => {
}
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
}
const contractFn = deployment.exchange.batchFillOrders(
@@ -506,7 +502,7 @@ blockchainTests.resets('Exchange wrappers', env => {
}
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
}
const contractFn = deployment.exchange.batchFillOrKillOrders(
@@ -600,7 +596,7 @@ blockchainTests.resets('Exchange wrappers', env => {
}
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
} else {
totalFillResults.push(nullFillResults);
}
@@ -714,7 +710,7 @@ blockchainTests.resets('Exchange wrappers', env => {
takerFillAmount,
);
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
totalFillResults = addFillResults(totalFillResults, expectedFillResults);
@@ -912,7 +908,7 @@ blockchainTests.resets('Exchange wrappers', env => {
makerAssetBought,
);
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
totalFillResults = addFillResults(totalFillResults, expectedFillResults);

View File

@@ -99,7 +99,7 @@ export class FillOrderWrapper {
await this._assertOrderStateAsync(signedOrder, initTakerAssetFilledAmount);
// Simulate and execute fill then assert outputs
const [fillResults, fillEvent, txReceipt] = await this._fillOrderAsync(signedOrder, from, opts);
const [simulatedFillResults, simulatedFillEvent, simulatedFinalBalanceStore] = await simulateFillOrderAsync(
const [simulatedFillResults, simulatedFillEvent, simulatedFinalBalanceStore] = simulateFillOrder(
txReceipt,
signedOrder,
from,
@@ -169,13 +169,13 @@ export class FillOrderWrapper {
* @param initBalanceStore Account balances prior to the fill.
* @return The expected account balances, fill results, and fill events.
*/
async function simulateFillOrderAsync(
function simulateFillOrder(
txReceipt: TransactionReceiptWithDecodedLogs,
signedOrder: SignedOrder,
takerAddress: string,
initBalanceStore: BalanceStore,
opts: { takerAssetFillAmount?: BigNumber } = {},
): Promise<[FillResults, FillEventArgs, BalanceStore]> {
): [FillResults, FillEventArgs, BalanceStore] {
const balanceStore = LocalBalanceStore.create(initBalanceStore);
const takerAssetFillAmount =
opts.takerAssetFillAmount !== undefined ? opts.takerAssetFillAmount : signedOrder.takerAssetAmount;
@@ -188,28 +188,28 @@ async function simulateFillOrderAsync(
);
const fillEvent = FillOrderWrapper.simulateFillEvent(signedOrder, takerAddress, fillResults);
// Taker -> Maker
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
takerAddress,
signedOrder.makerAddress,
fillResults.takerAssetFilledAmount,
signedOrder.takerAssetData,
);
// Maker -> Taker
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
signedOrder.makerAddress,
takerAddress,
fillResults.makerAssetFilledAmount,
signedOrder.makerAssetData,
);
// Taker -> Fee Recipient
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
takerAddress,
signedOrder.feeRecipientAddress,
fillResults.takerFeePaid,
signedOrder.takerFeeAssetData,
);
// Maker -> Fee Recipient
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
signedOrder.makerAddress,
signedOrder.feeRecipientAddress,
fillResults.makerFeePaid,

View File

@@ -109,54 +109,9 @@ blockchainTests.resets('fillOrder integration tests', env => {
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
async function simulateFillAsync(
order: SignedOrder,
txReceipt: TransactionReceiptWithDecodedLogs,
msgValue?: BigNumber,
): Promise<LocalBalanceStore> {
let remainingValue = msgValue !== undefined ? msgValue : DeploymentManager.protocolFee;
const localBalanceStore = LocalBalanceStore.create(balanceStore);
// Transaction gas cost
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
// Taker -> Maker
await localBalanceStore.transferAssetAsync(
taker.address,
maker.address,
order.takerAssetAmount,
order.takerAssetData,
);
// Maker -> Taker
await localBalanceStore.transferAssetAsync(
maker.address,
taker.address,
order.makerAssetAmount,
order.makerAssetData,
);
// Protocol fee
if (remainingValue.isGreaterThanOrEqualTo(DeploymentManager.protocolFee)) {
localBalanceStore.sendEth(
txReceipt.from,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
);
remainingValue = remainingValue.minus(DeploymentManager.protocolFee);
} else {
await localBalanceStore.transferAssetAsync(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
deployment.assetDataEncoder.ERC20Token(deployment.tokens.weth.address).getABIEncodedTransactionData(),
);
}
return localBalanceStore;
}
function verifyFillEvents(order: SignedOrder, receipt: TransactionReceiptWithDecodedLogs): void {
// Ensure that the fill event was correct.
verifyEvents<ExchangeFillEventArgs>(
@@ -207,7 +162,8 @@ blockchainTests.resets('fillOrder integration tests', env => {
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
// Check balances
const expectedBalances = await simulateFillAsync(order, receipt);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, receipt, deployment, DeploymentManager.protocolFee);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
@@ -233,7 +189,8 @@ blockchainTests.resets('fillOrder integration tests', env => {
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
// Check balances
const expectedBalances = await simulateFillAsync(order, receipt);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, receipt, deployment, DeploymentManager.protocolFee);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
@@ -310,7 +267,7 @@ blockchainTests.resets('fillOrder integration tests', env => {
const [finalizePoolReceipt] = await delegator.finalizePoolsAsync([poolId]);
// Check balances
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(
deployment.staking.stakingProxy.address,
operator.address,
operatorReward,
@@ -371,7 +328,8 @@ blockchainTests.resets('fillOrder integration tests', env => {
const order = await maker.signOrderAsync();
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount, { value: constants.ZERO_AMOUNT });
const rewardsAvailable = DeploymentManager.protocolFee;
const expectedBalances = await simulateFillAsync(order, receipt, constants.ZERO_AMOUNT);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, receipt, deployment);
// End the epoch. This should wrap the staking proxy's ETH balance.
const endEpochReceipt = await delegator.endEpochAsync();
@@ -392,7 +350,7 @@ blockchainTests.resets('fillOrder integration tests', env => {
const [finalizePoolReceipt] = await delegator.finalizePoolsAsync([poolId]);
// Check balances
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(
deployment.staking.stakingProxy.address,
operator.address,
operatorReward,

View File

@@ -296,7 +296,7 @@ export class MatchOrderTester {
localBalanceStore.burnGas(takerAddress, DeploymentManager.gasPrice.times(transactionReceipt.gasUsed));
// Simulate the fill.
const expectedMatchResults = await this._simulateMatchOrdersAsync(
const expectedMatchResults = this._simulateMatchOrders(
orders,
takerAddress,
toFullMatchTransferAmounts(expectedTransferAmounts),
@@ -319,12 +319,12 @@ export class MatchOrderTester {
* @param localBalanceStore The balance store to use for the simulation.
* @return The new account balances and fill events that occurred during the match.
*/
protected async _simulateMatchOrdersAsync(
protected _simulateMatchOrders(
orders: MatchedOrders,
takerAddress: string,
transferAmounts: MatchTransferAmounts,
localBalanceStore: LocalBalanceStore,
): Promise<MatchResults> {
): MatchResults {
// prettier-ignore
const matchResults = {
orders: {
@@ -343,7 +343,7 @@ export class MatchOrderTester {
};
// Right maker asset -> left maker
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.rightOrder.makerAddress,
orders.leftOrder.makerAddress,
transferAmounts.rightMakerAssetBoughtByLeftMakerAmount,
@@ -352,7 +352,7 @@ export class MatchOrderTester {
if (orders.leftOrder.makerAddress !== orders.leftOrder.feeRecipientAddress) {
// Left maker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.leftOrder.makerAddress,
orders.leftOrder.feeRecipientAddress,
transferAmounts.leftMakerFeeAssetPaidByLeftMakerAmount,
@@ -361,7 +361,7 @@ export class MatchOrderTester {
}
// Left maker asset -> right maker
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.leftOrder.makerAddress,
orders.rightOrder.makerAddress,
transferAmounts.leftMakerAssetBoughtByRightMakerAmount,
@@ -370,7 +370,7 @@ export class MatchOrderTester {
if (orders.rightOrder.makerAddress !== orders.rightOrder.feeRecipientAddress) {
// Right maker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.rightOrder.makerAddress,
orders.rightOrder.feeRecipientAddress,
transferAmounts.rightMakerFeeAssetPaidByRightMakerAmount,
@@ -379,7 +379,7 @@ export class MatchOrderTester {
}
// Left taker profit
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.leftOrder.makerAddress,
takerAddress,
transferAmounts.leftMakerAssetReceivedByTakerAmount,
@@ -387,7 +387,7 @@ export class MatchOrderTester {
);
// Right taker profit
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.rightOrder.makerAddress,
takerAddress,
transferAmounts.rightMakerAssetReceivedByTakerAmount,
@@ -395,7 +395,7 @@ export class MatchOrderTester {
);
// Left taker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
orders.leftOrder.feeRecipientAddress,
transferAmounts.leftTakerFeeAssetPaidByTakerAmount,
@@ -403,7 +403,7 @@ export class MatchOrderTester {
);
// Right taker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
orders.rightOrder.feeRecipientAddress,
transferAmounts.rightTakerFeeAssetPaidByTakerAmount,
@@ -424,13 +424,13 @@ export class MatchOrderTester {
this._deployment.staking.stakingProxy.address,
transferAmounts.rightProtocolFeePaidByTakerInEthAmount,
);
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
this._deployment.staking.stakingProxy.address,
transferAmounts.leftProtocolFeePaidByTakerInWethAmount,
wethAssetData,
);
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
this._deployment.staking.stakingProxy.address,
transferAmounts.rightProtocolFeePaidByTakerInWethAmount,
@@ -513,7 +513,7 @@ export class MatchOrderTester {
// Add the latest match to the batch match results
batchMatchResults.matches.push(
await this._simulateMatchOrdersAsync(
this._simulateMatchOrders(
matchedOrders,
takerAddress,
toFullMatchTransferAmounts(transferAmounts[i]),

View File

@@ -163,7 +163,7 @@ blockchainTests.resets('matchOrdersWithMaximalFill integration tests', env => {
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
describe('matchOrdersWithMaximalFill', () => {

View File

@@ -163,7 +163,7 @@ blockchainTests.resets('matchOrders integration tests', env => {
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
describe('matchOrders', () => {

View File

@@ -0,0 +1,500 @@
// tslint:disable: max-file-line-count
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
import { exchangeDataEncoder, ExchangeRevertErrors } from '@0x/contracts-exchange';
import {
blockchainTests,
constants,
describe,
ExchangeFunctionName,
expect,
orderHashUtils,
transactionHashUtils,
} from '@0x/contracts-test-utils';
import { SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Actor } from '../framework/actors/base';
import { FeeRecipient } from '../framework/actors/fee_recipient';
import { Maker } from '../framework/actors/maker';
import { Taker } from '../framework/actors/taker';
import { actorAddressesByName } from '../framework/actors/utils';
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
import { LocalBalanceStore } from '../framework/balances/local_balance_store';
import { DeploymentManager } from '../framework/deployment_manager';
// tslint:disable:no-unnecessary-type-assertion
blockchainTests.resets('Transaction <> protocol fee integration tests', env => {
let deployment: DeploymentManager;
let balanceStore: BlockchainBalanceStore;
let maker: Maker;
let feeRecipient: FeeRecipient;
let alice: Taker;
let bob: Taker;
let charlie: Taker;
let wethless: Taker; // Used to test revert scenarios
let order: SignedOrder; // All orders will have the same fields, modulo salt and expiration time
let transactionA: SignedZeroExTransaction; // fillOrder transaction signed by Alice
let transactionB: SignedZeroExTransaction; // fillOrder transaction signed by Bob
let transactionC: SignedZeroExTransaction; // fillOrder transaction signed by Charlie
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 4,
numErc721TokensToDeploy: 0,
numErc1155TokensToDeploy: 0,
});
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
const [makerToken, takerToken, makerFeeToken, takerFeeToken] = deployment.tokens.erc20;
alice = new Taker({ name: 'Alice', deployment });
bob = new Taker({ name: 'Bob', deployment });
charlie = new Taker({ name: 'Charlie', deployment });
wethless = new Taker({ name: 'wethless', deployment });
feeRecipient = new FeeRecipient({
name: 'Fee recipient',
deployment,
});
maker = new Maker({
name: 'Maker',
deployment,
orderConfig: {
feeRecipientAddress: feeRecipient.address,
makerAssetData: assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(),
takerAssetData: assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(),
makerFeeAssetData: assetDataEncoder.ERC20Token(makerFeeToken.address).getABIEncodedTransactionData(),
takerFeeAssetData: assetDataEncoder.ERC20Token(takerFeeToken.address).getABIEncodedTransactionData(),
},
});
for (const taker of [alice, bob, charlie]) {
await taker.configureERC20TokenAsync(takerToken);
await taker.configureERC20TokenAsync(takerFeeToken);
await taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address);
}
await wethless.configureERC20TokenAsync(takerToken);
await wethless.configureERC20TokenAsync(takerFeeToken);
await wethless.configureERC20TokenAsync(
deployment.tokens.weth,
deployment.staking.stakingProxy.address,
constants.ZERO_AMOUNT, // wethless taker has approved the proxy, but has no weth
);
await maker.configureERC20TokenAsync(makerToken);
await maker.configureERC20TokenAsync(makerFeeToken);
balanceStore = new BlockchainBalanceStore(
{
...actorAddressesByName([alice, bob, charlie, maker, feeRecipient]),
StakingProxy: deployment.staking.stakingProxy.address,
},
{ erc20: { makerToken, takerToken, makerFeeToken, takerFeeToken, wETH: deployment.tokens.weth } },
{},
);
await balanceStore.updateBalancesAsync();
order = await maker.signOrderAsync();
let data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
transactionA = await alice.signTransactionAsync({ data });
order = await maker.signOrderAsync();
data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
transactionB = await bob.signTransactionAsync({ data });
order = await maker.signOrderAsync();
data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
transactionC = await charlie.signTransactionAsync({ data });
});
after(async () => {
Actor.reset();
});
const REFUND_AMOUNT = new BigNumber(1);
function protocolFeeError(
failedOrder: SignedOrder,
failedTransaction: SignedZeroExTransaction,
): ExchangeRevertErrors.TransactionExecutionError {
const nestedError = new ExchangeRevertErrors.PayProtocolFeeError(
orderHashUtils.getOrderHashHex(failedOrder),
DeploymentManager.protocolFee,
maker.address,
wethless.address,
'0x',
).encode();
return new ExchangeRevertErrors.TransactionExecutionError(
transactionHashUtils.getTransactionHashHex(failedTransaction),
nestedError,
);
}
describe('executeTransaction', () => {
const ETH_FEE_WITH_REFUND = DeploymentManager.protocolFee.plus(REFUND_AMOUNT);
let expectedBalances: LocalBalanceStore;
beforeEach(async () => {
await balanceStore.updateBalancesAsync();
expectedBalances = LocalBalanceStore.create(balanceStore);
});
afterEach(async () => {
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
});
describe('Simple', () => {
it('Alice executeTransaction => Alice fillOrder; protocol fee in ETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Bob fillOrder; protocol fee in ETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Alice fillOrder; protocol fee in wETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, REFUND_AMOUNT);
});
it('Alice executeTransaction => Bob fillOrder; protocol fee in wETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, REFUND_AMOUNT);
});
it('Alice executeTransaction => wETH-less taker fillOrder; reverts because protocol fee cannot be paid', async () => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const transaction = await wethless.signTransactionAsync({ data });
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
return expect(tx).to.revertWith(protocolFeeError(order, transaction));
});
it('Alice executeTransaction => Alice batchFillOrders; mixed protocol fees', async () => {
const orders = [order, await maker.signOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchFillOrders,
orders,
);
const batchFillTransaction = await alice.signTransactionAsync({ data });
const txReceipt = await deployment.exchange
.executeTransaction(batchFillTransaction, batchFillTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills(orders, alice.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Bob batchFillOrders; mixed protocol fees', async () => {
const orders = [order, await maker.signOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchFillOrders,
orders,
);
const batchFillTransaction = await bob.signTransactionAsync({ data });
const txReceipt = await deployment.exchange
.executeTransaction(batchFillTransaction, batchFillTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills(orders, bob.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
});
describe('Nested', () => {
it('Alice executeTransaction => Alice executeTransaction => Alice fillOrder; protocol fee in ETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Alice executeTransaction => Bob fillOrder; protocol fee in ETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Alice executeTransaction => Alice fillOrder; protocol fee in wETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, REFUND_AMOUNT);
});
it('Alice executeTransaction => Alice executeTransaction => Bob fillOrder; protocol fee in wETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, REFUND_AMOUNT);
});
it('Alice executeTransaction => Alice executeTransaction => wETH-less taker fillOrder; reverts because protocol fee cannot be paid', async () => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const transaction = await wethless.signTransactionAsync({ data });
const recursiveData = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const tx = deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashUtils.getTransactionHashHex(recursiveTransaction),
protocolFeeError(order, transaction).encode(),
);
return expect(tx).to.revertWith(expectedError);
});
});
});
describe('batchExecuteTransactions', () => {
let expectedBalances: LocalBalanceStore;
beforeEach(async () => {
await balanceStore.updateBalancesAsync();
expectedBalances = LocalBalanceStore.create(balanceStore);
});
afterEach(async () => {
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
});
describe('Simple', () => {
// All orders' protocol fees paid in ETH by sender
const ETH_FEES_WITH_REFUND = DeploymentManager.protocolFee.times(3).plus(REFUND_AMOUNT);
// First order's protocol fee paid in ETH by sender, the other two paid in WETH by their respective takers
const MIXED_FEES_WITH_REFUND = DeploymentManager.protocolFee.times(1).plus(REFUND_AMOUNT);
it('Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder; protocol fees in ETH', async () => {
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[alice.address, bob.address, charlie.address],
txReceipt,
deployment,
ETH_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Alice fillOrder, Charlie fillOrder; protocol fees in ETH', async () => {
const transactions = [transactionB, transactionA, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, alice.address, charlie.address],
txReceipt,
deployment,
ETH_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, Alice fillOrder; protocol fees in ETH', async () => {
const transactions = [transactionB, transactionC, transactionA];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, charlie.address, alice.address],
txReceipt,
deployment,
ETH_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder; protocol fees in wETH', async () => {
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills(
[order, order, order],
[alice.address, bob.address, charlie.address],
txReceipt,
deployment,
REFUND_AMOUNT,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Alice fillOrder, Charlie fillOrder; protocol fees in wETH', async () => {
const transactions = [transactionB, transactionA, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, alice.address, charlie.address],
txReceipt,
deployment,
REFUND_AMOUNT,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, Alice fillOrder; protocol fees in wETH', async () => {
const transactions = [transactionB, transactionC, transactionA];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, charlie.address, alice.address],
txReceipt,
deployment,
REFUND_AMOUNT,
);
});
it('Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[alice.address, bob.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Alice fillOrder, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [transactionB, transactionA, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, alice.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, Alice fillOrder; mixed protocol fees', async () => {
const transactions = [transactionB, transactionC, transactionA];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, charlie.address, alice.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, wETH-less taker fillOrder; reverts because protocol fee cannot be paid', async () => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const failTransaction = await wethless.signTransactionAsync({ data });
const transactions = [transactionA, transactionB, failTransaction];
const signatures = transactions.map(transaction => transaction.signature);
const tx = deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
return expect(tx).to.revertWith(protocolFeeError(order, failTransaction));
});
});
describe('Nested', () => {
// First two orders' protocol fees paid in ETH by sender, the others paid in WETH by their respective takers
const MIXED_FEES_WITH_REFUND = DeploymentManager.protocolFee.times(2.5);
// Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder
let nestedTransaction: SignedZeroExTransaction;
// Second fillOrder transaction signed by Bob
let transactionB2: SignedZeroExTransaction;
// Second fillOrder transaction signed by Charlie
let transactionC2: SignedZeroExTransaction;
before(async () => {
let newOrder = await maker.signOrderAsync();
let data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [newOrder]);
transactionB2 = await bob.signTransactionAsync({ data });
newOrder = await maker.signOrderAsync();
data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [newOrder]);
transactionC2 = await charlie.signTransactionAsync({ data });
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const recursiveData = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.getABIEncodedTransactionData();
nestedTransaction = await alice.signTransactionAsync({ data: recursiveData });
});
it('Alice executeTransaction => nested batchExecuteTransactions; mixed protocol fees', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(nestedTransaction, nestedTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[alice.address, bob.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => nested batchExecuteTransactions, Bob fillOrder, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [nestedTransaction, transactionB2, transactionC2];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order, order, order],
[alice.address, bob.address, charlie.address, bob.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, nested batchExecuteTransactions, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [transactionB2, nestedTransaction, transactionC2];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order, order, order],
[bob.address, alice.address, bob.address, charlie.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, nested batchExecuteTransactions; mixed protocol fees', async () => {
const transactions = [transactionB2, transactionC2, nestedTransaction];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order, order, order],
[bob.address, charlie.address, alice.address, bob.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
});
});
});

View File

@@ -0,0 +1,807 @@
// tslint:disable: max-file-line-count
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
import {
ExchangeCancelEventArgs,
ExchangeCancelUpToEventArgs,
exchangeDataEncoder,
ExchangeEvents,
ExchangeFillEventArgs,
ExchangeRevertErrors,
ExchangeSignatureValidatorApprovalEventArgs,
ExchangeTransactionExecutionEventArgs,
} from '@0x/contracts-exchange';
import { ReferenceFunctions } from '@0x/contracts-exchange-libs';
import {
blockchainTests,
constants,
describe,
ExchangeFunctionName,
expect,
getLatestBlockTimestampAsync,
orderHashUtils,
randomAddress,
transactionHashUtils,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { FillResults, OrderStatus, SignatureType, SignedOrder } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import { LogWithDecodedArgs } from 'ethereum-types';
import { Actor } from '../framework/actors/base';
import { FeeRecipient } from '../framework/actors/fee_recipient';
import { Maker } from '../framework/actors/maker';
import { Taker } from '../framework/actors/taker';
import { DeploymentManager } from '../framework/deployment_manager';
// tslint:disable:no-unnecessary-type-assertion
blockchainTests.resets('Transaction integration tests', env => {
let deployment: DeploymentManager;
let maker: Maker;
let takers: [Taker, Taker];
let feeRecipient: FeeRecipient;
let sender: Actor;
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 4,
numErc721TokensToDeploy: 0,
numErc1155TokensToDeploy: 0,
});
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
const [makerToken, takerToken, makerFeeToken, takerFeeToken] = deployment.tokens.erc20;
takers = [new Taker({ name: 'Taker 1', deployment }), new Taker({ name: 'Taker 2', deployment })];
feeRecipient = new FeeRecipient({
name: 'Fee recipient',
deployment,
});
maker = new Maker({
name: 'Maker',
deployment,
orderConfig: {
feeRecipientAddress: feeRecipient.address,
makerAssetData: assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(),
takerAssetData: assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(),
makerFeeAssetData: assetDataEncoder.ERC20Token(makerFeeToken.address).getABIEncodedTransactionData(),
takerFeeAssetData: assetDataEncoder.ERC20Token(takerFeeToken.address).getABIEncodedTransactionData(),
},
});
sender = new Actor({ name: 'Transaction sender', deployment });
for (const taker of takers) {
await taker.configureERC20TokenAsync(takerToken);
await taker.configureERC20TokenAsync(takerFeeToken);
await taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address);
}
await maker.configureERC20TokenAsync(makerToken);
await maker.configureERC20TokenAsync(makerFeeToken);
});
after(async () => {
Actor.reset();
});
function defaultFillEvent(order: SignedOrder): ExchangeFillEventArgs {
return {
makerAddress: maker.address,
feeRecipientAddress: feeRecipient.address,
makerAssetData: order.makerAssetData,
takerAssetData: order.takerAssetData,
makerFeeAssetData: order.makerFeeAssetData,
takerFeeAssetData: order.takerFeeAssetData,
orderHash: orderHashUtils.getOrderHashHex(order),
takerAddress: takers[0].address,
senderAddress: sender.address,
makerAssetFilledAmount: order.makerAssetAmount,
takerAssetFilledAmount: order.takerAssetAmount,
makerFeePaid: order.makerFee,
takerFeePaid: order.takerFee,
protocolFeePaid: DeploymentManager.protocolFee,
};
}
function defaultCancelEvent(order: SignedOrder): ExchangeCancelEventArgs {
return {
makerAddress: maker.address,
feeRecipientAddress: feeRecipient.address,
makerAssetData: order.makerAssetData,
takerAssetData: order.takerAssetData,
senderAddress: sender.address,
orderHash: orderHashUtils.getOrderHashHex(order),
};
}
describe('executeTransaction', () => {
describe('general functionality', () => {
it('should log the correct transactionHash if successfully executed', async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs(
transactionReceipt.logs,
[{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction) }],
ExchangeEvents.TransactionExecution,
);
});
it('should revert if the transaction is expired', async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction = await takers[0].signTransactionAsync({
data,
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
});
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.Expired,
transactionHashHex,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the actual gasPrice is greater than expected', async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const actualGasPrice = transaction.gasPrice.plus(1);
const expectedError = new ExchangeRevertErrors.TransactionGasPriceError(
transactionHashHex,
actualGasPrice,
transaction.gasPrice,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ gasPrice: actualGasPrice, from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the actual gasPrice is less than expected', async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const actualGasPrice = transaction.gasPrice.minus(1);
const expectedError = new ExchangeRevertErrors.TransactionGasPriceError(
transactionHashHex,
actualGasPrice,
transaction.gasPrice,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ gasPrice: actualGasPrice, from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
});
describe('fill methods', () => {
for (const fnName of [
...constants.SINGLE_FILL_FN_NAMES,
...constants.BATCH_FILL_FN_NAMES,
...constants.MARKET_FILL_FN_NAMES,
]) {
it(`${fnName} should revert if signature is invalid and not called by signer`, async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
transaction.signature = hexUtils.concat(hexUtils.random(65), SignatureType.EthSign);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadTransactionSignature,
transactionHashHex,
transaction.signerAddress,
transaction.signature,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
it(`${fnName} should be successful if signed by taker and called by sender`, async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs<ExchangeFillEventArgs>(
transactionReceipt.logs,
[defaultFillEvent(order)],
ExchangeEvents.Fill,
);
});
it(`${fnName} should be successful if called by taker without a transaction signature`, async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, constants.NULL_BYTES)
.awaitTransactionSuccessAsync({ from: takers[0].address });
verifyEventsFromLogs<ExchangeFillEventArgs>(
transactionReceipt.logs,
[{ ...defaultFillEvent(order), senderAddress: takers[0].address }],
ExchangeEvents.Fill,
);
});
it(`${fnName} should return the correct data if successful`, async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const returnData = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.callAsync({ from: sender.address });
const decodedReturnData = deployment.exchange.getABIDecodedReturnData(fnName, returnData);
const fillResults = Array.isArray(decodedReturnData) ? decodedReturnData[0] : decodedReturnData;
expect(fillResults).to.deep.equal(
ReferenceFunctions.calculateFillResults(
order,
order.takerAssetAmount,
DeploymentManager.protocolFeeMultiplier,
DeploymentManager.gasPrice,
),
);
});
it(`${fnName} should revert if transaction has already been executed`, async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.AlreadyExecuted,
transactionHashHex,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
it(`${fnName} should revert and rethrow error if executeTransaction is called recursively with a signature`, async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const recursiveData = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await takers[0].signTransactionAsync({
data: recursiveData,
});
const recursiveTransactionHashHex = transactionHashUtils.getTransactionHashHex(
recursiveTransaction,
);
const noReentrancyError = new ExchangeRevertErrors.TransactionInvalidContextError(
transactionHashHex,
transaction.signerAddress,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
recursiveTransactionHashHex,
noReentrancyError,
);
const tx = deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
it(`${fnName} should be successful if executeTransaction is called recursively by taker without a signature`, async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const recursiveData = deployment.exchange
.executeTransaction(transaction, constants.NULL_BYTES)
.getABIEncodedTransactionData();
const recursiveTransaction = await takers[0].signTransactionAsync({
data: recursiveData,
});
const transactionReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: takers[0].address });
verifyEventsFromLogs<ExchangeFillEventArgs>(
transactionReceipt.logs,
[{ ...defaultFillEvent(order), senderAddress: takers[0].address }],
ExchangeEvents.Fill,
);
});
if (
[
ExchangeFunctionName.FillOrderNoThrow,
ExchangeFunctionName.BatchFillOrdersNoThrow,
ExchangeFunctionName.MarketBuyOrdersNoThrow,
ExchangeFunctionName.MarketSellOrdersNoThrow,
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
ExchangeFunctionName.MarketSellOrdersFillOrKill,
].indexOf(fnName) === -1
) {
it(`${fnName} should revert and rethrow error if the underlying function reverts`, async () => {
const order = await maker.signOrderAsync();
order.signature = constants.NULL_BYTES;
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const nestedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.InvalidLength,
orderHashUtils.getOrderHashHex(order),
order.makerAddress,
order.signature,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashHex,
nestedError,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
}
}
});
describe('cancelOrder', () => {
it('should revert if not signed by or called by maker', async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const nestedError = new ExchangeRevertErrors.ExchangeInvalidContextError(
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
orderHashUtils.getOrderHashHex(order),
takers[0].address,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashHex,
nestedError,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
it('should be successful if signed by maker and called by sender', async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]);
const transaction = await maker.signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs<ExchangeCancelEventArgs>(
transactionReceipt.logs,
[defaultCancelEvent(order)],
ExchangeEvents.Cancel,
);
});
it('should be successful if called by maker without a signature', async () => {
const order = await maker.signOrderAsync();
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]);
const transaction = await maker.signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, constants.NULL_BYTES)
.awaitTransactionSuccessAsync({ from: maker.address });
verifyEventsFromLogs<ExchangeCancelEventArgs>(
transactionReceipt.logs,
[{ ...defaultCancelEvent(order), senderAddress: maker.address }],
ExchangeEvents.Cancel,
);
});
});
describe('batchCancelOrders', () => {
it('should revert if not signed by or called by maker', async () => {
const orders = [await maker.signOrderAsync(), await maker.signOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchCancelOrders,
orders,
);
const transaction = await takers[0].signTransactionAsync({ data });
const transactionHashHex = transactionHashUtils.getTransactionHashHex(transaction);
const nestedError = new ExchangeRevertErrors.ExchangeInvalidContextError(
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
orderHashUtils.getOrderHashHex(orders[0]),
takers[0].address,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashHex,
nestedError,
);
const tx = deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
return expect(tx).to.revertWith(expectedError);
});
it('should be successful if signed by maker and called by sender', async () => {
const orders = [await maker.signOrderAsync(), await maker.signOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchCancelOrders,
orders,
);
const transaction = await maker.signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs<ExchangeCancelEventArgs>(
transactionReceipt.logs,
[defaultCancelEvent(orders[0]), defaultCancelEvent(orders[1])],
ExchangeEvents.Cancel,
);
});
it('should be successful if called by maker without a signature', async () => {
const orders = [await maker.signOrderAsync(), await maker.signOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(
ExchangeFunctionName.BatchCancelOrders,
orders,
);
const transaction = await maker.signTransactionAsync({ data });
transaction.signature = constants.NULL_BYTES;
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: maker.address });
verifyEventsFromLogs<ExchangeCancelEventArgs>(
transactionReceipt.logs,
[
{ ...defaultCancelEvent(orders[0]), senderAddress: maker.address },
{ ...defaultCancelEvent(orders[1]), senderAddress: maker.address },
],
ExchangeEvents.Cancel,
);
});
});
describe('cancelOrdersUpTo', () => {
it('should be successful if signed by maker and called by sender', async () => {
const targetEpoch = constants.ZERO_AMOUNT;
const data = deployment.exchange.cancelOrdersUpTo(targetEpoch).getABIEncodedTransactionData();
const transaction = await maker.signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs<ExchangeCancelUpToEventArgs>(
transactionReceipt.logs,
[
{
makerAddress: maker.address,
orderSenderAddress: sender.address,
orderEpoch: targetEpoch.plus(1),
},
],
ExchangeEvents.CancelUpTo,
);
});
it('should be successful if called by maker without a signature', async () => {
const targetEpoch = constants.ZERO_AMOUNT;
const data = deployment.exchange.cancelOrdersUpTo(targetEpoch).getABIEncodedTransactionData();
const transaction = await maker.signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, constants.NULL_BYTES)
.awaitTransactionSuccessAsync({ from: maker.address });
verifyEventsFromLogs<ExchangeCancelUpToEventArgs>(
transactionReceipt.logs,
[
{
makerAddress: maker.address,
orderSenderAddress: constants.NULL_ADDRESS,
orderEpoch: targetEpoch.plus(1),
},
],
ExchangeEvents.CancelUpTo,
);
});
});
describe('preSign', () => {
it('should preSign a hash for the signer', async () => {
const order = await maker.signOrderAsync();
const orderHash = orderHashUtils.getOrderHashHex(order);
const data = deployment.exchange.preSign(orderHash).getABIEncodedTransactionData();
const transaction = await takers[0].signTransactionAsync({ data });
let isPreSigned = await deployment.exchange.preSigned(orderHash, takers[0].address).callAsync();
expect(isPreSigned).to.be.eq(false);
await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
isPreSigned = await deployment.exchange.preSigned(orderHash, takers[0].address).callAsync();
expect(isPreSigned).to.be.eq(true);
});
it('should preSign a hash for the caller if called without a signature', async () => {
const order = await maker.signOrderAsync();
const orderHash = orderHashUtils.getOrderHashHex(order);
const data = deployment.exchange.preSign(orderHash).getABIEncodedTransactionData();
const transaction = await takers[0].signTransactionAsync({ data });
let isPreSigned = await deployment.exchange.preSigned(orderHash, takers[0].address).callAsync();
expect(isPreSigned).to.be.eq(false);
await deployment.exchange
.executeTransaction(transaction, constants.NULL_BYTES)
.awaitTransactionSuccessAsync({ from: takers[0].address });
isPreSigned = await deployment.exchange.preSigned(orderHash, takers[0].address).callAsync();
expect(isPreSigned).to.be.eq(true);
});
});
describe('setSignatureValidatorApproval', () => {
it('should approve a validator for the signer', async () => {
const validatorAddress = randomAddress();
const shouldApprove = true;
const data = deployment.exchange
.setSignatureValidatorApproval(validatorAddress, shouldApprove)
.getABIEncodedTransactionData();
const transaction = await takers[0].signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, transaction.signature)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs<ExchangeSignatureValidatorApprovalEventArgs>(
transactionReceipt.logs,
[
{
signerAddress: takers[0].address,
validatorAddress,
isApproved: shouldApprove,
},
],
ExchangeEvents.SignatureValidatorApproval,
);
});
it('should approve a validator for the caller if called with no signature', async () => {
const validatorAddress = randomAddress();
const shouldApprove = true;
const data = deployment.exchange
.setSignatureValidatorApproval(validatorAddress, shouldApprove)
.getABIEncodedTransactionData();
const transaction = await takers[0].signTransactionAsync({ data });
const transactionReceipt = await deployment.exchange
.executeTransaction(transaction, constants.NULL_BYTES)
.awaitTransactionSuccessAsync({ from: takers[0].address });
verifyEventsFromLogs<ExchangeSignatureValidatorApprovalEventArgs>(
transactionReceipt.logs,
[
{
signerAddress: takers[0].address,
validatorAddress,
isApproved: shouldApprove,
},
],
ExchangeEvents.SignatureValidatorApproval,
);
});
});
});
describe('batchExecuteTransactions', () => {
it('should successfully call fillOrder via 2 transactions with different taker signatures', async () => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await takers[1].signTransactionAsync({ data: data2 });
const transactionReceipt = await deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs<ExchangeTransactionExecutionEventArgs>(
transactionReceipt.logs,
[
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction1) },
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction2) },
],
ExchangeEvents.TransactionExecution,
);
verifyEventsFromLogs<ExchangeFillEventArgs>(
transactionReceipt.logs,
[defaultFillEvent(order1), { ...defaultFillEvent(order2), takerAddress: takers[1].address }],
ExchangeEvents.Fill,
);
});
it('should successfully call fillOrder via 2 transactions when called by taker with no signatures', async () => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await takers[0].signTransactionAsync({ data: data2 });
transaction1.signature = constants.NULL_BYTES;
transaction2.signature = constants.NULL_BYTES;
const transactionReceipt = await deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.awaitTransactionSuccessAsync({ from: takers[0].address });
verifyEventsFromLogs<ExchangeTransactionExecutionEventArgs>(
transactionReceipt.logs,
[
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction1) },
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction2) },
],
ExchangeEvents.TransactionExecution,
);
verifyEventsFromLogs<ExchangeFillEventArgs>(
transactionReceipt.logs,
[
{ ...defaultFillEvent(order1), senderAddress: takers[0].address },
{ ...defaultFillEvent(order2), senderAddress: takers[0].address },
],
ExchangeEvents.Fill,
);
});
it('should successfully call fillOrder via 2 transactions when one is signed by taker1 and executeTransaction is called by taker2', async () => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await takers[1].signTransactionAsync({ data: data2 });
const transactionReceipt = await deployment.exchange
.batchExecuteTransactions([transaction1, transaction2], [transaction1.signature, constants.NULL_BYTES])
.awaitTransactionSuccessAsync({ from: takers[1].address });
verifyEventsFromLogs<ExchangeTransactionExecutionEventArgs>(
transactionReceipt.logs,
[
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction1) },
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction2) },
],
ExchangeEvents.TransactionExecution,
);
verifyEventsFromLogs<ExchangeFillEventArgs>(
transactionReceipt.logs,
[
{ ...defaultFillEvent(order1), senderAddress: takers[1].address },
{
...defaultFillEvent(order2),
takerAddress: takers[1].address,
senderAddress: takers[1].address,
},
],
ExchangeEvents.Fill,
);
});
it('should return the correct data for 2 different fillOrder calls', async () => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await takers[1].signTransactionAsync({ data: data2 });
const returnData = await deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.callAsync({ from: sender.address });
const fillResults1: FillResults = deployment.exchange.getABIDecodedReturnData('fillOrder', returnData[0]);
const fillResults2: FillResults = deployment.exchange.getABIDecodedReturnData('fillOrder', returnData[1]);
expect(fillResults1).to.deep.equal(
ReferenceFunctions.calculateFillResults(
order1,
order1.takerAssetAmount,
DeploymentManager.protocolFeeMultiplier,
DeploymentManager.gasPrice,
),
);
expect(fillResults2).to.deep.equal(
ReferenceFunctions.calculateFillResults(
order2,
order2.takerAssetAmount,
DeploymentManager.protocolFeeMultiplier,
DeploymentManager.gasPrice,
),
);
});
it('should successfully call fillOrder and cancelOrder via 2 transactions', async () => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await maker.signTransactionAsync({ data: data2 });
const transactionReceipt = await deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.awaitTransactionSuccessAsync({ from: sender.address });
verifyEventsFromLogs<ExchangeTransactionExecutionEventArgs>(
transactionReceipt.logs,
[
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction1) },
{ transactionHash: transactionHashUtils.getTransactionHashHex(transaction2) },
],
ExchangeEvents.TransactionExecution,
);
const fillLogIndex = transactionReceipt.logs.findIndex(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
const cancelLogIndex = transactionReceipt.logs.findIndex(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogIndex).to.greaterThan(fillLogIndex);
verifyEventsFromLogs<ExchangeFillEventArgs>(
transactionReceipt.logs,
[defaultFillEvent(order1)],
ExchangeEvents.Fill,
);
verifyEventsFromLogs<ExchangeCancelEventArgs>(
transactionReceipt.logs,
[defaultCancelEvent(order2)],
ExchangeEvents.Cancel,
);
});
it('should return the correct data for a fillOrder and cancelOrder call', async () => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await maker.signTransactionAsync({ data: data2 });
const returnData = await deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.callAsync({ from: sender.address });
const fillResults: FillResults = deployment.exchange.getABIDecodedReturnData('fillOrder', returnData[0]);
expect(fillResults).to.deep.equal(
ReferenceFunctions.calculateFillResults(
order1,
order1.takerAssetAmount,
DeploymentManager.protocolFeeMultiplier,
DeploymentManager.gasPrice,
),
);
expect(returnData[1]).to.eq(constants.NULL_BYTES);
});
it('should revert if a single transaction reverts', async () => {
const order = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
const transaction1 = await maker.signTransactionAsync({ data: data1 });
const transaction2 = await takers[0].signTransactionAsync({ data: data2 });
const tx = deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.awaitTransactionSuccessAsync({ from: sender.address });
const nestedError = new ExchangeRevertErrors.OrderStatusError(
orderHashUtils.getOrderHashHex(order),
OrderStatus.Cancelled,
).encode();
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHashUtils.getTransactionHashHex(transaction2),
nestedError,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if a single transaction is expired', async () => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order2]);
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await takers[1].signTransactionAsync({
data: data2,
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
});
const tx = deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, transaction2.signature],
)
.awaitTransactionSuccessAsync({ from: sender.address });
const expiredTransactionHash = transactionHashUtils.getTransactionHashHex(transaction2);
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.Expired,
expiredTransactionHash,
);
return expect(tx).to.revertWith(expectedError);
});
});
});

View File

@@ -1,16 +1,10 @@
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
import { ForwarderContract } from '@0x/contracts-exchange-forwarder';
import {
blockchainTests,
constants,
getLatestBlockTimestampAsync,
hexConcat,
toBaseUnitAmount,
} from '@0x/contracts-test-utils';
import { blockchainTests, constants, getLatestBlockTimestampAsync, toBaseUnitAmount } from '@0x/contracts-test-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { SignatureType, SignedOrder } from '@0x/types';
import { AbiEncoder, BigNumber, ExchangeForwarderRevertErrors } from '@0x/utils';
import { AbiEncoder, BigNumber, ExchangeForwarderRevertErrors, hexUtils } from '@0x/utils';
import { deployEth2DaiBridgeAsync } from '../bridges/deploy_eth2dai_bridge';
import { deployUniswapBridgeAsync } from '../bridges/deploy_uniswap_bridge';
@@ -111,7 +105,7 @@ blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
takerFeeAssetData: wethAssetData,
expirationTimeSeconds: new BigNumber(currentBlockTimestamp).plus(fifteenMinutesInSeconds),
salt: generatePseudoRandomSalt(),
signature: hexConcat(SignatureType.Wallet),
signature: hexUtils.concat(SignatureType.Wallet),
};
eth2DaiBridgeOrder = {
...orderDefaults,
@@ -152,11 +146,11 @@ blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
const tokenIds = { erc721: { [erc721Token.address]: [nftId] } };
balanceStore = new BlockchainBalanceStore(tokenOwners, tokenContracts, tokenIds);
testFactory = new ForwarderTestFactory(forwarder, deployment, balanceStore, taker, forwarderFeeRecipient);
testFactory = new ForwarderTestFactory(forwarder, deployment, balanceStore, taker);
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
describe('marketSellOrdersWithEth', () => {
@@ -199,7 +193,10 @@ blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
eth2DaiBridgeOrder,
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
];
await testFactory.marketSellTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
await testFactory.marketSellTestAsync(orders, 2.56, {
forwarderFeeAmounts: [toBaseUnitAmount(0.1)],
forwarderFeeRecipientAddresses: [forwarderFeeRecipient.address],
});
});
it('should fully fill a single UniswapBridge order without a taker fee', async () => {
await testFactory.marketSellTestAsync([uniswapBridgeOrder], 1);
@@ -240,7 +237,7 @@ blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
uniswapBridgeOrder,
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
];
await testFactory.marketSellTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
await testFactory.marketSellTestAsync(orders, 2.56);
});
it('should fill multiple bridge orders', async () => {
await testFactory.marketSellTestAsync([eth2DaiBridgeOrder, uniswapBridgeOrder], 1.23);
@@ -298,7 +295,10 @@ blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
eth2DaiBridgeOrder,
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
];
await testFactory.marketBuyTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
await testFactory.marketBuyTestAsync(orders, 2.56, {
forwarderFeeAmounts: [toBaseUnitAmount(0.1)],
forwarderFeeRecipientAddresses: [forwarderFeeRecipient.address],
});
});
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount (Eth2Dai)', async () => {
const expectedError = new ExchangeForwarderRevertErrors.CompleteBuyFailedError(
@@ -349,7 +349,7 @@ blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
uniswapBridgeOrder,
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
];
await testFactory.marketBuyTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
await testFactory.marketBuyTestAsync(orders, 2.56);
});
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount (Uniswap)', async () => {
const expectedError = new ExchangeForwarderRevertErrors.CompleteBuyFailedError(

View File

@@ -7,7 +7,7 @@ import {
constants,
expect,
getLatestBlockTimestampAsync,
getPercentageOfValue,
randomAddress,
toBaseUnitAmount,
} from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
@@ -102,11 +102,11 @@ blockchainTests('Forwarder integration tests', env => {
const tokenIds = { erc721: { [erc721Token.address]: [nftId] } };
balanceStore = new BlockchainBalanceStore(tokenOwners, tokenContracts, tokenIds);
testFactory = new ForwarderTestFactory(forwarder, deployment, balanceStore, taker, forwarderFeeRecipient);
testFactory = new ForwarderTestFactory(forwarder, deployment, balanceStore, taker);
});
after(async () => {
Actor.count = 0;
Actor.reset();
});
blockchainTests.resets('constructor', () => {
@@ -180,12 +180,7 @@ blockchainTests('Forwarder integration tests', env => {
await balanceStore.updateBalancesAsync();
// Execute test case
const tx = await forwarder
.marketSellOrdersWithEth(
[order],
[order.signature],
constants.ZERO_AMOUNT,
forwarderFeeRecipient.address,
)
.marketSellOrdersWithEth([order], [order.signature], [], [])
.awaitTransactionSuccessAsync({
value: order.takerAssetAmount.plus(DeploymentManager.protocolFee),
from: taker.address,
@@ -224,12 +219,7 @@ blockchainTests('Forwarder integration tests', env => {
const ethValue = order.takerAssetAmount.plus(DeploymentManager.protocolFee).plus(2);
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(taker.address);
const tx = await forwarder
.marketSellOrdersWithEth(
[order],
[order.signature],
constants.ZERO_AMOUNT,
forwarderFeeRecipient.address,
)
.marketSellOrdersWithEth([order], [order.signature], [], [])
.awaitTransactionSuccessAsync({
value: ethValue,
from: taker.address,
@@ -312,17 +302,83 @@ blockchainTests('Forwarder integration tests', env => {
takerAssetAmount: toBaseUnitAmount(36),
});
await testFactory.marketSellTestAsync([order], 0.67, {
forwarderFeePercentage: new BigNumber(2),
forwarderFeeAmounts: [toBaseUnitAmount(0.2)],
forwarderFeeRecipientAddresses: [forwarderFeeRecipient.address],
});
});
it('should fail if the fee is set too high', async () => {
const order = await maker.signOrderAsync();
const forwarderFeePercentage = new BigNumber(6);
const revertError = new ExchangeForwarderRevertErrors.FeePercentageTooLargeError(
getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage),
it('should fill the order and send the same fees to different feeRecipient addresses', async () => {
const order = await maker.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(157),
takerAssetAmount: toBaseUnitAmount(36),
});
await testFactory.marketSellTestAsync([order], 0.67, {
forwarderFeeAmounts: [toBaseUnitAmount(0.2), toBaseUnitAmount(0.2)],
forwarderFeeRecipientAddresses: [randomAddress(), randomAddress()],
});
});
it('should fill the order and send different fees to different feeRecipient addresses', async () => {
const order = await maker.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(157),
takerAssetAmount: toBaseUnitAmount(36),
});
await testFactory.marketSellTestAsync([order], 0.67, {
forwarderFeeAmounts: [toBaseUnitAmount(0.2), toBaseUnitAmount(0.1)],
forwarderFeeRecipientAddresses: [randomAddress(), randomAddress()],
});
});
it('should fill the order and send the same fees to multiple instances of the same feeRecipient address', async () => {
const order = await maker.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(157),
takerAssetAmount: toBaseUnitAmount(36),
});
const feeRecipient = randomAddress();
await testFactory.marketSellTestAsync([order], 0.67, {
forwarderFeeAmounts: [toBaseUnitAmount(0.2), toBaseUnitAmount(0.2)],
forwarderFeeRecipientAddresses: [feeRecipient, feeRecipient],
});
});
it('should fill the order and send different fees to multiple instances of the same feeRecipient address', async () => {
const order = await maker.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(157),
takerAssetAmount: toBaseUnitAmount(36),
});
const feeRecipient = randomAddress();
await testFactory.marketSellTestAsync([order], 0.67, {
forwarderFeeAmounts: [toBaseUnitAmount(0.2), toBaseUnitAmount(0.1)],
forwarderFeeRecipientAddresses: [feeRecipient, feeRecipient],
});
});
it('should fail if ethFeeAmounts is longer than feeRecipients', async () => {
const order = await maker.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(157),
takerAssetAmount: toBaseUnitAmount(36),
});
const forwarderFeeAmounts = [toBaseUnitAmount(0.2)];
const forwarderFeeRecipientAddresses: string[] = [];
const revertError = new ExchangeForwarderRevertErrors.EthFeeLengthMismatchError(
new BigNumber(forwarderFeeAmounts.length),
new BigNumber(forwarderFeeRecipientAddresses.length),
);
await testFactory.marketSellTestAsync([order], 0.5, {
forwarderFeePercentage,
await testFactory.marketSellTestAsync([order], 0.67, {
forwarderFeeAmounts,
forwarderFeeRecipientAddresses,
revertError,
});
});
it('should fail if feeRecipients is longer than ethFeeAmounts', async () => {
const order = await maker.signOrderAsync({
makerAssetAmount: toBaseUnitAmount(157),
takerAssetAmount: toBaseUnitAmount(36),
});
const forwarderFeeAmounts: BigNumber[] = [];
const forwarderFeeRecipientAddresses = [randomAddress()];
const revertError = new ExchangeForwarderRevertErrors.EthFeeLengthMismatchError(
new BigNumber(forwarderFeeAmounts.length),
new BigNumber(forwarderFeeRecipientAddresses.length),
);
await testFactory.marketSellTestAsync([order], 0.67, {
forwarderFeeAmounts,
forwarderFeeRecipientAddresses,
revertError,
});
});
@@ -497,13 +553,7 @@ blockchainTests('Forwarder integration tests', env => {
await balanceStore.updateBalancesAsync();
// Execute test case
const tx = await forwarder
.marketBuyOrdersWithEth(
[order],
desiredMakerAssetFillAmount,
[order.signature],
constants.ZERO_AMOUNT,
forwarderFeeRecipient.address,
)
.marketBuyOrdersWithEth([order], desiredMakerAssetFillAmount, [order.signature], [], [])
.awaitTransactionSuccessAsync({
value: ethValue,
from: taker.address,
@@ -511,20 +561,10 @@ blockchainTests('Forwarder integration tests', env => {
// Compute expected balances
const expectedBalances = LocalBalanceStore.create(balanceStore);
await expectedBalances.transferAssetAsync(
maker.address,
taker.address,
makerAssetFillAmount,
makerAssetData,
);
expectedBalances.transferAsset(maker.address, taker.address, makerAssetFillAmount, makerAssetData);
expectedBalances.wrapEth(taker.address, deployment.tokens.weth.address, ethValue);
await expectedBalances.transferAssetAsync(
taker.address,
maker.address,
primaryTakerAssetFillAmount,
wethAssetData,
);
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(taker.address, maker.address, primaryTakerAssetFillAmount, wethAssetData);
expectedBalances.transferAsset(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
@@ -554,13 +594,7 @@ blockchainTests('Forwarder integration tests', env => {
await balanceStore.updateBalancesAsync();
// Execute test case
const tx = await forwarder
.marketBuyOrdersWithEth(
[order],
desiredMakerAssetFillAmount,
[order.signature],
constants.ZERO_AMOUNT,
forwarderFeeRecipient.address,
)
.marketBuyOrdersWithEth([order], desiredMakerAssetFillAmount, [order.signature], [], [])
.awaitTransactionSuccessAsync({
value: takerAssetFillAmount.plus(DeploymentManager.protocolFee),
from: taker.address,
@@ -568,24 +602,14 @@ blockchainTests('Forwarder integration tests', env => {
// Compute expected balances
const expectedBalances = LocalBalanceStore.create(balanceStore);
await expectedBalances.transferAssetAsync(
maker.address,
taker.address,
makerAssetFillAmount,
makerAssetData,
);
expectedBalances.transferAsset(maker.address, taker.address, makerAssetFillAmount, makerAssetData);
expectedBalances.wrapEth(
taker.address,
deployment.tokens.weth.address,
takerAssetFillAmount.plus(DeploymentManager.protocolFee),
);
await expectedBalances.transferAssetAsync(
taker.address,
maker.address,
takerAssetFillAmount,
wethAssetData,
);
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(taker.address, maker.address, takerAssetFillAmount, wethAssetData);
expectedBalances.transferAsset(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
@@ -605,31 +629,37 @@ blockchainTests('Forwarder integration tests', env => {
takerAssetAmount: toBaseUnitAmount(11),
});
await testFactory.marketBuyTestAsync([order], 0.33, {
forwarderFeePercentage: new BigNumber(2),
forwarderFeeAmounts: [toBaseUnitAmount(0.2)],
forwarderFeeRecipientAddresses: [randomAddress()],
});
});
it('should fail if the fee is set too high', async () => {
it('should fail if there is not enough ETH remaining to complete the fill', async () => {
const order = await maker.signOrderAsync();
const revertError = new ExchangeForwarderRevertErrors.FeePercentageTooLargeError(
getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, new BigNumber(6)),
const forwarderFeeAmounts = [toBaseUnitAmount(1)];
const revertError = new ExchangeForwarderRevertErrors.CompleteBuyFailedError(
order.takerAssetAmount,
constants.ZERO_AMOUNT,
);
await testFactory.marketBuyTestAsync([order], 0.5, {
forwarderFeePercentage: new BigNumber(6),
ethValueAdjustment: -1,
forwarderFeeAmounts,
forwarderFeeRecipientAddresses: [randomAddress()],
revertError,
});
});
it('should fail if there is not enough ETH remaining to pay the fee', async () => {
const order = await maker.signOrderAsync();
const forwarderFeePercentage = new BigNumber(2);
const ethFee = getPercentageOfValue(
order.takerAssetAmount.times(0.5).plus(DeploymentManager.protocolFee),
forwarderFeePercentage,
const forwarderFeeAmounts = [toBaseUnitAmount(1)];
const value = forwarderFeeAmounts[0].minus(1);
const revertError = new ExchangeForwarderRevertErrors.InsufficientEthForFeeError(
forwarderFeeAmounts[0],
value,
);
const revertError = new ExchangeForwarderRevertErrors.InsufficientEthForFeeError(ethFee, ethFee.minus(1));
await testFactory.marketBuyTestAsync([order], 0.5, {
ethValueAdjustment: -1,
forwarderFeePercentage,
await testFactory.marketBuyTestAsync([order], 0, {
revertError,
ethValueAdjustment: -1,
forwarderFeeAmounts,
forwarderFeeRecipientAddresses: [randomAddress()],
});
});
});

View File

@@ -1,19 +1,10 @@
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
import { ForwarderContract } from '@0x/contracts-exchange-forwarder';
import {
constants,
expect,
getPercentageOfValue,
hexSlice,
Numberish,
OrderStatus,
provider,
} from '@0x/contracts-test-utils';
import { constants, expect, OrderStatus, provider } from '@0x/contracts-test-utils';
import { AssetProxyId, OrderInfo, SignedOrder } from '@0x/types';
import { BigNumber, RevertError } from '@0x/utils';
import { BigNumber, hexUtils, RevertError } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { FeeRecipient } from '../framework/actors/fee_recipient';
import { Taker } from '../framework/actors/taker';
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
import { LocalBalanceStore } from '../framework/balances/local_balance_store';
@@ -27,7 +18,8 @@ interface ForwarderFillState {
}
interface MarketSellOptions {
forwarderFeePercentage: Numberish;
forwarderFeeAmounts: BigNumber[];
forwarderFeeRecipientAddresses: string[];
revertError: RevertError;
bridgeExcessBuyAmount: BigNumber;
}
@@ -37,8 +29,8 @@ interface MarketBuyOptions extends MarketSellOptions {
}
function areUnderlyingAssetsEqual(assetData1: string, assetData2: string): boolean {
const assetProxyId1 = hexSlice(assetData1, 0, 4);
const assetProxyId2 = hexSlice(assetData2, 0, 4);
const assetProxyId1 = hexUtils.slice(assetData1, 0, 4);
const assetProxyId2 = hexUtils.slice(assetData2, 0, 4);
if (
(assetProxyId1 === AssetProxyId.ERC20 || assetProxyId1 === AssetProxyId.ERC20Bridge) &&
(assetProxyId2 === AssetProxyId.ERC20 || assetProxyId2 === AssetProxyId.ERC20Bridge)
@@ -64,7 +56,6 @@ export class ForwarderTestFactory {
private readonly _deployment: DeploymentManager,
private readonly _balanceStore: BlockchainBalanceStore,
private readonly _taker: Taker,
private readonly _forwarderFeeRecipient: FeeRecipient,
) {}
public async marketBuyTestAsync(
@@ -73,7 +64,8 @@ export class ForwarderTestFactory {
options: Partial<MarketBuyOptions> = {},
): Promise<void> {
const ethValueAdjustment = options.ethValueAdjustment || 0;
const forwarderFeePercentage = options.forwarderFeePercentage || 0;
const forwarderFeeAmounts = options.forwarderFeeAmounts || [];
const forwarderFeeRecipientAddresses = options.forwarderFeeRecipientAddresses || [];
const orderInfoBefore = await Promise.all(
orders.map(order => this._deployment.exchange.getOrderInfo(order).callAsync()),
@@ -90,19 +82,16 @@ export class ForwarderTestFactory {
makerAssetAcquiredAmount,
} = await this._simulateForwarderFillAsync(orders, orderInfoBefore, fractionalNumberOfOrdersToFill, options);
const ethSpentOnForwarderFee = getPercentageOfValue(wethSpentAmount, forwarderFeePercentage);
const feePercentage = getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage);
const tx = this._forwarder
.marketBuyOrdersWithEth(
orders,
makerAssetAcquiredAmount.minus(options.bridgeExcessBuyAmount || 0),
orders.map(signedOrder => signedOrder.signature),
feePercentage,
this._forwarderFeeRecipient.address,
forwarderFeeAmounts,
forwarderFeeRecipientAddresses,
)
.awaitTransactionSuccessAsync({
value: wethSpentAmount.plus(ethSpentOnForwarderFee).plus(ethValueAdjustment),
value: wethSpentAmount.plus(BigNumber.sum(0, ...forwarderFeeAmounts)).plus(ethValueAdjustment),
from: this._taker.address,
});
@@ -135,19 +124,18 @@ export class ForwarderTestFactory {
options,
);
const forwarderFeePercentage = options.forwarderFeePercentage || 0;
const ethSpentOnForwarderFee = getPercentageOfValue(wethSpentAmount, forwarderFeePercentage);
const feePercentage = getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage);
const forwarderFeeAmounts = options.forwarderFeeAmounts || [];
const forwarderFeeRecipientAddresses = options.forwarderFeeRecipientAddresses || [];
const tx = this._forwarder
.marketSellOrdersWithEth(
orders,
orders.map(signedOrder => signedOrder.signature),
feePercentage,
this._forwarderFeeRecipient.address,
forwarderFeeAmounts,
forwarderFeeRecipientAddresses,
)
.awaitTransactionSuccessAsync({
value: wethSpentAmount.plus(ethSpentOnForwarderFee),
value: wethSpentAmount.plus(BigNumber.sum(0, ...forwarderFeeAmounts)),
from: this._taker.address,
});
@@ -192,6 +180,15 @@ export class ForwarderTestFactory {
): Promise<ForwarderFillState> {
await this._balanceStore.updateBalancesAsync();
const balances = LocalBalanceStore.create(this._balanceStore);
const forwarderFeeAmounts = options.forwarderFeeAmounts || [];
const forwarderFeeRecipientAddresses = options.forwarderFeeRecipientAddresses || [];
forwarderFeeAmounts.forEach((feeAmount, i) =>
// In reality the Forwarder is a middleman in this transaction and the ETH gets wrapped and unwrapped.
balances.sendEth(this._taker.address, forwarderFeeRecipientAddresses[i], feeAmount),
);
const currentTotal = {
wethSpentAmount: constants.ZERO_AMOUNT,
makerAssetAcquiredAmount: constants.ZERO_AMOUNT,
@@ -207,7 +204,7 @@ export class ForwarderTestFactory {
continue;
}
const { wethSpentAmount, makerAssetAcquiredAmount } = await this._simulateSingleFillAsync(
const { wethSpentAmount, makerAssetAcquiredAmount } = this._simulateSingleFill(
balances,
order,
ordersInfoBefore[i].orderTakerAssetFilledAmount,
@@ -222,23 +219,16 @@ export class ForwarderTestFactory {
);
}
const ethSpentOnForwarderFee = getPercentageOfValue(
currentTotal.wethSpentAmount,
options.forwarderFeePercentage || 0,
);
// In reality the Forwarder is a middleman in this transaction and the ETH gets wrapped and unwrapped.
balances.sendEth(this._taker.address, this._forwarderFeeRecipient.address, ethSpentOnForwarderFee);
return { ...currentTotal, balances };
}
private async _simulateSingleFillAsync(
private _simulateSingleFill(
balances: LocalBalanceStore,
order: SignedOrder,
takerAssetFilled: BigNumber,
fillFraction: number,
bridgeExcessBuyAmount: BigNumber,
): Promise<ForwarderFillState> {
): ForwarderFillState {
let { makerAssetAmount, takerAssetAmount, makerFee, takerFee } = order;
makerAssetAmount = makerAssetAmount.times(fillFraction).integerValue(BigNumber.ROUND_CEIL);
takerAssetAmount = takerAssetAmount.times(fillFraction).integerValue(BigNumber.ROUND_CEIL);
@@ -272,43 +262,23 @@ export class ForwarderTestFactory {
balances.wrapEth(this._forwarder.address, this._deployment.tokens.weth.address, wethSpentAmount);
// (In reality this is done all at once, but we simulate it order by order)
// Maker -> Forwarder
await balances.transferAssetAsync(
order.makerAddress,
this._forwarder.address,
makerAssetAmount,
order.makerAssetData,
);
// Maker -> Order fee recipient
await balances.transferAssetAsync(
order.makerAddress,
order.feeRecipientAddress,
makerFee,
order.makerFeeAssetData,
);
// Forwarder -> Maker
await balances.transferAssetAsync(
this._forwarder.address,
order.makerAddress,
takerAssetAmount,
order.takerAssetData,
);
balances.transferAsset(this._forwarder.address, order.makerAddress, takerAssetAmount, order.takerAssetData);
// Maker -> Forwarder
balances.transferAsset(order.makerAddress, this._forwarder.address, makerAssetAmount, order.makerAssetData);
// Forwarder -> Order fee recipient
await balances.transferAssetAsync(
this._forwarder.address,
order.feeRecipientAddress,
takerFee,
order.takerFeeAssetData,
);
balances.transferAsset(this._forwarder.address, order.feeRecipientAddress, takerFee, order.takerFeeAssetData);
// Maker -> Order fee recipient
balances.transferAsset(order.makerAddress, order.feeRecipientAddress, makerFee, order.makerFeeAssetData);
// Forwarder pays the protocol fee in WETH
await balances.transferAssetAsync(
balances.transferAsset(
this._forwarder.address,
this._deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
order.takerAssetData,
);
// Forwarder gives acquired maker asset to taker
await balances.transferAssetAsync(
balances.transferAsset(
this._forwarder.address,
this._taker.address,
makerAssetAcquiredAmount,

View File

@@ -31,6 +31,10 @@ export class Actor {
} = {};
protected readonly _transactionFactory: TransactionFactory;
public static reset(): void {
Actor.count = 0;
}
constructor(config: ActorConfig) {
Actor.count++;
@@ -142,6 +146,12 @@ export class Actor {
customTransactionParams: Partial<ZeroExTransaction>,
signatureType: SignatureType = SignatureType.EthSign,
): Promise<SignedZeroExTransaction> {
return this._transactionFactory.newSignedTransactionAsync(customTransactionParams, signatureType);
return this._transactionFactory.newSignedTransactionAsync(
{
gasPrice: DeploymentManager.gasPrice,
...customTransactionParams,
},
signatureType,
);
}
}

View File

@@ -3,9 +3,11 @@ import { KeeperMixin } from './keeper';
import { MakerMixin } from './maker';
import { PoolOperatorMixin } from './pool_operator';
import { StakerMixin } from './staker';
import { TakerMixin } from './taker';
export class OperatorMaker extends PoolOperatorMixin(MakerMixin(Actor)) {}
export class StakerMaker extends StakerMixin(MakerMixin(Actor)) {}
export class StakerOperator extends StakerMixin(PoolOperatorMixin(Actor)) {}
export class OperatorStakerMaker extends PoolOperatorMixin(StakerMixin(MakerMixin(Actor))) {}
export class StakerKeeper extends StakerMixin(KeeperMixin(Actor)) {}
export class MakerTaker extends MakerMixin(TakerMixin(Actor)) {}

View File

@@ -1,6 +1,10 @@
import { constants, OrderFactory, orderUtils } from '@0x/contracts-test-utils';
import { constants, OrderFactory } from '@0x/contracts-test-utils';
import { Order, SignedOrder } from '@0x/types';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { AssertionResult } from '../assertions/function_assertion';
import { validJoinStakingPoolAssertion } from '../assertions/joinStakingPool';
import { Actor, ActorConfig, Constructor } from './base';
@@ -45,6 +49,12 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
...orderConfig,
};
this.orderFactory = new OrderFactory(this.actor.privateKey, defaultOrderParams);
// Register this mixin's assertion generators
this.actor.simulationActions = {
...this.actor.simulationActions,
validJoinStakingPool: this._validJoinStakingPool(),
};
}
/**
@@ -58,8 +68,7 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
* Cancels one of the maker's orders.
*/
public async cancelOrderAsync(order: SignedOrder): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createCancel(order);
return this.actor.deployment.exchange.cancelOrder(params.order).awaitTransactionSuccessAsync({
return this.actor.deployment.exchange.cancelOrder(order).awaitTransactionSuccessAsync({
from: this.actor.address,
});
}
@@ -74,6 +83,19 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
from: this.actor.address,
});
}
private async *_validJoinStakingPool(): AsyncIterableIterator<AssertionResult | void> {
const { stakingPools } = this.actor.simulationEnvironment!;
const assertion = validJoinStakingPoolAssertion(this.actor.deployment);
while (true) {
const poolId = _.sample(Object.keys(stakingPools));
if (poolId === undefined) {
yield undefined;
} else {
yield assertion.executeAsync([poolId], { from: this.actor.address });
}
}
}
};
}

View File

@@ -83,8 +83,8 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
const { stakingPools } = this.actor.simulationEnvironment!;
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools);
while (true) {
const operatorShare = getRandomInteger(0, constants.PPM);
yield assertion.executeAsync(operatorShare, false, { from: this.actor.address });
const operatorShare = getRandomInteger(0, constants.PPM).toNumber();
yield assertion.executeAsync([operatorShare, false], { from: this.actor.address });
}
}
@@ -96,8 +96,8 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
if (poolId === undefined) {
yield undefined;
} else {
const operatorShare = getRandomInteger(0, stakingPools[poolId].operatorShare);
yield assertion.executeAsync(poolId, operatorShare, { from: this.actor.address });
const operatorShare = getRandomInteger(0, stakingPools[poolId].operatorShare).toNumber();
yield assertion.executeAsync([poolId, operatorShare], { from: this.actor.address });
}
}
}

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