Compare commits

...

58 Commits

Author SHA1 Message Date
Jacob Evans
2113fb490d Publish
- @0x/contracts-asset-proxy@3.1.0
 - @0x/contracts-coordinator@3.0.3
 - @0x/contracts-dev-utils@1.0.3
 - @0x/contracts-erc1155@2.0.3
 - @0x/contracts-erc20-bridge-sampler@1.0.3
 - @0x/contracts-erc20@3.0.3
 - @0x/contracts-erc721@3.0.3
 - @0x/contracts-exchange-forwarder@4.0.3
 - @0x/contracts-exchange-libs@4.0.3
 - @0x/contracts-exchange@3.0.3
 - @0x/contracts-extensions@5.1.2
 - @0x/contracts-integrations@2.1.0
 - @0x/contracts-multisig@4.0.3
 - @0x/contracts-staking@2.0.3
 - @0x/contracts-test-utils@5.1.0
 - @0x/contracts-utils@4.0.3
 - 0x.js@9.0.3
 - @0x/abi-gen@5.0.3
 - @0x/assert@3.0.3
 - @0x/asset-swapper@3.0.3
 - @0x/base-contract@6.0.3
 - @0x/connect@6.0.3
 - @0x/contract-addresses@4.2.0
 - @0x/contract-artifacts@3.3.0
 - @0x/contract-wrappers-test@12.2.4
 - @0x/contract-wrappers@13.3.0
 - @0x/contracts-gen@2.0.3
 - @0x/dev-utils@3.1.0
 - @0x/instant@1.0.40
 - @0x/json-schemas@5.0.3
 - @0x/migrations@5.1.0
 - @0x/monorepo-scripts@1.0.46
 - @0x/order-utils@10.1.0
 - @0x/orderbook@2.0.1
 - @0x/sol-compiler@4.0.3
 - @0x/sol-coverage@4.0.3
 - @0x/sol-doc@3.1.0
 - @0x/sol-profiler@4.0.3
 - @0x/sol-trace@3.0.3
 - @0x/sol-tracing-utils@7.0.3
 - @0x/sra-spec@3.0.3
 - @0x/subproviders@6.0.3
 - @0x/utils@5.1.2
 - @0x/web3-wrapper@7.0.3
2020-01-06 11:10:22 +10:00
Jacob Evans
0afedbd252 Updated CHANGELOGS & MD docs 2020-01-06 11:10:03 +10:00
Jacob Evans
3dec38450a Merge pull request #2396 from Arctek/patch-1
Fix circular reference
2020-01-06 10:29:19 +10:00
Jacob Evans
d7a00b05e3 Merge pull request #2423 from 0xProject/fix/migrations-docker-wget-timestamping
@0x/migrations/Dockerfile: Remove --timestamping arg from wget invocation
2020-01-06 10:27:35 +10:00
Lawrence Forman
ff2cc8c887 Add aggregator mainnet tests (#2407)
* `@0x/contracts-erc20-bridge-sampler`: Add gas limits to external quote calls.
`@0x/contract-addresses`: Point `erc20BridgeSampler` to new version.

* `@0x/contracts-utils`: Add kovan addresses to `DeploymentConstants`.
`@0x/contract-addresses`: Add kovan `ERC20BridgeSampler` address.

* `@0x/contracts-erc20-bridge-sampler`: Fix changelog.

* `@0x/asset-swapper`: Ignore zero sample results from the sampler contract.
`@0x/asset-swapper`: Allow skipping Uniswap when dealing with low precision amounts with `minUniswapDecimals` option.
`@0x/asset-swapper`: Increase default `runLimit` from `1024` to `4096`.
`@0x/asset-swapper`: Increase default `numSamples` from `8` to `10`
`@0x/asset-swapper`: Fix ordering of optimized orders.
`@0x/asset-swapper`: Fix best and worst quotes being reversed sometimes.
`@0x/asset-swapper`: Fix rounding of quoted asset amounts.

* `@0x/asset-swapper`: Change default `minUniswapDecimals` option from 8 to 7.

* `@0x/asset-swapper`: Revert uniswap decimals fix.

* `@0x/contracts-test-utils`: Add `blockchainTests.live()` for live network tests.
`@0x/contracts-test-utils`: Add modifiers to `blockchainTests.fork()`.
`@0x/contracts-integrations`: Add aggregator mainnet tests.

* `@0x/contracts-integrations`: Fix `fork/resets` modifier ordering on dydx tests.
`@0x/contracts-integrations`: Move and tweak aggregation tests.

* `@0x/contracts-integrations`: Handle non-responsive third-party SRA ordebooks with a little more grace.

* `@0x/contracts-integrations`: Fix linter error.

* `@0x/contracts-test-utils`: Consolidate fork provider logic into `mocha_blockchain.ts`.

* `@0x/contracts-integrations`: Run prettier on aggregation fill tests.

* `@0x/dev-utils`: Add `locked` to `Web3Config`.

* `@0x/contracts-integrations`: Update mainnet fork tests.
`@0x/contracts-test-utils`: Fix forked tests being skipped.
`@0x/contracts-erc20-bridge-sampler`: Regenerate artifacts.

* `@0x/contracts-test-utils`: Remove unecessary `locked` option when creating forked ganache provider.

* Fix redundant zero check

* Set fee amount in fillable amounts test

Co-authored-by: Jacob Evans <dekz@dekz.net>
2020-01-03 23:47:40 -05:00
Lawrence Forman
0571a96cea Fix asset-swapper bugs and misc improvements. (#2406)
* `@0x/contracts-erc20-bridge-sampler`: Add gas limits to external quote calls.
`@0x/contract-addresses`: Point `erc20BridgeSampler` to new version.

* `@0x/asset-swapper`: Ignore zero sample results from the sampler contract.
`@0x/asset-swapper`: Allow skipping Uniswap when dealing with low precision amounts with `minUniswapDecimals` option.
`@0x/asset-swapper`: Increase default `runLimit` from `1024` to `4096`.
`@0x/asset-swapper`: Increase default `numSamples` from `8` to `10`
`@0x/asset-swapper`: Fix ordering of optimized orders.
`@0x/asset-swapper`: Fix best and worst quotes being reversed sometimes.
`@0x/asset-swapper`: Fix rounding of quoted asset amounts.

* `@0x/contracts-utils`: Add kovan addresses to `DeploymentConstants`.
`@0x/contract-addresses`: Add kovan `ERC20BridgeSampler` address.

* `@0x/asset-swapper`: Change default `minUniswapDecimals` option from 8 to 7.

* `@0x/contracts-erc20-bridge-sampler`: Fix changelog.

* `@0x/asset-swapper`: Revert uniswap decimals fix.

* `@0x/asset-swapper`: Undo bridge slippage when computing best case quote.

* `@0x/asset-swapper`: Take asset data from input orders instead of output orders in quote result calculation.

* `@0x/asset-swapper`: Move `SAMPLER_CONTRACT_GAS_LIMIT` constant to `market_operation_utils/constants`.

* Compare equivalent asset data

* Fix redundant zero check

* Update CHANGELOG

* Set fee amount in fillable amounts test

Co-authored-by: Jacob Evans <dekz@dekz.net>
2020-01-03 23:21:39 -05:00
Lawrence Forman
b7b457b076 Generate (complete) solidity docs (#2391)
* `@0x/sol-doc`: New doc generator.

* `@0x/sol-compiler`: Be more tolerant of AST-only compilation targets.

* `@0x/contracts-exchange`: Add more devdoc comments.
`@0x/contracts-exchange-libs`: Add more devdoc comments.

* `@0x/sol-doc`: Update package script.

* `@0x/sol-doc`: Remove unused files and update package scripts to be easier to configure.

* Add more devdocs to contracts.

* `@0x/sol-doc`: Remove doc artifacts.

* `@0x/sol-doc`: Add `.gitignore` and `.npmignore`.

* `@0x/contracts-exchange`: Fix compilation errors.

* Fix more broken contracts.

* `@0x/contracts-erc20-bridge-sampler`: Fix failing tests.

* `@0x/contracts-asset-proxy`: Remove accidentally introduced hackathion file (lol).

* `@0x/sol-doc`: Prevent some inherited contracts from being included in docs unintentionally.

* `@0x/sol-doc`: Rename test file.

* `@0x/contracts-exchange`: Update `orderEpoch` devdoc.

* `@0x/sol-doc`: Tweak event and function docs.

* Update CODEOWNERS.

* `@0x/sol-doc` Tweak function md generation.

* `@0x/sol-doc`: add `transformDocs()` tests.

* `@0x/sol-doc`: add `extract_docs` tests.

* `@0x/sol-doc` Fix linter errors.

* `@0x/contracts-erc20-bridge-sampler`: Fix broken `ERC20BridgeSampler.sol` compile.

* `@0x/sol-doc` Fix mismatched `dev-utils` dep version.

* `@0x/sol-doc`: Add `gen_md` tests.

* `@0x/sol-doc`: Remove `fs.promises` calls.

* `@0x/sol-doc`: Fix linter errors.

* `@0x/sol-doc`: Export all relevant types and functions.

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2020-01-03 22:59:18 -05:00
F. Eugene Aumson
d2c12005b2 Also publish versioned docker image tag
Not just `latest`
2020-01-03 16:51:02 -05:00
F. Eugene Aumson
25f26d7e5f Remove --timestamping arg from wget invocation 2020-01-03 12:40:40 -05:00
F. Eugene Aumson
9d5724e1a0 Fix 0xorg/ganache-cli docker image not supporting re-runs (#2420)
* Overwrite existing snapshot when unzipping

* Don't re-download snapshot if it isn't updated

* Update CHANGELOG.json
2020-01-02 20:19:06 -05:00
David Sun
784d23ec87 Merge pull request #2416 from 0xProject/feature/instant/disable-file-removal--in-s3
Instant - Added should_remove_files_in_s3 flag in discharge configuration
2019-12-22 20:54:43 -08:00
James Towle
709689a7ee Merge pull request #2417 from 0xProject/tests/fix/dev-utils-tests
Fixed the DevUtils tests
2019-12-21 16:55:33 -08:00
F. Eugene Aumson
35d5d3d995 Pare down Mesh env vars in Py test env (#2418) 2019-12-21 13:19:00 -05:00
Alex Towle
630a8d8a4e Addressed dorothy's nit 2019-12-20 19:14:52 -08:00
Alex Towle
54eb1d9055 Fixed the DevUtils tests 2019-12-20 15:51:45 -08:00
David Sun
cb63caea61 prettier 2019-12-20 12:57:40 -08:00
David Sun
8e046bb022 updated to new flag name 2019-12-20 12:07:59 -08:00
David Sun
189b53b8c4 added removal flag in configuration 2019-12-20 11:40:54 -08:00
Lawrence Forman
9b7277d464 Fix Kyber and Uniswap ERC20Bridges (#2412)
* `@0x/contracts-asset-proxy`: Fix `UniswapBridge` token -> token transfer logic.
`@0x/contract-addresses`: Update `UniswapBridge` mainnet address.

* `@0x/asset-proxy`: Fix `KyberBridge` incorrect `minConversionRate` calculation.

* `@0x/contract-addresses`: Update `KyberBridge` mainnet address.
2019-12-20 13:41:52 -05:00
F. Eugene Aumson
551a65c069 0x-sra-client.py: Fix bug in config_order, and other small improvements (#2399)
* Bug fix: unescape backslashes in regexes

Root problem is that there are too many backslashes in the SRA spec
itself.  See https://github.com/0xProject/0x-monorepo/issues/1727

This was previously fixed for heavily-tested endpoints (get and post
order, etc), but was only recently discovered for the get-order-config
endpoint.

* Demonstrate get_order_config()

* Rename DefaultApi to RelayerApi

* Simplify RelayerApi instantiation

* Document paylod and response schemas

* Stop caring which contracts are wrapped

* Increase platform agnosticism

* Update CHANGELOG

* Remove unnecessary f-string
2019-12-20 12:24:55 -05:00
F. Eugene Aumson
4c21a697f4 sra_client.py: Tweak Mesh block polling interval used in tests (#2413)
* Change Mesh block polling interval to 50ms

* Increase Mesh's Ethereum node RPC rate limit
2019-12-20 11:26:57 -05:00
Greg Hysz
a8506c07ae Merge pull request #2401 from 0xProject/feat/asset-proxy/DyDxBridgeTests
DydxBridge Contract Integration Tests
2019-12-19 22:16:44 -08:00
Greg Hysen
b0feb85b5c Fixed merge conflict 2019-12-19 21:36:43 -08:00
Greg Hysen
f371eba8ad Hardcoded address of ERC20BridgeProxy in unlocked accounts 2019-12-19 21:32:03 -08:00
Greg Hysen
265fa52ace Rounding error tests in DydxBridgeProxy 2019-12-19 21:32:03 -08:00
Greg Hysen
c1f5322d38 Added TestDydxUser contract - this is deployed to mainnent as the dYdX Account Owner for the mainnet integration tests. 2019-12-19 21:32:03 -08:00
Greg Hysen
4415e00b38 Added more integration tests for DydxBridge with the Exchange (demonstrates use cases) 2019-12-19 21:32:03 -08:00
Greg Hysen
d4e46c5a9c Updated Changelogs 2019-12-19 21:32:03 -08:00
Greg Hysen
bf5b9949fe Ran prettier 2019-12-19 21:32:03 -08:00
Greg Hysen
3c11a2b1da Increased mocha timeout for mainnet tests 2019-12-19 21:32:03 -08:00
Greg Hysen
1248868169 Added mainnet DydxBridge integration tests with dYdX SoloMargin contract 2019-12-19 21:32:03 -08:00
Greg Hysen
930b95a548 Added integration tests for DydxBridge with Exchange contract 2019-12-19 21:32:03 -08:00
Greg Hysen
27c9f68c7c Added dydx bridge to contract-addresses package 2019-12-19 21:32:02 -08:00
Greg Hysen
358d4d86a7 Added ERC20BridgeProxy.transferFrom tests for DydxBridge 2019-12-19 21:31:07 -08:00
Lawrence Forman
d55eea2239 ERC20BridgeSampler: Gas limits (#2405)
* `@0x/contracts-erc20-bridge-sampler`: Add gas limits to external quote calls.
`@0x/contract-addresses`: Point `erc20BridgeSampler` to new version.

* `@0x/contracts-utils`: Add kovan addresses to `DeploymentConstants`.
`@0x/contract-addresses`: Add kovan `ERC20BridgeSampler` address.

* `@0x/contracts-erc20-bridge-sampler`: Fix changelog.

* `@0x/contracts-erc20-bridge-contracts`: Fix invalid CHANGELOG json (I hope).
2019-12-20 00:08:39 -05:00
David Sun
4507954ea5 Merge pull request #2410 from 0xProject/fix/instant/coverage
Fix bundlewatch for instant and passing static-test
2019-12-19 22:37:42 -05:00
David Sun
8e0a83f8d8 bundlewatch 2019-12-19 19:20:24 -08:00
David Sun
b6ec09e6cf Merge pull request #2409 from 0xProject/feature/instant/minor-fixes-dai-aggregator
Fixes for instant (DAI + disable aggregator functionality)
2019-12-19 20:21:59 -05:00
David Sun
ed4e90623d more fixes 2019-12-19 16:49:20 -08:00
David Sun
38cdb48748 fixes 2019-12-19 16:36:00 -08:00
James Towle
71bfe9b745 Merge pull request #2402 from 0xProject/deploy/dev-utils/12-18-2019
Added artifacts, addresses, and wrappers
2019-12-19 14:38:01 -08:00
Alex Towle
9e7645a167 Removed hand-written timestamps 2019-12-19 13:26:00 -08:00
Alex Towle
6dccc37143 Removed forbidden fields in artifact 2019-12-19 11:40:49 -08:00
Alex Towle
3310310d8c Added artifacts, addresses, and wrappers 2019-12-19 10:32:01 -08:00
Greg Hysz
abb499aad8 Merge pull request #2403 from 0xProject/fix/circle/yarnInstall
Fix Circle builds
2019-12-19 10:24:48 -08:00
Greg Hysen
1afc09b08a Workaround for https://github.com/yarnpkg/yarn/issues/7773 2019-12-19 01:12:46 -08:00
Alex Browne
0e86d72f05 Merge pull request #2384 from 0xProject/update-code-owners-contracts-albrow
Update CODEOWNERS
2019-12-18 13:48:31 -08:00
mzhu25
c9857a2764 Merge pull request #2392 from 0xProject/feature/fuzz/better-input-gen
`@0x/contracts-integrations`: Better input generation for fuzzing
2019-12-18 12:03:23 -08:00
Michael Zhu
701ba3902c add comments 2019-12-18 11:38:33 -08:00
Michael Zhu
bb3ec970a9 lint 2019-12-18 11:38:33 -08:00
Michael Zhu
1d023e6db5 Add optional parameter to sample and sampleSize 2019-12-18 11:38:33 -08:00
Michael Zhu
1bd906ecb3 Add optional distribution parameter to Pseudorandom.integer, use Kumaraswamy distribution for operator share 2019-12-18 11:38:33 -08:00
James Towle
7cbffdb86b Merge pull request #2400 from 0xProject/feature/dev-utils/duplicate-erc721-bug-fix
Duplicate ERC721 Bug Fix
2019-12-18 10:46:27 -08:00
Alex Towle
b979196ffd Remove the taker patch 2019-12-17 19:02:57 -08:00
Alex Towle
2949db5f49 Fixed the bug and added tests that fail without the patch 2019-12-17 19:02:57 -08:00
Alex Towle
47c3ed9705 Fixed the bug and moved "contracts-tests" to "contracts-integrations" 2019-12-17 19:02:57 -08:00
Arctek
e00f059a4a Fix circular refence 2019-12-14 16:00:05 +10:30
Alex Browne
731a823cc2 Update CODEOWNERS
Remove albrow as code owner for the `contract-addresses` and `contract-artifacts` packages. It's been a long time since I've worked on these packages and I am no longer the best person to review changes to them.
2019-12-03 17:19:08 -08:00
292 changed files with 14266 additions and 8939 deletions

View File

@@ -23,7 +23,7 @@ jobs:
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
- run:
name: install-yarn
command: npm install --global yarn@1.17.0
command: npm install --force --global yarn@1.17.0
- run:
name: yarn
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
@@ -77,7 +77,7 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
# TODO(dorothy-zbornak): Re-enable after updating this package for
# 3.0. At that time, also remove exclusion from monorepo
# package.json's test script.
@@ -197,13 +197,10 @@ jobs:
- image: 0xorg/mesh:0xV3
environment:
ETHEREUM_RPC_URL: 'http://localhost:8545'
ETHEREUM_NETWORK_ID: '50'
ETHEREUM_CHAIN_ID: '1337'
USE_BOOTSTRAP_LIST: 'true'
VERBOSITY: 3
PRIVATE_KEY_PATH: ''
BLOCK_POLLING_INTERVAL: '5s'
P2P_LISTEN_PORT: '60557'
VERBOSITY: 5
BLOCK_POLLING_INTERVAL: '50ms'
ETHEREUM_RPC_MAX_REQUESTS_PER_24_HR_UTC: '1778000'
command: |
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
- image: 0xorg/launch-kit-backend:v3

28
.gitignore vendored
View File

@@ -160,33 +160,7 @@ contracts/exchange-forwarder/generated-wrappers/
contracts/exchange-forwarder/test/generated-wrappers/
contracts/dev-utils/generated-wrappers/
contracts/dev-utils/test/generated-wrappers/
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_registry/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_bridge_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/forwarder/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_asset_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_vault/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
# solc-bin in sol-compiler
packages/sol-compiler/solc_bin/

View File

@@ -14,8 +14,8 @@ packages/abi-gen/ @feuGeneA
packages/base-contract/ @xianny
packages/connect/ @fragosti
packages/abi-gen-templates/ @feuGeneA @xianny
packages/contract-addresses/ @albrow
packages/contract-artifacts/ @albrow
packages/contract-addresses/ @abandeali1
packages/contract-artifacts/ @abandeali1
packages/dev-utils/ @LogvinovLeon @fabioberger
packages/devnet/ @albrow
packages/ethereum-types/ @LogvinovLeon

View File

@@ -1,4 +1,22 @@
[
{
"version": "3.1.0",
"changes": [
{
"note": "Integration tests for DydxBridge with ERC20BridgeProxy.",
"pr": 2401
},
{
"note": "Fix `UniswapBridge` token -> token transfer call.",
"pr": 2412
},
{
"note": "Fix `KyberBridge` incorrect `minConversionRate` calculation.",
"pr": 2412
}
],
"timestamp": 1578272714
},
{
"timestamp": 1576540892,
"version": "3.0.2",

View File

@@ -5,6 +5,12 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.0 - _January 6, 2020_
* Integration tests for DydxBridge with ERC20BridgeProxy. (#2401)
* Fix `UniswapBridge` token -> token transfer call. (#2412)
* Fix `KyberBridge` incorrect `minConversionRate` calculation. (#2412)
## v3.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -24,6 +24,7 @@ import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IKyberNetworkProxy.sol";
@@ -34,6 +35,8 @@ contract KyberBridge is
IWallet,
DeploymentConstants
{
using LibSafeMath for uint256;
// @dev Structure used internally to get around stack limits.
struct TradeState {
IKyberNetworkProxy kyber;
@@ -41,6 +44,7 @@ contract KyberBridge is
address fromTokenAddress;
uint256 fromTokenBalance;
uint256 payableAmount;
uint256 conversionRate;
}
/// @dev Kyber ETH pseudo-address.
@@ -81,11 +85,23 @@ contract KyberBridge is
state.weth = IEtherToken(_getWethAddress());
// Decode the bridge data to get the `fromTokenAddress`.
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
// Query the balance of "from" tokens.
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
if (state.fromTokenBalance == 0) {
// Return failure if no input tokens.
return BRIDGE_FAILED;
}
// Compute the conversion rate, expressed in 18 decimals.
// The sequential notation is to get around stack limits.
state.conversionRate = KYBER_RATE_BASE;
state.conversionRate = state.conversionRate.safeMul(amount);
state.conversionRate = state.conversionRate.safeMul(
10 ** uint256(LibERC20Token.decimals(state.fromTokenAddress))
);
state.conversionRate = state.conversionRate.safeDiv(state.fromTokenBalance);
state.conversionRate = state.conversionRate.safeDiv(
10 ** uint256(LibERC20Token.decimals(toTokenAddress))
);
if (state.fromTokenAddress == toTokenAddress) {
// Just transfer the tokens if they're the same.
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
@@ -118,7 +134,7 @@ contract KyberBridge is
uint256(-1),
// Compute the minimum conversion rate, which is expressed in units with
// 18 decimal places.
(KYBER_RATE_BASE * amount) / state.fromTokenBalance,
state.conversionRate,
// No affiliate address.
address(0)
);

View File

@@ -134,8 +134,8 @@ contract UniswapBridge is
state.fromTokenBalance,
// Minimum buy amount.
amount,
// No minimum intermediate ETH buy amount.
0,
// Must buy at least 1 intermediate ETH.
1,
// Expires after this block.
block.timestamp,
// Recipient is `to`.

View File

@@ -19,9 +19,49 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "../src/bridges/DydxBridge.sol";
contract TestDydxBridgeToken {
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
mapping (address => uint256) private _balances;
/// @dev Sets initial balance of token holders.
constructor(address[] memory holders)
public
{
for (uint256 i = 0; i != holders.length; ++i) {
_balances[holders[i]] = INIT_HOLDER_BALANCE;
}
_balances[msg.sender] = INIT_HOLDER_BALANCE;
}
/// @dev Basic transferFrom implementation.
function transferFrom(address from, address to, uint256 amount)
external
returns (bool)
{
if (_balances[from] < amount || _balances[to] + amount < _balances[to]) {
return false;
}
_balances[from] -= amount;
_balances[to] += amount;
return true;
}
/// @dev Returns balance of `holder`.
function balanceOf(address holder)
external
view
returns (uint256)
{
return _balances[holder];
}
}
// solhint-disable space-after-comma
contract TestDydxBridge is
IDydx,
@@ -29,6 +69,8 @@ contract TestDydxBridge is
{
address private constant ALWAYS_REVERT_ADDRESS = address(1);
address private _testTokenAddress;
bool private _shouldRevertOnOperate;
event OperateAccount(
address owner,
@@ -49,6 +91,13 @@ contract TestDydxBridge is
bytes data
);
constructor(address[] memory holders)
public
{
// Deploy a test token. This represents the asset being deposited/withdrawn from dydx.
_testTokenAddress = address(new TestDydxBridgeToken(holders));
}
/// @dev Simulates `operate` in dydx contract.
/// Emits events so that arguments can be validated client-side.
function operate(
@@ -57,6 +106,10 @@ contract TestDydxBridge is
)
external
{
if (_shouldRevertOnOperate) {
revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE");
}
for (uint i = 0; i < accounts.length; ++i) {
emit OperateAccount(
accounts[i].owner,
@@ -78,9 +131,46 @@ contract TestDydxBridge is
actions[i].otherAccountId,
actions[i].data
);
if (actions[i].actionType == IDydx.ActionType.Withdraw) {
require(
IERC20Token(_testTokenAddress).transferFrom(
address(this),
actions[i].otherAddress,
actions[i].amount.value
),
"TestDydxBridge/WITHDRAW_FAILED"
);
} else if (actions[i].actionType == IDydx.ActionType.Deposit) {
require(
IERC20Token(_testTokenAddress).transferFrom(
actions[i].otherAddress,
address(this),
actions[i].amount.value
),
"TestDydxBridge/DEPOSIT_FAILED"
);
} else {
revert("TestDydxBridge/UNSUPPORTED_ACTION");
}
}
}
/// @dev If `true` then subsequent calls to `operate` will revert.
function setRevertOnOperate(bool shouldRevert)
external
{
_shouldRevertOnOperate = shouldRevert;
}
/// @dev Returns test token.
function getTestToken()
external
returns (address)
{
return _testTokenAddress;
}
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
function _getDydxAddress()
internal

View File

@@ -67,9 +67,11 @@ interface ITestContract {
/// @dev A minimalist ERC20/WETH token.
contract TestToken {
uint8 public decimals;
ITestContract private _testContract;
constructor() public {
constructor(uint8 decimals_) public {
decimals = decimals_;
_testContract = ITestContract(msg.sender);
}
@@ -165,7 +167,7 @@ contract TestKyberBridge is
uint256 private _nextFillAmount;
constructor() public {
weth = IEtherToken(address(new TestToken()));
weth = IEtherToken(address(new TestToken(18)));
}
/// @dev Implementation of `IKyberNetworkProxy.trade()`
@@ -195,11 +197,11 @@ contract TestKyberBridge is
return _nextFillAmount;
}
function createToken()
function createToken(uint8 decimals)
external
returns (address tokenAddress)
{
return address(new TestToken());
return address(new TestToken(decimals));
}
function setNextFillAmount(uint256 amount)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "3.0.2",
"version": "3.1.0",
"engines": {
"node": ">=6.12"
},
@@ -51,12 +51,12 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@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/abi-gen": "^5.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
@@ -79,16 +79,16 @@
"typescript": "3.0.1"
},
"dependencies": {
"@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/base-contract": "^6.0.3",
"@0x/contracts-dev-utils": "^1.0.3",
"@0x/contracts-erc1155": "^2.0.3",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-erc721": "^3.0.3",
"@0x/contracts-exchange-libs": "^4.0.3",
"@0x/order-utils": "^10.1.0",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

@@ -0,0 +1,40 @@
import { AbiEncoder, BigNumber } from '@0x/utils';
export enum DydxBridgeActionType {
Deposit,
Withdraw,
}
export interface DydxBrigeAction {
actionType: DydxBridgeActionType;
accountId: BigNumber;
marketId: BigNumber;
conversionRateNumerator: BigNumber;
conversionRateDenominator: BigNumber;
}
export interface DydxBridgeData {
accountNumbers: BigNumber[];
actions: DydxBrigeAction[];
}
export const dydxBridgeDataEncoder = AbiEncoder.create([
{
name: 'bridgeData',
type: 'tuple',
components: [
{ name: 'accountNumbers', type: 'uint256[]' },
{
name: 'actions',
type: 'tuple[]',
components: [
{ name: 'actionType', type: 'uint8' },
{ name: 'accountId', type: 'uint256' },
{ name: 'marketId', type: 'uint256' },
{ name: 'conversionRateNumerator', type: 'uint256' },
{ name: 'conversionRateDenominator', type: 'uint256' },
],
},
],
},
]);

View File

@@ -6,6 +6,7 @@ export {
ERC721ProxyContract,
Eth2DaiBridgeContract,
DydxBridgeContract,
TestDydxBridgeContract,
IAssetDataContract,
IAssetProxyContract,
MultiAssetProxyContract,
@@ -63,3 +64,4 @@ export {
TupleDataItem,
StateMutability,
} from 'ethereum-types';
export * from './dydx_bridge_encoder';

View File

@@ -1,8 +1,12 @@
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { DydxBridgeActionType, DydxBridgeData, dydxBridgeDataEncoder } from '../src/dydx_bridge_encoder';
import { ERC20BridgeProxyContract, IAssetDataContract } from '../src/wrappers';
import { artifacts } from './artifacts';
import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
@@ -11,7 +15,24 @@ blockchainTests.resets('DydxBridge unit tests', env => {
const marketId = new BigNumber(2);
const defaultAmount = new BigNumber(4);
const notAuthorized = '0x0000000000000000000000000000000000000001';
const defaultDepositAction = {
actionType: DydxBridgeActionType.Deposit,
accountId: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const defaultWithdrawAction = {
actionType: DydxBridgeActionType.Withdraw,
accountId: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
let testContract: TestDydxBridgeContract;
let testProxyContract: ERC20BridgeProxyContract;
let assetDataEncoder: IAssetDataContract;
let owner: string;
let authorized: string;
let accountOwner: string;
let receiver: string;
@@ -19,7 +40,7 @@ blockchainTests.resets('DydxBridge unit tests', env => {
before(async () => {
// Get accounts
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
[, /* owner */ authorized, accountOwner, receiver] = accounts;
[owner, authorized, accountOwner, receiver] = accounts;
// Deploy dydx bridge
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
@@ -27,81 +48,57 @@ blockchainTests.resets('DydxBridge unit tests', env => {
env.provider,
env.txDefaults,
artifacts,
[accountOwner, receiver],
);
// Deploy test erc20 bridge proxy
testProxyContract = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
artifacts.ERC20BridgeProxy,
env.provider,
env.txDefaults,
artifacts,
);
await testProxyContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
// Setup asset data encoder
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
});
describe('bridgeTransferFrom()', () => {
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,
bridgeData: DydxBridgeData,
sender: string,
): Promise<string> => {
const returnValue = await testContract
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
.bridgeTransferFrom(
constants.NULL_ADDRESS,
from,
to,
amount,
dydxBridgeDataEncoder.encode({ bridgeData }),
)
.callAsync({ from: sender });
return returnValue;
};
const callBridgeTransferFromAndVerifyEvents = async (
const executeBridgeTransferFromAndVerifyEvents = async (
from: string,
to: string,
amount: BigNumber,
bridgeData: BridgeData,
bridgeData: DydxBridgeData,
sender: string,
): Promise<void> => {
// Execute transaction.
const txReceipt = await testContract
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
.bridgeTransferFrom(
constants.NULL_ADDRESS,
from,
to,
amount,
dydxBridgeDataEncoder.encode({ bridgeData }),
)
.awaitTransactionSuccessAsync({ from: sender });
// Verify `OperateAccount` event.
@@ -122,62 +119,139 @@ blockchainTests.resets('DydxBridge unit tests', env => {
expectedOperateActionEvents.push({
actionType: action.actionType as number,
accountId: action.accountId,
amountSign: action.actionType === BridgeActionType.Deposit ? true : false,
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
amountDenomination: weiDenomination,
amountRef: deltaAmountRef,
amountValue: action.conversionRateDenominator.gt(0)
? amount.times(action.conversionRateNumerator).div(action.conversionRateDenominator)
? amount
.times(action.conversionRateNumerator)
.dividedToIntegerBy(action.conversionRateDenominator)
: amount,
primaryMarketId: marketId,
secondaryMarketId: constants.ZERO_AMOUNT,
otherAddress: action.actionType === BridgeActionType.Deposit ? from : to,
otherAddress: action.actionType === DydxBridgeActionType.Deposit ? from : to,
otherAccountId: constants.ZERO_AMOUNT,
data: '0x',
});
}
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
};
it('succeeds when calling with zero amount', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
constants.ZERO_AMOUNT,
bridgeData,
authorized,
);
});
it('succeeds when calling with no accounts', async () => {
const bridgeData = {
accountNumbers: [],
actions: [defaultDepositAction],
};
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling with no actions', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [],
};
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling `operate` with the `withdraw` action and a single accuont', async () => {
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction, defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction, defaultDepositAction],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when scaling the `amount` to deposit', async () => {
const conversionRateNumerator = new BigNumber(1);
@@ -193,7 +267,13 @@ blockchainTests.resets('DydxBridge unit tests', env => {
},
],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when scaling the `amount` to withdraw', async () => {
const conversionRateNumerator = new BigNumber(1);
@@ -209,7 +289,13 @@ blockchainTests.resets('DydxBridge unit tests', env => {
},
],
};
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
const bridgeData = {
@@ -240,5 +326,74 @@ blockchainTests.resets('DydxBridge unit tests', env => {
);
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
});
it('should revert when `Operate` reverts', async () => {
// Set revert flag.
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
// Execute transfer.
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
const tx = callBridgeTransferFrom(accountOwner, receiver, defaultAmount, bridgeData, authorized);
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
return expect(tx).to.revertWith(expectedError);
});
it('should revert when there is a rounding error', async () => {
// Setup a rounding error
const conversionRateNumerator = new BigNumber(5318);
const conversionRateDenominator = new BigNumber(47958);
const amount = new BigNumber(9000);
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [
defaultDepositAction,
{
...defaultWithdrawAction,
conversionRateNumerator,
conversionRateDenominator,
},
],
};
// Execute transfer and assert error.
const tx = callBridgeTransferFrom(accountOwner, receiver, amount, bridgeData, authorized);
const expectedError = new LibMathRevertErrors.RoundingError(
conversionRateNumerator,
conversionRateDenominator,
amount,
);
return expect(tx).to.revertWith(expectedError);
});
});
describe('ERC20BridgeProxy.transferFrom()', () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction],
};
let assetData: string;
before(async () => {
const testTokenAddress = await testContract.getTestToken().callAsync();
assetData = assetDataEncoder
.ERC20Bridge(testTokenAddress, testContract.address, dydxBridgeDataEncoder.encode({ bridgeData }))
.getABIEncodedTransactionData();
});
it('should succeed if `bridgeTransferFrom` succeeds', async () => {
await testProxyContract
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
.awaitTransactionSuccessAsync({ from: authorized });
});
it('should revert if `bridgeTransferFrom` reverts', async () => {
// Set revert flag.
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
const tx = testProxyContract
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
.awaitTransactionSuccessAsync({ from: authorized });
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
return expect(tx).to.revertWith(expectedError);
});
});
});

View File

@@ -3,6 +3,7 @@ import {
constants,
expect,
getRandomInteger,
getRandomPortion,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
@@ -17,6 +18,12 @@ import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
blockchainTests.resets('KyberBridge unit tests', env => {
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
const FROM_TOKEN_DECIMALS = 6;
const TO_TOKEN_DECIMALS = 18;
const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS);
const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS);
const WETH_BASE = new BigNumber(10).pow(18);
const KYBER_RATE_BASE = WETH_BASE;
let testContract: TestKyberBridgeContract;
before(async () => {
@@ -45,10 +52,10 @@ blockchainTests.resets('KyberBridge unit tests', env => {
before(async () => {
wethAddress = await testContract.weth().callAsync();
fromTokenAddress = await testContract.createToken().callAsync();
await testContract.createToken().awaitTransactionSuccessAsync();
toTokenAddress = await testContract.createToken().callAsync();
await testContract.createToken().awaitTransactionSuccessAsync();
fromTokenAddress = await testContract.createToken(FROM_TOKEN_DECIMALS).callAsync();
await testContract.createToken(FROM_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
toTokenAddress = await testContract.createToken(TO_TOKEN_DECIMALS).callAsync();
await testContract.createToken(TO_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
});
const STATIC_KYBER_TRADE_ARGS = {
@@ -75,13 +82,14 @@ blockchainTests.resets('KyberBridge unit tests', env => {
}
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100));
return {
fromTokenAddress,
toTokenAddress,
amount,
toAddress: randomAddress(),
amount: getRandomInteger(1, 10e18),
fillAmount: getRandomInteger(1, 10e18),
fromTokenBalance: getRandomInteger(1, 10e18),
fillAmount: getRandomPortion(amount),
fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)),
...opts,
};
}
@@ -119,9 +127,12 @@ blockchainTests.resets('KyberBridge unit tests', env => {
}
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
const fromBase = opts.fromTokenAddress === wethAddress ? WETH_BASE : FROM_TOKEN_BASE;
const toBase = opts.toTokenAddress === wethAddress ? WETH_BASE : TO_TOKEN_BASE;
return opts.amount
.times(constants.ONE_ETHER)
.div(opts.fromTokenBalance)
.div(toBase)
.div(opts.fromTokenBalance.div(fromBase))
.times(KYBER_RATE_BASE)
.integerValue(BigNumber.ROUND_DOWN);
}

View File

@@ -176,7 +176,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
expect(calls[0].exchange).to.eq(exchangeAddress);
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
expect(calls[0].minEthBought).to.bignumber.eq(0);
expect(calls[0].minEthBought).to.bignumber.eq(1);
expect(calls[0].deadline).to.bignumber.eq(blockTime);
expect(calls[0].recipient).to.eq(opts.toAddress);
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "3.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "3.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.3 - _January 6, 2020_
* Dependencies updated
## v3.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "3.0.2",
"version": "3.0.3",
"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.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/abi-gen": "^5.0.3",
"@0x/contracts-asset-proxy": "^3.1.0",
"@0x/contracts-dev-utils": "^1.0.3",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-exchange": "^3.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/dev-utils": "^3.1.0",
"@0x/order-utils": "^10.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@0x/web3-wrapper": "^7.0.3",
"@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.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/assert": "^3.0.3",
"@0x/base-contract": "^6.0.3",
"@0x/contract-addresses": "^4.2.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/json-schemas": "^5.0.3",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/utils": "^5.1.2",
"ethereum-types": "^3.0.0",
"http-status-codes": "^1.3.2"
},

View File

@@ -1,4 +1,14 @@
[
{
"version": "1.0.3",
"changes": [
{
"note": "Fixed ERC721 duplicate token ID bug",
"pr": 2400
}
],
"timestamp": 1578272714
},
{
"timestamp": 1576540892,
"version": "1.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.3 - _January 6, 2020_
* Fixed ERC721 duplicate token ID bug (#2400)
## v1.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -7,7 +7,7 @@
"evmVersion": "constantinople",
"optimizer": {
"enabled": true,
"runs": 1666,
"runs": 200,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {

View File

@@ -35,8 +35,7 @@ contract DevUtils is
OrderValidationUtils,
LibTransactionDecoder,
LibEIP712ExchangeDomain,
EthBalanceChecker,
OrderTransferSimulationUtils
EthBalanceChecker
{
constructor (address _exchange)
public

View File

@@ -147,7 +147,7 @@ contract LibAssetData {
balance = scaledBalance;
}
}
}
}
// Balance will be 0 if assetProxyId is unknown
return balance;
@@ -316,7 +316,7 @@ contract LibAssetData {
return (balances, allowances);
}
/// @dev Decode AssetProxy identifier
/// @dev Decode AssetProxy identifier
/// @param assetData AssetProxy-compliant asset data describing an ERC-20, ERC-721, ERC1155, or MultiAsset asset.
/// @return The AssetProxy identifier
function decodeAssetProxyId(bytes memory assetData)
@@ -353,7 +353,7 @@ contract LibAssetData {
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
/// @return The AssetProxy identifier, and the address of the ERC-20
/// @return The AssetProxy identifier, and the address of the ERC-20
/// contract hosting this asset.
function decodeERC20AssetData(bytes memory assetData)
public
@@ -591,7 +591,7 @@ contract LibAssetData {
function revertIfInvalidAssetData(bytes memory assetData)
public
pure
pure
{
bytes4 assetProxyId = assetData.readBytes4(0);

View File

@@ -54,6 +54,51 @@ contract OrderTransferSimulationUtils is
_EXCHANGE = IExchange(_exchange);
}
/// @dev Simulates the maker transfers within an order and returns the index of the first failed transfer.
/// @param order The order to simulate transfers for.
/// @param takerAddress The address of the taker that will fill the order.
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
function getSimulatedOrderMakerTransferResults(
LibOrder.Order memory order,
address takerAddress,
uint256 takerAssetFillAmount
)
public
returns (OrderTransferResults orderTransferResults)
{
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResults(
order,
takerAssetFillAmount,
_EXCHANGE.protocolFeeMultiplier(),
tx.gasprice
);
bytes[] memory assetData = new bytes[](2);
address[] memory fromAddresses = new address[](2);
address[] memory toAddresses = new address[](2);
uint256[] memory amounts = new uint256[](2);
// Transfer `makerAsset` from maker to taker
assetData[0] = order.makerAssetData;
fromAddresses[0] = order.makerAddress;
toAddresses[0] = takerAddress;
amounts[0] = fillResults.makerAssetFilledAmount;
// Transfer `makerFeeAsset` from maker to feeRecipient
assetData[1] = order.makerFeeAssetData;
fromAddresses[1] = order.makerAddress;
toAddresses[1] = order.feeRecipientAddress;
amounts[1] = fillResults.makerFeePaid;
return _simulateTransferFromCalls(
assetData,
fromAddresses,
toAddresses,
amounts
);
}
/// @dev Simulates all of the transfers within an order and returns the index of the first failed transfer.
/// @param order The order to simulate transfers for.
/// @param takerAddress The address of the taker that will fill the order.
@@ -104,6 +149,54 @@ contract OrderTransferSimulationUtils is
toAddresses[3] = order.feeRecipientAddress;
amounts[3] = fillResults.makerFeePaid;
return _simulateTransferFromCalls(
assetData,
fromAddresses,
toAddresses,
amounts
);
}
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
/// @param orders Array of orders to individually simulate transfers for.
/// @param takerAddresses Array of addresses of takers that will fill each order.
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
function getSimulatedOrdersTransferResults(
LibOrder.Order[] memory orders,
address[] memory takerAddresses,
uint256[] memory takerAssetFillAmounts
)
public
returns (OrderTransferResults[] memory orderTransferResults)
{
uint256 length = orders.length;
orderTransferResults = new OrderTransferResults[](length);
for (uint256 i = 0; i != length; i++) {
orderTransferResults[i] = getSimulatedOrderTransferResults(
orders[i],
takerAddresses[i],
takerAssetFillAmounts[i]
);
}
return orderTransferResults;
}
/// @dev Makes the simulation call with information about the transfers and processes
/// the returndata.
/// @param assetData The assetdata to use to make transfers.
/// @param fromAddresses The addresses to transfer funds.
/// @param toAddresses The addresses that will receive funds
/// @param amounts The amounts involved in the transfer.
function _simulateTransferFromCalls(
bytes[] memory assetData,
address[] memory fromAddresses,
address[] memory toAddresses,
uint256[] memory amounts
)
internal
returns (OrderTransferResults orderTransferResults)
{
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
@@ -132,29 +225,4 @@ contract OrderTransferSimulationUtils is
revert("UNKNOWN_RETURN_DATA");
}
}
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
/// @param orders Array of orders to individually simulate transfers for.
/// @param takerAddresses Array of addresses of takers that will fill each order.
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
function getSimulatedOrdersTransferResults(
LibOrder.Order[] memory orders,
address[] memory takerAddresses,
uint256[] memory takerAssetFillAmounts
)
public
returns (OrderTransferResults[] memory orderTransferResults)
{
uint256 length = orders.length;
orderTransferResults = new OrderTransferResults[](length);
for (uint256 i = 0; i != length; i++) {
orderTransferResults[i] = getSimulatedOrderTransferResults(
orders[i],
takerAddresses[i],
takerAssetFillAmounts[i]
);
}
return orderTransferResults;
}
}

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.5;
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
@@ -25,10 +25,12 @@ import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./LibAssetData.sol";
import "./OrderTransferSimulationUtils.sol";
contract OrderValidationUtils is
LibAssetData
LibAssetData,
OrderTransferSimulationUtils
{
using LibBytes for bytes;
using LibSafeMath for uint256;
@@ -50,7 +52,6 @@ contract OrderValidationUtils is
/// amount of each asset that can be filled.
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
public
view
returns (
LibOrder.OrderInfo memory orderInfo,
uint256 fillableTakerAssetAmount,
@@ -99,7 +100,6 @@ contract OrderValidationUtils is
} else {
// Get the transferable amount of the `makerFeeAsset`
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, order.makerFeeAssetData);
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
@@ -120,6 +120,13 @@ contract OrderValidationUtils is
transferableTakerAssetAmount
);
// Execute the maker transfers.
fillableTakerAssetAmount = getSimulatedOrderMakerTransferResults(
order,
order.takerAddress,
fillableTakerAssetAmount
) == OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
}
@@ -135,7 +142,6 @@ contract OrderValidationUtils is
/// the `takerAssetData` to get the final amount of each asset that can be filled.
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
public
view
returns (
LibOrder.OrderInfo[] memory ordersInfo,
uint256[] memory fillableTakerAssetAmounts,

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "1.0.2",
"version": "1.0.3",
"engines": {
"node": ">=6.12"
},
@@ -8,7 +8,7 @@
"main": "lib/src/index.js",
"scripts": {
"build": "yarn pre_build && tsc -b",
"test": "yarn assert_deployable && echo !!! Tests are run via @0x/contracts-tests !!!",
"test": "yarn assert_deployable && echo !!! Tests are run via @0x/contracts-integrations !!!",
"assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"",
"build:ci": "yarn build",
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy",
@@ -41,10 +41,10 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.2",
"@0x/assert": "^3.0.2",
"@0x/contracts-gen": "^2.0.2",
"@0x/sol-compiler": "^4.0.2",
"@0x/abi-gen": "^5.0.3",
"@0x/assert": "^3.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/sol-compiler": "^4.0.3",
"@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.2"
"@0x/base-contract": "^6.0.3"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "2.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "2.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.3 - _January 6, 2020_
* Dependencies updated
## v2.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "2.0.2",
"version": "2.0.3",
"engines": {
"node": ">=6.12"
},
@@ -52,11 +52,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@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/abi-gen": "^5.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
@@ -80,10 +80,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.2",
"@0x/contracts-test-utils": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/base-contract": "^6.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,10 +1,20 @@
[
{
"version": "1.0.3",
"changes": [
{
"note": "Add gas limits to external quote calls.",
"pr": 2405
}
],
"timestamp": 1578272714
},
{
"version": "1.0.2",
"changes": [
{
"note": "Do not query empty/unsigned orders. Swallow revets on DEX quotes.",
"pr": 2365
"pr": 2395
}
],
"timestamp": 1576540892

View File

@@ -5,9 +5,13 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.3 - _January 6, 2020_
* Add gas limits to external quote calls. (#2405)
## v1.0.2 - _December 17, 2019_
* Do not query empty/unsigned orders. Swallow revets on DEX quotes. (#2365)
* Do not query empty/unsigned orders. Swallow revets on DEX quotes. (#2395)
## v1.0.1 - _December 9, 2019_

View File

@@ -37,6 +37,9 @@ contract ERC20BridgeSampler is
DeploymentConstants
{
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 600e3;
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 250e3;
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
/// @param orders Native orders to query.
@@ -144,7 +147,7 @@ contract ERC20BridgeSampler is
);
// The fillable amount is zero if the order is not fillable or if the
// signature is invalid.
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE) ||
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE ||
!isValidSignature) {
orderFillableTakerAssetAmounts[i] = 0;
} else {
@@ -268,12 +271,13 @@ contract ERC20BridgeSampler is
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
_getKyberNetworkProxyAddress().staticcall(abi.encodeWithSelector(
IKyberNetwork(0).getExpectedRate.selector,
_takerToken,
_makerToken,
takerTokenAmounts[i]
));
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_SAMPLE_CALL_GAS)(
abi.encodeWithSelector(
IKyberNetwork(0).getExpectedRate.selector,
_takerToken,
_makerToken,
takerTokenAmounts[i]
));
uint256 rate = 0;
if (didSucceed) {
rate = abi.decode(resultData, (uint256));
@@ -307,12 +311,13 @@ contract ERC20BridgeSampler is
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
IEth2Dai(0).getBuyAmount.selector,
makerToken,
takerToken,
takerTokenAmounts[i]
));
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
abi.encodeWithSelector(
IEth2Dai(0).getBuyAmount.selector,
makerToken,
takerToken,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
@@ -341,12 +346,13 @@ contract ERC20BridgeSampler is
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
IEth2Dai(0).getPayAmount.selector,
takerToken,
makerToken,
makerTokenAmounts[i]
));
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
abi.encodeWithSelector(
IEth2Dai(0).getPayAmount.selector,
takerToken,
makerToken,
makerTokenAmounts[i]
));
uint256 sellAmount = 0;
if (didSucceed) {
sellAmount = abi.decode(resultData, (uint256));
@@ -493,10 +499,11 @@ contract ERC20BridgeSampler is
return 0;
}
(bool didSucceed, bytes memory resultData) =
uniswapExchangeAddress.staticcall(abi.encodeWithSelector(
functionSelector,
inputAmount
));
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
abi.encodeWithSelector(
functionSelector,
inputAmount
));
if (didSucceed) {
outputAmount = abi.decode(resultData, (uint256));
}

View File

@@ -305,6 +305,8 @@ contract TestERC20BridgeSampler is
TestERC20BridgeSamplerEth2Dai public eth2Dai;
TestERC20BridgeSamplerKyberNetwork public kyber;
uint8 private constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED) + 1;
constructor() public {
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
eth2Dai = new TestERC20BridgeSamplerEth2Dai();
@@ -336,9 +338,8 @@ contract TestERC20BridgeSampler is
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.orderStatus = LibOrder.OrderStatus(uint256(orderHash) % MAX_ORDER_STATUS);
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
fillableTakerAssetAmount =
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
isValidSignature = uint256(orderHash) % 2 == 1;

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20-bridge-sampler",
"version": "1.0.2",
"version": "1.0.3",
"engines": {
"node": ">=6.12"
},
@@ -50,18 +50,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.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/abi-gen": "^5.0.3",
"@0x/contracts-asset-proxy": "^3.1.0",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-exchange": "^3.0.3",
"@0x/contracts-exchange-libs": "^4.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@0x/web3-wrapper": "^7.0.3",
"@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.2",
"@0x/base-contract": "^6.0.3",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/utils": "^5.1.2",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "3.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "3.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.3 - _January 6, 2020_
* Dependencies updated
## v3.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.0.2",
"version": "3.0.3",
"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.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/abi-gen": "^5.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"@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.2"
"@0x/base-contract": "^6.0.3"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "3.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "3.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.3 - _January 6, 2020_
* Dependencies updated
## v3.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "3.0.2",
"version": "3.0.3",
"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.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/abi-gen": "^5.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"@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.2"
"@0x/base-contract": "^6.0.3"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "4.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "4.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.3 - _January 6, 2020_
* Dependencies updated
## v4.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "4.0.2",
"version": "4.0.3",
"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.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/abi-gen": "^5.0.3",
"@0x/contracts-asset-proxy": "^3.1.0",
"@0x/contracts-dev-utils": "^1.0.3",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-erc721": "^3.0.3",
"@0x/contracts-exchange": "^3.0.3",
"@0x/contracts-exchange-libs": "^4.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/order-utils": "^10.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -89,7 +89,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.2",
"@0x/base-contract": "^6.0.3",
"@0x/typescript-typings": "^5.0.1",
"ethereum-types": "^3.0.0"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "4.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "4.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.3 - _January 6, 2020_
* Dependencies updated
## v4.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -29,9 +29,11 @@ contract LibEIP712ExchangeDomain {
// EIP712 Exchange Domain Version value
string constant internal _EIP712_EXCHANGE_DOMAIN_VERSION = "3.0.0";
// Hash of the EIP712 Domain Separator data
// solhint-disable-next-line var-name-mixedcase
// solhint-disable var-name-mixedcase
/// @dev Hash of the EIP712 Domain Separator data
/// @return 0 Domain hash.
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
// solhint-enable var-name-mixedcase
/// @param chainId Chain ID of the network this contract is deployed on.
/// @param verifyingContractAddressIfExists Address of the verifying contract (null if the address of this contract)

View File

@@ -60,6 +60,7 @@ library LibOrder {
}
// solhint-disable max-line-length
/// @dev Canonical order structure.
struct Order {
address makerAddress; // Address that created the order.
address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.
@@ -78,8 +79,9 @@ library LibOrder {
}
// solhint-enable max-line-length
/// @dev Order information returned by `getOrderInfo()`.
struct OrderInfo {
uint8 orderStatus; // Status that describes order's validity and fillability.
OrderStatus orderStatus; // Status that describes order's validity and fillability.
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "4.0.2",
"version": "4.0.3",
"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.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/abi-gen": "^5.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/subproviders": "^6.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@0x/web3-wrapper": "^7.0.3",
"@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.2",
"@0x/contracts-utils": "^4.0.2",
"@0x/order-utils": "^10.0.1",
"@0x/base-contract": "^6.0.3",
"@0x/contracts-utils": "^4.0.3",
"@0x/order-utils": "^10.1.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/utils": "^5.1.2",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "3.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "3.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.3 - _January 6, 2020_
* Dependencies updated
## v3.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -29,6 +29,7 @@ import "./MixinTransferSimulator.sol";
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinSignatureValidator,
// and MixinTransactions are all inherited via the other Mixins that are
// used.
/// @dev The 0x Exchange contract.
contract Exchange is
LibEIP712ExchangeDomain,
MixinMatchOrders,

View File

@@ -62,11 +62,11 @@ contract MixinAssetProxyDispatcher is
/// @dev Gets an asset proxy.
/// @param assetProxyId Id of the asset proxy.
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
/// @return assetProxy The asset proxy address registered to assetProxyId. Returns 0x0 if no proxy is registered.
function getAssetProxy(bytes4 assetProxyId)
external
view
returns (address)
returns (address assetProxy)
{
return _assetProxies[assetProxyId];
}

View File

@@ -45,14 +45,21 @@ contract MixinExchangeCore is
using LibSafeMath for uint256;
using LibBytes for bytes;
// Mapping of orderHash => amount of takerAsset already bought by maker
/// @dev Mapping of orderHash => amount of takerAsset already bought by maker
/// @param 0 Order hash.
/// @return 0 The amount of taker asset filled.
mapping (bytes32 => uint256) public filled;
// Mapping of orderHash => cancelled
/// @dev Mapping of orderHash => cancelled
/// @param 0 Order hash.
/// @return 0 Whether the order was cancelled.
mapping (bytes32 => bool) public cancelled;
// Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable
// Orders with specified senderAddress and with a salt less than their epoch are considered cancelled
/// @dev Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable
/// Orders with specified senderAddress and with a salt less than their epoch are considered cancelled
/// @param 0 Address of the order's maker.
/// @param 1 Address of the order's sender.
/// @return 0 Minimum valid order epoch.
mapping (address => mapping (address => uint256)) public orderEpoch;
/// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
@@ -94,7 +101,7 @@ contract MixinExchangeCore is
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function fillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
@@ -125,7 +132,7 @@ contract MixinExchangeCore is
/// @dev Gets information about an order: status, hash, and amount filled.
/// @param order Order to gather information on.
/// @return OrderInfo Information about the order and its state.
/// @return orderInfo Information about the order and its state.
/// See LibOrder.OrderInfo for a complete description.
function getOrderInfo(LibOrder.Order memory order)
public
@@ -140,7 +147,7 @@ contract MixinExchangeCore is
// edge cases in the supporting infrastructure because they have
// an 'infinite' price when computed by a simple division.
if (order.makerAssetAmount == 0) {
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_MAKER_ASSET_AMOUNT);
orderInfo.orderStatus = LibOrder.OrderStatus.INVALID_MAKER_ASSET_AMOUNT;
return orderInfo;
}
@@ -149,35 +156,35 @@ contract MixinExchangeCore is
// Instead of distinguishing between unfilled and filled zero taker
// amount orders, we choose not to support them.
if (order.takerAssetAmount == 0) {
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_TAKER_ASSET_AMOUNT);
orderInfo.orderStatus = LibOrder.OrderStatus.INVALID_TAKER_ASSET_AMOUNT;
return orderInfo;
}
// Validate order availability
if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FULLY_FILLED);
orderInfo.orderStatus = LibOrder.OrderStatus.FULLY_FILLED;
return orderInfo;
}
// Validate order expiration
// solhint-disable-next-line not-rely-on-time
if (block.timestamp >= order.expirationTimeSeconds) {
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.EXPIRED);
orderInfo.orderStatus = LibOrder.OrderStatus.EXPIRED;
return orderInfo;
}
// Check if order has been cancelled
if (cancelled[orderInfo.orderHash]) {
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
orderInfo.orderStatus = LibOrder.OrderStatus.CANCELLED;
return orderInfo;
}
if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
orderInfo.orderStatus = LibOrder.OrderStatus.CANCELLED;
return orderInfo;
}
// All other statuses are ruled out: order is Fillable
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FILLABLE);
orderInfo.orderStatus = LibOrder.OrderStatus.FILLABLE;
return orderInfo;
}
@@ -185,7 +192,7 @@ contract MixinExchangeCore is
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function _fillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
@@ -255,7 +262,7 @@ contract MixinExchangeCore is
_assertValidCancel(order, orderInfo);
// Noop if order is already unfillable
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
return;
}
@@ -337,7 +344,7 @@ contract MixinExchangeCore is
view
{
// An order can only be filled if its status is FILLABLE.
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
LibRichErrors.rrevert(LibExchangeRichErrors.OrderStatusError(
orderInfo.orderHash,
LibOrder.OrderStatus(orderInfo.orderStatus)

View File

@@ -29,10 +29,12 @@ contract MixinProtocolFees is
IProtocolFees,
Ownable
{
// The protocol fee multiplier -- the owner can update this field.
/// @dev The protocol fee multiplier -- the owner can update this field.
/// @return 0 Gas multplier.
uint256 public protocolFeeMultiplier;
// The address of the registered protocolFeeCollector contract -- the owner can update this field.
/// @dev The address of the registered protocolFeeCollector contract -- the owner can update this field.
/// @return 0 Contract to forward protocol fees to.
address public protocolFeeCollector;
/// @dev Allows the owner to update the protocol fee multiplier.

View File

@@ -47,10 +47,16 @@ contract MixinSignatureValidator is
// bytes4(keccak256("isValidWalletSignature(bytes32,address,bytes)"))
bytes4 private constant LEGACY_WALLET_MAGIC_VALUE = 0xb0671381;
// Mapping of hash => signer => signed
/// @dev Mapping of hash => signer => signed
/// @param 0 Order hash.
/// @param 1 Signer address.
/// @return 0 Whether the hash is presigned.
mapping (bytes32 => mapping (address => bool)) public preSigned;
// Mapping of signer => validator => approved
/// @dev Mapping of signer => validator => approved
/// @param 0 Signer address.
/// @param 1 Signature validator address.
/// @return 0 Whether the validator is allowed to validate on behalf of the signer.
mapping (address => mapping (address => bool)) public allowedValidators;
/// @dev Approves a hash on-chain.

View File

@@ -36,11 +36,14 @@ contract MixinTransactions is
{
using LibZeroExTransaction for LibZeroExTransaction.ZeroExTransaction;
// Mapping of transaction hash => executed
// This prevents transactions from being executed more than once.
/// @dev Mapping of transaction hash => executed
/// This prevents transactions from being executed more than once.
/// @param 0 The transaction hash.
/// @return 0 Whether the transation was executed.
mapping (bytes32 => bool) public transactionsExecuted;
// Address of current transaction signer
/// @dev Address of current transaction signer.
/// @return 0 The address associated with the the current transaction.
address public currentContextAddress;
/// @dev Executes an Exchange method call in the context of signer.
@@ -62,7 +65,7 @@ contract MixinTransactions is
/// @dev Executes a batch of Exchange method calls in the context of signer(s).
/// @param transactions Array of 0x transaction structures.
/// @param signatures Array of proofs that transactions have been signed by signer(s).
/// @return Array containing ABI encoded return data for each of the underlying Exchange function calls.
/// @return returnData Array containing ABI encoded return data for each of the underlying Exchange function calls.
function batchExecuteTransactions(
LibZeroExTransaction.ZeroExTransaction[] memory transactions,
bytes[] memory signatures
@@ -70,10 +73,10 @@ contract MixinTransactions is
public
payable
disableRefundUntilEnd
returns (bytes[] memory)
returns (bytes[] memory returnData)
{
uint256 length = transactions.length;
bytes[] memory returnData = new bytes[](length);
returnData = new bytes[](length);
for (uint256 i = 0; i != length; i++) {
returnData[i] = _executeTransaction(transactions[i], signatures[i]);
}
@@ -117,7 +120,7 @@ contract MixinTransactions is
_setCurrentContextAddressIfRequired(signerAddress, address(0));
emit TransactionExecution(transactionHash);
return returnData;
}

View File

@@ -36,10 +36,11 @@ contract MixinWrapperFunctions is
{
using LibSafeMath for uint256;
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @dev Fills the input order. Reverts if exact `takerAssetFillAmount` not filled.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return fillResults Amounts filled and fees paid.
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
@@ -62,7 +63,7 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return Array of amounts filled and fees paid by makers and taker.
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
@@ -89,7 +90,7 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return Array of amounts filled and fees paid by makers and taker.
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
@@ -116,7 +117,7 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return Array of amounts filled and fees paid by makers and taker.
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
@@ -145,7 +146,7 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
@@ -186,7 +187,7 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param makerAssetFillAmount Desired amount of makerAsset to buy.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
@@ -234,7 +235,7 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmount Minimum amount of takerAsset to sell.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketSellOrdersFillOrKill(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
@@ -259,7 +260,7 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param makerAssetFillAmount Minimum amount of makerAsset to buy.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketBuyOrdersFillOrKill(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
@@ -295,7 +296,7 @@ contract MixinWrapperFunctions is
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @param fillResults ignature Proof that order has been created by maker.
function _fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
@@ -324,7 +325,7 @@ contract MixinWrapperFunctions is
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function _fillOrderNoThrow(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,

View File

@@ -30,7 +30,7 @@ import "../src/Exchange.sol";
contract TestWrapperFunctions is
Exchange
{
uint8 internal constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED);
LibOrder.OrderStatus internal constant MAX_ORDER_STATUS = LibOrder.OrderStatus.CANCELLED;
uint256 internal constant ALWAYS_FAILING_SALT = uint256(-1);
string internal constant ALWAYS_FAILING_SALT_REVERT_REASON = "ALWAYS_FAILING_SALT";
@@ -61,7 +61,7 @@ contract TestWrapperFunctions is
// Lower uint128 of `order.salt` is the `orderTakerAssetFilledAmount`.
orderInfo.orderTakerAssetFilledAmount = uint128(order.salt);
// High byte of `order.salt` is the `orderStatus`.
orderInfo.orderStatus = uint8(order.salt >> 248) % (MAX_ORDER_STATUS + 1);
orderInfo.orderStatus = LibOrder.OrderStatus(uint8(order.salt >> 248) % (uint8(MAX_ORDER_STATUS) + 1));
orderInfo.orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange",
"version": "3.0.2",
"version": "3.0.3",
"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.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/abi-gen": "^5.0.3",
"@0x/contracts-asset-proxy": "^3.1.0",
"@0x/contracts-exchange-libs": "^4.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-multisig": "^4.0.3",
"@0x/contracts-staking": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/web3-wrapper": "^7.0.3",
"@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.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",
"@0x/base-contract": "^6.0.3",
"@0x/contracts-dev-utils": "^1.0.3",
"@0x/contracts-erc1155": "^2.0.3",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-erc721": "^3.0.3",
"@0x/order-utils": "^10.1.0",
"@0x/utils": "^5.1.2",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "5.1.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "5.1.1",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.1.2 - _January 6, 2020_
* Dependencies updated
## v5.1.1 - _December 17, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "5.1.1",
"version": "5.1.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.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/abi-gen": "^5.0.3",
"@0x/contracts-asset-proxy": "^3.1.0",
"@0x/contracts-dev-utils": "^1.0.3",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-erc721": "^3.0.3",
"@0x/contracts-exchange": "^3.0.3",
"@0x/contracts-exchange-libs": "^4.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/order-utils": "^10.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -91,7 +91,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.2",
"@0x/base-contract": "^6.0.3",
"@0x/typescript-typings": "^5.0.1",
"ethereum-types": "^3.0.0"
},

View File

@@ -1,4 +1,18 @@
[
{
"version": "2.1.0",
"changes": [
{
"note": "Integration tests for DydxBridge with (i) Exchange v3 and (ii) Mainnet dYdX SoloMargin contract.",
"pr": 2401
},
{
"note": "Add aggregator mainnet tests.",
"pr": 2407
}
],
"timestamp": 1578272714
},
{
"timestamp": 1576540892,
"version": "2.0.2",

View File

@@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.0 - _January 6, 2020_
* Integration tests for DydxBridge with (i) Exchange v3 and (ii) Mainnet dYdX SoloMargin contract. (#2401)
* Add aggregator mainnet tests. (#2407)
## v2.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -0,0 +1,207 @@
/*
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;
/// @dev THIS IS A SELF-CONTAINED CONVENIENCE CONTRACT FOR TESTING THE DYDX BRIDGE ON MAINNET.
/// Currently deployed at 0xeb58c2caa96f39626dcceb74fdbb7a9a8b54ec18.
/// Steps:
/// 1. Deploy to mainnet (can copy-paste this into Remix and deploy).
/// 2. Send some DAI to the contract.
/// 3. Call `init()` to configure.
interface IERC20Token {
/// @dev `msg.sender` approves `_spender` to spend `_value` tokens
/// @param spender The address of the account able to transfer the tokens
/// @param value The amount of wei to be approved for transfer
/// @return Always true if the call has enough gas to complete execution
function approve(address spender, uint256 value)
external
returns (bool);
/// @dev Query the balance of owner
/// @param owner The address from which the balance will be retrieved
/// @return Balance of owner
function balanceOf(address owner)
external
view
returns (uint256);
}
/// @dev TestDydxUser uses this interface to interact with dydx.
interface IDydx {
/// @dev Respresents an operator's privileges.
struct OperatorArg {
address operator;
bool trusted;
}
/// @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;
/// @dev Sets operators of dydx account.
/// @param operators to give/remove access.
function setOperators(OperatorArg[] calldata operators)
external;
}
/// @dev Deploy this contract and call `init` to run the mainnet DydxBridge integration tests.
contract TestDydxUser {
address public constant DYDX_BRIDGE_ADDRESS = 0x96DdBa19b69D6EA2549f6a12d005595167414744;
address public constant DYDX_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
address public constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
uint256 public constant DYDX_DAI_MARKET_ID = 3;
bytes4 public constant MAGIC_BYTES = bytes4(keccak256("init()"));
function init()
public
returns (bytes4)
{
// 1. Assert that this account has DAI.
uint256 daiBalance = IERC20Token(DAI_ADDRESS).balanceOf(address(this));
require(
daiBalance > 0,
"TestDydxUser/DAI_BALANCE_MUST_BE_NONZERO"
);
// 2. Set allowance for Dydx to transfer DAI.
require(
IERC20Token(DAI_ADDRESS).approve(
DYDX_ADDRESS,
daiBalance
),
"TestDydxUser/FAILED_TO_SET_DAI_ALLOWANCE"
);
// 3. Add DydxBridge as operator on dydx.
// This will revert on failure.
IDydx.OperatorArg[] memory operatorArgs = new IDydx.OperatorArg[](1);
operatorArgs[0] = IDydx.OperatorArg({
operator: DYDX_BRIDGE_ADDRESS,
trusted: true
});
IDydx(DYDX_ADDRESS).setOperators(operatorArgs);
// 4. Deposit 1/2 DAI balance into dydx. This allows us to test withdrawals.
// 4.i Create dydx account struct.
IDydx.AccountInfo[] memory accounts = new IDydx.AccountInfo[](1);
accounts[0] = IDydx.AccountInfo({
owner: address(this),
number: 0
});
// 4.ii 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: daiBalance / 2 // amount to deposit.
});
// 4.iii Create dydx deposit action.
IDydx.ActionArgs[] memory actions = new IDydx.ActionArgs[](1);
actions[0] = IDydx.ActionArgs({
actionType: IDydx.ActionType.Deposit, // deposit tokens.
amount: dydxAmount, // amount to deposit.
accountId: 0, // index in the `accounts` when calling `operate`.
primaryMarketId: DYDX_DAI_MARKET_ID, // indicates which token to deposit.
otherAddress: address(this), // deposit from the account owner.
// unused parameters
secondaryMarketId: 0,
otherAccountId: 0,
data: hex''
});
// 4.iv Deposit DAI into dydx. This will revert on failure.
IDydx(DYDX_ADDRESS).operate(
accounts,
actions
);
// Return magic bytes on success.
return MAGIC_BYTES;
}
}

View File

@@ -0,0 +1,180 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.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/LibFillResults.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
/// @dev A forwarder contract for filling 0x asset-swapper aggregated orders.
/// The forwarder is necessary to purchase taker assets and set up
/// approvals in one transaction. Only call the functions on this contract
/// in an `eth_call` context or you will lose money!
contract TestMainnetAggregatorFills is
DeploymentConstants
{
using LibSafeMath for uint256;
address constant internal EXCHANGE_ADDRESS = 0x61935CbDd02287B511119DDb11Aeb42F1593b7Ef;
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
struct SimulatedMarketFillResults {
uint256 makerAssetBalanceBefore;
uint256 takerAssetBalanceBefore;
uint256 makerAssetBalanceAfter;
uint256 takerAssetBalanceAfter;
LibFillResults.FillResults fillResults;
}
// solhint-disable-next-line no-empty-blocks
function() external payable {}
/// @dev Buy taker assets with ETH from `takerOrders` and then perform a
/// market buy on `makerOrders`.
function marketBuy(
address makerTokenAddress,
address takerTokenAddress,
LibOrder.Order[] memory makerOrders,
LibOrder.Order[] memory takerOrders,
bytes[] memory makerOrderSignatures,
bytes[] memory takerOrderSignatures,
uint256 makerAssetBuyAmount
)
public
payable
returns (SimulatedMarketFillResults memory results)
{
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
results.fillResults = IExchange(EXCHANGE_ADDRESS)
.marketBuyOrdersNoThrow
.value(address(this).balance)(
makerOrders,
makerAssetBuyAmount,
makerOrderSignatures
);
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
}
/// @dev Buy taker assets with ETH from `takerOrders` and then perform a
/// market sell on `makerOrders`.
function marketSell(
address makerTokenAddress,
address takerTokenAddress,
LibOrder.Order[] memory makerOrders,
LibOrder.Order[] memory takerOrders,
bytes[] memory makerOrderSignatures,
bytes[] memory takerOrderSignatures,
uint256 takerAssetSellAmount
)
public
payable
returns (SimulatedMarketFillResults memory results)
{
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
results.fillResults = IExchange(EXCHANGE_ADDRESS)
.marketSellOrdersNoThrow
.value(address(this).balance)(
makerOrders,
takerAssetSellAmount,
makerOrderSignatures
);
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
}
/// @dev Like `marketSell`, but calls `fillOrder()` individually to detect
/// errors.
function fillOrders(
address makerTokenAddress,
address takerTokenAddress,
LibOrder.Order[] memory makerOrders,
LibOrder.Order[] memory takerOrders,
bytes[] memory makerOrderSignatures,
bytes[] memory takerOrderSignatures,
uint256 takerAssetSellAmount
)
public
payable
returns (SimulatedMarketFillResults memory results)
{
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
for (uint256 i = 0; i < makerOrders.length; i++) {
if (takerAssetSellAmount == 0) {
break;
}
LibFillResults.FillResults memory fillResults = IExchange(EXCHANGE_ADDRESS)
.fillOrder
.value(address(this).balance)(
makerOrders[i],
takerAssetSellAmount,
makerOrderSignatures[i]
);
results.fillResults = LibFillResults.addFillResults(results.fillResults, fillResults);
takerAssetSellAmount = takerAssetSellAmount.safeSub(fillResults.takerAssetFilledAmount);
}
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
}
function _approveAssetProxy(address tokenAddress) private {
address assetProxyAddress = IExchange(EXCHANGE_ADDRESS).getAssetProxy(ERC20_PROXY_ID);
LibERC20Token.approve(tokenAddress, assetProxyAddress, uint256(-1));
}
/// @dev Buys as much of `takerOrders` as possible with the ETH transferred
/// to this contract, leaving enough ETH behind for protocol fees.
function _prepareFunds(
address takerTokenAddress,
LibOrder.Order[] memory makerOrders,
LibOrder.Order[] memory takerOrders,
bytes[] memory takerOrderSignatures
)
private
{
_approveAssetProxy(_getWethAddress());
uint256 protocolFee = IExchange(EXCHANGE_ADDRESS).protocolFeeMultiplier() * tx.gasprice;
uint256 maxProtocolFees = protocolFee * (takerOrders.length + makerOrders.length);
uint256 ethSellAmount = msg.value.safeSub(maxProtocolFees);
IEtherToken(_getWethAddress()).deposit.value(ethSellAmount)();
if (takerTokenAddress != _getWethAddress()) {
IExchange(EXCHANGE_ADDRESS)
.marketSellOrdersNoThrow
.value(maxProtocolFees)(
takerOrders,
ethSellAmount,
takerOrderSignatures
);
_approveAssetProxy(takerTokenAddress);
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.0.2",
"version": "2.1.0",
"engines": {
"node": ">=6.12"
},
@@ -18,7 +18,7 @@
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 1000000 --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",
@@ -38,7 +38,7 @@
},
"config": {
"publicInterfaceContracts": "TestFramework",
"abis": "./test/generated-artifacts/@(TestEth2Dai|TestEth2DaiBridge|TestFramework|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
"abis": "./test/generated-artifacts/@(TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFramework|TestMainnetAggregatorFills|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -51,22 +51,22 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@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/abi-gen": "^5.0.3",
"@0x/contract-addresses": "^4.2.0",
"@0x/contract-wrappers": "^13.3.0",
"@0x/contracts-coordinator": "^3.0.3",
"@0x/contracts-dev-utils": "^1.0.3",
"@0x/contracts-exchange-forwarder": "^4.0.3",
"@0x/contracts-exchange-libs": "^4.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-utils": "^4.0.3",
"@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/dev-utils": "^3.1.0",
"@0x/migrations": "^5.1.0",
"@0x/order-utils": "^10.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.2",
"@0x/web3-wrapper": "^7.0.3",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -89,19 +89,21 @@
"typescript": "3.0.1"
},
"dependencies": {
"@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/asset-swapper": "^3.0.3",
"@0x/base-contract": "^6.0.3",
"@0x/contracts-asset-proxy": "^3.1.0",
"@0x/contracts-erc1155": "^2.0.3",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-erc721": "^3.0.3",
"@0x/contracts-exchange": "^3.0.3",
"@0x/contracts-multisig": "^4.0.3",
"@0x/contracts-staking": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.1",
"@0x/utils": "^5.1.2",
"ethereum-types": "^3.0.0",
"ethereumjs-util": "^6.2.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -0,0 +1,239 @@
import { MarketBuySwapQuote, MarketSellSwapQuote, Orderbook, SwapQuoter } from '@0x/asset-swapper';
import { blockchainTests, expect, Numberish } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { FillResults, SignedOrder } from '@0x/types';
import { BigNumber, logUtils } from '@0x/utils';
import * as _ from 'lodash';
import { TestMainnetAggregatorFillsContract } from '../wrappers';
import { tokens } from './tokens';
blockchainTests.live('Aggregator Mainnet Tests', env => {
// Mainnet address of the `TestMainnetAggregatorFills` contract.
const TEST_CONTRACT_ADDRESS = '0x37Ca306F42748b7fe105F89FCBb2CD03D27c8146';
const TAKER_ADDRESS = '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'; // Vitalik
const ORDERBOOK_POLLING_MS = 1000;
const GAS_PRICE = new BigNumber(1);
const TAKER_ASSET_ETH_VALUE = 500e18;
const MIN_BALANCE = 500.1e18;
const SYMBOLS = ['ETH', 'DAI', 'USDC', 'FOAM'];
const TEST_PAIRS = _.flatten(SYMBOLS.map(m => SYMBOLS.filter(t => t !== m).map(t => [m, t])));
const FILL_VALUES = [1, 10, 1e2, 1e3, 1e4, 2.5e4, 5e4];
let testContract: TestMainnetAggregatorFillsContract;
let swapQuoter: SwapQuoter;
let takerEthBalance: BigNumber;
const orderbooks: { [name: string]: Orderbook } = {};
async function getTakerOrdersAsync(takerAssetSymbol: string): Promise<SignedOrder[]> {
if (takerAssetSymbol === 'ETH') {
return [];
}
return getOrdersAsync(takerAssetSymbol, 'ETH');
}
// Fetches ETH -> taker asset orders for the forwarder contract.
async function getOrdersAsync(makerAssetSymbol: string, takerAssetSymbol: string): Promise<SignedOrder[]> {
const takerTokenAddress = tokens[takerAssetSymbol].address;
const makerTokenAddress = tokens[makerAssetSymbol].address;
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
const orders = _.flatten(
await Promise.all(
Object.keys(orderbooks).map(async name =>
getOrdersFromOrderBookAsync(name, makerAssetData, takerAssetData),
),
),
);
const uniqueOrders: SignedOrder[] = [];
for (const order of orders) {
if (!order.makerFee.eq(0) || !order.takerFee.eq(0)) {
continue;
}
if (uniqueOrders.findIndex(o => isSameOrder(order, o)) === -1) {
uniqueOrders.push(order);
}
}
return uniqueOrders;
}
async function getOrdersFromOrderBookAsync(
name: string,
makerAssetData: string,
takerAssetData: string,
): Promise<SignedOrder[]> {
try {
return (await orderbooks[name].getOrdersAsync(makerAssetData, takerAssetData)).map(r => r.order);
} catch (err) {
logUtils.warn(`Failed to retrieve orders from orderbook "${name}".`);
}
return [];
}
function isSameOrder(a: SignedOrder, b: SignedOrder): boolean {
for (const [k, v] of Object.entries(a)) {
if (k in (b as any)) {
if (BigNumber.isBigNumber(v) && !v.eq((b as any)[k])) {
return false;
}
if (v !== (b as any)[k]) {
return false;
}
}
}
return true;
}
function toTokenUnits(symbol: string, weis: Numberish): BigNumber {
return new BigNumber(weis).div(new BigNumber(10).pow(tokens[symbol].decimals));
}
function fromTokenUnits(symbol: string, units: Numberish): BigNumber {
return new BigNumber(units)
.times(new BigNumber(10).pow(tokens[symbol].decimals))
.integerValue(BigNumber.ROUND_DOWN);
}
interface MarketOperationResult {
makerAssetBalanceBefore: BigNumber;
takerAssetBalanceBefore: BigNumber;
makerAssetBalanceAfter: BigNumber;
takerAssetBalanceAfter: BigNumber;
fillResults: FillResults;
}
// Liquidity is low right now so it's possible we didn't have
// enough taker assets to cover the orders, so occasionally we'll get incomplete
// fills. This function will catch those cases.
// TODO(dorothy-zbornak): Remove this special case when liquidity is up.
function checkHadEnoughTakerAsset(
quote: MarketBuySwapQuote | MarketSellSwapQuote,
result: MarketOperationResult,
): boolean {
if (result.takerAssetBalanceBefore.gte(quote.worstCaseQuoteInfo.takerAssetAmount)) {
return true;
}
const takerAssetPct = result.takerAssetBalanceBefore
.div(quote.worstCaseQuoteInfo.takerAssetAmount)
.times(100)
.toNumber()
.toFixed(1);
logUtils.warn(`Could not acquire enough taker asset to complete the fill: ${takerAssetPct}%`);
expect(result.fillResults.makerAssetFilledAmount).to.bignumber.lt(quote.worstCaseQuoteInfo.makerAssetAmount);
return false;
}
before(async () => {
testContract = new TestMainnetAggregatorFillsContract(TEST_CONTRACT_ADDRESS, env.provider, {
...env.txDefaults,
gasPrice: GAS_PRICE,
gas: 10e6,
});
swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(env.provider, 'https://api.0x.org/sra');
// Pool orderbooks because we're desperate for liquidity.
orderbooks.swapQuoter = swapQuoter.orderbook;
orderbooks.bamboo = Orderbook.getOrderbookForPollingProvider({
httpEndpoint: 'https://sra.bamboorelay.com/0x/v3',
pollingIntervalMs: ORDERBOOK_POLLING_MS,
});
// TODO(dorothy-zbornak): Uncomment when radar's SRA is up.
// orderbooks.radar = Orderbook.getOrderbookForPollingProvider({
// httpEndpoint: 'https://api-v3.radarrelay.com/v3',
// pollingIntervalMs: ORDERBOOK_POLLING_MS,
// });
takerEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(TAKER_ADDRESS);
});
it('taker has minimum ETH', async () => {
expect(takerEthBalance).to.bignumber.gte(MIN_BALANCE);
});
describe('market sells', () => {
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
for (const fillValue of FILL_VALUES) {
const fillAmount = fromTokenUnits(takerSymbol, new BigNumber(fillValue).div(tokens[takerSymbol].price));
it(`sell ${toTokenUnits(takerSymbol, fillAmount)} ${takerSymbol} for ${makerSymbol}`, async () => {
const [quote, takerOrders] = await Promise.all([
swapQuoter.getMarketSellSwapQuoteAsync(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
fillAmount,
{ gasPrice: GAS_PRICE },
),
getTakerOrdersAsync(takerSymbol),
]);
// Buy taker assets from `takerOrders` and and perform a
// market sell on the bridge orders.
const fill = await testContract
.marketSell(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
quote.orders,
takerOrders,
quote.orders.map(o => o.signature),
takerOrders.map(o => o.signature),
quote.takerAssetFillAmount,
)
.callAsync({
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
from: TAKER_ADDRESS,
gasPrice: quote.gasPrice,
});
if (checkHadEnoughTakerAsset(quote, fill)) {
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
quote.worstCaseQuoteInfo.makerAssetAmount,
);
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
quote.takerAssetFillAmount,
);
}
});
}
}
});
describe('market buys', () => {
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
for (const fillValue of FILL_VALUES) {
const fillAmount = fromTokenUnits(makerSymbol, new BigNumber(fillValue).div(tokens[makerSymbol].price));
it(`buy ${toTokenUnits(makerSymbol, fillAmount)} ${makerSymbol} with ${takerSymbol}`, async () => {
const [quote, takerOrders] = await Promise.all([
swapQuoter.getMarketBuySwapQuoteAsync(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
fillAmount,
{ gasPrice: GAS_PRICE },
),
getTakerOrdersAsync(takerSymbol),
]);
// Buy taker assets from `takerOrders` and and perform a
// market buy on the bridge orders.
const fill = await testContract
.marketBuy(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
quote.orders,
takerOrders,
quote.orders.map(o => o.signature),
takerOrders.map(o => o.signature),
quote.makerAssetFillAmount,
)
.callAsync({
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
from: TAKER_ADDRESS,
gasPrice: quote.gasPrice,
});
if (checkHadEnoughTakerAsset(quote, fill)) {
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
quote.worstCaseQuoteInfo.takerAssetAmount,
);
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
quote.makerAssetFillAmount,
);
}
});
}
}
});
});

View File

@@ -0,0 +1,77 @@
export const tokens: { [symbol: string]: { address: string; decimals: number; price: number } } = {
ETH: {
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
decimals: 18,
price: 133,
},
SAI: {
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
decimals: 18,
price: 1,
},
DAI: {
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
decimals: 18,
price: 1,
},
USDC: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
decimals: 6,
price: 1,
},
WBTC: {
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
decimals: 8,
price: 6900,
},
MKR: {
address: '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2',
decimals: 18,
price: 454,
},
BAT: {
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
decimals: 18,
price: 0.17,
},
OMG: {
address: '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07',
decimals: 18,
price: 0.65,
},
ZRX: {
address: '0xE41d2489571d322189246DaFA5ebDe1F4699F498',
decimals: 18,
price: 0.19,
},
ZIL: {
address: '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27',
decimals: 12,
price: 0.004,
},
FOAM: {
address: '0x4946Fcea7C692606e8908002e55A582af44AC121',
decimals: 18,
price: 0.004,
},
USDT: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
decimals: 6,
price: 0.019,
},
REP: {
address: '0x1985365e9f78359a9B6AD760e32412f4a445E862',
decimals: 18,
price: 8.9,
},
MANA: {
address: '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942',
decimals: 18,
price: 0.025,
},
LINK: {
address: '0x514910771AF9Ca656af840dff83E8264EcF986CA',
decimals: 18,
price: 1.8,
},
};

View File

@@ -5,16 +5,20 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as TestDydxUser from '../test/generated-artifacts/TestDydxUser.json';
import * as TestEth2Dai from '../test/generated-artifacts/TestEth2Dai.json';
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
import * as TestFramework from '../test/generated-artifacts/TestFramework.json';
import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json';
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json';
import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json';
export const artifacts = {
TestDydxUser: TestDydxUser as ContractArtifact,
TestEth2Dai: TestEth2Dai as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestFramework: TestFramework as ContractArtifact,
TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact,
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
TestUniswapExchange: TestUniswapExchange as ContractArtifact,
TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
import { artifacts as assetProxyArtifacts, TestDydxBridgeContract } from '@0x/contracts-asset-proxy';
import { BlockchainTestsEnvironment } from '@0x/contracts-test-utils';
import { DeploymentManager } from '../framework/deployment_manager';
/**
* Deploys test DydxBridge contract configured to work alongside the provided `deployment`.
*/
export async function deployDydxBridgeAsync(
deployment: DeploymentManager,
environment: BlockchainTestsEnvironment,
): Promise<TestDydxBridgeContract> {
const tokenHolders = deployment.accounts;
const dydxBridge = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.TestDydxBridge,
environment.provider,
deployment.txDefaults,
assetProxyArtifacts,
tokenHolders,
);
return dydxBridge;
}

View File

@@ -0,0 +1,159 @@
import {
artifacts as assetProxyArtifacts,
DydxBridgeActionType,
DydxBridgeContract,
DydxBridgeData,
dydxBridgeDataEncoder,
} from '@0x/contracts-asset-proxy';
import { artifacts as erc20Artifacts } from '@0x/contracts-erc20';
import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types';
import { contractAddresses, dydxAccountOwner } from '../mainnet_fork_utils';
import { dydxEvents } from './abi/dydxEvents';
blockchainTests.fork.resets('Mainnet dydx bridge tests', env => {
let testContract: DydxBridgeContract;
// random account to receive tokens from dydx
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
const defaultAccountNumber = new BigNumber(0);
const daiMarketId = new BigNumber(3);
const defaultAmount = toBaseUnitAmount(0.01);
const defaultDepositAction = {
actionType: DydxBridgeActionType.Deposit as number,
accountId: constants.ZERO_AMOUNT,
marketId: daiMarketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const defaultWithdrawAction = {
actionType: DydxBridgeActionType.Withdraw as number,
accountId: constants.ZERO_AMOUNT,
marketId: daiMarketId,
// This ratio must be less than the `1` to account
// for interest in dydx balances because the test
// account has an initial dydx balance of zero.
conversionRateNumerator: new BigNumber(1),
conversionRateDenominator: new BigNumber(2),
};
before(async () => {
testContract = new DydxBridgeContract(contractAddresses.dydxBridge, env.provider, env.txDefaults, {
DydxBridge: assetProxyArtifacts.DydxBridge.compilerOutput.abi,
ERC20: erc20Artifacts.ERC20Token.compilerOutput.abi,
Dydx: dydxEvents.abi,
});
});
describe('bridgeTransferFrom()', () => {
const callAndVerifyDydxEvents = async (bridgeData: DydxBridgeData): Promise<void> => {
const txReceipt = await testContract
.bridgeTransferFrom(
constants.NULL_ADDRESS,
dydxAccountOwner,
receiver,
defaultAmount,
dydxBridgeDataEncoder.encode({ bridgeData }),
)
.awaitTransactionSuccessAsync({ from: contractAddresses.erc20BridgeProxy, gasPrice: 0 });
// Construct expected events
const expectedDepositEvents = [];
const expectedWithdrawEvents = [];
for (const action of bridgeData.actions) {
const scaledAmount = action.conversionRateDenominator.gt(0)
? defaultAmount
.times(action.conversionRateNumerator)
.dividedToIntegerBy(action.conversionRateDenominator)
: defaultAmount;
switch (action.actionType) {
case DydxBridgeActionType.Deposit:
expectedDepositEvents.push({
accountOwner: dydxAccountOwner,
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
market: action.marketId,
update: [[true, scaledAmount]],
from: dydxAccountOwner,
});
break;
case DydxBridgeActionType.Withdraw:
expectedWithdrawEvents.push({
accountOwner: dydxAccountOwner,
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
market: action.marketId,
update: [[false, scaledAmount]],
to: receiver,
});
break;
default:
throw new Error(`Unrecognized Action: ${action.actionType}`);
}
}
// Verify events
let nextExpectedDepositEventIdx = 0;
let nextExpectedWithdrawEventIdx = 0;
for (const rawLog of txReceipt.logs) {
// tslint:disable-next-line no-unnecessary-type-assertion
const log = rawLog as LogWithDecodedArgs<DecodedLogArgs>;
if (log.event !== 'LogDeposit' && log.event !== 'LogWithdraw') {
continue;
}
const expectedEvent =
log.event === 'LogDeposit'
? expectedDepositEvents[nextExpectedDepositEventIdx++]
: expectedWithdrawEvents[nextExpectedWithdrawEventIdx++];
expect(log.args.accountOwner, 'accountOwner').to.equal(expectedEvent.accountOwner);
expect(log.args.accountNumber, 'accountNumber').to.bignumber.equal(expectedEvent.accountNumber);
expect(log.args.market, 'market').to.bignumber.equal(expectedEvent.market);
expect(log.args.from, 'from').to.equal(expectedEvent.from);
// We only check the first update field because it's the delta balance (amount deposited).
// The next field is the new total, which depends on interest rates at the time of execution.
expect(log.args.update[0][0], 'update sign').to.equal(expectedEvent.update[0][0]);
const updateValueHex = log.args.update[0][1]._hex;
const updateValueBn = new BigNumber(updateValueHex, 16);
expect(updateValueBn, 'update value').to.bignumber.equal(expectedEvent.update[0][1]);
}
};
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
});
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultDepositAction],
});
});
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction],
});
});
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction],
});
});
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
actions: [defaultWithdrawAction, defaultDepositAction],
});
});
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
await callAndVerifyDydxEvents({
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction, defaultDepositAction],
});
});
});
});

View File

@@ -0,0 +1,54 @@
import { artifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
import { blockchainTests, constants, expect, orderHashUtils } from '@0x/contracts-test-utils';
import { Order } from '@0x/types';
import { BigNumber } from '@0x/utils';
blockchainTests('DevUtils.getOrderHash', env => {
let devUtils: DevUtilsContract;
let exchange: ExchangeContract;
let chainId: number;
before(async () => {
chainId = await env.getChainIdAsync();
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
env.provider,
env.txDefaults,
exchangeArtifacts,
new BigNumber(chainId),
);
devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
artifacts.DevUtils,
env.provider,
env.txDefaults,
artifacts,
exchange.address,
);
});
it('should return the order hash', async () => {
const order: Order = {
makerAddress: constants.NULL_ADDRESS,
takerAddress: constants.NULL_ADDRESS,
senderAddress: constants.NULL_ADDRESS,
feeRecipientAddress: constants.NULL_ADDRESS,
makerAssetData: constants.NULL_ADDRESS,
takerAssetData: constants.NULL_ADDRESS,
makerFeeAssetData: constants.NULL_ADDRESS,
takerFeeAssetData: constants.NULL_ADDRESS,
salt: new BigNumber(0),
makerFee: new BigNumber(0),
takerFee: new BigNumber(0),
makerAssetAmount: new BigNumber(0),
takerAssetAmount: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0),
exchangeAddress: exchange.address,
chainId,
};
expect(await devUtils.getOrderHash(order, new BigNumber(chainId), exchange.address).callAsync()).to.be.equal(
orderHashUtils.getOrderHashHex(order),
);
});
});

View File

@@ -1,6 +1,5 @@
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as crypto from 'crypto';
import { LogWithDecodedArgs } from 'ethereum-types';
import {
artifacts as proxyArtifacts,
@@ -19,19 +18,12 @@ import {
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
import { chaiSetup, constants, LogDecoder, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { blockchainTests, constants, expect, LogDecoder } from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber, providerUtils, StringRevertError, LibBytesRevertErrors } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import { BigNumber, hexUtils, LibBytesRevertErrors, StringRevertError } from '@0x/utils';
import { artifacts, LibAssetDataContract } from '@0x/contracts-dev-utils';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
const KNOWN_ERC20_ENCODING = {
address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
@@ -69,7 +61,8 @@ const KNOWN_STATIC_CALL_ENCODING = {
'0xc339d10a0000000000000000000000006dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f0000000000000000000000000000000000000000000000000000000000000060b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60000000000000000000000000000000000000000000000000000000000000024ed2cfc9c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000',
};
describe('LibAssetData', () => {
// TODO(jalextowle): This file could really be cleaned up by using the DeploymentManager tool.
blockchainTests.resets('LibAssetData', env => {
let exchange: ExchangeContract;
let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract;
@@ -93,44 +86,43 @@ describe('LibAssetData', () => {
let erc1155TokenId: BigNumber;
before(async () => {
await blockchainLifecycle.startAsync();
const chainId = await providerUtils.getChainIdAsync(provider);
const chainId = await env.getChainIdAsync();
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
provider,
txDefaults,
env.provider,
env.txDefaults,
{},
new BigNumber(chainId),
);
erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
proxyArtifacts.ERC20Proxy,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
);
erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
proxyArtifacts.ERC721Proxy,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
);
erc1155Proxy = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
proxyArtifacts.ERC1155Proxy,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
);
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
proxyArtifacts.MultiAssetProxy,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
);
staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync(
proxyArtifacts.StaticCallProxy,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
);
@@ -142,25 +134,25 @@ describe('LibAssetData', () => {
libAssetData = await LibAssetDataContract.deployFrom0xArtifactAsync(
artifacts.LibAssetData,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
exchange.address,
);
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
proxyArtifacts.TestStaticCallTarget,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
);
[tokenOwnerAddress] = await web3Wrapper.getAvailableAddressesAsync();
[tokenOwnerAddress] = await env.getAccountAddressesAsync();
erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyERC20Token,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
'Dummy',
'DUM',
@@ -170,8 +162,8 @@ describe('LibAssetData', () => {
erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
erc721Artifacts.DummyERC721Token,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
'Dummy',
'DUM',
@@ -187,12 +179,12 @@ describe('LibAssetData', () => {
erc1155Token = await ERC1155MintableContract.deployFrom0xArtifactAsync(
erc1155Artifacts.ERC1155Mintable,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
);
const logDecoder = new LogDecoder(web3Wrapper, erc1155Artifacts);
const logDecoder = new LogDecoder(env.web3Wrapper, erc1155Artifacts);
const transactionReceipt = await logDecoder.getTxWithDecodedLogsAsync(
await erc1155Token.create('uri:Dummy', /*isNonFungible:*/ false).sendTransactionAsync(),
);
@@ -204,17 +196,6 @@ describe('LibAssetData', () => {
.awaitTransactionSuccessAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
it('should have a deployed-to address', () => {
expect(libAssetData.address.slice(0, 2)).to.equal('0x');
});
@@ -345,7 +326,7 @@ describe('LibAssetData', () => {
});
it('should revert for invalid assetProxyId', async () => {
const badAssetData = '0x' + crypto.randomBytes(4).toString('hex') + constants.NULL_ADDRESS;
const badAssetData = `0x${crypto.randomBytes(4).toString('hex')}${constants.NULL_ADDRESS}`;
await expect(libAssetData.revertIfInvalidAssetData(badAssetData).callAsync()).to.eventually.be.rejectedWith(
StringRevertError,
);
@@ -441,8 +422,7 @@ describe('LibAssetData', () => {
it('should return a balance of MAX_UINT256 if the the StaticCallProxy assetData contains data for a successful staticcall', async () => {
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData();
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const expectedResultHash = hexUtils.hash(hexUtils.leftPad(1));
const assetData = await libAssetData
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync();
@@ -452,8 +432,7 @@ describe('LibAssetData', () => {
it('should return a balance of 0 if the the StaticCallProxy assetData contains data for an unsuccessful staticcall', async () => {
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData();
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const expectedResultHash = hexUtils.hash(hexUtils.leftPad(1));
const assetData = await libAssetData
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync();

View File

@@ -1,161 +1,92 @@
import {
artifacts as proxyArtifacts,
ERC20ProxyContract,
ERC20Wrapper,
ERC721ProxyContract,
ERC721Wrapper,
MultiAssetProxyContract,
} from '@0x/contracts-asset-proxy';
import { ERC20ProxyContract } from '@0x/contracts-asset-proxy';
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
import {
chaiSetup,
constants,
orderHashUtils,
OrderFactory,
OrderStatus,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { ExchangeContract } from '@0x/contracts-exchange';
import { blockchainTests, constants, expect, orderHashUtils, OrderStatus } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { OrderTransferResults, SignedOrder } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import { BigNumber } from '@0x/utils';
import { artifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
import { Maker } from '../framework/actors/maker';
import { DeploymentManager } from '../framework/deployment_manager';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
let makerAddress: string;
// TODO(jalextowle): This can be cleaned up by using the actors more.
blockchainTests.resets('OrderValidationUtils/OrderTransferSimulatorUtils', env => {
let takerAddress: string;
let owner: string;
let erc20AssetData: string;
let erc20AssetData2: string;
let erc721AssetData: string;
let feeAssetData: string;
let maker: Maker;
let devUtils: DevUtilsContract;
let erc20Token: DummyERC20TokenContract;
let erc20Token2: DummyERC20TokenContract;
let feeErc20Token: DummyERC20TokenContract;
let erc721Token: DummyERC721TokenContract;
let exchange: ExchangeContract;
let devUtils: DevUtilsContract;
let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract;
let multiAssetProxy: MultiAssetProxyContract;
let exchange: ExchangeContract;
let deployment: DeploymentManager;
let erc20AssetData: string;
let erc20AssetData2: string;
let feeAssetData: string;
let erc721AssetData: string;
let signedOrder: SignedOrder;
let orderFactory: OrderFactory;
const tokenId = new BigNumber(123456789);
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
[takerAddress, owner] = await env.getAccountAddressesAsync();
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, makerAddress, takerAddress] = accounts.slice(0, 3));
const chainId = await providerUtils.getChainIdAsync(provider);
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 3,
numErc721TokensToDeploy: 1,
owner,
});
erc20Token = deployment.tokens.erc20[0];
erc20Token2 = deployment.tokens.erc20[1];
feeErc20Token = deployment.tokens.erc20[2];
erc20Proxy = deployment.assetProxies.erc20Proxy;
devUtils = deployment.devUtils;
exchange = deployment.exchange;
const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20Token2.address);
feeAssetData = assetDataUtils.encodeERC20AssetData(feeErc20Token.address);
const numDummyErc20ToDeploy = 3;
[erc20Token, erc20Token2, feeErc20Token] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy,
constants.DUMMY_TOKEN_DECIMALS,
);
erc20Proxy = await erc20Wrapper.deployProxyAsync();
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
erc721Proxy = await erc721Wrapper.deployProxyAsync();
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
provider,
txDefaults,
{},
new BigNumber(chainId),
);
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
proxyArtifacts.MultiAssetProxy,
provider,
txDefaults,
artifacts,
);
await exchange.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync({ from: owner });
await exchange.registerAssetProxy(erc721Proxy.address).awaitTransactionSuccessAsync({ from: owner });
await exchange.registerAssetProxy(multiAssetProxy.address).awaitTransactionSuccessAsync({ from: owner });
await erc20Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync({ from: owner });
await erc721Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync({ from: owner });
devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
artifacts.DevUtils,
provider,
txDefaults,
artifacts,
exchange.address,
);
feeAssetData = await devUtils.encodeERC20AssetData(feeErc20Token.address).callAsync();
erc20AssetData = await devUtils.encodeERC20AssetData(erc20Token.address).callAsync();
erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20Token2.address).callAsync();
erc721AssetData = await devUtils.encodeERC721AssetData(erc721Token.address, tokenId).callAsync();
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
makerAddress,
feeRecipientAddress: constants.NULL_ADDRESS,
makerAssetData: erc20AssetData,
takerAssetData: erc20AssetData2,
makerFeeAssetData: feeAssetData,
takerFeeAssetData: feeAssetData,
exchangeAddress: exchange.address,
chainId,
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
maker = new Maker({
name: 'Maker',
deployment,
orderConfig: {
makerAssetData: erc20AssetData,
takerAssetData: erc20AssetData2,
makerFeeAssetData: feeAssetData,
takerFeeAssetData: feeAssetData,
feeRecipientAddress: constants.NULL_ADDRESS,
},
});
const [tokenID] = await maker.configureERC721TokenAsync(deployment.tokens.erc721[0]);
erc721AssetData = assetDataUtils.encodeERC721AssetData(deployment.tokens.erc721[0].address, tokenID);
});
describe('getTransferableAssetAmount', () => {
it('should return the balance when balance < allowance', async () => {
const balance = new BigNumber(123);
const allowance = new BigNumber(456);
await erc20Token.setBalance(makerAddress, balance).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, balance).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, allowance).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const transferableAmount = await devUtils
.getTransferableAssetAmount(makerAddress, erc20AssetData)
.getTransferableAssetAmount(maker.address, erc20AssetData)
.callAsync();
expect(transferableAmount).to.bignumber.equal(balance);
});
it('should return the allowance when allowance < balance', async () => {
const balance = new BigNumber(456);
const allowance = new BigNumber(123);
await erc20Token.setBalance(makerAddress, balance).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, balance).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, allowance).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const transferableAmount = await devUtils
.getTransferableAssetAmount(makerAddress, erc20AssetData)
.getTransferableAssetAmount(maker.address, erc20AssetData)
.callAsync();
expect(transferableAmount).to.bignumber.equal(allowance);
});
@@ -165,23 +96,23 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
.callAsync();
const transferableAmount1 = new BigNumber(10);
const transferableAmount2 = new BigNumber(5);
await erc20Token.setBalance(makerAddress, transferableAmount1).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, transferableAmount1).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, transferableAmount1).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await erc20Token2.setBalance(makerAddress, transferableAmount2).awaitTransactionSuccessAsync();
await erc20Token2.setBalance(maker.address, transferableAmount2).awaitTransactionSuccessAsync();
await erc20Token2.approve(erc20Proxy.address, transferableAmount2).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const transferableAmount = await devUtils
.getTransferableAssetAmount(makerAddress, multiAssetData)
.getTransferableAssetAmount(maker.address, multiAssetData)
.callAsync();
expect(transferableAmount).to.bignumber.equal(transferableAmount2);
});
});
describe('getOrderRelevantState', () => {
beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
signedOrder = await maker.signOrderAsync({});
});
it('should return the correct orderInfo when the order is valid', async () => {
const [orderInfo] = await devUtils.getOrderRelevantState(signedOrder, signedOrder.signature).callAsync();
@@ -209,9 +140,9 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return a fillableTakerAssetAmount of 0 when fee balances/allowances are insufficient', async () => {
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -222,10 +153,44 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
const multiAssetData = await devUtils
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc20AssetData, erc20AssetData2])
.callAsync();
signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData });
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
signedOrder = await maker.signOrderAsync({ makerAssetData: multiAssetData });
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
.callAsync();
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return a fillableTakerAssetAmount of 0 when an erc721 asset is duplicated in the maker side of a multi-asset proxy order', async () => {
const multiAssetData = await devUtils
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc721AssetData, erc721AssetData])
.callAsync();
signedOrder = await maker.signOrderAsync({
makerAssetData: multiAssetData,
makerAssetAmount: new BigNumber(1),
takerAssetData: erc721AssetData,
takerAssetAmount: new BigNumber(1),
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
.callAsync();
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return a fillableTakerAssetAmount of 0 when an erc721 asset is duplicated in the taker side of a multi-asset proxy order', async () => {
const multiAssetData = await devUtils
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc721AssetData, erc721AssetData])
.callAsync();
signedOrder = await maker.signOrderAsync({
makerAssetData: erc721AssetData,
makerAssetAmount: new BigNumber(1),
takerAssetData: multiAssetData,
takerAssetAmount: new BigNumber(1),
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -233,16 +198,16 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return the correct fillableTakerAssetAmount when fee balances/allowances are partially sufficient', async () => {
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const divisor = 4;
await feeErc20Token
.setBalance(makerAddress, signedOrder.makerFee.dividedToIntegerBy(divisor))
.setBalance(maker.address, signedOrder.makerFee.dividedToIntegerBy(divisor))
.awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -254,14 +219,14 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
it('should return the correct fillableTakerAssetAmount when non-fee balances/allowances are partially sufficient', async () => {
const divisor = 4;
await erc20Token
.setBalance(makerAddress, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
.setBalance(maker.address, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
.awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -274,23 +239,23 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
const multiAssetData = await devUtils
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc20AssetData, erc20AssetData2])
.callAsync();
signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData });
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
signedOrder = await maker.signOrderAsync({ makerAssetData: multiAssetData });
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const divisor = 4;
await erc20Token2
.setBalance(makerAddress, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
.setBalance(maker.address, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
.awaitTransactionSuccessAsync();
await erc20Token2
.approve(erc20Proxy.address, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
.awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -300,9 +265,9 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
);
});
it('should return a fillableTakerAssetAmount of 0 when non-fee balances/allowances are insufficient', async () => {
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -310,13 +275,13 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return a fillableTakerAssetAmount equal to the takerAssetAmount when the order is unfilled and balances/allowances are sufficient', async () => {
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -324,16 +289,16 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
});
it('should return the correct fillableTakerAssetAmount when balances/allowances are partially sufficient and makerAsset=makerFeeAsset', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
signedOrder = await maker.signOrderAsync({
makerAssetData: feeAssetData,
makerAssetAmount: new BigNumber(10),
takerAssetAmount: new BigNumber(20),
makerFee: new BigNumber(40),
});
const transferableMakerAssetAmount = new BigNumber(10);
await feeErc20Token.setBalance(makerAddress, transferableMakerAssetAmount).awaitTransactionSuccessAsync();
await feeErc20Token.setBalance(maker.address, transferableMakerAssetAmount).awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, transferableMakerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const expectedFillableTakerAssetAmount = transferableMakerAssetAmount
.times(signedOrder.takerAssetAmount)
@@ -344,10 +309,10 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
expect(fillableTakerAssetAmount).to.bignumber.equal(expectedFillableTakerAssetAmount);
});
it('should return the correct fillabeTakerassetAmount when makerAsset balances/allowances are sufficient and there are no maker fees', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({ makerFee: constants.ZERO_AMOUNT });
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
signedOrder = await maker.signOrderAsync({ makerFee: constants.ZERO_AMOUNT });
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -355,13 +320,13 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
});
it('should return a fillableTakerAssetAmount when the remaining takerAssetAmount is less than the transferable amount', async () => {
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await erc20Token2.setBalance(takerAddress, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
@@ -375,7 +340,7 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
const takerAssetFillAmount = signedOrder.takerAssetAmount.dividedToIntegerBy(4);
await exchange
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
.awaitTransactionSuccessAsync({ from: takerAddress });
.awaitTransactionSuccessAsync({ from: takerAddress, value: DeploymentManager.protocolFee });
const [, fillableTakerAssetAmount] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
.callAsync();
@@ -384,15 +349,15 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
);
});
it('should return correct info even when there are no fees specified', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
signedOrder = await maker.signOrderAsync({
makerFee: new BigNumber(0),
takerFee: new BigNumber(0),
makerFeeAssetData: '0x',
takerFeeAssetData: '0x',
});
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [orderInfo, fillableTakerAssetAmount, isValidSignature] = await devUtils
.getOrderRelevantState(signedOrder, signedOrder.signature)
@@ -406,18 +371,21 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
});
describe('getOrderRelevantStates', async () => {
it('should return the correct information for multiple orders', async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
signedOrder = await maker.signOrderAsync();
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const signedOrder2 = await maker.signOrderAsync({
makerAssetData: erc721AssetData,
makerAssetAmount: new BigNumber(1),
});
const signedOrder2 = await orderFactory.newSignedOrderAsync({ makerAssetData: erc721AssetData });
const invalidSignature = '0x01';
await exchange.cancelOrder(signedOrder2).awaitTransactionSuccessAsync({ from: makerAddress });
await exchange.cancelOrder(signedOrder2).awaitTransactionSuccessAsync({ from: maker.address });
const [ordersInfo, fillableTakerAssetAmounts, isValidSignature] = await devUtils
.getOrderRelevantStates([signedOrder, signedOrder2], [signedOrder.signature, invalidSignature])
.callAsync();
@@ -435,7 +403,7 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
});
describe('getSimulatedOrderTransferResults', () => {
beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
signedOrder = await maker.signOrderAsync();
});
it('should return TakerAssetDataFailed if the takerAsset transfer fails', async () => {
const orderTransferResults = await devUtils
@@ -462,11 +430,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
from: takerAddress,
});
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: owner,
});
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const orderTransferResults = await devUtils
.getSimulatedOrderTransferResults(signedOrder, takerAddress, signedOrder.takerAssetAmount)
@@ -480,11 +448,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
from: takerAddress,
});
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: owner,
});
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
from: owner,
@@ -504,11 +472,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
from: takerAddress,
});
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: owner,
});
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
from: owner,
@@ -516,11 +484,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await feeErc20Token.approve(erc20Proxy.address, signedOrder.takerFee).awaitTransactionSuccessAsync({
from: takerAddress,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync({
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: owner,
});
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const orderTransferResults = await devUtils
.getSimulatedOrderTransferResults(signedOrder, takerAddress, signedOrder.takerAssetAmount)
@@ -536,11 +504,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
from: takerAddress,
});
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: owner,
});
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
from: owner,
@@ -548,11 +516,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await feeErc20Token.approve(erc20Proxy.address, signedOrder.takerFee).awaitTransactionSuccessAsync({
from: takerAddress,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync({
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: owner,
});
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const orderTransferResults = await devUtils
.getSimulatedOrderTransferResults(signedOrder, takerAddress, signedOrder.takerAssetAmount.dividedBy(2))
@@ -569,11 +537,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
from: takerAddress,
});
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: owner,
});
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
from: owner,
@@ -581,11 +549,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
await feeErc20Token.approve(erc20Proxy.address, signedOrder.takerFee).awaitTransactionSuccessAsync({
from: takerAddress,
});
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync({
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: owner,
});
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
from: makerAddress,
from: maker.address,
});
const [orderTransferResults1, orderTransferResults2] = await devUtils
.getSimulatedOrdersTransferResults(

View File

@@ -0,0 +1,278 @@
import { DydxBridgeActionType, dydxBridgeDataEncoder, TestDydxBridgeContract } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
import { SignedOrder } from '@0x/order-utils';
import { BigNumber, RevertError } from '@0x/utils';
import { DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import { deployDydxBridgeAsync } from '../bridges/deploy_dydx_bridge';
import { Actor } from '../framework/actors/base';
import { Maker } from '../framework/actors/maker';
import { Taker } from '../framework/actors/taker';
import { DeploymentManager } from '../framework/deployment_manager';
blockchainTests.resets('Exchange fills dydx orders', env => {
let testContract: TestDydxBridgeContract;
let makerToken: DummyERC20TokenContract;
let takerToken: DummyERC20TokenContract;
const marketId = new BigNumber(3);
const dydxConversionRateNumerator = toBaseUnitAmount(6);
const dydxConversionRateDenominator = toBaseUnitAmount(1);
let maker: Maker;
let taker: Taker;
let orderConfig: Partial<SignedOrder>;
let dydxBridgeProxyAssetData: string;
let deployment: DeploymentManager;
let testTokenAddress: string;
const defaultDepositAction = {
actionType: DydxBridgeActionType.Deposit as number,
accountId: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: dydxConversionRateNumerator,
conversionRateDenominator: dydxConversionRateDenominator,
};
const defaultWithdrawAction = {
actionType: DydxBridgeActionType.Withdraw as number,
accountId: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const bridgeData = {
accountNumbers: [new BigNumber(0)],
actions: [defaultDepositAction, defaultWithdrawAction],
};
before(async () => {
// Deploy contracts
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 2,
});
testContract = await deployDydxBridgeAsync(deployment, env);
const encodedBridgeData = dydxBridgeDataEncoder.encode({ bridgeData });
testTokenAddress = await testContract.getTestToken().callAsync();
dydxBridgeProxyAssetData = deployment.assetDataEncoder
.ERC20Bridge(testTokenAddress, testContract.address, encodedBridgeData)
.getABIEncodedTransactionData();
[makerToken, takerToken] = deployment.tokens.erc20;
// Configure default order parameters.
orderConfig = {
makerAssetAmount: toBaseUnitAmount(1),
takerAssetAmount: toBaseUnitAmount(1),
makerAssetData: deployment.assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(),
takerAssetData: deployment.assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(),
// Not important for this test.
feeRecipientAddress: constants.NULL_ADDRESS,
makerFeeAssetData: deployment.assetDataEncoder
.ERC20Token(makerToken.address)
.getABIEncodedTransactionData(),
takerFeeAssetData: deployment.assetDataEncoder
.ERC20Token(takerToken.address)
.getABIEncodedTransactionData(),
makerFee: toBaseUnitAmount(1),
takerFee: toBaseUnitAmount(1),
};
// Configure maker.
maker = new Maker({
name: 'Maker',
deployment,
orderConfig,
});
await maker.configureERC20TokenAsync(makerToken, deployment.assetProxies.erc20Proxy.address);
// Configure taker.
taker = new Taker({
name: 'Taker',
deployment,
});
await taker.configureERC20TokenAsync(takerToken, deployment.assetProxies.erc20Proxy.address);
});
after(async () => {
Actor.reset();
});
describe('fillOrder', () => {
interface DydxFillResults {
makerAssetFilledAmount: BigNumber;
takerAssetFilledAmount: BigNumber;
makerFeePaid: BigNumber;
takerFeePaid: BigNumber;
amountDepositedIntoDydx: BigNumber;
amountWithdrawnFromDydx: BigNumber;
}
const fillOrder = async (
signedOrder: SignedOrder,
customTakerAssetFillAmount?: BigNumber,
): Promise<DydxFillResults> => {
// Fill order
const takerAssetFillAmount =
customTakerAssetFillAmount !== undefined ? customTakerAssetFillAmount : signedOrder.takerAssetAmount;
const tx = await taker.fillOrderAsync(signedOrder, takerAssetFillAmount);
// Extract values from fill event.
// tslint:disable no-unnecessary-type-assertion
const fillEvent = _.find(tx.logs, log => {
return (log as any).event === 'Fill';
}) as LogWithDecodedArgs<DecodedLogArgs>;
// Extract amount deposited into dydx from maker.
const dydxDepositEvent = _.find(tx.logs, log => {
return (
(log as any).event === 'OperateAction' &&
(log as any).args.actionType === DydxBridgeActionType.Deposit
);
}) as LogWithDecodedArgs<DecodedLogArgs>;
// Extract amount withdrawn from dydx to taker.
const dydxWithdrawEvent = _.find(tx.logs, log => {
return (
(log as any).event === 'OperateAction' &&
(log as any).args.actionType === DydxBridgeActionType.Withdraw
);
}) as LogWithDecodedArgs<DecodedLogArgs>;
// Return values of interest for assertions.
return {
makerAssetFilledAmount: fillEvent.args.makerAssetFilledAmount,
takerAssetFilledAmount: fillEvent.args.takerAssetFilledAmount,
makerFeePaid: fillEvent.args.makerFeePaid,
takerFeePaid: fillEvent.args.takerFeePaid,
amountDepositedIntoDydx: dydxDepositEvent.args.amountValue,
amountWithdrawnFromDydx: dydxWithdrawEvent.args.amountValue,
};
};
it('should successfully fill a dydx order (DydxBridge used in makerAssetData)', async () => {
const signedOrder = await maker.signOrderAsync({
// Invert the dydx conversion rate when using the bridge in the makerAssetData.
makerAssetData: dydxBridgeProxyAssetData,
makerAssetAmount: dydxConversionRateDenominator,
takerAssetAmount: dydxConversionRateNumerator,
});
const dydxFillResults = await fillOrder(signedOrder);
expect(
dydxFillResults.makerAssetFilledAmount,
'makerAssetFilledAmount should equal amountWithdrawnFromDydx',
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
expect(
dydxFillResults.takerAssetFilledAmount,
'takerAssetFilledAmount should equal amountDepositedIntoDydx',
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
});
it('should successfully fill a dydx order (DydxBridge used in takerAssetData)', async () => {
const signedOrder = await maker.signOrderAsync({
// Match the dydx conversion rate when using the bridge in the takerAssetData.
takerAssetData: dydxBridgeProxyAssetData,
makerAssetAmount: dydxConversionRateNumerator,
takerAssetAmount: dydxConversionRateDenominator,
});
const dydxFillResults = await fillOrder(signedOrder);
expect(
dydxFillResults.makerAssetFilledAmount,
'makerAssetFilledAmount should equal amountDepositedIntoDydx',
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
expect(
dydxFillResults.takerAssetFilledAmount,
'takerAssetFilledAmount should equal amountWithdrawnFromDydx',
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
});
it('should successfully fill a dydx order (DydxBridge used in makerFeeAssetData)', async () => {
const signedOrder = await maker.signOrderAsync({
// Invert the dydx conversion rate when using the bridge in the makerFeeAssetData.
makerFeeAssetData: dydxBridgeProxyAssetData,
makerFee: dydxConversionRateDenominator,
takerFee: dydxConversionRateNumerator,
});
const dydxFillResults = await fillOrder(signedOrder);
expect(
dydxFillResults.makerFeePaid,
'makerFeePaid should equal amountWithdrawnFromDydx',
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
expect(
dydxFillResults.takerFeePaid,
'takerFeePaid should equal amountDepositedIntoDydx',
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
});
it('should successfully fill a dydx order (DydxBridge used in takerFeeAssetData)', async () => {
const signedOrder = await maker.signOrderAsync({
// Match the dydx conversion rate when using the bridge in the takerFeeAssetData.
takerFeeAssetData: dydxBridgeProxyAssetData,
makerFee: dydxConversionRateNumerator,
takerFee: dydxConversionRateDenominator,
});
const dydxFillResults = await fillOrder(signedOrder);
expect(
dydxFillResults.makerFeePaid,
'makerFeePaid should equal amountDepositedIntoDydx',
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
expect(
dydxFillResults.takerFeePaid,
'takerFeePaid should equal amountWithdrawnFromDydx',
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
});
it('should partially fill a dydx order (DydxBridge used in makerAssetData)', async () => {
const signedOrder = await maker.signOrderAsync({
// Invert the dydx conversion rate when using the bridge in the makerAssetData.
makerAssetData: dydxBridgeProxyAssetData,
makerAssetAmount: dydxConversionRateDenominator,
takerAssetAmount: dydxConversionRateNumerator,
});
const dydxFillResults = await fillOrder(signedOrder, signedOrder.takerAssetAmount.div(2));
expect(
dydxFillResults.makerAssetFilledAmount,
'makerAssetFilledAmount should equal amountWithdrawnFromDydx',
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
expect(
dydxFillResults.takerAssetFilledAmount,
'takerAssetFilledAmount should equal amountDepositedIntoDydx',
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
});
it('should revert in DydxBridge when there is a rounding error', async () => {
// This order will not trigger the rounding error in the Exchange,
// but will trigger one in the DydxBridge.
const badDepositAction = {
...defaultDepositAction,
conversionRateNumerator: new BigNumber(5318),
conversionRateDenominator: new BigNumber(47958),
};
const badBridgeData = {
accountNumbers: [new BigNumber(0)],
actions: [badDepositAction],
};
const encodedBridgeData = dydxBridgeDataEncoder.encode({ bridgeData: badBridgeData });
const badDydxBridgeProxyAssetData = deployment.assetDataEncoder
.ERC20Bridge(testTokenAddress, testContract.address, encodedBridgeData)
.getABIEncodedTransactionData();
const signedOrder = await maker.signOrderAsync({
makerAssetData: badDydxBridgeProxyAssetData,
makerAssetAmount: badDepositAction.conversionRateDenominator,
takerAssetAmount: badDepositAction.conversionRateNumerator,
});
const takerAssetFillAmount = new BigNumber(998);
// Compute expected error.
const target = takerAssetFillAmount
.multipliedBy(signedOrder.makerAssetAmount)
.dividedToIntegerBy(signedOrder.takerAssetAmount);
const expectedAssetProxyError = new LibMathRevertErrors.RoundingError(
signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount,
target,
);
// Call fillOrder and assert the rounding error generated by the DydxBridge.
const txPromise = taker.fillOrderAsync(signedOrder, takerAssetFillAmount);
let assetProxyError;
try {
await txPromise.catch();
} catch (e) {
assetProxyError = RevertError.decode(e.values.errorData);
}
expect(assetProxyError).to.deep.equal(expectedAssetProxyError);
});
});
});

View File

@@ -111,7 +111,7 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
await (owner as Actor).configureERC20TokenAsync(token as DummyERC20TokenContract);
balance = balanceStore.balances.erc20[owner.address][token.address] =
constants.INITIAL_ERC20_BALANCE;
return Pseudorandom.integer(balance.dividedToIntegerBy(2));
return Pseudorandom.integer(0, balance.dividedToIntegerBy(2));
}),
);
// Encode asset data

View File

@@ -6,7 +6,7 @@ import * as _ from 'lodash';
import { validCreateStakingPoolAssertion } from '../assertions/createStakingPool';
import { validDecreaseStakingPoolOperatorShareAssertion } from '../assertions/decreaseStakingPoolOperatorShare';
import { AssertionResult } from '../assertions/function_assertion';
import { Pseudorandom } from '../utils/pseudorandom';
import { Distributions, Pseudorandom } from '../utils/pseudorandom';
import { Actor, Constructor } from './base';
@@ -83,7 +83,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
private async *_validCreateStakingPool(): AsyncIterableIterator<AssertionResult> {
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, this.actor.simulationEnvironment!);
while (true) {
const operatorShare = Pseudorandom.integer(constants.PPM).toNumber();
const operatorShare = Pseudorandom.integer(
0,
constants.PPM,
Distributions.Kumaraswamy(0.2, 0.2),
).toNumber();
yield assertion.executeAsync([operatorShare, false], { from: this.actor.address });
}
}
@@ -96,7 +100,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
if (poolId === undefined) {
yield undefined;
} else {
const operatorShare = Pseudorandom.integer(stakingPools[poolId].operatorShare).toNumber();
const operatorShare = Pseudorandom.integer(
0,
stakingPools[poolId].operatorShare,
Distributions.Kumaraswamy(0.2, 0.2),
).toNumber();
yield assertion.executeAsync([poolId, operatorShare], { from: this.actor.address });
}
}

View File

@@ -79,7 +79,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
while (true) {
await balanceStore.updateErc20BalancesAsync();
const zrxBalance = balanceStore.balances.erc20[this.actor.address][zrx.address];
const amount = Pseudorandom.integer(zrxBalance);
const amount = Pseudorandom.integer(0, zrxBalance);
yield assertion.executeAsync([amount], { from: this.actor.address });
}
}
@@ -98,7 +98,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
undelegatedStake.currentEpochBalance,
undelegatedStake.nextEpochBalance,
);
const amount = Pseudorandom.integer(withdrawableStake);
const amount = Pseudorandom.integer(0, withdrawableStake);
yield assertion.executeAsync([amount], { from: this.actor.address });
}
}
@@ -118,7 +118,10 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
const fromStatus =
fromPoolId === undefined || stakingPools[fromPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
? StakeStatus.Undelegated
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
: (Pseudorandom.sample(
[StakeStatus.Undelegated, StakeStatus.Delegated],
[0.2, 0.8], // 20% chance of `Undelegated`, 80% chance of `Delegated`
) as StakeStatus);
const from = new StakeInfo(fromStatus, fromPoolId);
// Pick a random pool to move the stake to
@@ -128,7 +131,10 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
const toStatus =
toPoolId === undefined || stakingPools[toPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
? StakeStatus.Undelegated
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
: (Pseudorandom.sample(
[StakeStatus.Undelegated, StakeStatus.Delegated],
[0.2, 0.8], // 20% chance of `Undelegated`, 80% chance of `Delegated`
) as StakeStatus);
const to = new StakeInfo(toStatus, toPoolId);
// The next epoch balance of the `from` stake is the amount that can be moved
@@ -136,7 +142,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
from.status === StakeStatus.Undelegated
? this.stake[StakeStatus.Undelegated].nextEpochBalance
: this.stake[StakeStatus.Delegated][from.poolId].nextEpochBalance;
const amount = Pseudorandom.integer(moveableStake);
const amount = Pseudorandom.integer(0, moveableStake);
yield assertion.executeAsync([from, to, amount], { from: this.actor.address });
}

View File

@@ -75,12 +75,12 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
// Maker creates and signs a fillable order
const order = await maker.createFillableOrderAsync(this.actor);
// Taker fills the order by a random amount (up to the order's takerAssetAmount)
const fillAmount = Pseudorandom.integer(order.takerAssetAmount);
const fillAmount = Pseudorandom.integer(0, order.takerAssetAmount);
// Taker executes the fill with a random msg.value, so that sometimes the
// protocol fee is paid in ETH and other times it's paid in WETH.
yield assertion.executeAsync([order, fillAmount, order.signature], {
from: this.actor.address,
value: Pseudorandom.integer(DeploymentManager.protocolFee.times(2)),
value: Pseudorandom.integer(0, DeploymentManager.protocolFee.times(2)),
});
}
}

View File

@@ -2,6 +2,7 @@
"extends": ["@0x/tslint-config"],
"rules": {
"max-classes-per-file": false,
"no-non-null-assertion": false
"no-non-null-assertion": false,
"custom-no-magic-numbers": false
}
}

View File

@@ -13,11 +13,11 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
export function validDecreaseStakingPoolOperatorShareAssertion(
deployment: DeploymentManager,
pools: StakingPoolById,
): FunctionAssertion<[string, number], {}, void> {
): FunctionAssertion<[string, number], void, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[string, number], {}, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
after: async (_beforeInfo, result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
return new FunctionAssertion<[string, number], void, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
after: async (_beforeInfo: void, result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
// Ensure that the tx succeeded.
expect(result.success, `Error: ${result.data}`).to.be.true();

View File

@@ -8,7 +8,7 @@ import {
MultiAssetProxyContract,
StaticCallProxyContract,
} from '@0x/contracts-asset-proxy';
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as devUtilsArtifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as ERC1155Artifacts, ERC1155MintableContract } from '@0x/contracts-erc1155';
import { artifacts as ERC20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
import { artifacts as ERC721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
@@ -145,7 +145,7 @@ export class DeploymentManager {
exchangeArtifacts.Exchange,
environment.provider,
txDefaults,
{ ...ERC20Artifacts, ...exchangeArtifacts, ...stakingArtifacts },
{ ...ERC20Artifacts, ...exchangeArtifacts, ...stakingArtifacts, ...assetProxyArtifacts },
new BigNumber(chainId),
);
const governor = await ZeroExGovernorContract.deployFrom0xArtifactAsync(
@@ -196,7 +196,13 @@ export class DeploymentManager {
staking.stakingProxy,
]);
const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, environment.provider);
const devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
devUtilsArtifacts.DevUtils,
environment.provider,
environment.txDefaults,
devUtilsArtifacts,
exchange.address,
);
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, environment.provider);
// Construct the new instance and return it.

View File

@@ -1,54 +1,87 @@
import { Numberish } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as seedrandom from 'seedrandom';
class PRNGWrapper {
public readonly seed = process.env.SEED || Math.random().toString();
private readonly _rng = seedrandom(this.seed);
public readonly rng = seedrandom(this.seed);
/*
* Pseudorandom version of _.sample. Picks an element of the given array with uniform probability.
* Return undefined if the array is empty.
* Pseudorandom version of _.sample. Picks an element of the given array. If an array of weights
* is provided, elements of `arr` are weighted according to the value in the corresponding index
* of `weights`. Otherwise, the samples are chosen uniformly at random. Return undefined if the
* array is empty.
*/
public sample<T>(arr: T[]): T | undefined {
public sample<T>(arr: T[], weights?: number[]): T | undefined {
if (arr.length === 0) {
return undefined;
}
const index = Math.abs(this._rng.int32()) % arr.length;
let index: number;
if (weights !== undefined) {
const cdf = weights.map((_weight, i) => _.sum(weights.slice(0, i + 1)) / _.sum(weights));
const x = this.rng();
index = cdf.findIndex(value => value > x);
} else {
index = Math.abs(this.rng.int32()) % arr.length;
}
return arr[index];
}
/*
* Pseudorandom version of _.sampleSize. Returns an array of `n` samples from the given array
* (with replacement), chosen with uniform probability. Return undefined if the array is empty.
* (with replacement). If an array of weights is provided, elements of `arr` are weighted
* according to the value in the corresponding index of `weights`. Otherwise, the samples are
* chosen uniformly at random. Return undefined if the array is empty.
*/
public sampleSize<T>(arr: T[], n: number): T[] | undefined {
public sampleSize<T>(arr: T[], n: number, weights?: number[]): T[] | undefined {
if (arr.length === 0) {
return undefined;
}
const samples = [];
for (let i = 0; i < n; i++) {
samples.push(this.sample(arr) as T);
samples.push(this.sample(arr, weights) as T);
}
return samples;
}
// tslint:disable:unified-signatures
/*
* Pseudorandom version of getRandomPortion/getRandomInteger. If two arguments are provided,
* treats those arguments as the min and max (inclusive) of the desired range. If only one
* argument is provided, picks an integer between 0 and the argument.
* Pseudorandom version of getRandomPortion/getRandomInteger. If no distribution is provided,
* samples an integer between the min and max uniformly at random. If a distribution is
* provided, samples an integer from the given distribution (assumed to be defined on the
* interval [0, 1]) scaled to [min, max].
*/
public integer(max: Numberish): BigNumber;
public integer(min: Numberish, max: Numberish): BigNumber;
public integer(a: Numberish, b?: Numberish): BigNumber {
if (b === undefined) {
return new BigNumber(this._rng()).times(a).integerValue(BigNumber.ROUND_HALF_UP);
} else {
const range = new BigNumber(b).minus(a);
return this.integer(range).plus(a);
}
public integer(min: Numberish, max: Numberish, distribution: () => Numberish = this.rng): BigNumber {
const range = new BigNumber(max).minus(min);
return new BigNumber(distribution())
.times(range)
.integerValue(BigNumber.ROUND_HALF_UP)
.plus(min);
}
/*
* Returns a function that produces samples from the Kumaraswamy distribution parameterized by
* the given alpha and beta. The Kumaraswamy distribution is like the beta distribution, but
* with a nice closed form. More info:
* https://en.wikipedia.org/wiki/Kumaraswamy_distribution
* https://www.johndcook.com/blog/2009/11/24/kumaraswamy-distribution/
* Alpha and beta default to 0.2, so that the distribution favors the extremes of the domain.
* The PDF for alpha=0.2, beta=0.2:
* https://www.wolframalpha.com/input/?i=0.2*0.2*x%5E%280.2-1%29*%281-x%5E0.2%29%5E%280.2-1%29+from+0+to+1
*/
public kumaraswamy(this: PRNGWrapper, alpha: Numberish = 0.2, beta: Numberish = 0.2): () => BigNumber {
const ONE = new BigNumber(1);
return () => {
const u = new BigNumber(this.rng()).modulo(ONE); // u ~ Uniform(0, 1)
// Evaluate the inverse CDF at `u` to obtain a sample from Kumaraswamy(alpha, beta)
return ONE.minus(ONE.minus(u).exponentiatedBy(ONE.dividedBy(beta))).exponentiatedBy(ONE.dividedBy(alpha));
};
}
}
export const Pseudorandom = new PRNGWrapper();
export const Distributions = {
Uniform: Pseudorandom.rng,
Kumaraswamy: Pseudorandom.kumaraswamy.bind(Pseudorandom),
};

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import { PoolOperator } from '../framework/actors/pool_operator';
@@ -14,12 +15,14 @@ export class PoolManagementSimulation extends Simulation {
const { actors } = this.environment;
const operators = filterActorsByRole(actors, PoolOperator);
const actions = [
...operators.map(operator => operator.simulationActions.validCreateStakingPool),
...operators.map(operator => operator.simulationActions.validDecreaseStakingPoolOperatorShare),
];
const [actions, weights] = _.unzip([
// 40% chance of executing validCreateStakingPool assertion for a random operator
...operators.map(operator => [operator.simulationActions.validCreateStakingPool, 0.4]),
// 60% chance of executing validDecreaseStakingPoolOperatorShare for a random operator
...operators.map(operator => [operator.simulationActions.validDecreaseStakingPoolOperatorShare, 0.6]),
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import { MakerTaker } from '../framework/actors/hybrids';
@@ -22,14 +23,17 @@ export class PoolMembershipSimulation extends Simulation {
const poolManagement = new PoolManagementSimulation(this.environment);
const actions = [
...makers.map(maker => maker.simulationActions.validJoinStakingPool),
...takers.map(taker => taker.simulationActions.validFillOrder),
poolManagement.generator,
];
const [actions, weights] = _.unzip([
// 20% chance of executing validJoinStakingPool for a random maker
...makers.map(maker => [maker.simulationActions.validJoinStakingPool, 0.2 / makers.length]),
// 60% chance of executing validFillOrder for a random taker
...takers.map(taker => [taker.simulationActions.validFillOrder, 0.6 / takers.length]),
// 20% chance of executing an assertion generated from the pool management simulation
[poolManagement.generator, 0.2],
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import { StakerOperator } from '../framework/actors/hybrids';
@@ -20,14 +21,19 @@ export class StakeManagementSimulation extends Simulation {
const poolManagement = new PoolManagementSimulation(this.environment);
const actions = [
...stakers.map(staker => staker.simulationActions.validStake),
...stakers.map(staker => staker.simulationActions.validUnstake),
...stakers.map(staker => staker.simulationActions.validMoveStake),
poolManagement.generator,
];
const [actions, weights] = _.unzip([
// 30% chance of executing validStake for a random staker
...stakers.map(staker => [staker.simulationActions.validStake, 0.3 / stakers.length]),
// 20% chance of executing validUnstake for a random staker
...stakers.map(staker => [staker.simulationActions.validUnstake, 0.2 / stakers.length]),
// 30% chance of executing validMoveStake for a random staker
...stakers.map(staker => [staker.simulationActions.validMoveStake, 0.3 / stakers.length]),
// 20% chance of executing an assertion generated from the pool management simulation
[poolManagement.generator, 0.2],
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import {
@@ -32,15 +33,20 @@ export class StakingRewardsSimulation extends Simulation {
const poolMembership = new PoolMembershipSimulation(this.environment);
const stakeManagement = new StakeManagementSimulation(this.environment);
const actions = [
...stakers.map(staker => staker.simulationActions.validWithdrawDelegatorRewards),
...keepers.map(keeper => keeper.simulationActions.validFinalizePool),
...keepers.map(keeper => keeper.simulationActions.validEndEpoch),
poolMembership.generator,
stakeManagement.generator,
];
const [actions, weights] = _.unzip([
// 10% chance of executing validWithdrawDelegatorRewards for a random staker
...stakers.map(staker => [staker.simulationActions.validWithdrawDelegatorRewards, 0.1 / stakers.length]),
// 10% chance of executing validFinalizePool for a random keeper
...keepers.map(keeper => [keeper.simulationActions.validFinalizePool, 0.1 / keepers.length]),
// 10% chance of executing validEndEpoch for a random keeper
...keepers.map(keeper => [keeper.simulationActions.validEndEpoch, 0.1 / keepers.length]),
// 50% chance of executing an assertion generated from the pool membership simulation
[poolMembership.generator, 0.5],
// 20% chance of executing an assertion generated from the stake management simulation
[stakeManagement.generator, 0.2],
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -1,6 +1,7 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"no-invalid-this": false
"no-invalid-this": false,
"custom-no-magic-numbers": false
}
}

View File

@@ -1,12 +1,19 @@
import { ContractWrappers } from '@0x/contract-wrappers';
import { ERC20ProxyContract, MultiAssetProxyContract } from '@0x/contracts-asset-proxy';
import { StakingProxyContract, ZrxVaultContract } from '@0x/contracts-staking';
import { blockchainTests, describe, expect } from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { contractAddresses, contractWrappers } from './mainnet_fork_utils';
import { contractAddresses, getContractwrappers } from './mainnet_fork_utils';
blockchainTests.fork.resets('Mainnet configs tests', env => {
let contractWrappers: ContractWrappers;
before(async () => {
contractWrappers = getContractwrappers(env.provider);
});
blockchainTests.resets.fork('Mainnet configs tests', env => {
describe('Exchange', () => {
it('should be owned by the ZeroExGovernor ', async () => {
const owner = await contractWrappers.exchange.owner().callAsync();

View File

@@ -1,9 +1,14 @@
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { ContractWrappers } from '@0x/contract-wrappers';
import { provider } from '@0x/contracts-test-utils';
import { Web3ProviderEngine } from '@0x/dev-utils';
const chainId = 1;
const contractAddresses = getContractAddressesForChainOrThrow(chainId);
const contractWrappers = new ContractWrappers(provider, { chainId, contractAddresses });
export const dydxAccountOwner = '0xeb58c2caa96f39626dcceb74fdbb7a9a8b54ec18';
export const contractAddresses = getContractAddressesForChainOrThrow(chainId);
export { contractAddresses, contractWrappers };
/**
* Create contract wrappers for the mainnet given a mainnet/forked provider.
*/
export function getContractwrappers(provider: Web3ProviderEngine): ContractWrappers {
return new ContractWrappers(provider, { chainId, contractAddresses });
}

View File

@@ -3,9 +3,11 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/test_dydx_user';
export * from '../test/generated-wrappers/test_eth2_dai';
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
export * from '../test/generated-wrappers/test_framework';
export * from '../test/generated-wrappers/test_mainnet_aggregator_fills';
export * from '../test/generated-wrappers/test_uniswap_bridge';
export * from '../test/generated-wrappers/test_uniswap_exchange';
export * from '../test/generated-wrappers/test_uniswap_exchange_factory';

View File

@@ -4,9 +4,11 @@
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/TestFramework.json",
"test/generated-artifacts/TestDydxUser.json",
"test/generated-artifacts/TestEth2Dai.json",
"test/generated-artifacts/TestEth2DaiBridge.json",
"test/generated-artifacts/TestFramework.json",
"test/generated-artifacts/TestMainnetAggregatorFills.json",
"test/generated-artifacts/TestUniswapBridge.json",
"test/generated-artifacts/TestUniswapExchange.json",
"test/generated-artifacts/TestUniswapExchangeFactory.json"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "4.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "4.0.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.3 - _January 6, 2020_
* Dependencies updated
## v4.0.2 - _December 17, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-multisig",
"version": "4.0.2",
"version": "4.0.3",
"engines": {
"node": ">=6.12"
},
@@ -49,18 +49,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/multisig/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.2",
"@0x/contracts-asset-proxy": "^3.0.2",
"@0x/contracts-erc20": "^3.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/abi-gen": "^5.0.3",
"@0x/contracts-asset-proxy": "^3.1.0",
"@0x/contracts-erc20": "^3.0.3",
"@0x/contracts-gen": "^2.0.3",
"@0x/contracts-test-utils": "^5.1.0",
"@0x/contracts-utils": "^4.0.3",
"@0x/dev-utils": "^3.1.0",
"@0x/sol-compiler": "^4.0.3",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
"@0x/utils": "^5.1.1",
"@0x/web3-wrapper": "^7.0.2",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -78,7 +78,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.2",
"@0x/base-contract": "^6.0.3",
"@0x/typescript-typings": "^5.0.1",
"ethereum-types": "^3.0.0"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1578272714,
"version": "2.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1576540892,
"version": "2.0.2",

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