Compare commits

..

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

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

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

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

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

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

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

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

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

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

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

* fixed testing

* refactored aggregate.ts and embeded into asset-swapper

* added adjusted rates for taker and maker fees

* remove PrunedSignedOrders

* updated contract-addresses and addressed some other todos

* streamlined logic

* patched in lawrences changes

* renamed aggregator utils and removed market_utils.ts

* added ack heartbeats

* fixed bug

* patches

* added dummy order things

* Dummy with valid sig

* Tweak gas price calculation to wei

* added test coverage and fixed bugs

* fixed migrations

* Fix CHANGELOGs and types export

* Deploy latest ERC20BridgeSampler on Mainnet

* `@0x/types` Revert CHANGELOG.

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

* made protocol fee multiplier async

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

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

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

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

View File

@@ -5,6 +5,16 @@ 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
## v3.0.1 - _December 9, 2019_
* Dependencies updated
@@ -26,6 +36,7 @@ CHANGELOG
## v2.3.0-beta.4 - _December 2, 2019_
* Implement `KyberBridge`. (#2352)
* Implement `DydxBridge`. (#2365)
## v2.3.0-beta.3 - _November 20, 2019_

View File

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

View File

@@ -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

@@ -0,0 +1,89 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
interface IDydx {
/// @dev Represents the unique key that specifies an account
struct AccountInfo {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
/// @dev Arguments that are passed to Solo in an ordered list as part of a single operation.
/// Each ActionArgs has an actionType which specifies which action struct that this data will be
/// parsed into before being processed.
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
enum AssetDenomination {
Wei, // the amount is denominated in wei
Par // the amount is denominated in par
}
enum AssetReference {
Delta, // the amount is given as a delta from the current value
Target // the amount is given as an exact number to end up at
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
/// @dev The main entry-point to Solo that allows users and contracts to manage accounts.
/// Take one or more actions on one or more accounts. The msg.sender must be the owner or
/// operator of all accounts except for those being liquidated, vaporized, or traded with.
/// One call to operate() is considered a singular "operation". Account collateralization is
/// ensured only after the completion of the entire operation.
/// @param accounts A list of all accounts that will be used in this operation. Cannot contain
/// duplicates. In each action, the relevant account will be referred-to by its
/// index in the list.
/// @param actions An ordered list of all actions that will be taken in this operation. The
/// actions will be processed in order.
function operate(
AccountInfo[] calldata accounts,
ActionArgs[] calldata actions
)
external;
}

View File

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

View File

@@ -0,0 +1,191 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "../src/bridges/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,
DydxBridge
{
address private constant ALWAYS_REVERT_ADDRESS = address(1);
address private _testTokenAddress;
bool private _shouldRevertOnOperate;
event OperateAccount(
address owner,
uint256 number
);
event OperateAction(
ActionType actionType,
uint256 accountId,
bool amountSign,
AssetDenomination amountDenomination,
AssetReference amountRef,
uint256 amountValue,
uint256 primaryMarketId,
uint256 secondaryMarketId,
address otherAddress,
uint256 otherAccountId,
bytes data
);
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(
AccountInfo[] calldata accounts,
ActionArgs[] calldata actions
)
external
{
if (_shouldRevertOnOperate) {
revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE");
}
for (uint i = 0; i < accounts.length; ++i) {
emit OperateAccount(
accounts[i].owner,
accounts[i].number
);
}
for (uint i = 0; i < actions.length; ++i) {
emit OperateAction(
actions[i].actionType,
actions[i].accountId,
actions[i].amount.sign,
actions[i].amount.denomination,
actions[i].amount.ref,
actions[i].amount.value,
actions[i].primaryMarketId,
actions[i].secondaryMarketId,
actions[i].otherAddress,
actions[i].otherAccountId,
actions[i].data
);
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
returns (address)
{
return address(this);
}
/// @dev overrides `_getERC20BridgeProxyAddress()` from `DeploymentConstants` for testing.
function _getERC20BridgeProxyAddress()
internal
view
returns (address)
{
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
}
}

View File

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

View File

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

View File

@@ -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

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

View File

@@ -4,6 +4,7 @@
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/chai_bridge';
export * from '../generated-wrappers/dydx_bridge';
export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_bridge_proxy';
export * from '../generated-wrappers/erc20_proxy';
@@ -11,8 +12,27 @@ export * from '../generated-wrappers/erc721_proxy';
export * from '../generated-wrappers/eth2_dai_bridge';
export * from '../generated-wrappers/i_asset_data';
export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
export * from '../generated-wrappers/i_authorizable';
export * from '../generated-wrappers/i_chai';
export * from '../generated-wrappers/i_dydx';
export * from '../generated-wrappers/i_dydx_bridge';
export * from '../generated-wrappers/i_erc20_bridge';
export * from '../generated-wrappers/i_eth2_dai';
export * from '../generated-wrappers/i_kyber_network_proxy';
export * from '../generated-wrappers/i_uniswap_exchange';
export * from '../generated-wrappers/i_uniswap_exchange_factory';
export * from '../generated-wrappers/kyber_bridge';
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../generated-wrappers/mixin_authorizable';
export * from '../generated-wrappers/multi_asset_proxy';
export * from '../generated-wrappers/ownable';
export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_chai_bridge';
export * from '../generated-wrappers/test_dydx_bridge';
export * from '../generated-wrappers/test_erc20_bridge';
export * from '../generated-wrappers/test_eth2_dai_bridge';
export * from '../generated-wrappers/test_kyber_bridge';
export * from '../generated-wrappers/test_static_call_target';
export * from '../generated-wrappers/test_uniswap_bridge';
export * from '../generated-wrappers/uniswap_bridge';

View File

@@ -6,6 +6,7 @@
import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json';
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.json';
@@ -16,6 +17,8 @@ import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
import * as IChai from '../test/generated-artifacts/IChai.json';
import * as IDydx from '../test/generated-artifacts/IDydx.json';
import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.json';
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
@@ -28,6 +31,7 @@ import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.js
import * as Ownable from '../test/generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json';
import * as TestDydxBridge from '../test/generated-artifacts/TestDydxBridge.json';
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json';
@@ -45,6 +49,7 @@ export const artifacts = {
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact,
DydxBridge: DydxBridge as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
@@ -53,12 +58,15 @@ export const artifacts = {
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IChai: IChai as ContractArtifact,
IDydx: IDydx as ContractArtifact,
IDydxBridge: IDydxBridge as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
IUniswapExchange: IUniswapExchange as ContractArtifact,
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
TestChaiBridge: TestChaiBridge as ContractArtifact,
TestDydxBridge: TestDydxBridge as ContractArtifact,
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestKyberBridge: TestKyberBridge as ContractArtifact,

View File

@@ -0,0 +1,399 @@
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
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';
blockchainTests.resets('DydxBridge unit tests', env => {
const defaultAccountNumber = new BigNumber(1);
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;
before(async () => {
// Get accounts
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
[owner, authorized, accountOwner, receiver] = accounts;
// Deploy dydx bridge
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestDydxBridge,
env.provider,
env.txDefaults,
artifacts,
[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()', () => {
const callBridgeTransferFrom = async (
from: string,
to: string,
amount: BigNumber,
bridgeData: DydxBridgeData,
sender: string,
): Promise<string> => {
const returnValue = await testContract
.bridgeTransferFrom(
constants.NULL_ADDRESS,
from,
to,
amount,
dydxBridgeDataEncoder.encode({ bridgeData }),
)
.callAsync({ from: sender });
return returnValue;
};
const executeBridgeTransferFromAndVerifyEvents = async (
from: string,
to: string,
amount: BigNumber,
bridgeData: DydxBridgeData,
sender: string,
): Promise<void> => {
// Execute transaction.
const txReceipt = await testContract
.bridgeTransferFrom(
constants.NULL_ADDRESS,
from,
to,
amount,
dydxBridgeDataEncoder.encode({ bridgeData }),
)
.awaitTransactionSuccessAsync({ from: sender });
// Verify `OperateAccount` event.
const expectedOperateAccountEvents = [];
for (const accountNumber of bridgeData.accountNumbers) {
expectedOperateAccountEvents.push({
owner: accountOwner,
number: accountNumber,
});
}
verifyEventsFromLogs(txReceipt.logs, expectedOperateAccountEvents, TestDydxBridgeEvents.OperateAccount);
// Verify `OperateAction` event.
const weiDenomination = 0;
const deltaAmountRef = 0;
const expectedOperateActionEvents = [];
for (const action of bridgeData.actions) {
expectedOperateActionEvents.push({
actionType: action.actionType as number,
accountId: action.accountId,
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
amountDenomination: weiDenomination,
amountRef: deltaAmountRef,
amountValue: action.conversionRateDenominator.gt(0)
? amount
.times(action.conversionRateNumerator)
.dividedToIntegerBy(action.conversionRateDenominator)
: amount,
primaryMarketId: marketId,
secondaryMarketId: constants.ZERO_AMOUNT,
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 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 executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultWithdrawAction],
};
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 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 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 executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when scaling the `amount` to deposit', async () => {
const conversionRateNumerator = new BigNumber(1);
const conversionRateDenominator = new BigNumber(2);
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [
defaultWithdrawAction,
{
...defaultDepositAction,
conversionRateNumerator,
conversionRateDenominator,
},
],
};
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('succeeds when scaling the `amount` to withdraw', async () => {
const conversionRateNumerator = new BigNumber(1);
const conversionRateDenominator = new BigNumber(2);
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [
defaultDepositAction,
{
...defaultWithdrawAction,
conversionRateNumerator,
conversionRateDenominator,
},
],
};
await executeBridgeTransferFromAndVerifyEvents(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
});
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
const callBridgeTransferFromPromise = callBridgeTransferFrom(
accountOwner,
receiver,
defaultAmount,
bridgeData,
notAuthorized,
);
const expectedError = RevertReason.DydxBridgeOnlyCallableByErc20BridgeProxy;
return expect(callBridgeTransferFromPromise).to.revertWith(expectedError);
});
it('should return magic bytes if call succeeds', async () => {
const bridgeData = {
accountNumbers: [defaultAccountNumber],
actions: [defaultDepositAction],
};
const returnValue = await callBridgeTransferFrom(
accountOwner,
receiver,
defaultAmount,
bridgeData,
authorized,
);
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
});
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

@@ -4,6 +4,7 @@
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/chai_bridge';
export * from '../test/generated-wrappers/dydx_bridge';
export * from '../test/generated-wrappers/erc1155_proxy';
export * from '../test/generated-wrappers/erc20_bridge_proxy';
export * from '../test/generated-wrappers/erc20_proxy';
@@ -14,6 +15,8 @@ export * from '../test/generated-wrappers/i_asset_proxy';
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/i_authorizable';
export * from '../test/generated-wrappers/i_chai';
export * from '../test/generated-wrappers/i_dydx';
export * from '../test/generated-wrappers/i_dydx_bridge';
export * from '../test/generated-wrappers/i_erc20_bridge';
export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network_proxy';
@@ -26,6 +29,7 @@ export * from '../test/generated-wrappers/multi_asset_proxy';
export * from '../test/generated-wrappers/ownable';
export * from '../test/generated-wrappers/static_call_proxy';
export * from '../test/generated-wrappers/test_chai_bridge';
export * from '../test/generated-wrappers/test_dydx_bridge';
export * from '../test/generated-wrappers/test_erc20_bridge';
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
export * from '../test/generated-wrappers/test_kyber_bridge';

View File

@@ -4,6 +4,7 @@
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/ChaiBridge.json",
"generated-artifacts/DydxBridge.json",
"generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20BridgeProxy.json",
"generated-artifacts/ERC20Proxy.json",
@@ -11,12 +12,32 @@
"generated-artifacts/Eth2DaiBridge.json",
"generated-artifacts/IAssetData.json",
"generated-artifacts/IAssetProxy.json",
"generated-artifacts/IAssetProxyDispatcher.json",
"generated-artifacts/IAuthorizable.json",
"generated-artifacts/IChai.json",
"generated-artifacts/IDydx.json",
"generated-artifacts/IDydxBridge.json",
"generated-artifacts/IERC20Bridge.json",
"generated-artifacts/IEth2Dai.json",
"generated-artifacts/IKyberNetworkProxy.json",
"generated-artifacts/IUniswapExchange.json",
"generated-artifacts/IUniswapExchangeFactory.json",
"generated-artifacts/KyberBridge.json",
"generated-artifacts/MixinAssetProxyDispatcher.json",
"generated-artifacts/MixinAuthorizable.json",
"generated-artifacts/MultiAssetProxy.json",
"generated-artifacts/Ownable.json",
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestChaiBridge.json",
"generated-artifacts/TestDydxBridge.json",
"generated-artifacts/TestERC20Bridge.json",
"generated-artifacts/TestEth2DaiBridge.json",
"generated-artifacts/TestKyberBridge.json",
"generated-artifacts/TestStaticCallTarget.json",
"generated-artifacts/TestUniswapBridge.json",
"generated-artifacts/UniswapBridge.json",
"test/generated-artifacts/ChaiBridge.json",
"test/generated-artifacts/DydxBridge.json",
"test/generated-artifacts/ERC1155Proxy.json",
"test/generated-artifacts/ERC20BridgeProxy.json",
"test/generated-artifacts/ERC20Proxy.json",
@@ -27,6 +48,8 @@
"test/generated-artifacts/IAssetProxyDispatcher.json",
"test/generated-artifacts/IAuthorizable.json",
"test/generated-artifacts/IChai.json",
"test/generated-artifacts/IDydx.json",
"test/generated-artifacts/IDydxBridge.json",
"test/generated-artifacts/IERC20Bridge.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetworkProxy.json",
@@ -39,6 +62,7 @@
"test/generated-artifacts/Ownable.json",
"test/generated-artifacts/StaticCallProxy.json",
"test/generated-artifacts/TestChaiBridge.json",
"test/generated-artifacts/TestDydxBridge.json",
"test/generated-artifacts/TestERC20Bridge.json",
"test/generated-artifacts/TestEth2DaiBridge.json",
"test/generated-artifacts/TestKyberBridge.json",

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v3.0.1 - _December 9, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "3.0.1",
"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.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.1",
"@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.1",
"@0x/base-contract": "^6.0.1",
"@0x/contract-addresses": "^4.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/json-schemas": "^5.0.1",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@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.2",
"ethereum-types": "^3.0.0",
"http-status-codes": "^1.3.2"
},

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v1.0.1 - _December 9, 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.1",
"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.1",
"@0x/assert": "^3.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/sol-compiler": "^4.0.1",
"@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.1"
"@0x/base-contract": "^6.0.3"
},
"publishConfig": {
"access": "public"

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v2.0.1 - _December 9, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "2.0.1",
"version": "2.0.3",
"engines": {
"node": ">=6.12"
},
@@ -52,15 +52,15 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -80,10 +80,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@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,4 +1,24 @@
[
{
"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": 2395
}
],
"timestamp": 1576540892
},
{
"timestamp": 1575931811,
"version": "1.0.1",

View File

@@ -5,6 +5,14 @@ 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. (#2395)
## v1.0.1 - _December 9, 2019_
* Dependencies updated

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20-bridge-sampler",
"version": "1.0.1",
"version": "1.0.3",
"engines": {
"node": ">=6.12"
},
@@ -38,7 +38,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(DeploymentConstants|ERC20BridgeSampler|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
},
"repository": {
"type": "git",
@@ -50,18 +50,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.1",
"@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.1",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/base-contract": "^6.0.3",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@0x/utils": "^5.1.2",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},

View File

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

View File

@@ -6,7 +6,7 @@ import {
getRandomPortion,
randomAddress,
} from '@0x/contracts-test-utils';
import { Order, OrderInfo } from '@0x/types';
import { Order } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
@@ -30,12 +30,13 @@ blockchainTests('erc20-bridge-sampler', env => {
const INVALID_ASSET_DATA = hexUtils.random(37);
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
const EMPTY_ORDERS_ERROR = 'EMPTY_ORDERS';
const UNSUPPORTED_ASSET_PROXY_ERROR = 'UNSUPPORTED_ASSET_PROXY';
const INVALID_ASSET_DATA_ERROR = 'INVALID_ASSET_DATA';
const UNSUPPORTED_UNISWAP_EXCHANGE_ERROR = 'UNSUPPORTED_UNISWAP_EXCHANGE';
const UNSUPPORTED_SOURCE_ERROR = 'UNSUPPORTED_SOURCE';
const INVALID_TOKEN_PAIR_ERROR = 'INVALID_TOKEN_PAIR';
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
const UNSUPPORTED_SOURCE_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_SOURCE';
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
@@ -192,13 +193,22 @@ blockchainTests('erc20-bridge-sampler', env => {
return quotes;
}
function getDeterministicOrderInfo(order: Order): OrderInfo {
const hash = getPackedHash(hexUtils.leftPad(order.salt, 32));
return {
orderHash: hash,
orderStatus: new BigNumber(hash).mod(255).toNumber(),
orderTakerAssetFilledAmount: new BigNumber(hash).mod(order.takerAssetAmount),
};
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
const orderStatus = new BigNumber(hash).mod(255).toNumber();
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
if (orderStatus !== 3 || !isValidSignature) {
return constants.ZERO_AMOUNT;
}
return order.takerAssetAmount.minus(new BigNumber(hash).mod(order.takerAssetAmount));
}
function getDeterministicFillableMakerAssetAmount(order: Order): BigNumber {
const takerAmount = getDeterministicFillableTakerAssetAmount(order);
return order.makerAssetAmount
.times(takerAmount)
.div(order.takerAssetAmount)
.integerValue(BigNumber.ROUND_DOWN);
}
function getERC20AssetData(tokenAddress: string): string {
@@ -238,57 +248,115 @@ blockchainTests('erc20-bridge-sampler', env => {
return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
}
describe('queryOrders()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
async function enableFailTriggerAsync(): Promise<void> {
await testContract.enableFailTrigger().awaitTransactionSuccessAsync({ value: 1 });
}
it('returns the results of `getOrderInfo()` for each order', async () => {
describe('getOrderFillableTakerAssetAmounts()', () => {
it('returns the expected amount for each order', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expected = orders.map(getDeterministicOrderInfo);
const actual = await testContract.queryOrders(orders).callAsync();
const signatures: string[] = _.times(orders.length, hexUtils.random);
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq(expected);
});
it('returns empty for no orders', async () => {
const actual = await testContract.queryOrders([]).callAsync();
const actual = await testContract.getOrderFillableTakerAssetAmounts([], []).callAsync();
expect(actual).to.deep.eq([]);
});
it('returns zero for an order with zero maker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with zero taker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with an empty signature', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
const signatures: string[] = _.times(orders.length, () => constants.NULL_BYTES);
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
});
describe('getOrderFillableMakerAssetAmounts()', () => {
it('returns the expected amount for each order', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const signatures: string[] = _.times(orders.length, hexUtils.random);
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq(expected);
});
it('returns empty for no orders', async () => {
const actual = await testContract.getOrderFillableMakerAssetAmounts([], []).callAsync();
expect(actual).to.deep.eq([]);
});
it('returns zero for an order with zero maker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with zero taker asset amount', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
const signatures: string[] = _.times(orders.length, hexUtils.random);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
it('returns zero for an order with an empty signature', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
const signatures: string[] = _.times(orders.length, () => constants.NULL_BYTES);
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
});
});
describe('queryOrdersAndSampleSells()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns the results of `getOrderInfo()` for each order', async () => {
it('returns expected fillable amounts for each order', async () => {
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
const [orderInfos] = await testContract
.queryOrdersAndSampleSells(orders, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedOrderInfos);
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
SELL_SOURCES.map(n => allSources[n]),
sampleAmounts,
)
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleSells([], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
@@ -296,7 +364,8 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
ORDERS,
SIGNATURES,
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(TAKER_TOKEN),
)
@@ -307,10 +376,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -321,10 +391,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -335,10 +406,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -349,10 +421,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
@@ -362,39 +435,34 @@ blockchainTests('erc20-bridge-sampler', env => {
});
describe('queryOrdersAndSampleBuys()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns the results of `getOrderInfo()` for each order', async () => {
it('returns expected fillable amounts for each order', async () => {
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
const [orderInfos] = await testContract
.queryOrdersAndSampleBuys(orders, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedOrderInfos);
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
BUY_SOURCES.map(n => allSources[n]),
sampleAmounts,
)
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleBuys([], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
@@ -402,7 +470,8 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
ORDERS,
SIGNATURES,
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(MAKER_TOKEN),
)
@@ -414,7 +483,8 @@ blockchainTests('erc20-bridge-sampler', env => {
const sources = [...BUY_SOURCES, 'Kyber'];
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
ORDERS,
SIGNATURES,
sources.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -425,10 +495,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -439,10 +510,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -453,10 +525,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -467,10 +540,11 @@ blockchainTests('erc20-bridge-sampler', env => {
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
@@ -480,9 +554,6 @@ blockchainTests('erc20-bridge-sampler', env => {
});
describe('sampleSells()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -528,9 +599,6 @@ blockchainTests('erc20-bridge-sampler', env => {
});
describe('sampleBuys()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -583,10 +651,7 @@ blockchainTests('erc20-bridge-sampler', env => {
});
});
describe('sampleSellsFromKyberNetwork()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -601,7 +666,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token - token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
const quotes = await testContract
@@ -610,6 +675,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
@@ -619,6 +694,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
@@ -627,12 +712,19 @@ blockchainTests('erc20-bridge-sampler', env => {
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleSellsFromEth2Dai()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleSellsFromEth2Dai()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -647,7 +739,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
@@ -656,6 +748,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
@@ -665,6 +767,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
@@ -673,12 +785,19 @@ blockchainTests('erc20-bridge-sampler', env => {
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleBuysFromEth2Dai()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleBuysFromEth2Dai()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -693,7 +812,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
@@ -702,6 +821,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
@@ -711,6 +840,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
@@ -719,12 +858,19 @@ blockchainTests('erc20-bridge-sampler', env => {
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleSellsFromUniswap()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleSellsFromUniswap()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -739,7 +885,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
@@ -748,6 +894,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
@@ -757,6 +913,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
@@ -766,27 +932,38 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the taker token', async () => {
it('returns zero if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if no exchange exists for the taker token', async () => {
const nonExistantToken = randomAddress();
const sampleAmounts = getSampleAmounts(nonExistantToken);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleBuysFromUniswap()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
blockchainTests.resets('sampleBuysFromUniswap()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
@@ -801,7 +978,7 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
it('can quote token -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
@@ -810,6 +987,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
@@ -819,6 +1006,16 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if token -> ETH fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
@@ -828,20 +1025,34 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
it('returns zero if ETH -> token fails', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the taker token', async () => {
it('returns zero if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
const sampleAmounts = getSampleAmounts(nonExistantToken);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
expect(quotes).to.deep.eq(expectedQuotes);
});
it('returns zero if no exchange exists for the taker token', async () => {
const nonExistantToken = randomAddress();
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
const quotes = await testContract
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
});

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v3.0.1 - _December 9, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.0.1",
"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.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@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.1"
"@0x/base-contract": "^6.0.3"
},
"publishConfig": {
"access": "public"

View File

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

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v3.0.1 - _December 9, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "3.0.1",
"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.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@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.1"
"@0x/base-contract": "^6.0.3"
},
"publishConfig": {
"access": "public"

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v4.0.1 - _December 9, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "4.0.1",
"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.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@0x/types": "^3.1.1",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -89,8 +89,8 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.1",
"@0x/typescript-typings": "^5.0.0",
"@0x/base-contract": "^6.0.3",
"@0x/typescript-typings": "^5.0.1",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v4.0.1 - _December 9, 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.1",
"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.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/subproviders": "^6.0.1",
"@0x/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.1",
"@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.1",
"@0x/contracts-utils": "^4.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@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.2",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

View File

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

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v3.0.1 - _December 9, 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.1",
"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.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-multisig": "^4.0.1",
"@0x/contracts-staking": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@0x/types": "^3.1.1",
"@0x/typescript-typings": "^5.0.1",
"@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.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc1155": "^2.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/utils": "^5.1.0",
"@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

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

View File

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

View File

@@ -5,6 +5,14 @@ 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
## v5.1.0 - _December 9, 2019_
* Export function `encodeDutchAuctionAssetData` (#2373)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "5.1.0",
"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.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@0x/types": "^3.1.1",
"@0x/utils": "^5.1.2",
"@0x/web3-wrapper": "^7.0.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -91,8 +91,8 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.1",
"@0x/typescript-typings": "^5.0.0",
"@0x/base-contract": "^6.0.3",
"@0x/typescript-typings": "^5.0.1",
"ethereum-types": "^3.0.0"
},
"publishConfig": {

View File

@@ -1,4 +1,27 @@
[
{
"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",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1575931811,
"version": "2.0.1",

View File

@@ -5,6 +5,15 @@ 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
## v2.0.1 - _December 9, 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.1",
"version": "2.1.0",
"engines": {
"node": ">=6.12"
},
@@ -18,7 +18,8 @@
"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",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
@@ -37,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": {
@@ -50,25 +51,27 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.1",
"@0x/contract-addresses": "^4.0.0",
"@0x/contracts-coordinator": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-exchange-forwarder": "^4.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-utils": "^4.0.1",
"@0x/coordinator-server": "^1.0.4",
"@0x/dev-utils": "^3.0.1",
"@0x/migrations": "^5.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/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.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.1",
"@0x/web3-wrapper": "^7.0.3",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
"@types/seedrandom": "^2.4.28",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
@@ -78,6 +81,7 @@
"mocha": "^6.2.0",
"nock": "^10.0.6",
"npm-run-all": "^4.1.2",
"seedrandom": "^3.0.5",
"shx": "^0.2.2",
"solhint": "^1.4.1",
"truffle": "^5.0.32",
@@ -85,19 +89,21 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-erc1155": "^2.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-multisig": "^4.0.1",
"@0x/contracts-staking": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@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.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

@@ -19,7 +19,7 @@ const coordinatorEndpoint = 'http://localhost:';
const DEFAULT_PROTOCOL_FEE_MULTIPLIER = new BigNumber(150000);
// tslint:disable:custom-no-magic-numbers
blockchainTests.skip('Coordinator Client', env => {
blockchainTests('Coordinator Client', env => {
const takerTokenFillAmount = new BigNumber(0);
const chainId = 1337;
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);

View File

@@ -33,7 +33,7 @@ import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { AssetProxyDispatcher, Authorizable, Ownable } from './framework/wrapper_interfaces';
import { AssetProxyDispatcher, Authorizable, Ownable } from './framework/utils/wrapper_interfaces';
// tslint:disable:no-unnecessary-type-assertion
blockchainTests('Deployment and Configuration End to End Tests', env => {

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);
});
});
});

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