Compare commits

...

88 Commits

Author SHA1 Message Date
xianny
51ca3109eb Publish
- @0x/contracts-asset-proxy@3.0.2
 - @0x/contracts-coordinator@3.0.2
 - @0x/contracts-dev-utils@1.0.2
 - @0x/contracts-erc1155@2.0.2
 - @0x/contracts-erc20-bridge-sampler@1.0.2
 - @0x/contracts-erc20@3.0.2
 - @0x/contracts-erc721@3.0.2
 - @0x/contracts-exchange-forwarder@4.0.2
 - @0x/contracts-exchange-libs@4.0.2
 - @0x/contracts-exchange@3.0.2
 - @0x/contracts-extensions@5.1.1
 - @0x/contracts-integrations@2.0.2
 - @0x/contracts-multisig@4.0.2
 - @0x/contracts-staking@2.0.2
 - @0x/contracts-test-utils@5.0.1
 - @0x/contracts-tests@0.0.8
 - @0x/contracts-utils@4.0.2
 - 0x.js@9.0.2
 - @0x/abi-gen@5.0.2
 - @0x/assert@3.0.2
 - @0x/asset-swapper@3.0.2
 - @0x/base-contract@6.0.2
 - @0x/connect@6.0.2
 - @0x/contract-addresses@4.1.0
 - @0x/contract-artifacts@3.2.0
 - @0x/contract-wrappers-test@12.2.3
 - @0x/contract-wrappers@13.2.0
 - @0x/contracts-gen@2.0.2
 - @0x/dev-utils@3.0.2
 - @0x/instant@1.0.39
 - @0x/json-schemas@5.0.2
 - @0x/migrations@5.0.2
 - @0x/monorepo-scripts@1.0.45
 - @0x/order-utils@10.0.1
 - @0x/orderbook@2.0.0
 - @0x/sol-compiler@4.0.2
 - @0x/sol-coverage@4.0.2
 - @0x/sol-doc@3.0.2
 - @0x/sol-profiler@4.0.2
 - @0x/sol-resolver@3.0.2
 - @0x/sol-trace@3.0.2
 - @0x/sol-tracing-utils@7.0.2
 - @0x/sra-spec@3.0.2
 - @0x/subproviders@6.0.2
 - @0x/types@3.1.1
 - @0x/typescript-typings@5.0.1
 - @0x/utils@5.1.1
 - @0x/web3-wrapper@7.0.2
2019-12-16 16:05:16 -08:00
xianny
2bcb79dc44 Updated CHANGELOGS & MD docs 2019-12-16 16:05:03 -08:00
xianny
ecec985649 pin python regex version 2019-12-16 14:22:48 -08:00
Lawrence Forman
994908549d Asset-swapper aggregator utils (#2353)
* `@0x/asset-swapper`: Add ERC20Bridge aggregator library.

* `@0x/asset-swapper`: Finish off `aggregate.ts`.

* `@0x/types`: Add `OrderWithoutDomain` type.

* `@0x/asset-swapper`: Add testing infra for sampler/aggregator.

* `@0x/types`: Add `SignedOrderWithoutDomain` type.

* `@0x/asset-swapper`: Update aggregator to take and return orders with signatures.

* `@0x/asset-swapper`: Fix broken aggregator tests.

* `@0x/asset-swapper`: Pass the sampler contract into aggregator entry points.

* `@0x/contract-artifacts`: Add `IERC20BridgeSampler` artifact.

* `@0x/contract-wrappers`: Add `IERC20BridgeSampler` wrapper.

* `@0x/asset-swapper`: Address review comments.

* fixed testing

* refactored aggregate.ts and embeded into asset-swapper

* added adjusted rates for taker and maker fees

* remove PrunedSignedOrders

* updated contract-addresses and addressed some other todos

* streamlined logic

* patched in lawrences changes

* renamed aggregator utils and removed market_utils.ts

* added ack heartbeats

* fixed bug

* patches

* added dummy order things

* Dummy with valid sig

* Tweak gas price calculation to wei

* added test coverage and fixed bugs

* fixed migrations

* Fix CHANGELOGs and types export

* Deploy latest ERC20BridgeSampler on Mainnet

* `@0x/types` Revert CHANGELOG.

* `@0x/asset-swapper`: Address review comments.
`@0x/contract-addresses`: Make kyber lowercase.

* made protocol fee multiplier async

* `@0x/asset-swapper: Fix build errors and do some code cleanup.

* use assetDataUtils where possible
2019-12-16 12:35:58 -08:00
Greg Hysz
6808e0d531 Merge pull request #2365 from 0xProject/feat/asset-proxy/DyDxBridge
dYdX Bridge
2019-12-13 13:46:06 -08:00
Greg Hysen
410c95308a Updated dydx account encoding to assume that all actions are on partial balances 2019-12-13 12:02:23 -08:00
Greg Hysen
bec1f23616 Fixed merge conflicts 2019-12-13 11:06:16 -08:00
Greg Hysen
34596b7f83 Use safeGetPartialAmountFloor 2019-12-13 10:58:22 -08:00
Greg Hysen
5ca7169ee5 Reverted to version of dydx bridge that only allows from to be the account owner 2019-12-13 10:58:22 -08:00
Greg Hysen
3300aaa1b9 Refactored so that deposits are done from taker asset data and withdrawals from maker asset data. 2019-12-13 10:58:22 -08:00
Greg Hysen
54afc8a4a1 Fixed merge conflicts 2019-12-13 10:58:22 -08:00
Greg Hysen
f19f4310f4 Updating bridge to work w/o MultiAssetProxy 2019-12-13 10:57:57 -08:00
Greg Hysen
444125a7e1 Simplified the dydx bridge implememtation that does not use the bridge as the maker. 2019-12-13 10:57:57 -08:00
Greg Hysen
56cbb69401 DyDx bridge implementation using contract as maker with signature validation. 2019-12-13 10:56:54 -08:00
Lawrence Forman
70870ffcd2 Swallow reverts in ERC20BridgeSampler (#2395)
* `@0x/erc20-bridge-sampler`: Do not query empty/unsigned orders. Swallow revets on DEX quotes.

* `@0x/contracts-utils`: Add `DEV_UTILS_ADDRESS` and `KYBER_ETH_ADDRESS` to `DeploymentConstants`.

* `@0x/contracts-erc20-bridge-sampler`: Address review comments.
2019-12-13 10:53:25 -08:00
mzhu25
a556d91673 Merge pull request #2387 from 0xProject/feature/fuzz/staking-rewards
`@0x/contracts-integrations`: Staking rewards fuzz test
2019-12-12 15:43:29 -08:00
Michael Zhu
8ecbde8e1e Chagne StoredBalance functions to not mutate in place 2019-12-12 15:21:42 -08:00
Michael Zhu
a24b293818 register actors in the SimulationEnvironment constructor 2019-12-12 14:38:07 -08:00
Xianny
cab5ebf94b re-enable coordinator client tests (#2394) 2019-12-12 14:36:52 -08:00
Jacob Evans
a54b5baef2 Merge pull request #2393 from 0xProject/feature/orderbook-orderstore-async
orderbook: Make OrderStore async for use in db adapter
2019-12-12 10:30:17 -08:00
Jacob Evans
c324fe204e Make OrderStore async for use in db adapter
CHANGELOGS
2019-12-12 09:43:07 -08:00
Amir Bandeali
37d972ed9e Merge pull request #2389 from 0xProject/feat/contracts/mainnet-fork
Allow mainnet fork to be used for contract tests
2019-12-11 22:50:09 -08:00
Michael Zhu
e4a3b1cb05 fix bug in LibFractions reference function 2019-12-11 18:12:02 -08:00
Michael Zhu
49538f272e address comments 2019-12-11 16:54:48 -08:00
mzhu25
1283232144 Merge pull request #2372 from 0xProject/feature/fuzz/prng
`@0x/contracts-integrations`: Seeded RNG and simulation logging
2019-12-11 10:23:08 -08:00
Michael Zhu
2f9891f0aa fix CI failures 2019-12-10 00:32:48 -08:00
Michael Zhu
865a2b1fb0 add/update comments 2019-12-09 23:45:38 -08:00
Michael Zhu
1fde62eeb6 fix bug in finalizePool 2019-12-09 23:45:38 -08:00
Michael Zhu
6754cd48e2 refactor + fix lint 2019-12-09 23:45:38 -08:00
Michael Zhu
ccb477687a fixing bugs 2019-12-09 23:45:38 -08:00
Michael Zhu
be0e6c8925 Staking rewards simulation/fuzz test 2019-12-09 23:45:38 -08:00
Michael Zhu
1c2cb947c0 Add assertion generators to keeper, staker, taker mixins for the new function assertions 2019-12-09 23:45:38 -08:00
Michael Zhu
4663eec950 Add function assertions required for staking rewards fuzzing: withdrawDelegatorRewards, finalizePool, and endEpoch. Also adds payProtocolFee-related assertions to fillOrder 2019-12-09 23:45:37 -08:00
Michael Zhu
fff3c1eb36 update pool membership simulation to use multiple makers and takers, partial fills 2019-12-09 23:43:16 -08:00
Michael Zhu
4b7434d1e8 post-rebase lockfile update 2019-12-09 23:42:32 -08:00
Michael Zhu
8cc35a60e6 Add yarn command to run a specific fuzz test 2019-12-09 23:42:32 -08:00
Michael Zhu
130653a1aa move logger, pseudorandom, wrapper_interfaces to framework/utils/ 2019-12-09 23:42:32 -08:00
Michael Zhu
1dcbebd130 lint 2019-12-09 23:42:32 -08:00
Michael Zhu
faf306ad23 Simulation logging, hopefully address function assertion lifetime issue 2019-12-09 23:42:32 -08:00
Michael Zhu
d11cdcd5d2 Use seeded rng for simulations 2019-12-09 23:42:32 -08:00
Amir Bandeali
0e59bd0bf3 Add mainnet config tests 2019-12-09 16:16:22 -08:00
Amir Bandeali
c0c6154ec1 Add fork option to describe and blockchainTests 2019-12-09 16:14:41 -08:00
Amir Bandeali
cb5384c2fb Use fork configs if FORK_RPC_URL env var is set 2019-12-09 16:14:41 -08:00
Amir Bandeali
038c836fe5 Rename fillorder_test to fill_order_test 2019-12-09 16:14:41 -08:00
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
387 changed files with 15348 additions and 7352 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,22 @@
[
{
"timestamp": 1576540892,
"version": "3.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [
@@ -55,6 +73,10 @@
{
"note": "Implement `KyberBridge`.",
"pr": 2352
},
{
"note": "Implement `DydxBridge`.",
"pr": 2365
}
],
"timestamp": 1575290197

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.2 - _December 17, 2019_
* Dependencies updated
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Implement `KyberBridge`. (#2352)
@@ -22,6 +30,7 @@ CHANGELOG
## v2.3.0-beta.4 - _December 2, 2019_
* Implement `KyberBridge`. (#2352)
* Implement `DydxBridge`. (#2365)
## v2.3.0-beta.3 - _November 20, 2019_

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

@@ -0,0 +1,241 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IDydxBridge.sol";
import "../interfaces/IDydx.sol";
contract DydxBridge is
IERC20Bridge,
IDydxBridge,
DeploymentConstants
{
using LibSafeMath for uint256;
/// @dev Callback for `IERC20Bridge`. Deposits or withdraws tokens from a dydx account.
/// Notes:
/// 1. This bridge must be set as an operator of the input dydx account.
/// 2. This function may only be called in the context of the 0x Exchange.
/// 3. The maker or taker of the 0x order must be the dydx account owner.
/// 4. Deposits into dydx are made from the `from` address.
/// 5. Withdrawals from dydx are made to the `to` address.
/// 6. Calling this function must always withdraw at least `amount`,
/// otherwise the `ERC20Bridge` will revert.
/// @param from The sender of the tokens and owner of the dydx account.
/// @param to The recipient of the tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to deposit or withdraw.
/// @param encodedBridgeData An abi-encoded `BridgeData` struct.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address,
address from,
address to,
uint256 amount,
bytes calldata encodedBridgeData
)
external
returns (bytes4 success)
{
// Ensure that only the `ERC20BridgeProxy` can call this function.
require(
msg.sender == _getERC20BridgeProxyAddress(),
"DydxBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY"
);
// Decode bridge data.
(BridgeData memory bridgeData) = abi.decode(encodedBridgeData, (BridgeData));
// The dydx accounts are owned by the `from` address.
IDydx.AccountInfo[] memory accounts = _createAccounts(from, bridgeData);
// Create dydx actions to run on the dydx accounts.
IDydx.ActionArgs[] memory actions = _createActions(
from,
to,
amount,
bridgeData
);
// Run operation. This will revert on failure.
IDydx(_getDydxAddress()).operate(accounts, actions);
return BRIDGE_SUCCESS;
}
/// @dev Creates an array of accounts for dydx to operate on.
/// All accounts must belong to the same owner.
/// @param accountOwner Owner of the dydx account.
/// @param bridgeData A `BridgeData` struct.
function _createAccounts(
address accountOwner,
BridgeData memory bridgeData
)
internal
returns (IDydx.AccountInfo[] memory accounts)
{
uint256[] memory accountNumbers = bridgeData.accountNumbers;
uint256 nAccounts = accountNumbers.length;
accounts = new IDydx.AccountInfo[](nAccounts);
for (uint256 i = 0; i < nAccounts; ++i) {
accounts[i] = IDydx.AccountInfo({
owner: accountOwner,
number: accountNumbers[i]
});
}
}
/// @dev Creates an array of actions to carry out on dydx.
/// @param depositFrom Deposit value from this address (owner of the dydx account).
/// @param withdrawTo Withdraw value to this address.
/// @param amount The amount of value available to operate on.
/// @param bridgeData A `BridgeData` struct.
function _createActions(
address depositFrom,
address withdrawTo,
uint256 amount,
BridgeData memory bridgeData
)
internal
returns (IDydx.ActionArgs[] memory actions)
{
BridgeAction[] memory bridgeActions = bridgeData.actions;
uint256 nBridgeActions = bridgeActions.length;
actions = new IDydx.ActionArgs[](nBridgeActions);
for (uint256 i = 0; i < nBridgeActions; ++i) {
// Cache current bridge action.
BridgeAction memory bridgeAction = bridgeActions[i];
// Scale amount, if conversion rate is set.
uint256 scaledAmount;
if (bridgeAction.conversionRateDenominator > 0) {
scaledAmount = LibMath.safeGetPartialAmountFloor(
bridgeAction.conversionRateNumerator,
bridgeAction.conversionRateDenominator,
amount
);
} else {
scaledAmount = amount;
}
// Construct dydx action.
if (bridgeAction.actionType == BridgeActionType.Deposit) {
// Deposit tokens from the account owner into their dydx account.
actions[i] = _createDepositAction(
depositFrom,
scaledAmount,
bridgeAction
);
} else if (bridgeAction.actionType == BridgeActionType.Withdraw) {
// Withdraw tokens from dydx to the `otherAccount`.
actions[i] = _createWithdrawAction(
withdrawTo,
scaledAmount,
bridgeAction
);
} else {
// If all values in the `Action` enum are handled then this
// revert is unreachable: Solidity will revert when casting
// from `uint8` to `Action`.
revert("DydxBridge/UNRECOGNIZED_BRIDGE_ACTION");
}
}
}
/// @dev Returns a dydx `DepositAction`.
/// @param depositFrom Deposit tokens from this address who is also the account owner.
/// @param amount of tokens to deposit.
/// @param bridgeAction A `BridgeAction` struct.
/// @return depositAction The encoded dydx action.
function _createDepositAction(
address depositFrom,
uint256 amount,
BridgeAction memory bridgeAction
)
internal
pure
returns (
IDydx.ActionArgs memory depositAction
)
{
// Create dydx amount.
IDydx.AssetAmount memory dydxAmount = IDydx.AssetAmount({
sign: true, // true if positive.
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
value: amount // amount to deposit.
});
// Create dydx deposit action.
depositAction = IDydx.ActionArgs({
actionType: IDydx.ActionType.Deposit, // deposit tokens.
amount: dydxAmount, // amount to deposit.
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
primaryMarketId: bridgeAction.marketId, // indicates which token to deposit.
otherAddress: depositFrom, // deposit from the account owner.
// unused parameters
secondaryMarketId: 0,
otherAccountId: 0,
data: hex''
});
}
/// @dev Returns a dydx `WithdrawAction`.
/// @param withdrawTo Withdraw tokens to this address.
/// @param amount of tokens to withdraw.
/// @param bridgeAction A `BridgeAction` struct.
/// @return withdrawAction The encoded dydx action.
function _createWithdrawAction(
address withdrawTo,
uint256 amount,
BridgeAction memory bridgeAction
)
internal
pure
returns (
IDydx.ActionArgs memory withdrawAction
)
{
// Create dydx amount.
IDydx.AssetAmount memory amountToWithdraw = IDydx.AssetAmount({
sign: false, // false if negative.
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
value: amount // amount to withdraw.
});
// Create withdraw action.
withdrawAction = IDydx.ActionArgs({
actionType: IDydx.ActionType.Withdraw, // withdraw tokens.
amount: amountToWithdraw, // amount to withdraw.
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
primaryMarketId: bridgeAction.marketId, // indicates which token to withdraw.
otherAddress: withdrawTo, // withdraw tokens to this address.
// unused parameters
secondaryMarketId: 0,
otherAccountId: 0,
data: hex''
});
}
}

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,89 @@
/*
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;
interface IDydx {
/// @dev Represents the unique key that specifies an account
struct AccountInfo {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
/// @dev Arguments that are passed to Solo in an ordered list as part of a single operation.
/// Each ActionArgs has an actionType which specifies which action struct that this data will be
/// parsed into before being processed.
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
enum AssetDenomination {
Wei, // the amount is denominated in wei
Par // the amount is denominated in par
}
enum AssetReference {
Delta, // the amount is given as a delta from the current value
Target // the amount is given as an exact number to end up at
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
/// @dev The main entry-point to Solo that allows users and contracts to manage accounts.
/// Take one or more actions on one or more accounts. The msg.sender must be the owner or
/// operator of all accounts except for those being liquidated, vaporized, or traded with.
/// One call to operate() is considered a singular "operation". Account collateralization is
/// ensured only after the completion of the entire operation.
/// @param accounts A list of all accounts that will be used in this operation. Cannot contain
/// duplicates. In each action, the relevant account will be referred-to by its
/// index in the list.
/// @param actions An ordered list of all actions that will be taken in this operation. The
/// actions will be processed in order.
function operate(
AccountInfo[] calldata accounts,
ActionArgs[] calldata actions
)
external;
}

View File

@@ -0,0 +1,42 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
interface IDydxBridge {
/// @dev This is the subset of `IDydx.ActionType` that are supported by the bridge.
enum BridgeActionType {
Deposit, // Deposit tokens into dydx account.
Withdraw // Withdraw tokens from dydx account.
}
struct BridgeAction {
BridgeActionType actionType; // Action to run on dydx account.
uint256 accountId; // Index in `BridgeData.accountNumbers` for this action.
uint256 marketId; // Market to operate on.
uint256 conversionRateNumerator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
uint256 conversionRateDenominator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
}
struct BridgeData {
uint256[] accountNumbers; // Account number used to identify the owner's specific account.
BridgeAction[] actions; // Actions to carry out on the owner's accounts.
}
}

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

@@ -0,0 +1,101 @@
/*
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/DydxBridge.sol";
// solhint-disable space-after-comma
contract TestDydxBridge is
IDydx,
DydxBridge
{
address private constant ALWAYS_REVERT_ADDRESS = address(1);
event OperateAccount(
address owner,
uint256 number
);
event OperateAction(
ActionType actionType,
uint256 accountId,
bool amountSign,
AssetDenomination amountDenomination,
AssetReference amountRef,
uint256 amountValue,
uint256 primaryMarketId,
uint256 secondaryMarketId,
address otherAddress,
uint256 otherAccountId,
bytes data
);
/// @dev Simulates `operate` in dydx contract.
/// Emits events so that arguments can be validated client-side.
function operate(
AccountInfo[] calldata accounts,
ActionArgs[] calldata actions
)
external
{
for (uint i = 0; i < accounts.length; ++i) {
emit OperateAccount(
accounts[i].owner,
accounts[i].number
);
}
for (uint i = 0; i < actions.length; ++i) {
emit OperateAction(
actions[i].actionType,
actions[i].accountId,
actions[i].amount.sign,
actions[i].amount.denomination,
actions[i].amount.ref,
actions[i].amount.value,
actions[i].primaryMarketId,
actions[i].secondaryMarketId,
actions[i].otherAddress,
actions[i].otherAccountId,
actions[i].data
);
}
}
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
function _getDydxAddress()
internal
view
returns (address)
{
return address(this);
}
/// @dev overrides `_getERC20BridgeProxyAddress()` from `DeploymentConstants` for testing.
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.2",
"engines": {
"node": ">=6.12"
},
@@ -38,8 +38,7 @@
"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",
"abis": "./test/generated-artifacts/@(ChaiBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -52,15 +51,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.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/types": "^3.1.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -80,15 +79,16 @@
"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/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/base-contract": "^6.0.2",
"@0x/contracts-dev-utils": "^1.0.2",
"@0x/contracts-erc1155": "^2.0.2",
"@0x/contracts-erc20": "^3.0.2",
"@0x/contracts-erc721": "^3.0.2",
"@0x/contracts-exchange-libs": "^4.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

@@ -5,6 +5,8 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
import * as DydxBridge from '../generated-artifacts/DydxBridge.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';
@@ -12,22 +14,62 @@ import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
import * as IAssetData from '../generated-artifacts/IAssetData.json';
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
import * as IChai from '../generated-artifacts/IChai.json';
import * as IDydx from '../generated-artifacts/IDydx.json';
import * as IDydxBridge from '../generated-artifacts/IDydxBridge.json';
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json';
import * as IKyberNetworkProxy from '../generated-artifacts/IKyberNetworkProxy.json';
import * as IUniswapExchange from '../generated-artifacts/IUniswapExchange.json';
import * as IUniswapExchangeFactory from '../generated-artifacts/IUniswapExchangeFactory.json';
import * as KyberBridge from '../generated-artifacts/KyberBridge.json';
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
import * as Ownable from '../generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
import * as TestChaiBridge from '../generated-artifacts/TestChaiBridge.json';
import * as TestDydxBridge from '../generated-artifacts/TestDydxBridge.json';
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
import * as TestKyberBridge from '../generated-artifacts/TestKyberBridge.json';
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
import * as TestUniswapBridge from '../generated-artifacts/TestUniswapBridge.json';
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
export const artifacts = {
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
Ownable: Ownable as ContractArtifact,
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact,
DydxBridge: DydxBridge as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IChai: IChai as ContractArtifact,
IDydx: IDydx as ContractArtifact,
IDydxBridge: IDydxBridge 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,
TestDydxBridge: TestDydxBridge as ContractArtifact,
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestKyberBridge: TestKyberBridge as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
};

View File

@@ -5,6 +5,7 @@ export {
ERC20ProxyContract,
ERC721ProxyContract,
Eth2DaiBridgeContract,
DydxBridgeContract,
IAssetDataContract,
IAssetProxyContract,
MultiAssetProxyContract,

View File

@@ -3,6 +3,8 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/chai_bridge';
export * from '../generated-wrappers/dydx_bridge';
export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_bridge_proxy';
export * from '../generated-wrappers/erc20_proxy';
@@ -10,8 +12,27 @@ export * from '../generated-wrappers/erc721_proxy';
export * from '../generated-wrappers/eth2_dai_bridge';
export * from '../generated-wrappers/i_asset_data';
export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
export * from '../generated-wrappers/i_authorizable';
export * from '../generated-wrappers/i_chai';
export * from '../generated-wrappers/i_dydx';
export * from '../generated-wrappers/i_dydx_bridge';
export * from '../generated-wrappers/i_erc20_bridge';
export * from '../generated-wrappers/i_eth2_dai';
export * from '../generated-wrappers/i_kyber_network_proxy';
export * from '../generated-wrappers/i_uniswap_exchange';
export * from '../generated-wrappers/i_uniswap_exchange_factory';
export * from '../generated-wrappers/kyber_bridge';
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../generated-wrappers/mixin_authorizable';
export * from '../generated-wrappers/multi_asset_proxy';
export * from '../generated-wrappers/ownable';
export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_chai_bridge';
export * from '../generated-wrappers/test_dydx_bridge';
export * from '../generated-wrappers/test_erc20_bridge';
export * from '../generated-wrappers/test_eth2_dai_bridge';
export * from '../generated-wrappers/test_kyber_bridge';
export * from '../generated-wrappers/test_static_call_target';
export * from '../generated-wrappers/test_uniswap_bridge';
export * from '../generated-wrappers/uniswap_bridge';

View File

@@ -5,6 +5,8 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
import * as DydxBridge from '../test/generated-artifacts/DydxBridge.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 +16,9 @@ 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 IDydx from '../test/generated-artifacts/IDydx.json';
import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.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 +30,8 @@ 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 TestDydxBridge from '../test/generated-artifacts/TestDydxBridge.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 +48,8 @@ export const artifacts = {
ERC721Proxy: ERC721Proxy as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact,
DydxBridge: DydxBridge as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
@@ -48,11 +57,16 @@ export const artifacts = {
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IChai: IChai as ContractArtifact,
IDydx: IDydx as ContractArtifact,
IDydxBridge: IDydxBridge 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,
TestDydxBridge: TestDydxBridge 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

@@ -0,0 +1,244 @@
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
blockchainTests.resets('DydxBridge unit tests', env => {
const defaultAccountNumber = new BigNumber(1);
const marketId = new BigNumber(2);
const defaultAmount = new BigNumber(4);
const notAuthorized = '0x0000000000000000000000000000000000000001';
let testContract: TestDydxBridgeContract;
let authorized: string;
let accountOwner: string;
let receiver: string;
before(async () => {
// Get accounts
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
[, /* owner */ authorized, accountOwner, receiver] = accounts;
// Deploy dydx bridge
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestDydxBridge,
env.provider,
env.txDefaults,
artifacts,
);
});
describe('bridgeTransferFrom()', () => {
enum BridgeActionType {
Deposit,
Withdraw,
}
interface BrigeAction {
actionType: BridgeActionType;
accountId: BigNumber;
marketId: BigNumber;
conversionRateNumerator: BigNumber;
conversionRateDenominator: BigNumber;
}
interface BridgeData {
accountNumbers: BigNumber[];
actions: BrigeAction[];
}
const defaultDepositAction = {
actionType: BridgeActionType.Deposit as number,
accountId: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const defaultWithdrawAction = {
actionType: BridgeActionType.Withdraw as number,
accountId: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const bridgeDataEncoder = AbiEncoder.create([
{
name: 'bridgeData',
type: 'tuple',
components: [
{ name: 'accountNumbers', type: 'uint256[]' },
{
name: 'actions',
type: 'tuple[]',
components: [
{ name: 'actionType', type: 'uint8' },
{ name: 'accountId', type: 'uint256' },
{ name: 'marketId', type: 'uint256' },
{ name: 'conversionRateNumerator', type: 'uint256' },
{ name: 'conversionRateDenominator', type: 'uint256' },
],
},
],
},
]);
const callBridgeTransferFrom = async (
from: string,
to: string,
amount: BigNumber,
bridgeData: BridgeData,
sender: string,
): Promise<string> => {
const returnValue = await testContract
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
.callAsync({ from: sender });
return returnValue;
};
const callBridgeTransferFromAndVerifyEvents = async (
from: string,
to: string,
amount: BigNumber,
bridgeData: BridgeData,
sender: string,
): Promise<void> => {
// Execute transaction.
const txReceipt = await testContract
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
.awaitTransactionSuccessAsync({ from: sender });
// Verify `OperateAccount` event.
const expectedOperateAccountEvents = [];
for (const accountNumber of bridgeData.accountNumbers) {
expectedOperateAccountEvents.push({
owner: accountOwner,
number: accountNumber,
});
}
verifyEventsFromLogs(txReceipt.logs, expectedOperateAccountEvents, TestDydxBridgeEvents.OperateAccount);
// Verify `OperateAction` event.
const weiDenomination = 0;
const deltaAmountRef = 0;
const expectedOperateActionEvents = [];
for (const action of bridgeData.actions) {
expectedOperateActionEvents.push({
actionType: action.actionType as number,
accountId: action.accountId,
amountSign: action.actionType === BridgeActionType.Deposit ? true : false,
amountDenomination: weiDenomination,
amountRef: deltaAmountRef,
amountValue: action.conversionRateDenominator.gt(0)
? amount.times(action.conversionRateNumerator).div(action.conversionRateDenominator)
: amount,
primaryMarketId: marketId,
secondaryMarketId: constants.ZERO_AMOUNT,
otherAddress: action.actionType === BridgeActionType.Deposit ? from : to,
otherAccountId: constants.ZERO_AMOUNT,
data: '0x',
});
}
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
};
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('succeeds when calling `operate` with the `withdraw` action and a single accuont', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction, defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction, defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('succeeds when scaling the `amount` to deposit', async () => {
const conversionRateNumerator = new BigNumber(1);
const conversionRateDenominator = new BigNumber(2);
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [
defaultWithdrawAction,
{
...defaultDepositAction,
conversionRateNumerator,
conversionRateDenominator,
},
],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('succeeds when scaling the `amount` to withdraw', async () => {
const conversionRateNumerator = new BigNumber(1);
const conversionRateDenominator = new BigNumber(2);
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [
defaultDepositAction,
{
...defaultWithdrawAction,
conversionRateNumerator,
conversionRateDenominator,
},
],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
});
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
const callBridgeTransferFromPromise = callBridgeTransferFrom(
accountOwner,
receiver,
defaultAmount,
bridgeData,
notAuthorized,
);
const expectedError = RevertReason.DydxBridgeOnlyCallableByErc20BridgeProxy;
return expect(callBridgeTransferFromPromise).to.revertWith(expectedError);
});
it('should return magic bytes if call succeeds', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
const returnValue = await callBridgeTransferFrom(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
});
});
});

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,8 @@
* 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/dydx_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 +14,9 @@ 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_dydx';
export * from '../test/generated-wrappers/i_dydx_bridge';
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 +28,8 @@ 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_dydx_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,8 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/ChaiBridge.json",
"generated-artifacts/DydxBridge.json",
"generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20BridgeProxy.json",
"generated-artifacts/ERC20Proxy.json",
@@ -10,11 +12,32 @@
"generated-artifacts/Eth2DaiBridge.json",
"generated-artifacts/IAssetData.json",
"generated-artifacts/IAssetProxy.json",
"generated-artifacts/IAssetProxyDispatcher.json",
"generated-artifacts/IAuthorizable.json",
"generated-artifacts/IChai.json",
"generated-artifacts/IDydx.json",
"generated-artifacts/IDydxBridge.json",
"generated-artifacts/IERC20Bridge.json",
"generated-artifacts/IEth2Dai.json",
"generated-artifacts/IKyberNetworkProxy.json",
"generated-artifacts/IUniswapExchange.json",
"generated-artifacts/IUniswapExchangeFactory.json",
"generated-artifacts/KyberBridge.json",
"generated-artifacts/MixinAssetProxyDispatcher.json",
"generated-artifacts/MixinAuthorizable.json",
"generated-artifacts/MultiAssetProxy.json",
"generated-artifacts/Ownable.json",
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestChaiBridge.json",
"generated-artifacts/TestDydxBridge.json",
"generated-artifacts/TestERC20Bridge.json",
"generated-artifacts/TestEth2DaiBridge.json",
"generated-artifacts/TestKyberBridge.json",
"generated-artifacts/TestStaticCallTarget.json",
"generated-artifacts/TestUniswapBridge.json",
"generated-artifacts/UniswapBridge.json",
"test/generated-artifacts/ChaiBridge.json",
"test/generated-artifacts/DydxBridge.json",
"test/generated-artifacts/ERC1155Proxy.json",
"test/generated-artifacts/ERC20BridgeProxy.json",
"test/generated-artifacts/ERC20Proxy.json",
@@ -24,6 +47,9 @@
"test/generated-artifacts/IAssetProxy.json",
"test/generated-artifacts/IAssetProxyDispatcher.json",
"test/generated-artifacts/IAuthorizable.json",
"test/generated-artifacts/IChai.json",
"test/generated-artifacts/IDydx.json",
"test/generated-artifacts/IDydxBridge.json",
"test/generated-artifacts/IERC20Bridge.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetworkProxy.json",
@@ -35,6 +61,8 @@
"test/generated-artifacts/MultiAssetProxy.json",
"test/generated-artifacts/Ownable.json",
"test/generated-artifacts/StaticCallProxy.json",
"test/generated-artifacts/TestChaiBridge.json",
"test/generated-artifacts/TestDydxBridge.json",
"test/generated-artifacts/TestERC20Bridge.json",
"test/generated-artifacts/TestEth2DaiBridge.json",
"test/generated-artifacts/TestKyberBridge.json",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"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.2",
"@0x/contracts-asset-proxy": "^3.0.2",
"@0x/contracts-dev-utils": "^1.0.2",
"@0x/contracts-erc20": "^3.0.2",
"@0x/contracts-exchange": "^3.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/dev-utils": "^3.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/sol-compiler": "^4.0.2",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@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/contract-addresses": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/json-schemas": "^5.0.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/assert": "^3.0.2",
"@0x/base-contract": "^6.0.2",
"@0x/contract-addresses": "^4.1.0",
"@0x/contracts-utils": "^4.0.2",
"@0x/json-schemas": "^5.0.2",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"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,22 @@
[
{
"timestamp": 1576540892,
"version": "1.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1575931811,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"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.2",
"@0x/assert": "^3.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/sol-compiler": "^4.0.2",
"@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.2"
},
"publishConfig": {
"access": "public"

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"engines": {
"node": ">=6.12"
},
@@ -52,15 +52,15 @@
},
"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.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -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.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,23 @@
[
{
"version": "1.0.2",
"changes": [
{
"note": "Do not query empty/unsigned orders. Swallow revets on DEX quotes.",
"pr": 2365
}
],
"timestamp": 1576540892
},
{
"timestamp": 1575931811,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.2 - _December 17, 2019_
* Do not query empty/unsigned orders. Swallow revets on DEX quotes. (#2365)
## v1.0.1 - _December 9, 2019_
* Dependencies updated
## v1.0.0 - _December 2, 2019_
* Created package. (#2344)

View File

@@ -1,92 +0,0 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "./IEth2Dai.sol";
import "./IKyberNetwork.sol";
contract DeploymentConstants {
/// @dev Address of the 0x Exchange contract.
address constant public EXCHANGE_ADDRESS = 0x080bf510FCbF18b91105470639e9561022937712;
/// @dev Address of the Eth2Dai MatchingMarket contract.
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
/// @dev Address of the UniswapExchangeFactory contract.
address constant public UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
/// @dev Address of the KyberNeworkProxy contract.
address constant public KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
/// @dev Address of the WETH contract.
address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev Kyber ETH pseudo-address.
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev An overridable way to retrieve the 0x Exchange contract.
/// @return zeroex The 0x Exchange contract.
function _getExchangeContract()
internal
view
returns (IExchange zeroex)
{
return IExchange(EXCHANGE_ADDRESS);
}
/// @dev An overridable way to retrieve the Eth2Dai exchange contract.
/// @return eth2dai The Eth2Dai exchange contract.
function _getEth2DaiContract()
internal
view
returns (IEth2Dai eth2dai)
{
return IEth2Dai(ETH2DAI_ADDRESS);
}
/// @dev An overridable way to retrieve the Uniswap exchange factory contract.
/// @return uniswap The UniswapExchangeFactory contract.
function _getUniswapExchangeFactoryContract()
internal
view
returns (IUniswapExchangeFactory uniswap)
{
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
}
/// @dev An overridable way to retrieve the Kyber network proxy contract.
/// @return kyber The KyberNeworkProxy contract.
function _getKyberNetworkContract()
internal
view
returns (IKyberNetwork kyber)
{
return IKyberNetwork(KYBER_NETWORK_PROXY_ADDRESS);
}
/// @dev An overridable way to retrieve the WETH contract address.
/// @return weth The WETH contract address.
function _getWETHAddress()
internal
view
returns (address weth)
{
return WETH_ADDRESS;
}
}

View File

@@ -23,11 +23,13 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFacto
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "./IDevUtils.sol";
import "./IERC20BridgeSampler.sol";
import "./IEth2Dai.sol";
import "./IKyberNetwork.sol";
import "./IUniswapExchangeQuotes.sol";
import "./DeploymentConstants.sol";
contract ERC20BridgeSampler is
@@ -38,26 +40,32 @@ contract ERC20BridgeSampler is
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
/// by each order in `orders`.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleSells(
LibOrder.Order[] memory orders,
bytes[] memory orderSignatures,
address[] memory sources,
uint256[] memory takerTokenAmounts
)
public
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[] memory orderFillableTakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
)
{
require(orders.length != 0, "EMPTY_ORDERS");
orderInfos = queryOrders(orders);
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
orderFillableTakerAssetAmounts = getOrderFillableTakerAssetAmounts(
orders,
orderSignatures
);
makerTokenAmountsBySource = sampleSells(
sources,
_assetDataToTokenAddress(orders[0].takerAssetData),
@@ -68,26 +76,32 @@ contract ERC20BridgeSampler is
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
/// by each order in `orders`.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleBuys(
LibOrder.Order[] memory orders,
bytes[] memory orderSignatures,
address[] memory sources,
uint256[] memory makerTokenAmounts
)
public
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[] memory orderFillableMakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
)
{
require(orders.length != 0, "EMPTY_ORDERS");
orderInfos = queryOrders(orders);
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
orderFillableMakerAssetAmounts = getOrderFillableMakerAssetAmounts(
orders,
orderSignatures
);
makerTokenAmountsBySource = sampleBuys(
sources,
_assetDataToTokenAddress(orders[0].takerAssetData),
@@ -96,18 +110,77 @@ contract ERC20BridgeSampler is
);
}
/// @dev Queries the status of several native orders.
/// @dev Queries the fillable taker asset amounts of native orders.
/// Effectively ignores orders that have empty signatures or
/// maker/taker asset amounts (returning 0).
/// @param orders Native orders to query.
/// @return orderInfos Order info for each respective order.
function queryOrders(LibOrder.Order[] memory orders)
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
/// by each order in `orders`.
function getOrderFillableTakerAssetAmounts(
LibOrder.Order[] memory orders,
bytes[] memory orderSignatures
)
public
view
returns (LibOrder.OrderInfo[] memory orderInfos)
returns (uint256[] memory orderFillableTakerAssetAmounts)
{
uint256 numOrders = orders.length;
orderInfos = new LibOrder.OrderInfo[](numOrders);
for (uint256 i = 0; i < numOrders; i++) {
orderInfos[i] = _getExchangeContract().getOrderInfo(orders[i]);
orderFillableTakerAssetAmounts = new uint256[](orders.length);
for (uint256 i = 0; i != orders.length; i++) {
// Ignore orders with no signature or empty maker/taker amounts.
if (orderSignatures[i].length == 0 ||
orders[i].makerAssetAmount == 0 ||
orders[i].takerAssetAmount == 0) {
orderFillableTakerAssetAmounts[i] = 0;
continue;
}
(
LibOrder.OrderInfo memory orderInfo,
uint256 fillableTakerAssetAmount,
bool isValidSignature
) = IDevUtils(_getDevUtilsAddress()).getOrderRelevantState(
orders[i],
orderSignatures[i]
);
// The fillable amount is zero if the order is not fillable or if the
// signature is invalid.
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE) ||
!isValidSignature) {
orderFillableTakerAssetAmounts[i] = 0;
} else {
orderFillableTakerAssetAmounts[i] = fillableTakerAssetAmount;
}
}
}
/// @dev Queries the fillable taker asset amounts of native orders.
/// Effectively ignores orders that have empty signatures or
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
/// by each order in `orders`.
function getOrderFillableMakerAssetAmounts(
LibOrder.Order[] memory orders,
bytes[] memory orderSignatures
)
public
view
returns (uint256[] memory orderFillableMakerAssetAmounts)
{
orderFillableMakerAssetAmounts = getOrderFillableTakerAssetAmounts(
orders,
orderSignatures
);
// `orderFillableMakerAssetAmounts` now holds taker asset amounts, so
// convert them to maker asset amounts.
for (uint256 i = 0; i < orders.length; ++i) {
if (orderFillableMakerAssetAmounts[i] != 0) {
orderFillableMakerAssetAmounts[i] = LibMath.getPartialAmountCeil(
orderFillableMakerAssetAmounts[i],
orders[i].takerAssetAmount,
orders[i].makerAssetAmount
);
}
}
}
@@ -187,18 +260,24 @@ contract ERC20BridgeSampler is
returns (uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
address _takerToken = takerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : takerToken;
address _makerToken = makerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : makerToken;
address _takerToken = takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken;
address _makerToken = makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken;
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(uint256 rate,) = _getKyberNetworkContract().getExpectedRate(
_takerToken,
_makerToken,
takerTokenAmounts[i]
);
(bool didSucceed, bytes memory resultData) =
_getKyberNetworkProxyAddress().staticcall(abi.encodeWithSelector(
IKyberNetwork(0).getExpectedRate.selector,
_takerToken,
_makerToken,
takerTokenAmounts[i]
));
uint256 rate = 0;
if (didSucceed) {
rate = abi.decode(resultData, (uint256));
}
makerTokenAmounts[i] =
rate *
takerTokenAmounts[i] *
@@ -227,11 +306,18 @@ contract ERC20BridgeSampler is
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
makerTokenAmounts[i] = _getEth2DaiContract().getBuyAmount(
makerToken,
takerToken,
takerTokenAmounts[i]
);
(bool didSucceed, bytes memory resultData) =
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
IEth2Dai(0).getBuyAmount.selector,
makerToken,
takerToken,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
}
makerTokenAmounts[i] = buyAmount;
}
}
@@ -254,11 +340,18 @@ contract ERC20BridgeSampler is
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] = _getEth2DaiContract().getPayAmount(
takerToken,
makerToken,
makerTokenAmounts[i]
);
(bool didSucceed, bytes memory resultData) =
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
IEth2Dai(0).getPayAmount.selector,
takerToken,
makerToken,
makerTokenAmounts[i]
));
uint256 sellAmount = 0;
if (didSucceed) {
sellAmount = abi.decode(resultData, (uint256));
}
takerTokenAmounts[i] = sellAmount;
}
}
@@ -280,26 +373,38 @@ contract ERC20BridgeSampler is
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
for (uint256 i = 0; i < numSamples; i++) {
if (makerToken == _getWETHAddress()) {
makerTokenAmounts[i] = takerTokenExchange.getTokenToEthInputPrice(
if (makerToken == _getWethAddress()) {
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthInputPrice.selector,
takerTokenAmounts[i]
);
} else if (takerToken == _getWETHAddress()) {
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
} else if (takerToken == _getWethAddress()) {
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenInputPrice.selector,
takerTokenAmounts[i]
);
} else {
uint256 ethBought = takerTokenExchange.getTokenToEthInputPrice(
uint256 ethBought = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthInputPrice.selector,
takerTokenAmounts[i]
);
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
ethBought
);
if (ethBought != 0) {
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenInputPrice.selector,
ethBought
);
} else {
makerTokenAmounts[i] = 0;
}
}
}
}
@@ -322,26 +427,38 @@ contract ERC20BridgeSampler is
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
for (uint256 i = 0; i < numSamples; i++) {
if (makerToken == _getWETHAddress()) {
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
if (makerToken == _getWethAddress()) {
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthOutputPrice.selector,
makerTokenAmounts[i]
);
} else if (takerToken == _getWETHAddress()) {
takerTokenAmounts[i] = makerTokenExchange.getEthToTokenOutputPrice(
} else if (takerToken == _getWethAddress()) {
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenOutputPrice.selector,
makerTokenAmounts[i]
);
} else {
uint256 ethSold = makerTokenExchange.getEthToTokenOutputPrice(
uint256 ethSold = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenOutputPrice.selector,
makerTokenAmounts[i]
);
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
ethSold
);
if (ethSold != 0) {
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthOutputPrice.selector,
ethSold
);
} else {
takerTokenAmounts[i] = 0;
}
}
}
}
@@ -357,6 +474,34 @@ contract ERC20BridgeSampler is
return LibERC20Token.decimals(tokenAddress);
}
/// @dev Gracefully calls a Uniswap pricing function.
/// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange.
/// @param functionSelector Selector of the target function.
/// @param inputAmount Quantity parameter particular to the pricing function.
/// @return outputAmount The returned amount from the function call. Will be
/// zero if the call fails or if `uniswapExchangeAddress` is zero.
function _callUniswapExchangePriceFunction(
address uniswapExchangeAddress,
bytes4 functionSelector,
uint256 inputAmount
)
private
view
returns (uint256 outputAmount)
{
if (uniswapExchangeAddress == address(0)) {
return 0;
}
(bool didSucceed, bytes memory resultData) =
uniswapExchangeAddress.staticcall(abi.encodeWithSelector(
functionSelector,
inputAmount
));
if (didSucceed) {
outputAmount = abi.decode(resultData, (uint256));
}
}
/// @dev Samples a supported sell source, defined by its address.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
@@ -373,16 +518,16 @@ contract ERC20BridgeSampler is
view
returns (uint256[] memory makerTokenAmounts)
{
if (source == address(_getEth2DaiContract())) {
if (source == _getEth2DaiAddress()) {
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
}
if (source == address(_getUniswapExchangeFactoryContract())) {
if (source == _getUniswapExchangeFactoryAddress()) {
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
}
if (source == address(_getKyberNetworkContract())) {
if (source == _getKyberNetworkProxyAddress()) {
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
}
revert("UNSUPPORTED_SOURCE");
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
}
/// @dev Samples a supported buy source, defined by its address.
@@ -401,13 +546,13 @@ contract ERC20BridgeSampler is
view
returns (uint256[] memory takerTokenAmounts)
{
if (source == address(_getEth2DaiContract())) {
if (source == _getEth2DaiAddress()) {
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
}
if (source == address(_getUniswapExchangeFactoryContract())) {
if (source == _getUniswapExchangeFactoryAddress()) {
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
}
revert("UNSUPPORTED_SOURCE");
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
}
/// @dev Retrive an existing Uniswap exchange contract.
@@ -420,9 +565,9 @@ contract ERC20BridgeSampler is
returns (IUniswapExchangeQuotes exchange)
{
exchange = IUniswapExchangeQuotes(
address(_getUniswapExchangeFactoryContract().getExchange(tokenAddress))
address(IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
.getExchange(tokenAddress))
);
require(address(exchange) != address(0), "UNSUPPORTED_UNISWAP_EXCHANGE");
}
/// @dev Extract the token address from ERC20 proxy asset data.
@@ -433,19 +578,19 @@ contract ERC20BridgeSampler is
pure
returns (address tokenAddress)
{
require(assetData.length == 36, "INVALID_ASSET_DATA");
require(assetData.length == 36, "ERC20BridgeSampler/INVALID_ASSET_DATA");
bytes4 selector;
assembly {
selector := and(mload(add(assetData, 0x20)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
tokenAddress := mload(add(assetData, 0x24))
}
require(selector == ERC20_PROXY_ID, "UNSUPPORTED_ASSET_PROXY");
require(selector == ERC20_PROXY_ID, "ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY");
}
function _assertValidPair(address makerToken, address takerToken)
private
pure
{
require(makerToken != takerToken, "INVALID_TOKEN_PAIR");
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
}
}

View File

@@ -0,0 +1,45 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
interface IDevUtils {
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
/// @param order The order structure.
/// @param signature Signature provided by maker that proves the order's authenticity.
/// `0x01` can always be provided if the signature does not need to be validated.
/// @return The orderInfo (hash, status, and `takerAssetAmount` already filled for the given order),
/// fillableTakerAssetAmount (amount of the order's `takerAssetAmount` that is fillable given all on-chain state),
/// and isValidSignature (validity of the provided signature).
/// NOTE: If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled"
/// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final
/// amount of each asset that can be filled.
function getOrderRelevantState(LibOrder.Order calldata order, bytes calldata signature)
external
view
returns (
LibOrder.OrderInfo memory orderInfo,
uint256 fillableTakerAssetAmount,
bool isValidSignature
);
}

View File

@@ -19,59 +19,82 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
interface IERC20BridgeSampler {
/// @dev Query native orders and sample sell orders on multiple DEXes at once.
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
/// @param takerTokenAmounts Taker sell amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
/// by each order in `orders`.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleSells(
LibOrder.Order[] calldata orders,
bytes[] calldata orderSignatures,
address[] calldata sources,
uint256[] calldata takerTokenAmounts
)
external
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[] memory orderFillableTakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
);
/// @dev Query native orders and sample buy orders on multiple DEXes at once.
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
/// @param makerTokenAmounts Maker sell amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
/// by each order in `orders`.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleBuys(
LibOrder.Order[] calldata orders,
bytes[] calldata orderSignatures,
address[] calldata sources,
uint256[] calldata makerTokenAmounts
)
external
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[] memory orderFillableMakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
);
/// @dev Queries the status of several native orders.
/// @dev Queries the fillable taker asset amounts of native orders.
/// @param orders Native orders to query.
/// @return orderInfos Order info for each respective order.
function queryOrders(LibOrder.Order[] calldata orders)
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
/// by each order in `orders`.
function getOrderFillableTakerAssetAmounts(
LibOrder.Order[] calldata orders,
bytes[] calldata orderSignatures
)
external
view
returns (LibOrder.OrderInfo[] memory orderInfos);
returns (uint256[] memory orderFillableTakerAssetAmounts);
/// @dev Queries the fillable maker asset amounts of native orders.
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
/// by each order in `orders`.
function getOrderFillableMakerAssetAmounts(
LibOrder.Order[] calldata orders,
bytes[] calldata orderSignatures
)
external
view
returns (uint256[] memory orderFillableMakerAssetAmounts);
/// @dev Sample sell quotes on multiple DEXes at once.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.

View File

@@ -23,6 +23,7 @@ import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../src/ERC20BridgeSampler.sol";
import "../src/IEth2Dai.sol";
import "../src/IDevUtils.sol";
import "../src/IKyberNetwork.sol";
@@ -90,9 +91,28 @@ library LibDeterministicQuotes {
}
contract FailTrigger {
// Give this address a balance to force operations to fail.
address payable constant public FAILURE_ADDRESS = 0xe9dB8717BC5DFB20aaf538b4a5a02B7791FF430C;
// Funds `FAILURE_ADDRESS`.
function enableFailTrigger() external payable {
FAILURE_ADDRESS.transfer(msg.value);
}
function _revertIfShouldFail() internal view {
if (FAILURE_ADDRESS.balance != 0) {
revert("FAIL_TRIGGERED");
}
}
}
contract TestERC20BridgeSamplerUniswapExchange is
IUniswapExchangeQuotes,
DeploymentConstants
DeploymentConstants,
FailTrigger
{
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
@@ -112,10 +132,11 @@ contract TestERC20BridgeSamplerUniswapExchange is
view
returns (uint256 tokensBought)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicSellQuote(
salt,
tokenAddress,
WETH_ADDRESS,
_getWethAddress(),
ethSold
);
}
@@ -128,9 +149,10 @@ contract TestERC20BridgeSamplerUniswapExchange is
view
returns (uint256 ethSold)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicBuyQuote(
salt,
WETH_ADDRESS,
_getWethAddress(),
tokenAddress,
tokensBought
);
@@ -144,10 +166,11 @@ contract TestERC20BridgeSamplerUniswapExchange is
view
returns (uint256 ethBought)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicSellQuote(
salt,
tokenAddress,
WETH_ADDRESS,
_getWethAddress(),
tokensSold
);
}
@@ -160,9 +183,10 @@ contract TestERC20BridgeSamplerUniswapExchange is
view
returns (uint256 tokensSold)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicBuyQuote(
salt,
WETH_ADDRESS,
_getWethAddress(),
tokenAddress,
ethBought
);
@@ -172,7 +196,8 @@ contract TestERC20BridgeSamplerUniswapExchange is
contract TestERC20BridgeSamplerKyberNetwork is
IKyberNetwork,
DeploymentConstants
DeploymentConstants,
FailTrigger
{
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
@@ -187,8 +212,9 @@ contract TestERC20BridgeSamplerKyberNetwork is
view
returns (uint256 expectedRate, uint256)
{
fromToken = fromToken == ETH_ADDRESS ? WETH_ADDRESS : fromToken;
toToken = toToken == ETH_ADDRESS ? WETH_ADDRESS : toToken;
_revertIfShouldFail();
fromToken = fromToken == ETH_ADDRESS ? _getWethAddress() : fromToken;
toToken = toToken == ETH_ADDRESS ? _getWethAddress() : toToken;
expectedRate = LibDeterministicQuotes.getDeterministicRate(
SALT,
fromToken,
@@ -199,7 +225,8 @@ contract TestERC20BridgeSamplerKyberNetwork is
contract TestERC20BridgeSamplerEth2Dai is
IEth2Dai
IEth2Dai,
FailTrigger
{
bytes32 constant private SALT = 0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7;
@@ -213,6 +240,7 @@ contract TestERC20BridgeSamplerEth2Dai is
view
returns (uint256 buyAmount)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicSellQuote(
SALT,
payToken,
@@ -231,6 +259,7 @@ contract TestERC20BridgeSamplerEth2Dai is
view
returns (uint256 payAmount)
{
_revertIfShouldFail();
return LibDeterministicQuotes.getDeterministicBuyQuote(
SALT,
payToken,
@@ -269,7 +298,8 @@ contract TestERC20BridgeSamplerUniswapExchangeFactory is
contract TestERC20BridgeSampler is
ERC20BridgeSampler
ERC20BridgeSampler,
FailTrigger
{
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
TestERC20BridgeSamplerEth2Dai public eth2Dai;
@@ -288,18 +318,30 @@ contract TestERC20BridgeSampler is
uniswap.createTokenExchanges(tokenAddresses);
}
// `IExchange.getOrderInfo()`, overridden to return deterministic order infos.
function getOrderInfo(LibOrder.Order memory order)
// `IDevUtils.getOrderRelevantState()`, overridden to return deterministic
// states.
function getOrderRelevantState(
LibOrder.Order memory order,
bytes memory
)
public
pure
returns (LibOrder.OrderInfo memory orderInfo)
view
returns (
LibOrder.OrderInfo memory orderInfo,
uint256 fillableTakerAssetAmount,
bool isValidSignature
)
{
// The order hash is just the hash of the salt.
bytes32 orderHash = keccak256(abi.encode(order.salt));
// Everything else is derived from the hash.
orderInfo.orderHash = orderHash;
orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1));
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
orderInfo.orderTakerAssetFilledAmount =
uint256(orderHash) % order.takerAssetAmount;
fillableTakerAssetAmount =
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
isValidSignature = uint256(orderHash) % 2 == 1;
}
// Overriden to return deterministic decimals.
@@ -312,38 +354,38 @@ contract TestERC20BridgeSampler is
}
// Overriden to point to a this contract.
function _getExchangeContract()
function _getDevUtilsAddress()
internal
view
returns (IExchange zeroex)
returns (address devUtilAddress)
{
return IExchange(address(this));
return address(this);
}
// Overriden to point to a custom contract.
function _getEth2DaiContract()
function _getEth2DaiAddress()
internal
view
returns (IEth2Dai eth2dai_)
returns (address eth2daiAddress)
{
return eth2Dai;
return address(eth2Dai);
}
// Overriden to point to a custom contract.
function _getUniswapExchangeFactoryContract()
function _getUniswapExchangeFactoryAddress()
internal
view
returns (IUniswapExchangeFactory uniswap_)
returns (address uniswapAddress)
{
return uniswap;
return address(uniswap);
}
// Overriden to point to a custom contract.
function _getKyberNetworkContract()
function _getKyberNetworkProxyAddress()
internal
view
returns (IKyberNetwork kyber_)
returns (address kyberAddress)
{
return kyber;
return address(kyber);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20-bridge-sampler",
"version": "1.0.0",
"version": "1.0.2",
"engines": {
"node": ">=6.12"
},
@@ -38,7 +38,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(DeploymentConstants|ERC20BridgeSampler|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
},
"repository": {
"type": "git",
@@ -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.2",
"@0x/contracts-asset-proxy": "^3.0.2",
"@0x/contracts-erc20": "^3.0.2",
"@0x/contracts-exchange": "^3.0.2",
"@0x/contracts-exchange-libs": "^4.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@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/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/base-contract": "^6.0.2",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

@@ -5,16 +5,16 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json';
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
export const artifacts = {
DeploymentConstants: DeploymentConstants as ContractArtifact,
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
IDevUtils: IDevUtils as ContractArtifact,
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetwork: IKyberNetwork as ContractArtifact,

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 { Order } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
@@ -31,16 +26,17 @@ 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';
const UNSUPPORTED_ASSET_PROXY_ERROR = 'UNSUPPORTED_ASSET_PROXY';
const INVALID_ASSET_DATA_ERROR = 'INVALID_ASSET_DATA';
const UNSUPPORTED_UNISWAP_EXCHANGE_ERROR = 'UNSUPPORTED_UNISWAP_EXCHANGE';
const UNSUPPORTED_SOURCE_ERROR = 'UNSUPPORTED_SOURCE';
const INVALID_TOKEN_PAIR_ERROR = 'INVALID_TOKEN_PAIR';
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
const UNSUPPORTED_SOURCE_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_SOURCE';
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
@@ -60,7 +56,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 {
@@ -197,17 +193,26 @@ blockchainTests('erc20-bridge-sampler', env => {
return quotes;
}
function getDeterministicOrderInfo(order: Order): OrderInfo {
const hash = getPackedHash(toHex(order.salt, 32));
return {
orderHash: hash,
orderStatus: new BigNumber(hash).mod(255).toNumber(),
orderTakerAssetFilledAmount: new BigNumber(hash).mod(order.takerAssetAmount),
};
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
const orderStatus = new BigNumber(hash).mod(255).toNumber();
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
if (orderStatus !== 3 || !isValidSignature) {
return constants.ZERO_AMOUNT;
}
return order.takerAssetAmount.minus(new BigNumber(hash).mod(order.takerAssetAmount));
}
function getDeterministicFillableMakerAssetAmount(order: Order): BigNumber {
const takerAmount = getDeterministicFillableTakerAssetAmount(order);
return order.makerAssetAmount
.times(takerAmount)
.div(order.takerAssetAmount)
.integerValue(BigNumber.ROUND_DOWN);
}
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 +239,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),
};
}
@@ -243,57 +248,115 @@ blockchainTests('erc20-bridge-sampler', env => {
return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
}
describe('queryOrders()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
async function enableFailTriggerAsync(): Promise<void> {
await testContract.enableFailTrigger().awaitTransactionSuccessAsync({ value: 1 });
}
it('returns the results of `getOrderInfo()` for each order', async () => {
describe('getOrderFillableTakerAssetAmounts()', () => {
it('returns the expected amount for each order', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expected = orders.map(getDeterministicOrderInfo);
const actual = await testContract.queryOrders(orders).callAsync();
const signatures: string[] = _.times(orders.length, hexUtils.random);
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq(expected);
});
it('returns empty for no orders', async () => {
const actual = await testContract.queryOrders([]).callAsync();
const actual = await testContract.getOrderFillableTakerAssetAmounts([], []).callAsync();
expect(actual).to.deep.eq([]);
});
it('returns zero for an order with zero maker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with zero taker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with an empty signature', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
const signatures: string[] = _.times(orders.length, () => constants.NULL_BYTES);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
});
describe('getOrderFillableMakerAssetAmounts()', () => {
it('returns the expected amount for each order', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const signatures: string[] = _.times(orders.length, hexUtils.random);
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq(expected);
});
it('returns empty for no orders', async () => {
const actual = await testContract.getOrderFillableMakerAssetAmounts([], []).callAsync();
expect(actual).to.deep.eq([]);
});
it('returns zero for an order with zero maker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with zero taker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with an empty signature', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
const signatures: string[] = _.times(orders.length, () => constants.NULL_BYTES);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
});
describe('queryOrdersAndSampleSells()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns the results of `getOrderInfo()` for each order', async () => {
it('returns expected fillable amounts for each order', async () => {
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
const [orderInfos] = await testContract
.queryOrdersAndSampleSells(orders, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedOrderInfos);
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
SELL_SOURCES.map(n => allSources[n]),
sampleAmounts,
)
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleSells([], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
@@ -301,7 +364,8 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
ORDERS,
SIGNATURES,
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(TAKER_TOKEN),
)
@@ -312,10 +376,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -326,10 +391,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -340,10 +406,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -354,10 +421,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -367,39 +435,34 @@ blockchainTests('erc20-bridge-sampler', env => {
});
describe('queryOrdersAndSampleBuys()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns the results of `getOrderInfo()` for each order', async () => {
it('returns expected fillable amounts for each order', async () => {
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
const [orderInfos] = await testContract
.queryOrdersAndSampleBuys(orders, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedOrderInfos);
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
BUY_SOURCES.map(n => allSources[n]),
sampleAmounts,
)
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleBuys([], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
@@ -407,7 +470,8 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
ORDERS,
SIGNATURES,
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(MAKER_TOKEN),
)
@@ -419,7 +483,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const sources = [...BUY_SOURCES, 'Kyber'];
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
ORDERS,
SIGNATURES,
sources.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -430,10 +495,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -444,10 +510,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -458,10 +525,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -472,10 +540,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -485,9 +554,6 @@ blockchainTests('erc20-bridge-sampler', env => {
});
describe('sampleSells()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -533,9 +599,6 @@ blockchainTests('erc20-bridge-sampler', env => {
});
describe('sampleBuys()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -588,10 +651,7 @@ blockchainTests('erc20-bridge-sampler', env => {
});
});
describe('sampleSellsFromKyberNetwork()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -606,7 +666,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token - token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
const quotes = await testContract
@@ -615,6 +675,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
@@ -624,6 +694,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
@@ -632,12 +712,19 @@ blockchainTests('erc20-bridge-sampler', env => {
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleSellsFromEth2Dai()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleSellsFromEth2Dai()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -652,7 +739,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
@@ -661,6 +748,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
@@ -670,6 +767,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
@@ -678,12 +785,19 @@ blockchainTests('erc20-bridge-sampler', env => {
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleBuysFromEth2Dai()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleBuysFromEth2Dai()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -698,7 +812,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
@@ -707,6 +821,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
@@ -716,6 +840,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
@@ -724,12 +858,19 @@ blockchainTests('erc20-bridge-sampler', env => {
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleSellsFromUniswap()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleSellsFromUniswap()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -744,7 +885,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
@@ -753,6 +894,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
@@ -762,6 +913,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
@@ -771,27 +932,38 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the taker token', async () => {
it('returns zero if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if no exchange exists for the taker token', async () => {
const nonExistantToken = randomAddress();
const sampleAmounts = getSampleAmounts(nonExistantToken);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleBuysFromUniswap()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleBuysFromUniswap()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -806,7 +978,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
@@ -815,6 +987,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
@@ -824,6 +1006,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
@@ -833,20 +1025,34 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the taker token', async () => {
it('returns zero if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
const sampleAmounts = getSampleAmounts(nonExistantToken);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if no exchange exists for the taker token', async () => {
const nonExistantToken = randomAddress();
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
});

View File

@@ -3,8 +3,8 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/deployment_constants';
export * from '../test/generated-wrappers/erc20_bridge_sampler';
export * from '../test/generated-wrappers/i_dev_utils';
export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network';

View File

@@ -5,8 +5,8 @@
"files": [
"generated-artifacts/ERC20BridgeSampler.json",
"generated-artifacts/IERC20BridgeSampler.json",
"test/generated-artifacts/DeploymentConstants.json",
"test/generated-artifacts/ERC20BridgeSampler.json",
"test/generated-artifacts/IDevUtils.json",
"test/generated-artifacts/IERC20BridgeSampler.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetwork.json",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"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.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@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.2"
},
"publishConfig": {
"access": "public"

View File

@@ -3,6 +3,9 @@ export {
DummyMultipleReturnERC20TokenContract,
DummyNoReturnERC20TokenContract,
WETH9Contract,
WETH9Events,
WETH9DepositEventArgs,
WETH9TransferEventArgs,
ZRXTokenContract,
DummyERC20TokenTransferEventArgs,
ERC20TokenEventArgs,

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,22 @@
[
{
"timestamp": 1576540892,
"version": "3.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"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.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@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.2"
},
"publishConfig": {
"access": "public"

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"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.2",
"@0x/contracts-asset-proxy": "^3.0.2",
"@0x/contracts-dev-utils": "^1.0.2",
"@0x/contracts-erc20": "^3.0.2",
"@0x/contracts-erc721": "^3.0.2",
"@0x/contracts-exchange": "^3.0.2",
"@0x/contracts-exchange-libs": "^4.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/sol-compiler": "^4.0.2",
"@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.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -89,8 +89,8 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/base-contract": "^6.0.2",
"@0x/typescript-typings": "^5.0.1",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

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,22 @@
[
{
"timestamp": 1576540892,
"version": "4.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1575931811,
"version": "4.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.0.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"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.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/dev-utils": "^3.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/subproviders": "^6.0.2",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@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/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/base-contract": "^6.0.2",
"@0x/contracts-utils": "^4.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

View File

@@ -103,7 +103,7 @@ export function calculateFillResults(
order.takerAssetAmount,
order.makerAssetAmount,
);
const makerFeePaid = safeGetPartialAmountFloor(makerAssetFilledAmount, order.makerAssetAmount, order.makerFee);
const makerFeePaid = safeGetPartialAmountFloor(takerAssetFilledAmount, order.takerAssetAmount, order.makerFee);
const takerFeePaid = safeGetPartialAmountFloor(takerAssetFilledAmount, order.takerAssetAmount, order.takerFee);
return {
makerAssetFilledAmount,
@@ -113,3 +113,30 @@ export function calculateFillResults(
protocolFeePaid: safeMul(protocolFeeMultiplier, gasPrice),
};
}
export const LibFractions = {
add: (n1: BigNumber, d1: BigNumber, n2: BigNumber, d2: BigNumber): [BigNumber, BigNumber] => {
if (n1.isZero()) {
return [n2, d2];
}
if (n2.isZero()) {
return [n1, d1];
}
const numerator = safeAdd(safeMul(n1, d2), safeMul(n2, d1));
const denominator = safeMul(d1, d2);
return [numerator, denominator];
},
normalize: (
numerator: BigNumber,
denominator: BigNumber,
maxValue: BigNumber = new BigNumber(2).exponentiatedBy(127),
): [BigNumber, BigNumber] => {
if (numerator.isGreaterThan(maxValue) || denominator.isGreaterThan(maxValue)) {
let rescaleBase = numerator.isGreaterThanOrEqualTo(denominator) ? numerator : denominator;
rescaleBase = safeDiv(rescaleBase, maxValue);
return [safeDiv(numerator, rescaleBase), safeDiv(denominator, rescaleBase)];
} else {
return [numerator, denominator];
}
},
};

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,22 @@
[
{
"timestamp": 1576540892,
"version": "3.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"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.2",
"@0x/contracts-asset-proxy": "^3.0.2",
"@0x/contracts-exchange-libs": "^4.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-multisig": "^4.0.2",
"@0x/contracts-staking": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/web3-wrapper": "^7.0.2",
"@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.2",
"@0x/contracts-dev-utils": "^1.0.2",
"@0x/contracts-erc1155": "^2.0.2",
"@0x/contracts-erc20": "^3.0.2",
"@0x/contracts-erc721": "^3.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/utils": "^5.1.1",
"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

@@ -107,23 +107,6 @@ describe('Reference functions', () => {
).to.throw(expectedError.message);
});
it('reverts if `order.makerAssetAmount` is 0', () => {
const order = makeOrder({
makerAssetAmount: constants.ZERO_AMOUNT,
takerAssetAmount: ONE_ETHER,
});
const takerAssetFilledAmount = ONE_ETHER;
const expectedError = new LibMathRevertErrors.DivisionByZeroError();
return expect(() =>
LibReferenceFunctions.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
),
).to.throw(expectedError.message);
});
it('reverts if `order.takerAssetAmount` is 0', () => {
const order = makeOrder({
makerAssetAmount: ONE_ETHER,

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,23 @@
[
{
"timestamp": 1576540892,
"version": "5.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "5.1.0",
"changes": [
{
"note": "Export function `encodeDutchAuctionAssetData`",
"pr": 2373
}
],
"timestamp": 1575931811
},
{
"version": "5.0.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.1.1 - _December 17, 2019_
* Dependencies updated
## 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.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.2",
"@0x/contracts-asset-proxy": "^3.0.2",
"@0x/contracts-dev-utils": "^1.0.2",
"@0x/contracts-erc20": "^3.0.2",
"@0x/contracts-erc721": "^3.0.2",
"@0x/contracts-exchange": "^3.0.2",
"@0x/contracts-exchange-libs": "^4.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/contracts-utils": "^4.0.2",
"@0x/dev-utils": "^3.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/sol-compiler": "^4.0.2",
"@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.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@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,8 +91,8 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/base-contract": "^6.0.2",
"@0x/typescript-typings": "^5.0.1",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

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,22 @@
[
{
"timestamp": 1576540892,
"version": "2.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1575931811,
"version": "2.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.0.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.2 - _December 17, 2019_
* Dependencies updated
## 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.2",
"engines": {
"node": ">=6.12"
},
@@ -19,6 +19,7 @@
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"test:fuzz": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/fuzz_tests/*.js' --timeout 0 --bail --exit",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
@@ -50,25 +51,27 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.0",
"@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/abi-gen": "^5.0.2",
"@0x/contract-addresses": "^4.1.0",
"@0x/contract-wrappers": "^13.2.0",
"@0x/contracts-coordinator": "^3.0.2",
"@0x/contracts-dev-utils": "^1.0.2",
"@0x/contracts-exchange-forwarder": "^4.0.2",
"@0x/contracts-exchange-libs": "^4.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/contracts-utils": "^4.0.2",
"@0x/coordinator-server": "^1.0.5",
"@0x/dev-utils": "^3.0.2",
"@0x/migrations": "^5.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/sol-compiler": "^4.0.2",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
"@types/seedrandom": "^2.4.28",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
@@ -78,6 +81,7 @@
"mocha": "^6.2.0",
"nock": "^10.0.6",
"npm-run-all": "^4.1.2",
"seedrandom": "^3.0.5",
"shx": "^0.2.2",
"solhint": "^1.4.1",
"truffle": "^5.0.32",
@@ -85,18 +89,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/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/base-contract": "^6.0.2",
"@0x/contracts-asset-proxy": "^3.0.2",
"@0x/contracts-erc1155": "^2.0.2",
"@0x/contracts-erc20": "^3.0.2",
"@0x/contracts-erc721": "^3.0.2",
"@0x/contracts-exchange": "^3.0.2",
"@0x/contracts-multisig": "^4.0.2",
"@0x/contracts-staking": "^2.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

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