Compare commits

...

319 Commits

Author SHA1 Message Date
fabioberger
3b9d84fa58 Publish
- @0x/contracts-asset-proxy@2.3.0-beta.1
 - @0x/contracts-coordinator@2.1.0-beta.1
 - @0x/contracts-dev-utils@0.1.0-beta.1
 - @0x/contracts-erc1155@1.2.0-beta.1
 - @0x/contracts-erc20@2.3.0-beta.1
 - @0x/contracts-erc721@2.2.0-beta.1
 - @0x/contracts-exchange-forwarder@3.1.0-beta.1
 - @0x/contracts-exchange-libs@3.1.0-beta.1
 - @0x/contracts-exchange@2.2.0-beta.1
 - @0x/contracts-extensions@4.1.0-beta.1
 - @0x/contracts-integrations@1.0.1
 - @0x/contracts-multisig@3.2.0-beta.1
 - @0x/contracts-staking@1.1.0-beta.1
 - @0x/contracts-test-utils@3.2.0-beta.1
 - @0x/contracts-tests@0.0.2
 - @0x/contracts-utils@3.3.0-beta.1
 - 0x.js@8.0.0-beta.0
 - @0x/abi-gen-wrappers@5.4.0-beta.1
 - @0x/abi-gen@4.4.0-beta.1
 - @0x/assert@2.2.0-beta.1
 - @0x/asset-buyer@6.2.0-beta.1
 - @0x/asset-swapper@2.1.0-beta.1
 - @0x/base-contract@5.5.0-beta.1
 - @0x/connect@5.1.0-beta.1
 - @0x/contract-addresses@3.3.0-beta.2
 - @0x/contract-artifacts@2.3.0-beta.2
 - @0x/contract-wrappers@12.2.0-beta.1
 - @0x/contracts-gen@1.1.0-beta.1
 - @0x/dev-utils@2.4.0-beta.1
 - ethereum-types@2.2.0-beta.1
 - @0x/instant@1.0.33
 - @0x/json-schemas@4.1.0-beta.1
 - @0x/migrations@4.4.0-beta.1
 - @0x/monorepo-scripts@1.0.39
 - @0x/order-utils@8.5.0-beta.1
 - @0x/orderbook@0.1.0-beta.1
 - @0x/sol-compiler@3.2.0-beta.1
 - @0x/sol-coverage@3.1.0-beta.1
 - @0x/sol-doc@2.1.0-beta.1
 - @0x/sol-profiler@3.2.0-beta.1
 - @0x/sol-resolver@2.1.0-beta.1
 - @0x/sol-trace@2.1.0-beta.1
 - @0x/sol-tracing-utils@6.1.0-beta.1
 - @0x/sra-spec@2.1.0-beta.1
 - @0x/subproviders@5.1.0-beta.1
 - @0x/testnet-faucets@1.0.89
 - @0x/tslint-config@3.1.0-beta.1
 - @0x/types@2.5.0-beta.1
 - @0x/typescript-typings@4.4.0-beta.1
 - @0x/utils@4.6.0-beta.1
 - @0x/web3-wrapper@6.1.0-beta.1
2019-11-07 20:43:12 +00:00
fabioberger
6fd96a6fd7 Updated CHANGELOGS & MD docs 2019-11-07 20:42:58 +00:00
fabioberger
c93b02d55e Update DevUtils artifacts in contract-artifacts 2019-11-07 19:33:12 +00:00
fabioberger
568f87d5eb Add CHANGELOG entry about the updated DevUtils contract addresses 2019-11-07 19:24:07 +00:00
fabioberger
49ad0f0d54 Update devUtils contract addresses 2019-11-07 19:22:28 +00:00
fabioberger
0e642f59e1 Fix contract-addresses version in 0x.js 2019-11-07 19:00:26 +00:00
fabioberger
7c5730fb03 Update dep versions that were accidentally published 2019-11-07 18:11:40 +00:00
fabioberger
45f0f755ab Merge branch 'development' of github.com:0xProject/0x-monorepo into development 2019-11-07 18:07:23 +00:00
fabioberger
1ef2913c5b Update all CHANGELOGs to prep for publish 2019-11-07 18:07:15 +00:00
F. Eugene Aumson
fecbf220b6 Remove --serial from monorepo test:contracts (#2320) 2019-11-07 13:03:55 -05:00
fabioberger
17a5f05cf3 Make contract-test package private 2019-11-07 18:02:38 +00:00
Xianny
6a852ab0ed Replace assetDataUtils with DevUtilsContract wherever possible (#2304)
* Replace assetDataUtils with DevUtilsContract wherever possible

Does not replace from @0x/instant and some @0x/order-utils uses

* Add revertIfInvalidAssetData to LibAssetData

This is needed to replace `assetDataUtils.decodeAssetDataOrThrow`.
Because it's used in packages and not only contracts, we should wait
to deploy the updated contract so we can update `@0x/contract-artifacts`,
`@0x/abi-gen-wrappers`, and `@0x/contract-wrappers` first.

* remove usages of signatureUtils

* fix test for  optimised encoding

* refactor @0x/contracts-integrations

* update changelogs

* Move @0x/contracts-dev-utils from devDependencies to dependencies
It is exported as part of the package
2019-11-06 19:40:20 -08:00
Jacob Evans
ec26cff656 Fix contract-addresses CHANGELOG (prettier) 2019-11-07 08:48:48 +11:00
xianny
cdd34a1214 Updated CHANGELOGS & MD docs ONLY for pkgs that made it to npmjs (partial publish) 2019-11-06 12:50:58 -08:00
F. Eugene Aumson
857a4042ef abi-gen: Include templates in published tarball (#2315)
* abi-gen: Include templates in published tarball

* abi-gen: update CHANGELOG

* abi-gen/package.json: explicitly include code

Apparently when you include a "files" section, it by default only
includes the code directly referenced by the package.json.  (For this
package, it was including the lib/src/index.js referenced by "main", and
the bin/abi-gen.js referenced by "bin", but no other *.js files, nor any
*.d.ts files.)
2019-11-06 02:39:35 -05:00
F. Eugene Aumson
f51c80adb2 Change all instances of networkId to chainId (#2313)
* abi-gen/test: recompile contract fixtures for 3.0

It seems this hadn't been done since the merge with the 3.0 branch.

* Sync `monorepo$ yarn test` exclusions to CI config

* sra-spec: correct typo

* contract-wrappers: TODO after coord.-server update

* utils: fix typo in comment

* Refactor networkId to chainId everywhere

* Update CHANGELOGs
2019-11-06 01:18:55 -05:00
F. Eugene Aumson
e61f23d001 Migrate Python libraries to v3 (#2284)
* .gitignore migrations/0x_ganache_snapshot

* .gitignore new-ish Python contract wrappers

These should have been added back when we started generating these
wrappers.

* rm superfluous contract artifact in Python package

All of the contract artifacts were removed from the Python package
recently, because now they're copied from the monorepo/packages area as
an automated build step.  Somehow this one artifact slipped through the
cracks.

* Eliminate circular dependency

This was preventing the Exchange wrapper from ever importing its
validator!

* Improve output of monorepo-level parallel script

- Capture stderr (and have it included in stdout) so that it doesn't
leak onto the console for commands that didn't actually fail.

- Include all error output in the Exception object (eliminate print
statement).

* Silence new versions of linters

Newer versions care about this stuff.  Old versions didn't, and we don't
either.

* Support Rich Reverts via Web3.py middleware

* Fix bug in generated wrappers' bytes handling

`bytes.fromhex(bytes.decode('utf-8')` is just plain wrong.  It would
work for some cases, but is not working when trying to fill orders with
the latest Exchange contract.

* Migrate to Exchange v3

* Fix typo in DevUtils documentation

* Include new contracts in docs

* Re-enable Python checks in CI

* Accept strings for bytes

* Fix CircleCI build artifacts for gen'd python

I swear the previous way was working before, but it wasn't working now,
so this fixes it.

* Accept a provider OR a Web3 object

In various places.  This allows the caller to install middleware (which
in web3.py is installed on a Web3 object, not on a provider) before
executing any RPC calls, which is important for the case where one wants
to produce signatures locally before submitting to a remote node.

* wrapper base: don't assume there are accounts

* Eliminate some inline linter directives

* make CHANGELOGs be REVERSE chronological

* Update CHANGELOG entries and bump version numbers

* @0x/contract-addresses: Put addr's in JSON, not TS

This allows easier consumption by other languages.  (Specifically, it
eliminates the overhead of keeping the Python addresses package in sync
with the TypeScript one.)

* sra_client.py: incl. docker in `./setup.py clean`

* sra_client.py: Migrate to protocol v3

Removed script that existed only to exclude runs of sra_client builds
(parallel_without_sra_client).  Now `parallel` is used by CI,
re-including sra_client in CI checks.

* abi-gen/templates/Py: clarify if/else logic

In response to
https://github.com/0xProject/0x-monorepo/pull/2284#discussion_r342200906

* sra_client.py: Update CHANGELOG and bump version

* contract_addresses/setup.py: rm unnecessary rm

* json_schemas.py: corrections to dev dependencies

* In tests against deployment, also run doctests

* contract_wrappers example: rm xtra Order attribute

Thanks to @steveklebanoff for catching this.
https://github.com/0xProject/0x-monorepo/pull/2284#pullrequestreview-312065368
2019-11-05 23:04:29 -05:00
Greg Hysz
cbe4c4fbf9 Merge pull request #2319 from 0xProject/fix/development/blockTimestampTooLowNonDeterministicBug
Fix development
2019-11-05 17:23:18 -08:00
xianny
deffdabc30 Revert "Updated CHANGELOGS & MD docs"
This reverts commit f65d8cc325.
2019-11-05 17:06:05 -08:00
xianny
8811a5387a Revert "Publish"
This reverts commit 9336d4e545.
2019-11-05 17:05:58 -08:00
xianny
9336d4e545 Publish
- @0x/contracts-asset-proxy@2.3.0-beta.1
 - @0x/contracts-coordinator@2.1.0-beta.1
 - @0x/contracts-dev-utils@0.1.0-beta.1
 - @0x/contracts-erc1155@1.2.0-beta.1
 - @0x/contracts-erc20@2.3.0-beta.1
 - @0x/contracts-erc721@2.2.0-beta.1
 - @0x/contracts-exchange-forwarder@3.1.0-beta.1
 - @0x/contracts-exchange-libs@3.1.0-beta.1
 - @0x/contracts-exchange@2.2.0-beta.1
 - @0x/contracts-extensions@4.1.0-beta.1
 - @0x/contracts-integrations@1.0.1
 - @0x/contracts-multisig@3.2.0-beta.1
 - @0x/contracts-staking@1.1.0-beta.1
 - @0x/contracts-test-utils@3.2.0-beta.1
 - @0x/contracts-tests@0.0.2
 - @0x/contracts-utils@3.3.0-beta.1
 - 0x.js@7.1.0-beta.1
 - @0x/abi-gen-wrappers@5.4.0-beta.1
 - @0x/abi-gen@4.4.0-beta.1
 - @0x/assert@2.2.0-beta.1
 - @0x/asset-buyer@6.2.0-beta.1
 - @0x/asset-swapper@2.1.0-beta.1
 - @0x/base-contract@5.5.0-beta.1
 - @0x/connect@5.1.0-beta.1
 - @0x/contract-addresses@3.3.0-beta.1
 - @0x/contract-artifacts@2.3.0-beta.1
 - @0x/contract-wrappers@12.2.0-beta.1
 - @0x/contracts-gen@1.1.0-beta.1
 - @0x/dev-utils@2.4.0-beta.1
 - ethereum-types@2.2.0-beta.1
 - @0x/instant@1.0.33
 - @0x/json-schemas@4.1.0-beta.1
 - @0x/migrations@4.4.0-beta.1
 - @0x/monorepo-scripts@1.0.39
 - @0x/order-utils@8.5.0-beta.1
 - @0x/orderbook@0.1.0-beta.1
 - @0x/sol-compiler@3.2.0-beta.1
 - @0x/sol-coverage@3.1.0-beta.1
 - @0x/sol-doc@2.1.0-beta.1
 - @0x/sol-profiler@3.2.0-beta.1
 - @0x/sol-resolver@2.1.0-beta.1
 - @0x/sol-trace@2.1.0-beta.1
 - @0x/sol-tracing-utils@6.1.0-beta.1
 - @0x/sra-spec@2.1.0-beta.1
 - @0x/subproviders@5.1.0-beta.1
 - @0x/testnet-faucets@1.0.89
 - @0x/tslint-config@3.1.0-beta.0
 - @0x/types@2.5.0-beta.1
 - @0x/typescript-typings@4.4.0-beta.1
 - @0x/utils@4.6.0-beta.1
 - @0x/web3-wrapper@6.1.0-beta.1
2019-11-05 16:58:14 -08:00
xianny
f65d8cc325 Updated CHANGELOGS & MD docs 2019-11-05 16:58:00 -08:00
Greg Hysen
68656c4083 Fixes intermittent test failure by removing block timestamp contraints from staking scheduler tests 2019-11-05 16:57:37 -08:00
Greg Hysz
44793a9cf9 Merge pull request #2316 from 0xProject/tests/3.0/StakingMixinCumulativeRewardsUnitTEsts
Unit tests for MixinCumulativeRewards
2019-11-05 16:00:52 -08:00
Greg Hysen
2d0ad6f181 Updated staking changelog 2019-11-05 15:32:35 -08:00
Greg Hysen
a7f0717afb Unit tests for MixinCumulativeRewards 2019-11-05 15:32:04 -08:00
Greg Hysz
a9022352e7 Merge pull request #2314 from 0xProject/tests/3.0/MixinSchedulerUnitTests
Unit tests for MixinScheduler
2019-11-05 15:27:20 -08:00
Greg Hysen
9b2231ed24 Improved readability on some tests + updated staking changelog 2019-11-05 14:59:55 -08:00
Greg Hysen
c123200f38 Unit tests for MixinScheduler 2019-11-04 15:44:46 -08:00
James Towle
3c6c4128a6 Merge pull request #2294 from 0xProject/refactor/integration-tests/wrapper-functions
Refactor/integration tests/wrapper functions
2019-11-04 14:46:11 -08:00
Lawrence Forman
47e050cbaf Merge pull request #2312 from 0xProject/feat/contracts/staking/MixinStakingPoolRewards-unit-tests
MixinStakingPoolRewards unit tests
2019-11-04 17:19:03 -05:00
Alex Towle
62d15117c5 @0x:contracts-integrations Removed the dependency on number_utils from staking 2019-11-04 14:16:43 -08:00
Lawrence Forman
fb8360edfd Merge pull request #2311 from 0xProject/fix/contracts/staking/LibFixedMath-overflow-pt-2
Fix `LibFixedMath._mul()` overflow
2019-11-04 12:49:20 -05:00
Alex Towle
e557f2fb48 @0x:contracts-integrations Added decimal.js to dependencies so that number_utils can be used 2019-11-04 09:39:00 -08:00
Lawrence Forman
c957b48281 @0x/contracts-staking: Run prettier. 2019-11-04 11:10:07 -05:00
Lawrence Forman
c15c5e12b0 @0x/contracts-staking: Fix event name collision in MixinStakingPoolRewards unit tests. 2019-11-04 11:09:21 -05:00
Lawrence Forman
15c3c8074c @0x/contracts-staking: Add separate unit tests for withdrawDelegatorRewards() and _withdrawAndSyncDelegatorRewards(). 2019-11-04 10:43:37 -05:00
Lawrence Forman
cba72c811d @0x/contracts-staking: Add _computePoolRewardsSplit() tests to MixinStakingPoolRewards unit tests. 2019-11-04 10:43:37 -05:00
Lawrence Forman
28a2e56003 @0x/contracts-staking: Add more MixinStakingPoolRewards unit tests. 2019-11-04 10:43:37 -05:00
Lawrence Forman
8c4c3d56c6 @0x/contracts-staking: Create MixinStakingPoolRewards unit tests. 2019-11-04 10:43:37 -05:00
Alex Towle
911fcc0bed @0x:contracts-integrations Addressed review comments from @mzhu 2019-11-01 17:16:17 -07:00
Alex Towle
55fd16ccf1 @0x:contracts-integrations Addressed review comments from @dorothy-zbornak 2019-11-01 12:04:24 -07:00
Lawrence Forman
4e05e41f7f Merge pull request #2309 from 0xProject/feat/contracts/utils/LibERC20Token
LibERC20Token
2019-11-01 14:57:10 -04:00
Alex Towle
ebab80cff7 @0x:contracts-integrations Fixed test issues 2019-11-01 11:22:12 -07:00
Alex Towle
91cb162662 @0x:contracts-integrations Added tests for weth protocol fees 2019-11-01 11:22:12 -07:00
Alex Towle
fa26f8de51 @0x:contracts-integrations Refactored to reduce redundancy 2019-11-01 11:22:12 -07:00
Alex Towle
26602ac2db @0x:contracts-integrations Added assertions for events in all wrapper functions 2019-11-01 11:22:12 -07:00
Alex Towle
0b8af181d8 @0x:contracts-integrations Refactored the wrapper tests to use newer tools 2019-11-01 11:22:12 -07:00
Alex Towle
19d661d324 @0x:contracts-integrations Started to refactor the wrapper tests 2019-11-01 11:22:12 -07:00
Alex Towle
7d29b36246 @0x:contracts-integrations Moved the wrapper tests into the integrations package 2019-11-01 11:21:52 -07:00
Lawrence Forman
8ba7b95e86 @0x/contracts-utils: Move LibERC20Token out.
`@0x/contracts-erc20`: Move `LibERC20Token` in.
`@0x/contracts-erc20`: Use `LibBytes` and `LibRichErrors` in `LibERC20Token`.
`@0x/contracts-erc20`: Use `verifyEventsFromLogs` in `LibERC20Token` unit tests.
2019-11-01 13:58:22 -04:00
Lawrence Forman
30c72daed5 @0x/order-utils: Remove TransferFailedError from ForwarderRevertErrors (for real this time). 2019-11-01 13:36:07 -04:00
Lawrence Forman
575cb99839 @0x/contracts-utils: Update comments in LibERC20Token. 2019-11-01 13:36:07 -04:00
Lawrence Forman
0c064bf85b Update changelogs. 2019-11-01 13:36:07 -04:00
Lawrence Forman
0f3610c92a @0x/contracts-asset-proxy: Use LibERC20Token in UniswapBridge and Eth2DaiBridge. 2019-11-01 13:36:07 -04:00
Lawrence Forman
c8ef10baaf @0x/contracts-utils: Use simple assembly instead of abi.decode() in LibERC20Token._callWithOptionalBooleanResult().
`@0x/contracts-exchange-forwarder`: Use `LibERC20Token` in `MixinAssets`.
`@0x/order-utils`: Remove `TransferFailedError` from `ForwarderRevertErrors`.
2019-11-01 13:36:07 -04:00
Lawrence Forman
16dc73bd1e @0x/contracts-utils: Add LibERC20Token. 2019-11-01 13:36:07 -04:00
Lawrence Forman
4f56d68689 @0x/contracts-staking: Fix overflow w/ LibFixedMath._mul(-1, -2*255). 2019-11-01 13:35:23 -04:00
Greg Hysz
8e6d92cad5 Merge pull request #2310 from 0xProject/tests/3.0/StakingProxyUnitTests
Staking Proxy Unit Tests + Fallback Reverts if No Staking Contract
2019-11-01 10:26:54 -07:00
Greg Hysen
3a1c464543 Readability improvements to Staking Proxy unit tests + one extra case 2019-11-01 09:59:17 -07:00
Greg Hysen
a0c2f6b7b4 Updated Staking changelog 2019-10-31 18:25:33 -07:00
Greg Hysen
7bfbf0ad3a Staking Proxy unit tests + Staking Proxy fallback reverts if no staking contract is attached 2019-10-31 18:22:49 -07:00
mzhu25
82ee6750c7 Merge pull request #2307 from 0xProject/fix/integration-tests/tslint
`@0x/contracts-integrations`: Enable tslint and fix lint errors
2019-10-31 16:23:35 -07:00
mzhu25
c37fc30c55 Merge pull request #2302 from 0xProject/feature/integration-tests/fillOrder
`@0x/contracts-integrations`: fillOrder integration tests
2019-10-31 16:20:07 -07:00
Michael Zhu
09d13b2bfa default before/after in FunctionAssertion 2019-10-31 15:49:12 -07:00
Michael Zhu
af0de72bc3 address comments 2019-10-31 15:30:15 -07:00
Michael Zhu
43e32f6a1a fix other lint errors 2019-10-31 15:30:15 -07:00
Michael Zhu
e9e6452890 add tslint.json to package and update actor mixins 2019-10-31 15:28:02 -07:00
Michael Zhu
5f699b0c47 add test for weth protocol fees 2019-10-31 15:27:39 -07:00
Michael Zhu
bf18b86f9f address comments 2019-10-31 15:27:39 -07:00
Michael Zhu
56f7dd7538 fix deployment_manager_test 2019-10-31 15:27:39 -07:00
Michael Zhu
7aa88307f6 Flesh out fillOrder integration tests 2019-10-31 15:27:39 -07:00
James Towle
8aa69233e0 Merge pull request #2305 from 0xProject/sandstorm/framework/function_assertion_set
Function Assertion Sets
2019-10-31 14:07:57 -07:00
James Towle
e843333918 Merge pull request #2308 from 0xProject/fix/unblock-kroeger/add-contract-artifacts
`@0x:artifacts` Added the Staking and StakingProxy artifacts
2019-10-31 12:42:43 -07:00
Alex Towle
133a4dc4e1 @0x:contract-artifacts Trimmed artifacts and ran prettier 2019-10-31 10:21:40 -07:00
Alex Towle
c7945a542e @0x:abi-gen-wrappers Added exports 2019-10-31 10:18:41 -07:00
Alex Towle
b4e00baa07 @0x:artifacts Added the Staking and StakingProxy artifacts 2019-10-31 10:18:40 -07:00
Alex Towle
dde570706a @0x:contracts-integrations Put final cleaning touches on the function assertion set 2019-10-30 13:26:29 -07:00
Alex Towle
0b3e3ab990 @0x:contracts-integrations Addressed more review comments 2019-10-30 11:11:55 -07:00
Alex Towle
205c895d75 @0x:contracts-integrations Added better documentation to FunctionAssertions 2019-10-29 23:19:28 -07:00
Alex Towle
6402d29dd4 @0x:contracts-integrations Added collections of function assertions 2019-10-29 14:51:01 -07:00
mzhu25
dc18999931 Merge pull request #2293 from 0xProject/refactor/integrations/forwarder-tests
Refactor and move Forwarder tests to integrations package
2019-10-29 13:04:59 -07:00
Michael Zhu
43f8101d0b address comments 2019-10-29 11:26:56 -07:00
Michael Zhu
3d56c06ff3 Move Forwarder test to integrations; update to use new framework 2019-10-29 11:26:56 -07:00
Lawrence Forman
db9be73fec Merge pull request #2292 from 0xProject/feat/staking/MixinStakeBalances-unit-tests
Add MixinStakeBalances unit tests.
2019-10-29 08:11:59 -04:00
Lawrence Forman
a02892cbc8 @0x/contracts-staking: Remove duplicated test case in MixinStakeBalances unit tests. 2019-10-29 07:37:06 -04:00
Lawrence Forman
49c67fbb18 @0x/contracts-staking: Remove unused lodash import and only modifier from MixinStakeBalances unit tests. 2019-10-29 07:37:06 -04:00
Lawrence Forman
6f2e79208a @0x/contracts-staking: Add MixinStakeBalances unit tests. 2019-10-29 07:37:06 -04:00
Amir Bandeali
ceb3ba4116 Merge pull request #2296 from 0xProject/feat/3.0/testnet-migrations-2
Update 3.0 testnet migrations and addresses
2019-10-28 21:29:03 -07:00
Greg Hysz
08d4f1402f Merge pull request #2301 from 0xProject/fix/3.0/removeLibProxy
Removed LibProxy and inlined proxy call in StakingProxy.
2019-10-28 20:00:24 -07:00
Amir Bandeali
77fa97f259 Update DevUtils, Forwarder, and Coordinator addresses for each testnet 2019-10-28 17:40:50 -07:00
Amir Bandeali
3ac5d9add5 Add DevUtils, Forwarder, and Coordinator to testnet migrations 2019-10-28 17:40:45 -07:00
Amir Bandeali
cab89f312a Update CHANGELOG 2019-10-28 16:55:54 -07:00
Amir Bandeali
8972475389 Update testnet migrations and verifications 2019-10-28 16:55:54 -07:00
Amir Bandeali
330f2d54e2 Update contract-addresses with new testnet deployments 2019-10-28 16:55:54 -07:00
Greg Hysen
9c181f09ba Removed LibProxy and inlined proxy call in StakingProxy. 2019-10-28 16:42:52 -07:00
Amir Bandeali
011ecb8f4b Merge pull request #2297 from 0xProject/fix/multisig/allow-tx-to-be-over-confirmed
Allow transactions to be over confirmed without resetting the confirmation time
2019-10-28 12:49:19 -07:00
Amir Bandeali
bc2a9beb14 Merge pull request #2295 from 0xProject/feat/staking/remove-read-only-mode
Remove read-only mode and associated code
2019-10-28 12:02:10 -07:00
Amir Bandeali
091f5ed8b8 Allow transactions to be over confirmed without resetting the confirmation time 2019-10-28 10:45:17 -07:00
F. Eugene Aumson
ea9f535a7c contract_addresses.py: Add DevUtils contract (#2150)
* contract_addresses.py: Add DevUtils

* Update __init__.py

* Remove whitespace to satisfy linters
2019-10-28 13:32:24 -04:00
Amir Bandeali
f246314b1d Update default alpha from 1/2 to 2/3 2019-10-27 16:53:57 -07:00
Amir Bandeali
cdfd62a296 Fix reentrancy tests 2019-10-27 14:23:19 -07:00
Amir Bandeali
dcff7d511b Add tests for detachProtocolFeeCollector 2019-10-27 11:41:06 -07:00
Amir Bandeali
16a5475d24 Add detachProtocolFeeCollector function (in order to have a separate timelock for detaching) 2019-10-27 11:40:28 -07:00
Amir Bandeali
42468c3fa2 Remove read-only proxy from contract addresses and migrations 2019-10-27 11:12:22 -07:00
Amir Bandeali
9312d5d9f7 remove read-only mode related code from integrations 2019-10-27 11:12:22 -07:00
Amir Bandeali
33a0c22021 Remove tests related to read-only mode 2019-10-27 11:12:22 -07:00
Amir Bandeali
58e9c70203 Remove read-only mode related functions, storage variables, and contracts 2019-10-27 11:12:16 -07:00
F. Eugene Aumson
0067f10a6a @0x/utils: fix wrong RPC method in getChainIdAsync() (#2270)
It was using net_version, but it should be using the eth_chainId method
introduced in EIP-695.  I'm not sure whether/how the network ID differs
from the chain ID on mainnet and the testnets, but in Ganache in
particular, the network ID is 50 while the chain ID is 1337, and this
difference was causing problems for Python tests.  Specifically, the
Web3.py interface `Web3.eth.chainId` invokes the eth_chainId method, and
the result feeds into the order hash, which wasn't lining up with the
non-Python side of things.
2019-10-25 21:08:31 -04:00
Lawrence Forman
59210f5e5e Merge pull request #2287 from 0xProject/feat/staking/MixinStakingPool-unit-tests
Add MixinStakingPool unit tests.
2019-10-25 11:59:50 -04:00
Lawrence Forman
1c695b2759 Rebase onto 3.0 2019-10-25 10:56:03 -04:00
Lawrence Forman
c7222c17ae @0x/contracts-test-utils: prettier 2019-10-25 10:55:03 -04:00
Lawrence Forman
0f237d22f9 @0x/contracts-test-utils: Refactor verifyEvents() to just use verifyEventsFromLogs(). 2019-10-25 10:55:03 -04:00
Lawrence Forman
b1b1162b60 @0x/contracts-staking: Add MixinStakingPool unit tests. 2019-10-25 10:55:03 -04:00
Lawrence Forman
6ee1605a77 @0x/contracts-test-utils: Add verifyEventsFromLogs(). 2019-10-25 10:54:38 -04:00
Greg Hysz
a22b2e7a9f Merge pull request #2290 from 0xProject/fix/3.0/updatedInterfaces
Created IZrxVaultBackstop and updated IStaking.
2019-10-25 06:33:18 -07:00
Greg Hysz
86ed32a007 Merge pull request #2288 from 0xProject/fix/3.0/minorStakingReadability
Replaced `protocolFeePaid` -> `protocolFeeAmount`
2019-10-25 06:19:20 -07:00
Greg Hysen
8e8ea6a3ab Created IZrxVaultBackstop and updated IStaking. 2019-10-24 15:58:51 -07:00
Greg Hysen
cc7452da8f Replaced protocolFeePaid -> protocolFeeAmount. Changed some wording in comments. 2019-10-24 15:58:18 -07:00
Amir Bandeali
06715201a7 Merge pull request #2283 from 0xProject/fix/3.0/rename-bridge-withdraw-to
Rename `withdrawTo` to `bridgeTransferFrom`
2019-10-23 10:15:08 -07:00
Amir Bandeali
281658ba34 Rename withdrawTo to bridgeTransferFrom 2019-10-23 09:42:37 -07:00
Lawrence Forman
f192648c76 Merge pull request #2279 from 0xProject/fix/3.0-audit/staking/assert-valid-params-in-MixinParams
Assert storage params when calling `MixinParams.setParams()`.
2019-10-23 05:11:10 -04:00
Lawrence Forman
07e1d502e7 @0x/contracts-staking: Update changelog. 2019-10-23 04:40:47 -04:00
Lawrence Forman
703e890918 @0x/contracts-staking: Call StakingProxy.assertValidStorageParams() in MixinParams.setParams() 2019-10-23 04:40:47 -04:00
mzhu25
096950729e Merge pull request #2275 from 0xProject/refactor/integrations/coordinator-tests-part2
Coordinator test refactor [2/2]
2019-10-22 17:37:21 -07:00
Michael Zhu
8869d79c68 rebase 2019-10-22 17:05:08 -07:00
Michael Zhu
752dd04546 address comments 2019-10-22 17:05:08 -07:00
Michael Zhu
3e5d166ec4 moving away from extending DeploymentManager in case we want to deploy multiple extensions alongside one another 2019-10-22 17:05:08 -07:00
Michael Zhu
64bc1b0990 update coordinator tests to use deployment manager, balance stores, actor mixins 2019-10-22 17:05:08 -07:00
Michael Zhu
548b0db6ea coordinator deployment manager 2019-10-22 17:05:08 -07:00
Greg Hysz
c9607e8b2c Merge pull request #2280 from 0xProject/fix/3.0/handleNopInMoveStake
Force no-op when moving stake, in some scenarios
2019-10-22 16:51:46 -07:00
Greg Hysen
c676ecb8cf Force no-op when moving zero stake or moving from undelegated to undelegated. 2019-10-22 15:39:24 -07:00
James Towle
39804fdc83 Merge pull request #2281 from 0xProject/fix/ci/10-22-2019
`CI` Fix build failure
2019-10-22 15:20:24 -07:00
Alex Towle
1a1dc89454 CI Fix build failure 2019-10-22 14:53:13 -07:00
James Towle
e427698956 Merge pull request #2252 from 0xProject/feature/sandstorm/function-assertions
Initial Sandstorm Framework
2019-10-22 13:45:42 -07:00
Greg Hysz
575af6b6e8 Merge pull request #2278 from 0xProject/fix/3.0/removeProtocolFeeZeroAssertion
Remove assertion that protocol fee != zero
2019-10-22 13:24:59 -07:00
Alex Towle
3a1fc9ee5f @0x:contracts-integrations Fixed package after rebase 2019-10-22 13:15:01 -07:00
mzhu25
1237c7d479 Merge pull request #2277 from 0xProject/fix/3.0/rmeove-staking-fallback
Remove payable fallback from Staking.sol
2019-10-22 12:46:13 -07:00
Greg Hysen
c44e16a88f Removed assertion that protocol fee != 0 from staking contract. 2019-10-22 12:42:04 -07:00
Greg Hysz
06c180475e Merge pull request #2276 from 0xProject/cleanup/staking/finalizationDataStructures
Refactored finalization state.
2019-10-22 12:37:50 -07:00
Michael Zhu
74a2c3a199 Remove payable fallback from Staking.sol 2019-10-22 12:15:07 -07:00
Greg Hysen
9ac715f99d Protocol fee amount is not enforced by staking contract 2019-10-22 12:10:34 -07:00
Greg Hysen
22e39f782f Some (more) readability improvements and minor optimizations to staking finalization. 2019-10-22 12:09:18 -07:00
Alex Towle
f5a6b84fa3 CI: Added a seperate workflow for contracts/integrations 2019-10-22 12:07:50 -07:00
Alex Towle
718407ba6f @0x:contracts-integrations Switched to object destructuring in the function assertion unit test 2019-10-22 12:07:50 -07:00
Alex Towle
e603a81a46 @0x:contracts-integrations Simplified the tests 2019-10-22 12:07:50 -07:00
Alex Towle
03e35846fb @0x:contracts-integrations Removed the Cache 2019-10-22 12:07:50 -07:00
Alex Towle
c87364f86b @0x:contracts-integrations Addressed more review feedback 2019-10-22 12:07:50 -07:00
Alex Towle
a794a33551 @0x:contracts-integrations Addressed review comments 2019-10-22 12:06:38 -07:00
Alex Towle
494b437f1a @0x:contracts-integrations Renamed some functions in Address Manager 2019-10-22 12:04:47 -07:00
Alex Towle
92b80fc436 @0x:contracts-integrations Improved the caches 2019-10-22 12:04:47 -07:00
Alex Towle
d66101cd9d @0x:contracts-integrations Added unit tests for FunctionAssertion 2019-10-22 12:04:47 -07:00
Alex Towle
89ae04803f @0x:contract-integrations Added the integrations package to CI 2019-10-22 12:01:54 -07:00
Alex Towle
be95bce4cd @0x:contract-integrations Move caches to a new file 2019-10-22 12:01:30 -07:00
Alex Towle
01aee08c02 @0x:contracts-integrations Wrote a simple integration test and AddressManager 2019-10-22 12:01:30 -07:00
Alex Towle
6cba9fd77f @0x/contracts-integrations Created the FunctionAssertion class and examples 2019-10-22 11:59:17 -07:00
mzhu25
673d45361f Merge pull request #2273 from 0xProject/refactor/integrations/coordinator-tests
Coordinator test refactor [1/2] + actor mixin pattern
2019-10-22 11:37:02 -07:00
Greg Hysen
d91a7fc663 Some readability improvements and minor optimizations to staking finalization. 2019-10-22 11:02:25 -07:00
Greg Hysen
ce8fd44234 Removed lingering references to stale terminology ("active pools") 2019-10-22 11:02:25 -07:00
Greg Hysen
6617ad9531 Refactored finalization state.
1. Removes state variables:
   - totalWeightedStakeThisEpoch
   - totalFeesCollectedThisEpoch
   - numActivePoolsThisEpoch

2. No longer indexes by epoch % 2

3. Renamed event StakingPoolActivated → StakingPoolEarnedRewards.

4. Renamed structs:
   - ActivePool → PoolStats. This holds stats for a pool that earned rewards.
   - UnfinalizedState → AggregatedStats. This aggregates stats from the former struct.
2019-10-22 11:02:25 -07:00
Amir Bandeali
10f8051835 Merge pull request #2274 from 0xProject/feat/staking/separate-function-logic
Separate finalization from withdrawing rewards
2019-10-22 10:36:04 -07:00
Amir Bandeali
e7dc7167d0 Sync delegatedStakeToPoolByOwner whenever _withdrawAndSyncDelegatorRewards is called 2019-10-22 09:38:18 -07:00
Amir Bandeali
359b804001 Add test for withdrawing rewards before epoch is finalized 2019-10-21 22:27:06 -07:00
Amir Bandeali
fd9084b345 Add PoolNotFinalizedError rich revert 2019-10-21 22:27:06 -07:00
Amir Bandeali
44dac2cd80 Fix tests that assume touching stake will finalize pool 2019-10-21 22:27:06 -07:00
Amir Bandeali
a66ea2bf74 Assert that pool has been finalized in _withdrawAndSyncDelegatorRewards rather than calling finalizePool 2019-10-21 22:27:06 -07:00
Amir Bandeali
a362e9d2d8 Use safeMath where possible 2019-10-21 22:27:06 -07:00
Amir Bandeali
1885957bd3 Update tests to start at epoch 1 2019-10-21 22:27:06 -07:00
Amir Bandeali
1a409c3731 Initialize currentEpoch at 1 instead of 0 2019-10-21 22:27:06 -07:00
Amir Bandeali
7b7c64fc6a Add assertion function that checks if pool was finalized last epoch 2019-10-21 22:27:06 -07:00
Michael Zhu
102fcd3fb8 export actor classes 2019-10-21 11:17:53 -07:00
Michael Zhu
566e05aea4 typo 2019-10-21 11:17:53 -07:00
Michael Zhu
f014370531 document mixin constructor params 2019-10-21 11:17:53 -07:00
Michael Zhu
dfbbe9daa2 address comments 2019-10-21 11:17:53 -07:00
Michael Zhu
6b653fb00d prettier gdi 2019-10-21 11:17:53 -07:00
Michael Zhu
21cf2319d5 rename some stuff 2019-10-21 11:17:53 -07:00
Michael Zhu
4210477e71 Introduce actor mixin pattern 2019-10-21 11:17:53 -07:00
Michael Zhu
93b02e93b9 juggling some files around 2019-10-21 11:16:14 -07:00
Michael Zhu
f4cb8cfb7e Support arbitrary # of tokens in DeploymentManager 2019-10-21 11:16:14 -07:00
Xianny
ce9f051d42 Create @0x/contracts-tests (#2261)
* Create @0x/contracts-tests

* Move tests from @0x/contracts-dev-utils
2019-10-21 10:23:46 -07:00
Amir Bandeali
083216a0c6 Merge pull request #2271 from 0xProject/feat/3.0/zero-ex-governor
Rename AssetProxyOwner to ZeroExGovernor
2019-10-19 17:05:07 -07:00
Amir Bandeali
820b40e227 Import ZeroExGovernor instead of AssetProxyOwner in testnet migrations 2019-10-18 16:31:38 -07:00
mzhu25
59a38a8db0 Merge pull request #2269 from 0xProject/refactor/balance-stores
BalanceStore++
2019-10-18 09:01:33 -07:00
Lawrence Forman
d0884dcb4d Merge pull request #2267 from 0xProject/feat/3.0/staking/MixinStake-unit-tests
Create MixinStake unit tests
2019-10-18 11:41:18 -04:00
Lawrence Forman
c7ca625408 @0x/contracts-staking: Fix MixinStake unit tests typo. 2019-10-18 10:45:07 -04:00
Lawrence Forman
e46f51339a @0x/contracts-staking: Address review comments. 2019-10-18 10:42:47 -04:00
Michael Zhu
b45ec47eee address comments 2019-10-17 19:53:43 -07:00
Amir Bandeali
c50cbd7a75 Rename AssetProxyOwner to ZeroExGovernor throughout all contracts packages 2019-10-17 17:41:54 -07:00
Amir Bandeali
5ddc35fdf2 Rename AssetProxyOwner to ZeroExGovernor 2019-10-17 17:41:54 -07:00
Amir Bandeali
d6c064b9c3 Merge pull request #2224 from 0xProject/feat/3.0/testnet-migrations
3.0 testnet migrations
2019-10-17 17:38:09 -07:00
Amir Bandeali
caf6329bb3 Merge branch '3.0' into feat/3.0/testnet-migrations 2019-10-17 17:13:12 -07:00
Lawrence Forman
008938cf5b @0x/contracts-staking: Check order of increase and derease balance operations in MixinStake unit tests. 2019-10-17 14:09:46 +10:00
Fabio B
3fd29656cb Merge pull request #2268 from 0xProject/addERC1155MintableSupport
Add erc1155 mintable support
2019-10-17 11:50:02 +08:00
Michael Zhu
ffac52f42e lint 2019-10-16 20:19:04 -07:00
fabioberger
9114510c00 Add enum-naming linter ignore to wrappers to fix linter issue with URL in ERC1155Mintable 2019-10-17 12:16:42 +09:00
fabioberger
2dbda6fc42 Deploy ERC1155Mintable in migration 2019-10-17 12:16:08 +09:00
fabioberger
eae4001622 Update ganache-cli version in migration dockerfile 2019-10-17 12:16:08 +09:00
fabioberger
727d0498b6 Export ERC1155Mintable from abi-gen-wrapper 2019-10-17 12:16:08 +09:00
fabioberger
e43f2d39bf Add ERC1155Mintable to artifacts and abi-gen-wrappers packages 2019-10-17 12:16:08 +09:00
Michael Zhu
cde0169733 Update BlockchainBalanceStore to not rely on erc*_wrappers + better balance equality assertions 2019-10-16 18:46:55 -07:00
Xianny
0e90b0e7d0 Make wrappers small again (#2243)
* introduce --debug option to abi-gen and remove debug functions from @0x/abi-gen-wrappers
* make evmExecAsync protected; ignore deployedBytecode in doc comment
* trim deployedBytecode so it's undefined unless a contract has pure functions
* remove validateAndSendTransactionAsync
* Create `AwaitTransactionSuccessOpts` and `SendTransactionOpts` types 
* Remove duplicate types `IndexedFilterValues`, `DecodedLogEvent`, `EventCallback` from `@0x/base-contract`
2019-10-16 09:38:34 -07:00
Lawrence Forman
b793a31cdd @0x/contracts-staking: Create MixinStake unit tests. 2019-10-16 22:53:36 +10:00
James Towle
23198174f3 Merge pull request #2254 from 0xProject/fix/t14
Add SafeMath to Multisig
2019-10-15 15:40:48 -07:00
Alex Towle
523bc3f951 Updated Changelogs 2019-10-15 15:00:01 -07:00
Alex Towle
41d99e77c7 @0x:contracts-utils Removed SafeMath and the use of the contract throughout contracts/ 2019-10-15 14:31:48 -07:00
Alex Towle
90193c8197 @0x:contracts-multisig Changed SafeMath to LibSafeMath 2019-10-15 14:31:48 -07:00
Alex Towle
6f5c62914e @0x:contracts-multisig Switched to SafeMath in pastTimeLock 2019-10-15 14:31:48 -07:00
James Towle
17faeae47d Merge pull request #2265 from 0xProject/fix/3.0/c18
Fix/3.0/c18
2019-10-15 11:31:46 -07:00
Alex Towle
7283a16710 @0x:contracts-utils Updated changelog 2019-10-14 11:57:02 -07:00
Alex Towle
52c3dc4ad8 @0x:contracts-utils Fixed and improved the LibBytes tests 2019-10-14 11:54:02 -07:00
Alex Towle
1cf8ae5909 @0x:contracts-utils Removed several functions from LibBytes 2019-10-14 11:54:02 -07:00
Amir Bandeali
51282953bd Merge pull request #2263 from 0xProject/fix/3.0-audit/staking/cumulative-rewards-refactor
Staking: Refactor and slightly simplify rewards tracking
2019-10-14 18:28:03 +09:00
Lawrence Forman
a6603d6bd6 @0x/contracts-staking: Fix typo in MixinCumulativeRewards 2019-10-14 15:19:34 +10:00
Lawrence Forman
54a03eacd6 @0x/contracts-staking: Refactor MixinCumulativeRewards and MixinStakingPoolRewards to better encapsulate cumulative rewards.
`@0x/contracts-staking`: Make sure we don't overwrite an existing CR.
`@0x/contracts-staking`: Remove the need to initialize cumulative rewards when creating a pool.
`@0x/contracts-staking`: Just return an empty CR in `_getCumulativeRewardAtEpoch()` if one can't be found.
2019-10-14 12:36:26 +10:00
Amir Bandeali
43fa753a13 Merge pull request #2262 from 0xProject/fix/staking/simplify-finalization
Staking readability improvements
2019-10-14 10:45:55 +09:00
Amir Bandeali
9d9fe882b6 Remove unnecessary payable keywords 2019-10-14 09:58:31 +09:00
Amir Bandeali
4f6958b7b5 Do not import @0x/contracts-extensions to fix build 2019-10-14 09:46:06 +09:00
Amir Bandeali
9a5752fff9 Remove unused param in Pool struct 2019-10-14 09:45:37 +09:00
Amir Bandeali
c21932d149 Remove _creditRewardsToPool and remove return values from finalizePool 2019-10-13 17:31:55 +09:00
Amir Bandeali
ce6c05637f Update cumulative rewards tracking tests 2019-10-13 17:31:55 +09:00
Amir Bandeali
b0699fc238 Always set cumulative rewards when _withdrawAndSyncDelegatorRewards is called 2019-10-13 17:31:55 +09:00
Amir Bandeali
8bf7c4cf48 Remove unnecessary assert 2019-10-13 17:31:55 +09:00
Lawrence Forman
9f6d113fe8 Merge pull request #2255 from 0xProject/fix/3.0-audit/staking/LibFixedMath-arithmetic-overflows
Fix LibFixedMath arithmetic overflows
2019-10-12 07:38:05 +09:00
James Towle
646507c41a Merge pull request #2253 from 0xProject/fix/t8
Ownership Transferred Event
2019-10-10 16:58:08 -07:00
Alex Towle
65f2626544 @0x:contracts-utils Updated the OwnershipTransferred event to be closer to OpenZeppelin's event 2019-10-10 10:58:58 -07:00
Greg Hysz
7155d878b3 Merge pull request #2257 from 0xProject/cleanup/staking/loadSyncedUnsyncedBalance
Remove `loadSyncedBalance` and `loadUnsyncedBalance`
2019-10-10 16:15:17 +09:00
Greg Hysen
361576814c Removed loadSyncedBalance and loadUnsyncedBalance 2019-10-10 15:51:24 +09:00
Amir Bandeali
aa541d0cad Merge pull request #2256 from 0xProject/feat/staking/immutable
Separate immutable contracts, only inherit as needed
2019-10-10 15:19:37 +09:00
Amir Bandeali
7e58385a78 Separate immutable contracts, only inherit as needed 2019-10-10 15:00:35 +09:00
Amir Bandeali
b5545255d0 Merge pull request #2245 from 0xProject/fix/3.0/validate-assetdata
Enforce assetData is padded to 32 bytes
2019-10-10 10:55:22 +09:00
Lawrence Forman
a22ba8647c Update changelogs 2019-10-10 09:41:57 +09:00
Lawrence Forman
22fc0b4337 @0x/contracts-staking: Add another LibFixedMath.add() test. 2019-10-10 09:31:53 +09:00
Lawrence Forman
063d6ff24e @0x/contracts-staking: Add more overflow safeguards to LibFixedMath. 2019-10-10 09:16:01 +09:00
Lawrence Forman
09c0b83fe3 @0x/utils: Consolidated FixedMathRevertErrors 2019-10-10 09:16:01 +09:00
Lawrence Forman
a42f3d189c @0x/contracts-staking: Implement better overflow detection in LibFixedMath. 2019-10-10 09:16:01 +09:00
Alex Towle
7815da7257 @0x:contracts-utils Addressed review feedback 2019-10-09 10:49:38 -07:00
Amir Bandeali
8e2b971f5a Merge pull request #2249 from 0xProject/feat/staking/catastrophic-failure-backstop
Read-only mode backstop
2019-10-09 19:16:10 +09:00
Amir Bandeali
3fd7132a0d Merge pull request #2248 from 0xProject/fix/staking/cleanup-accounting
Simplify staking state
2019-10-09 18:00:06 +09:00
Amir Bandeali
93edb083fa Remove payable fallback from IStakingProxy, fix linting errors 2019-10-09 17:27:37 +09:00
Amir Bandeali
9e41c648dc Add backstop tests 2019-10-09 17:27:37 +09:00
Amir Bandeali
a7ef54dbff Implement ZrxVaultBackstop 2019-10-09 17:27:37 +09:00
Amir Bandeali
414084a7ad Track state of read-only mode in stakingProxy 2019-10-09 17:27:37 +09:00
Amir Bandeali
681e6eab7a Cleanup language used in comments and variable names 2019-10-09 17:15:25 +09:00
Amir Bandeali
701b203c58 Fix tests 2019-10-09 17:15:25 +09:00
Amir Bandeali
cbd0ca4b60 Make currentEpoch uint64 type, change INACTIVE => UNDELEGATED, and make global balances state a mapping from uin8 => StoredBalance 2019-10-09 17:15:25 +09:00
Amir Bandeali
1626566f93 Change getBalanceOfVault => getBalanceOfZrxVault 2019-10-09 17:15:25 +09:00
Amir Bandeali
ac75053f69 Remove unused rich revert 2019-10-09 17:15:25 +09:00
Amir Bandeali
13afc65b54 Do not store global inactive state 2019-10-09 17:15:25 +09:00
Amir Bandeali
aa0a1bb54d Fix tests to no longer utilize active stake 2019-10-09 17:15:25 +09:00
Amir Bandeali
2e36c7ef83 Remove ACTIVE state 2019-10-09 17:15:25 +09:00
Amir Bandeali
43399a9ad9 Refactor tests to use new getters 2019-10-09 17:15:25 +09:00
Amir Bandeali
2ef546210d Update tests to reflect new withdrawal logic 2019-10-09 17:15:25 +09:00
Amir Bandeali
7b379f3933 Simplify withdrawal flow and storage layout 2019-10-09 17:15:25 +09:00
Amir Bandeali
f8ac986a0f Remove unused struct field 2019-10-09 17:14:28 +09:00
Alex Towle
dc0a78434d @0x:contracts-utils Updated files 2019-10-08 19:27:39 -07:00
Alex Towle
d1b0384aef @0x:contracts-utils Added an event for ownership transfers 2019-10-08 19:22:27 -07:00
Greg Hysz
7ac7f45c4a Merge pull request #2250 from 0xProject/cleanup/staking/simplifyMakers
Simplify Makers Interactions with Staking Pools
2019-10-08 11:25:07 +09:00
Greg Hysen
b3c7ccec57 Changed nextPoolId to lastPoolId 2019-10-08 10:43:18 +09:00
Lawrence Forman
93725ecec0 Merge pull request #2233 from 0xProject/feat/erc20-bridge/uniswap
UniswapBridge
2019-10-08 10:31:47 +09:00
Amir Bandeali
3c31ef188a Merge pull request #2246 from 0xProject/fix/3.0/always-validate-signatures
Always validate order signatures
2019-10-08 10:25:28 +09:00
Lawrence Forman
53df2130ea @0x/contracts-asset-proxy: Remove only modifier on uniswap tests. 2019-10-08 09:27:53 +09:00
Amir Bandeali
8b695f9b98 Fix race condition in tests 2019-10-08 09:12:37 +09:00
Amir Bandeali
d914f6fce9 Test for failure with bad signature after partial fill 2019-10-08 09:12:37 +09:00
Amir Bandeali
e2e5152648 Always validate signatures for all types 2019-10-08 09:12:37 +09:00
Lawrence Forman
d3dcf7fb0c Merge pull request #2247 from 0xProject/fix/3.0-audit/staking/shadowed-variable
fix `_computeUnfinalizedDelegatorReward()` shadowed variable and function mutability
2019-10-07 18:50:32 -05:00
Greg Hysen
a0f5a8b64b Simplify Makers Interactions with Staking Pools.
- No longer an upper limit on how many makers can be in a pool.
- No longer a handshake for a maker to join a pool.
- No longer any special powers given to makers.
- Pool Id starts at 1 and increments by 1.
2019-10-08 06:00:24 +09:00
mzhu25
ee508f70bc Merge pull request #2228 from 0xProject/fix/3.0/refundFinalBalanceNoReentry
refundFinalBalanceNoReentry
2019-10-06 13:39:04 -07:00
mzhu25
200b3d450f Merge pull request #2229 from 0xProject/feature/3.0/update-coordinator
Update Coordinator for 3.0
2019-10-06 13:20:00 -07:00
Michael Zhu
52fc7517f9 Remove approval expirations, address other comments 2019-10-06 12:45:48 -07:00
Michael Zhu
cf517b1459 appease static tests 2019-10-06 12:44:58 -07:00
Michael Zhu
c17984b74f Refactor integration tests (CoordinatorTestFactory) 2019-10-06 12:44:58 -07:00
Michael Zhu
589d2212ee Test coordinator protocol fees 2019-10-06 12:44:58 -07:00
Michael Zhu
9b922f746b Update coordinator tests 2019-10-06 12:44:58 -07:00
Michael Zhu
0e7387550c Update contracts 2019-10-06 12:44:58 -07:00
Lawrence Forman
dbf22583b5 @0x/contracts-asset-proxy: Remove unecessary token allowance when coming from WETH. 2019-10-05 22:37:51 -05:00
Lawrence Forman
6825eb442b @0x/contracts-staking: Fix shadowed variable in MixinStakingPoolRewards._computeUnfinalizedDelegatorReward() and make the function pure. 2019-10-05 17:22:46 -05:00
James Towle
45f284973a Merge pull request #2240 from 0xProject/feature/integration-tests/deployment-manager
Deployment Manager
2019-10-05 15:14:12 -07:00
Lawrence Forman
ef6e691646 @0x/contracts-exchange-libs: Update generated source files after revase. 2019-10-05 16:29:06 -05:00
Lawrence Forman
e67888d65f @0x/contracs-asset-proxy: Pass in minimum buy amounts in the UniswapBridge.
`@0x/contracs-asset-proxy`: Slight refactors in `UniswapBridge`.
2019-10-05 16:27:24 -05:00
Lawrence Forman
584f8b13fe @0x/contracts-asset-proxy: Rebase and update Eth2DaiBridge to use IWallet from exchange-libs. 2019-10-05 13:57:51 -05:00
Lawrence Forman
f993b6d1ed @0x/contracts-exchange: Revert changes to IWallet. 2019-10-05 13:57:51 -05:00
Lawrence Forman
035dc607db @0x/contracts-asset-proxy: Use IWallet from exchange-libs.
`@0x/contracts-asset-proxy`: Fix some comment typos in `UniswapBridge`.
`@0x/contracts-asset-proxy`: Add more allowance tests to the `UniswapBridge` tests.
2019-10-05 13:57:51 -05:00
Lawrence Forman
cf2053ec77 @0x/contracts-exchange-libs: Move IWallet from asset-proxy and exchange packages into this package. 2019-10-05 13:57:51 -05:00
Lawrence Forman
3840ebf538 @0x/contracts-exchange: Move IWallet.sol to exchange-libs. 2019-10-05 13:57:51 -05:00
Lawrence Forman
80cb6b654b @0x/contracts-asset-proxy: Fix linter errors. 2019-10-05 13:57:51 -05:00
Lawrence Forman
ab70c4df74 @0x/contracts-asset-proxy: Remove only modifier on tests. 2019-10-05 13:57:51 -05:00
Lawrence Forman
95e461072f @0x/contracts-asset-proxy: Always set allowance. 2019-10-05 13:57:50 -05:00
Lawrence Forman
2593f1ff30 @0x/contracts-asset-proxy: Update CHANGELOG. 2019-10-05 13:57:50 -05:00
Lawrence Forman
c2261a6bbe @0x/contracts-asset-proxy: Finish off UniswapBridge tests. 2019-10-05 13:57:50 -05:00
Lawrence Forman
b383781870 @0x/contracts-asset-proxy: Getting around stack issues. 2019-10-05 13:57:50 -05:00
Lawrence Forman
7d121bafd0 @0x/contracts-asset-proxy: More work on UniswapBridge tests. 2019-10-05 13:57:50 -05:00
Lawrence Forman
6a2911d10f @0x/contracts-asset-proxy: Start work on UniswapBridge tests. 2019-10-05 13:57:50 -05:00
Lawrence Forman
17362bcf44 @0x/contracts-asset-proxy: Create UniswapBridge. 2019-10-05 13:57:50 -05:00
Amir Bandeali
87906a3af1 Add test for improperly padded assetData 2019-10-04 18:05:20 -07:00
Amir Bandeali
c0c27ed637 Enforce that assetData is padded to 32 bytes, excluding the id, before dispatching transfer 2019-10-04 18:04:44 -07:00
Amir Bandeali
6be5552944 fix static tests 2019-10-04 17:17:07 -07:00
Alex Towle
b4ae42cc9a @0x/contracts-integrations Added the integrations package to the top-level configuration 2019-10-04 16:32:39 -07:00
Alex Towle
3c6957095d @0x:contracts-integrations Addressed review feedback 2019-10-04 15:42:36 -07:00
fabioberger
2020d87824 Fix dist tag issue in publish script 2019-10-04 20:44:18 +08:00
Amir Bandeali
ac1063dd68 Merge branch '3.0' into feat/3.0/testnet-migrations 2019-10-03 23:04:07 -07:00
fabioberger
b8e01d7be5 Add beta version to next tslint-config release 2019-10-04 13:28:25 +08:00
Alex Towle
24e4567b25 @0x:contracts-integration-tests Added an integration-tests package and a deployment manager class 2019-10-03 19:41:51 -07:00
Jacob Evans
ccf40fd65e Merge pull request #2242 from 0xProject/fix-mesh-orderbook-types
HACK: Types as any until Mesh RPC client is published
2019-10-03 18:21:24 -07:00
Jacob Evans
d4729e2669 Increase HEAP space for Instant 2019-10-03 17:47:33 -07:00
Jacob Evans
52d38c63de HACK the types until Mesh RPC client is published 2019-10-03 16:40:42 -07:00
Jacob Evans
086c30831d Merge pull request #2241 from 0xProject/rm-dev-tools-pages
Remove dev tools pages
2019-10-03 16:31:44 -07:00
Jacob Evans
4be83de7e5 Remove dev tools pages 2019-10-03 16:25:46 -07:00
Amir Bandeali
650efb95e2 Add more contract configs tests 2019-10-01 22:18:09 -07:00
Michael Zhu
3948f8b66b Replace nonReentrant + refundFinalBalance with refundFinalBalanceNoReentry 2019-10-01 15:39:50 -07:00
Amir Bandeali
ffcd297e5b Rename old exchange address to exchangeV2 2019-09-30 21:03:27 -07:00
Amir Bandeali
cfb099c65c Fix linting errors 2019-09-30 19:00:17 -07:00
Amir Bandeali
ba5c702a9e Update CHANGELOGs 2019-09-30 19:00:17 -07:00
Amir Bandeali
9d4010299a Cleanup migration scripts 2019-09-30 19:00:17 -07:00
Amir Bandeali
b5492bb023 Add ZrxVault testnet addresses to deployment constants 2019-09-30 19:00:17 -07:00
Amir Bandeali
76724a6c73 Implement initial 3.0 migrations script 2019-09-30 19:00:17 -07:00
Amir Bandeali
57ec0858fe Fix migrations build 2019-09-30 19:00:17 -07:00
Amir Bandeali
b281b9aac8 Update 3.0 testnet addresses 2019-09-30 19:00:17 -07:00
Amir Bandeali
f4453c0966 Regenerate abi-gen-wrappers 2019-09-30 18:57:17 -07:00
Amir Bandeali
ebd328db06 Add getSelector helper function to Typescript templates 2019-09-30 18:57:17 -07:00
659 changed files with 48952 additions and 51168 deletions

View File

@@ -47,7 +47,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-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils @0x/contracts-staking
- 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-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-tests @0x/contracts-staking
test-exchange-ganache-3.0:
resource_class: medium+
docker:
@@ -58,6 +58,16 @@ jobs:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-exchange
test-integrations-ganache-3.0:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-integrations
test-contracts-rest-ganache-3.0:
resource_class: medium+
docker:
@@ -67,11 +77,11 @@ 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-dev-utils @0x/contracts-staking
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
- 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
# 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.
# - run: yarn wsrun test:circleci @0x/contracts-extensions
# TODO(abandeali): Re-enable after this package is complete.
# - run: yarn wsrun test:circleci @0x/contracts-coordinator
test-publish:
resource_class: medium+
docker:
@@ -82,7 +92,7 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run:
- run:
command: yarn test:publish:circleci
no_output_timeout: 1800
test-doc-generation:
@@ -108,6 +118,9 @@ jobs:
- run: yarn wsrun test:circleci @0x/abi-gen
# TODO (xianny): Needs to be updated for 3.0
# - run: yarn wsrun test:circleci @0x/asset-buyer
# TODO: Needs to be updated for 3.0. At that time, also remove
# exclusion from monorepo package.json's test script.
# - run: yarn wsrun test:circleci @0x/asset-swapper
- run: yarn wsrun test:circleci @0x/contract-artifacts
- run: yarn wsrun test:circleci @0x/assert
- run: yarn wsrun test:circleci @0x/base-contract
@@ -117,6 +130,9 @@ jobs:
- run: yarn wsrun test:circleci @0x/dev-utils
- run: yarn wsrun test:circleci @0x/json-schemas
- run: yarn wsrun test:circleci @0x/order-utils
# TODO: Needs to be updated for 3.0. At that time, also remove
# exclusion from monorepo package.json's test script.
# - run: yarn wsrun test:circleci @0x/orderbook
- run: yarn wsrun test:circleci @0x/sol-compiler
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
- run: yarn wsrun test:circleci @0x/sol-doc
@@ -184,14 +200,33 @@ jobs:
working_directory: ~/repo
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/ganache-cli:2.2.2
- image: 0xorg/launch-kit-backend:74bcc39
- image: 0xorg/ganache-cli:4.4.0-beta.1
environment:
RPC_URL: http://localhost:8545
VERSION: 4.4.0-beta.1
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
- image: 0xorg/mesh:6.0.0-beta-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'
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
environment:
RPC_URL: 'http://localhost:8545'
NETWORK_ID: 50
WHITELIST_ALL_TOKENS: True
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
MAKER_FEE_UNIT_AMOUNT: 0
TAKER_FEE_UNIT_AMOUNT: 0
MESH_ENDPOINT: 'ws://localhost:60557'
command: |
sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js"
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
steps:
- checkout
- restore_cache:
@@ -213,8 +248,14 @@ jobs:
- run:
command: |
cd python-packages
./parallel_without_sra_client coverage run setup.py test
./parallel coverage run setup.py test
./build_docs
- run:
command: |
# copy generated wrappers into contract_wrappers/build,
# JUST so CircleCI will persist them as build artifacts.
cd python-packages/contract_wrappers/src/zero_ex
for i in contract_wrappers/[^__]*/; do mkdir -p ../../build/$i; cp $i/__init__.py ../../build/$i; done
- save_cache:
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
paths:
@@ -239,8 +280,6 @@ jobs:
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/python-packages/sra_client/.coverage
- store_artifacts:
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
- store_artifacts:
path: ~/repo/python-packages/contract_addresses/build
- store_artifacts:
@@ -394,6 +433,9 @@ workflows:
- test-exchange-ganache-3.0:
requires:
- build
- test-integrations-ganache-3.0:
requires:
- build
- test-contracts-rest-ganache-3.0:
requires:
- build
@@ -415,12 +457,11 @@ workflows:
- test-exchange-ganache-3.0
- test-rest
- static-tests
# - test-python:
# requires:
# - build
# - test-rest
# - static-tests-python:
# requires:
# - test-python
- test-python:
requires:
- build
- static-tests-python:
requires:
- build
# skip python tox run for now, as we don't yet have multiple test environments to support.
# - test-rest-python

12
.gitignore vendored
View File

@@ -82,6 +82,7 @@ TODO.md
packages/testnet-faucets/server/
# generated contract artifacts/
contracts/integrations/generated-artifacts/
contracts/staking/generated-artifacts/
contracts/coordinator/generated-artifacts/
contracts/exchange/generated-artifacts/
@@ -115,6 +116,7 @@ contracts/dev-utils/build/
# generated contract wrappers
packages/python-contract-wrappers/generated/
contracts/integrations/generated-wrappers/
contracts/staking/generated-wrappers/
contracts/coordinator/generated-wrappers/
contracts/exchange/generated-wrappers/
@@ -128,6 +130,7 @@ contracts/erc1155/generated-wrappers/
contracts/extensions/generated-wrappers/
contracts/exchange-forwarder/generated-wrappers/
contracts/dev-utils/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
@@ -136,6 +139,8 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_regi
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_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
@@ -146,6 +151,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__in
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/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
@@ -162,10 +168,14 @@ __pycache__
python-packages/*/src/*.egg-info
python-packages/*/.coverage
# python keeps package-local copies of json schemas
# python keeps package-local copies of json schemas and contract addresses
python-packages/json_schemas/src/zero_ex/json_schemas/schemas
python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json
# Doc README copy
packages/*/docs/README.md
.DS_Store
# the snapshot that gets built for migrations sure does have a ton of files
packages/migrations/0x_ganache_snapshot*

View File

@@ -1,5 +1,7 @@
lib
.nyc_output
/contracts/integrations/generated-wrappers
/contracts/integrations/generated-artifacts
/contracts/staking/generated-wrappers
/contracts/staking/generated-artifacts
/contracts/coordinator/generated-wrappers

View File

@@ -1,4 +1,14 @@
[
{
"version": "2.3.0-beta.1",
"changes": [
{
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
"pr": 2034
}
],
"timestamp": 1573159180
},
{
"version": "2.3.0-beta.0",
"changes": [
@@ -25,6 +35,14 @@
{
"note": "Add `Eth2DaiBridge`",
"pr": 2221
},
{
"note": "Add `UniswapBridge`",
"pr": 2233
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1570135330

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.3.0-beta.1 - _November 7, 2019_
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
## v2.3.0-beta.0 - _October 3, 2019_
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
@@ -13,6 +17,8 @@ CHANGELOG
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
* Add `ERC20BridgeProxy` (#2220)
* Add `Eth2DaiBridge` (#2221)
* Add `UniswapBridge` (#2233)
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v2.2.8 - _September 17, 2019_

View File

@@ -19,7 +19,7 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
import "../archive/MixinAuthorizable.sol";
import "./interfaces/IAssetProxy.sol";
@@ -27,10 +27,10 @@ import "./interfaces/IAssetProxy.sol";
contract ERC1155Proxy is
MixinAuthorizable,
SafeMath,
IAssetProxy
{
using LibBytes for bytes;
using LibSafeMath for uint256;
// Id of this proxy.
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
@@ -71,7 +71,7 @@ contract ERC1155Proxy is
// to avoid copying over `ids` or `data`. This is possible if they are
// identical to `values` and the offsets for each are pointing to the
// same location in the ABI encoded calldata.
scaledValues[i] = _safeMul(values[i], amount);
scaledValues[i] = values[i].safeMul(amount);
}
// Execute `safeBatchTransferFrom` call

View File

@@ -73,7 +73,7 @@ contract ERC20BridgeProxy is
uint256 balanceBefore = balanceOf(tokenAddress, to);
// Call the bridge, who should transfer `amount` of `tokenAddress` to
// `to`.
bytes4 success = IERC20Bridge(bridgeAddress).withdrawTo(
bytes4 success = IERC20Bridge(bridgeAddress).bridgeTransferFrom(
tokenAddress,
from,
to,

View File

@@ -20,9 +20,10 @@ 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-exchange-libs/contracts/src/IWallet.sol";
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IEth2Dai.sol";
import "../interfaces/IWallet.sol";
// solhint-disable space-after-comma
@@ -42,7 +43,7 @@ contract Eth2DaiBridge is
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoeded "from" token address.
/// @return success The magic bytes if successful.
function withdrawTo(
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address to,
@@ -57,7 +58,7 @@ contract Eth2DaiBridge is
IEth2Dai exchange = _getEth2DaiContract();
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
IERC20Token(fromTokenAddress).approve(address(exchange), uint256(-1));
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
// Try to sell all of this contract's `fromTokenAddress` token balance.
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
@@ -67,7 +68,7 @@ contract Eth2DaiBridge is
amount
);
// Transfer the converted `toToken`s to `to`.
_transferERC20Token(toTokenAddress, to, boughtAmount);
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
return BRIDGE_SUCCESS;
}
@@ -94,49 +95,4 @@ contract Eth2DaiBridge is
{
return IEth2Dai(ETH2DAI_ADDRESS);
}
/// @dev Permissively transfers an ERC20 token that may not adhere to
/// specs.
/// @param tokenAddress The token contract address.
/// @param to The token recipient.
/// @param amount The amount of tokens to transfer.
function _transferERC20Token(
address tokenAddress,
address to,
uint256 amount
)
private
{
// Transfer tokens.
// We do a raw call so we can check the success separate
// from the return data.
(bool didSucceed, bytes memory returnData) = tokenAddress.call(
abi.encodeWithSelector(
IERC20Token(0).transfer.selector,
to,
amount
)
);
if (!didSucceed) {
assembly { revert(add(returnData, 0x20), mload(returnData)) }
}
// Check return data.
// If there is no return data, we assume the token incorrectly
// does not return a bool. In this case we expect it to revert
// on failure, which was handled above.
// If the token does return data, we require that it is a single
// value that evaluates to true.
assembly {
if returndatasize {
didSucceed := 0
if eq(returndatasize, 32) {
// First 64 bytes of memory are reserved scratch space
returndatacopy(0, 0, 32)
didSucceed := mload(0)
}
}
}
require(didSucceed, "ERC20_TRANSFER_FAILED");
}
}

View File

@@ -0,0 +1,219 @@
/*
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/interfaces/IEtherToken.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
import "../interfaces/IUniswapExchangeFactory.sol";
import "../interfaces/IUniswapExchange.sol";
import "../interfaces/IERC20Bridge.sol";
// solhint-disable space-after-comma
// solhint-disable not-rely-on-time
contract UniswapBridge is
IERC20Bridge,
IWallet
{
/* Mainnet addresses */
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
// stack overflows.
struct WithdrawToState {
IUniswapExchange exchange;
uint256 fromTokenBalance;
IEtherToken weth;
}
// solhint-disable no-empty-blocks
/// @dev Payable fallback to receive ETH from uniswap.
function ()
external
payable
{}
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
/// token encoded in the bridge data.
/// @param toTokenAddress The token to buy and transfer to `to`.
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoded "from" token address.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success)
{
// State memory object to avoid stack overflows.
WithdrawToState memory state;
// Decode the bridge data to get the `fromTokenAddress`.
(address fromTokenAddress) = abi.decode(bridgeData, (address));
// Just transfer the tokens if they're the same.
if (fromTokenAddress == toTokenAddress) {
LibERC20Token.transfer(fromTokenAddress, to, amount);
return BRIDGE_SUCCESS;
}
// Get the exchange for the token pair.
state.exchange = _getUniswapExchangeForTokenPair(
fromTokenAddress,
toTokenAddress
);
// Get our balance of `fromTokenAddress` token.
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Get the weth contract.
state.weth = getWethContract();
// Convert from WETH to a token.
if (fromTokenAddress == address(state.weth)) {
// Unwrap the WETH.
state.weth.withdraw(state.fromTokenBalance);
// Buy as much of `toTokenAddress` token with ETH as possible and
// transfer it to `to`.
state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
// Minimum buy amount.
amount,
// Expires after this block.
block.timestamp,
// Recipient is `to`.
to
);
// Convert from a token to WETH.
} else if (toTokenAddress == address(state.weth)) {
// Grant the exchange an allowance.
_grantExchangeAllowance(state.exchange, fromTokenAddress);
// Buy as much ETH with `fromTokenAddress` token as possible.
uint256 ethBought = state.exchange.tokenToEthSwapInput(
// Sell all tokens we hold.
state.fromTokenBalance,
// Minimum buy amount.
amount,
// Expires after this block.
block.timestamp
);
// Wrap the ETH.
state.weth.deposit.value(ethBought)();
// Transfer the WETH to `to`.
IEtherToken(toTokenAddress).transfer(to, ethBought);
// Convert from one token to another.
} else {
// Grant the exchange an allowance.
_grantExchangeAllowance(state.exchange, fromTokenAddress);
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
// and transfer it to `to`.
state.exchange.tokenToTokenTransferInput(
// Sell all tokens we hold.
state.fromTokenBalance,
// Minimum buy amount.
amount,
// No minimum intermediate ETH buy amount.
0,
// Expires after this block.
block.timestamp,
// Recipient is `to`.
to,
// Convert to `toTokenAddress`.
toTokenAddress
);
}
return BRIDGE_SUCCESS;
}
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
/// and sign for itself in orders. Always succeeds.
/// @return magicValue Success bytes, always.
function isValidSignature(
bytes32,
bytes calldata
)
external
view
returns (bytes4 magicValue)
{
return LEGACY_WALLET_MAGIC_VALUE;
}
/// @dev Overridable way to get the weth contract.
/// @return token The WETH contract.
function getWethContract()
public
view
returns (IEtherToken token)
{
return IEtherToken(WETH_ADDRESS);
}
/// @dev Overridable way to get the uniswap exchange factory contract.
/// @return factory The exchange factory contract.
function getUniswapExchangeFactoryContract()
public
view
returns (IUniswapExchangeFactory factory)
{
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
}
/// @dev Grants an unlimited allowance to the exchange for its token
/// on behalf of this contract.
/// @param exchange The Uniswap token exchange.
/// @param tokenAddress The token address for the exchange.
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
private
{
LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1));
}
/// @dev Retrieves the uniswap exchange for a given token pair.
/// In the case of a WETH-token exchange, this will be the non-WETH token.
/// In th ecase of a token-token exchange, this will be the first token.
/// @param fromTokenAddress The address of the token we are converting from.
/// @param toTokenAddress The address of the token we are converting to.
/// @return exchange The uniswap exchange.
function _getUniswapExchangeForTokenPair(
address fromTokenAddress,
address toTokenAddress
)
private
view
returns (IUniswapExchange exchange)
{
address exchangeTokenAddress = fromTokenAddress;
// Whichever isn't WETH is the exchange token.
if (fromTokenAddress == address(getWethContract())) {
exchangeTokenAddress = toTokenAddress;
}
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
return exchange;
}
}

View File

@@ -31,7 +31,7 @@ contract IERC20Bridge {
/// @param amount Amount of asset to transfer.
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
/// @return success The magic bytes `0x37708e9b` if successful.
function withdrawTo(
function bridgeTransferFrom(
address tokenAddress,
address from,
address to,

View File

@@ -0,0 +1,77 @@
/*
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 IUniswapExchange {
/// @dev Buys at least `minTokensBought` tokens with ETH and transfer them
/// to `recipient`.
/// @param minTokensBought The minimum number of tokens to buy.
/// @param deadline Time when this order expires.
/// @param recipient Who to transfer the tokens to.
/// @return tokensBought Amount of tokens bought.
function ethToTokenTransferInput(
uint256 minTokensBought,
uint256 deadline,
address recipient
)
external
payable
returns (uint256 tokensBought);
/// @dev Buys at least `minEthBought` ETH with tokens.
/// @param tokensSold Amount of tokens to sell.
/// @param minEthBought The minimum amount of ETH to buy.
/// @param deadline Time when this order expires.
/// @return ethBought Amount of tokens bought.
function tokenToEthSwapInput(
uint256 tokensSold,
uint256 minEthBought,
uint256 deadline
)
external
returns (uint256 ethBought);
/// @dev Buys at least `minTokensBought` tokens with the exchange token
/// and transfer them to `recipient`.
/// @param minTokensBought The minimum number of tokens to buy.
/// @param minEthBought The minimum amount of intermediate ETH to buy.
/// @param deadline Time when this order expires.
/// @param recipient Who to transfer the tokens to.
/// @param toTokenAddress The token being bought.
/// @return tokensBought Amount of tokens bought.
function tokenToTokenTransferInput(
uint256 tokensSold,
uint256 minTokensBought,
uint256 minEthBought,
uint256 deadline,
address recipient,
address toTokenAddress
)
external
returns (uint256 tokensBought);
/// @dev Retrieves the token that is associated with this exchange.
/// @return tokenAddress The token address.
function toTokenAddress()
external
view
returns (address tokenAddress);
}

View File

@@ -18,28 +18,15 @@
pragma solidity ^0.5.9;
import "./IUniswapExchange.sol";
// solhint-disable payable-fallback
contract TestLibProxyReceiver {
function ()
interface IUniswapExchangeFactory {
/// @dev Get the exchange for a token.
/// @param tokenAddress The address of the token contract.
function getExchange(address tokenAddress)
external
{
// Done in assembly to allow the return.
assembly {
let calldataSize := calldatasize()
// Copy all calldata into memory.
calldatacopy(0, 0, calldataSize)
// If the calldatasize is equal to 4, revert.
// This allows us to test `proxyCall` with reverts.
if eq(calldataSize, 4) {
revert(0, 4)
}
// Return. This allows us to test `proxyCall` with returns.
return(0, calldataSize)
}
}
view
returns (IUniswapExchange);
}

View File

@@ -72,7 +72,7 @@ contract TestERC20Bridge is
testToken.setBalance(owner, balance);
}
function withdrawTo(
function bridgeTransferFrom(
address tokenAddress,
address from,
address to,

View File

@@ -0,0 +1,432 @@
/*
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-utils/contracts/src/LibSafeMath.sol";
import "../src/bridges/UniswapBridge.sol";
import "../src/interfaces/IUniswapExchangeFactory.sol";
import "../src/interfaces/IUniswapExchange.sol";
// solhint-disable no-simple-event-func-name
contract TestEventsRaiser {
event TokenTransfer(
address token,
address from,
address to,
uint256 amount
);
event TokenApprove(
address spender,
uint256 allowance
);
event WethDeposit(
uint256 amount
);
event WethWithdraw(
uint256 amount
);
event EthToTokenTransferInput(
address exchange,
uint256 minTokensBought,
uint256 deadline,
address recipient
);
event TokenToEthSwapInput(
address exchange,
uint256 tokensSold,
uint256 minEthBought,
uint256 deadline
);
event TokenToTokenTransferInput(
address exchange,
uint256 tokensSold,
uint256 minTokensBought,
uint256 minEthBought,
uint256 deadline,
address recipient,
address toTokenAddress
);
function raiseEthToTokenTransferInput(
uint256 minTokensBought,
uint256 deadline,
address recipient
)
external
{
emit EthToTokenTransferInput(
msg.sender,
minTokensBought,
deadline,
recipient
);
}
function raiseTokenToEthSwapInput(
uint256 tokensSold,
uint256 minEthBought,
uint256 deadline
)
external
{
emit TokenToEthSwapInput(
msg.sender,
tokensSold,
minEthBought,
deadline
);
}
function raiseTokenToTokenTransferInput(
uint256 tokensSold,
uint256 minTokensBought,
uint256 minEthBought,
uint256 deadline,
address recipient,
address toTokenAddress
)
external
{
emit TokenToTokenTransferInput(
msg.sender,
tokensSold,
minTokensBought,
minEthBought,
deadline,
recipient,
toTokenAddress
);
}
function raiseTokenTransfer(
address from,
address to,
uint256 amount
)
external
{
emit TokenTransfer(
msg.sender,
from,
to,
amount
);
}
function raiseTokenApprove(address spender, uint256 allowance)
external
{
emit TokenApprove(spender, allowance);
}
function raiseWethDeposit(uint256 amount)
external
{
emit WethDeposit(amount);
}
function raiseWethWithdraw(uint256 amount)
external
{
emit WethWithdraw(amount);
}
}
/// @dev A minimalist ERC20/WETH token.
contract TestToken {
using LibSafeMath for uint256;
mapping (address => uint256) public balances;
string private _nextRevertReason;
/// @dev Set the balance for `owner`.
function setBalance(address owner)
external
payable
{
balances[owner] = msg.value;
}
/// @dev Set the revert reason for `transfer()`,
/// `deposit()`, and `withdraw()`.
function setRevertReason(string calldata reason)
external
{
_nextRevertReason = reason;
}
/// @dev Just calls `raiseTokenTransfer()` on the caller.
function transfer(address to, uint256 amount)
external
returns (bool)
{
_revertIfReasonExists();
TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount);
return true;
}
/// @dev Just calls `raiseTokenApprove()` on the caller.
function approve(address spender, uint256 allowance)
external
returns (bool)
{
TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance);
return true;
}
/// @dev `IWETH.deposit()` that increases balances and calls
/// `raiseWethDeposit()` on the caller.
function deposit()
external
payable
{
_revertIfReasonExists();
balances[msg.sender] += balances[msg.sender].safeAdd(msg.value);
TestEventsRaiser(msg.sender).raiseWethDeposit(msg.value);
}
/// @dev `IWETH.withdraw()` that just reduces balances and calls
/// `raiseWethWithdraw()` on the caller.
function withdraw(uint256 amount)
external
{
_revertIfReasonExists();
balances[msg.sender] = balances[msg.sender].safeSub(amount);
msg.sender.transfer(amount);
TestEventsRaiser(msg.sender).raiseWethWithdraw(amount);
}
/// @dev Retrieve the balance for `owner`.
function balanceOf(address owner)
external
view
returns (uint256)
{
return balances[owner];
}
function _revertIfReasonExists()
private
view
{
if (bytes(_nextRevertReason).length != 0) {
revert(_nextRevertReason);
}
}
}
contract TestExchange is
IUniswapExchange
{
address public tokenAddress;
string private _nextRevertReason;
constructor(address _tokenAddress) public {
tokenAddress = _tokenAddress;
}
function setFillBehavior(
string calldata revertReason
)
external
payable
{
_nextRevertReason = revertReason;
}
function ethToTokenTransferInput(
uint256 minTokensBought,
uint256 deadline,
address recipient
)
external
payable
returns (uint256 tokensBought)
{
TestEventsRaiser(msg.sender).raiseEthToTokenTransferInput(
minTokensBought,
deadline,
recipient
);
_revertIfReasonExists();
return address(this).balance;
}
function tokenToEthSwapInput(
uint256 tokensSold,
uint256 minEthBought,
uint256 deadline
)
external
returns (uint256 ethBought)
{
TestEventsRaiser(msg.sender).raiseTokenToEthSwapInput(
tokensSold,
minEthBought,
deadline
);
_revertIfReasonExists();
uint256 fillAmount = address(this).balance;
msg.sender.transfer(fillAmount);
return fillAmount;
}
function tokenToTokenTransferInput(
uint256 tokensSold,
uint256 minTokensBought,
uint256 minEthBought,
uint256 deadline,
address recipient,
address toTokenAddress
)
external
returns (uint256 tokensBought)
{
TestEventsRaiser(msg.sender).raiseTokenToTokenTransferInput(
tokensSold,
minTokensBought,
minEthBought,
deadline,
recipient,
toTokenAddress
);
_revertIfReasonExists();
return address(this).balance;
}
function toTokenAddress()
external
view
returns (address _tokenAddress)
{
return tokenAddress;
}
function _revertIfReasonExists()
private
view
{
if (bytes(_nextRevertReason).length != 0) {
revert(_nextRevertReason);
}
}
}
/// @dev UniswapBridge overridden to mock tokens and implement IUniswapExchangeFactory.
contract TestUniswapBridge is
IUniswapExchangeFactory,
TestEventsRaiser,
UniswapBridge
{
TestToken public wethToken;
// Token address to TestToken instance.
mapping (address => TestToken) private _testTokens;
// Token address to TestExchange instance.
mapping (address => TestExchange) private _testExchanges;
constructor() public {
wethToken = new TestToken();
_testTokens[address(wethToken)] = wethToken;
}
/// @dev Sets the balance of this contract for an existing token.
/// The wei attached will be the balance.
function setTokenBalance(address tokenAddress)
external
payable
{
TestToken token = _testTokens[tokenAddress];
token.deposit.value(msg.value)();
}
/// @dev Sets the revert reason for an existing token.
function setTokenRevertReason(address tokenAddress, string calldata revertReason)
external
{
TestToken token = _testTokens[tokenAddress];
token.setRevertReason(revertReason);
}
/// @dev Create a token and exchange (if they don't exist) for a new token
/// and sets the exchange revert and fill behavior. The wei attached
/// will be the fill amount for the exchange.
/// @param tokenAddress The token address. If zero, one will be created.
/// @param revertReason The revert reason for exchange operations.
function createTokenAndExchange(
address tokenAddress,
string calldata revertReason
)
external
payable
returns (TestToken token, TestExchange exchange)
{
token = TestToken(tokenAddress);
if (tokenAddress == address(0)) {
token = new TestToken();
}
_testTokens[address(token)] = token;
exchange = _testExchanges[address(token)];
if (address(exchange) == address(0)) {
_testExchanges[address(token)] = exchange = new TestExchange(address(token));
}
exchange.setFillBehavior.value(msg.value)(revertReason);
return (token, exchange);
}
/// @dev `IUniswapExchangeFactory.getExchange`
function getExchange(address tokenAddress)
external
view
returns (IUniswapExchange)
{
return IUniswapExchange(_testExchanges[tokenAddress]);
}
// @dev Use `wethToken`.
function getWethContract()
public
view
returns (IEtherToken)
{
return IEtherToken(address(wethToken));
}
// @dev This contract will double as the Uniswap contract.
function getUniswapExchangeFactoryContract()
public
view
returns (IUniswapExchangeFactory)
{
return IUniswapExchangeFactory(address(this));
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "2.3.0-beta.0",
"version": "2.3.0-beta.1",
"engines": {
"node": ">=6.12"
},
@@ -22,7 +22,7 @@
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
@@ -35,7 +35,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IWallet|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget).json",
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -48,12 +48,12 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/contracts-test-utils": "^3.2.0-beta.1",
"@0x/dev-utils": "^2.4.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -71,17 +71,18 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-erc1155": "^1.2.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
"@0x/contracts-erc1155": "^1.2.0-beta.1",
"@0x/contracts-erc20": "^2.3.0-beta.1",
"@0x/contracts-erc721": "^2.2.0-beta.1",
"@0x/contracts-utils": "^3.3.0-beta.1",
"@0x/order-utils": "^8.5.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},

View File

@@ -16,7 +16,8 @@ import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispat
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json';
import * as IWallet from '../generated-artifacts/IWallet.json';
import * as IUniswapExchange from '../generated-artifacts/IUniswapExchange.json';
import * as IUniswapExchangeFactory from '../generated-artifacts/IUniswapExchangeFactory.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';
@@ -25,6 +26,8 @@ import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
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,
@@ -36,14 +39,17 @@ export const artifacts = {
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IWallet: IWallet as ContractArtifact,
IUniswapExchange: IUniswapExchange as ContractArtifact,
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
};

View File

@@ -14,7 +14,8 @@ export * from '../generated-wrappers/i_asset_proxy_dispatcher';
export * from '../generated-wrappers/i_authorizable';
export * from '../generated-wrappers/i_erc20_bridge';
export * from '../generated-wrappers/i_eth2_dai';
export * from '../generated-wrappers/i_wallet';
export * from '../generated-wrappers/i_uniswap_exchange';
export * from '../generated-wrappers/i_uniswap_exchange_factory';
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../generated-wrappers/mixin_authorizable';
export * from '../generated-wrappers/multi_asset_proxy';
@@ -23,3 +24,5 @@ export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_erc20_bridge';
export * from '../generated-wrappers/test_eth2_dai_bridge';
export * from '../generated-wrappers/test_static_call_target';
export * from '../generated-wrappers/test_uniswap_bridge';
export * from '../generated-wrappers/uniswap_bridge';

View File

@@ -1,11 +1,4 @@
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { chaiSetup, expectTransactionFailedAsync, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -60,21 +53,13 @@ describe('Authorizable', () => {
});
it('should allow owner to add an authorized address', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.true();
});
it('should revert if owner attempts to authorize a duplicate address', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
return expectTransactionFailedAsync(
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
RevertReason.TargetAlreadyAuthorized,
@@ -84,11 +69,7 @@ describe('Authorizable', () => {
describe('removeAuthorizedAddress', () => {
it('should revert if not called by owner', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
await expectTransactionFailedAsync(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: notOwner }),
RevertReason.OnlyContractOwner,
@@ -96,16 +77,8 @@ describe('Authorizable', () => {
});
it('should allow owner to remove an authorized address', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.false();
});
@@ -122,11 +95,7 @@ describe('Authorizable', () => {
describe('removeAuthorizedAddressAtIndex', () => {
it('should revert if not called by owner', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
const index = new BigNumber(0);
await expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
@@ -137,11 +106,7 @@ describe('Authorizable', () => {
});
it('should revert if index is >= authorities.length', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
const index = new BigNumber(1);
return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
@@ -164,16 +129,8 @@ describe('Authorizable', () => {
it('should revert if address at index does not match target', async () => {
const address1 = address;
const address2 = notOwner;
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address1,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address2,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address1, { from: owner });
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address2, { from: owner });
const address1Index = new BigNumber(0);
return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
@@ -184,18 +141,11 @@ describe('Authorizable', () => {
});
it('should allow owner to remove an authorized address', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
const index = new BigNumber(0);
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
address,
index,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(address, index, {
from: owner,
});
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.false();
});
@@ -205,19 +155,11 @@ describe('Authorizable', () => {
it('should return all authorized addresses', async () => {
const initial = await authorizable.getAuthorizedAddresses.callAsync();
expect(initial).to.have.length(0);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
expect(afterAdd).to.have.length(1);
expect(afterAdd).to.include(address);
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
expect(afterRemove).to.have.length(0);
});

View File

@@ -1,3 +1,4 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import {
artifacts as erc1155Artifacts,
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
@@ -15,7 +16,6 @@ import {
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
import * as chai from 'chai';
@@ -23,7 +23,7 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper } from '../src';
import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper, IAssetDataContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
@@ -59,6 +59,8 @@ describe('ERC1155Proxy', () => {
// tokens
let fungibleTokens: BigNumber[];
let nonFungibleTokensOwnedBySpender: BigNumber[];
// devUtils for encoding and decoding assetData
let devUtils: DevUtilsContract;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
@@ -72,16 +74,8 @@ describe('ERC1155Proxy', () => {
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
authorized,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
erc1155Proxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(erc1155Proxy.address, { from: owner });
// deploy & configure ERC1155 tokens and receiver
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
erc1155Contract = erc1155Wrapper.getContract();
@@ -103,6 +97,8 @@ describe('ERC1155Proxy', () => {
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
});
// set up devUtils
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, { from: owner });
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -638,7 +634,7 @@ describe('ERC1155Proxy', () => {
return value.times(valueMultiplier);
});
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -696,25 +692,18 @@ describe('ERC1155Proxy', () => {
const tokenUri = '';
for (const tokenToCreate of tokensToCreate) {
// create token
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
tokenToCreate,
tokenUri,
{
await erc1155Wrapper
.getContract()
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
});
// mint balance for spender
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
tokenToCreate,
[spender],
[spenderInitialBalance],
{
await erc1155Wrapper
.getContract()
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
});
}
///// Step 2/5 /////
// Check balances before transfer
@@ -747,18 +736,16 @@ describe('ERC1155Proxy', () => {
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
const valuesToTransfer = tokensToTransfer;
const valueMultiplier = new BigNumber(2);
const assetData = assetDataUtils.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
// remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74;
const assetDataWithoutContractAddress = assetData.substr(offsetToTokenIds);
const expectedAssetDataWithoutContractAddress =
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/encodeERC1155AssetData) does not use optimized encoding
const assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider);
const selector = assetDataContract.ERC1155Assets.getSelector();
const assetDataWithoutContractAddress =
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
expect(assetDataWithoutContractAddress).to.be.equal(expectedAssetDataWithoutContractAddress);
const assetData = `${selector}000000000000000000000000${erc1155ContractAddress.substr(
2,
)}${assetDataWithoutContractAddress}`;
///// Step 4/5 /////
// Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2;
// the expected trade will be token IDs [1, 2] and amounts [2, 4]
@@ -805,25 +792,18 @@ describe('ERC1155Proxy', () => {
const tokenUri = '';
for (const tokenToCreate of tokensToCreate) {
// create token
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
tokenToCreate,
tokenUri,
{
await erc1155Wrapper
.getContract()
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
});
// mint balance for spender
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
tokenToCreate,
[spender],
[spenderInitialBalance],
{
await erc1155Wrapper
.getContract()
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
});
}
///// Step 2/5 /////
// Check balances before transfer
@@ -867,7 +847,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
const generatedAssetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -937,25 +917,18 @@ describe('ERC1155Proxy', () => {
const tokenUri = '';
for (const tokenToCreate of tokensToCreate) {
// create token
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
tokenToCreate,
tokenUri,
{
await erc1155Wrapper
.getContract()
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
});
// mint balance for spender
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
tokenToCreate,
[spender],
[spenderInitialBalance],
{
await erc1155Wrapper
.getContract()
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
});
}
///// Step 2/5 /////
// Check balances before transfer
@@ -996,7 +969,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
const generatedAssetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1059,7 +1032,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1106,7 +1079,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1157,7 +1130,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1208,7 +1181,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1259,7 +1232,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1311,7 +1284,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1358,7 +1331,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1409,7 +1382,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1456,7 +1429,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -1507,13 +1480,13 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender,
receiverContract,
erc1155Contract.address,
@@ -1538,13 +1511,13 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = assetDataUtils.encodeERC1155AssetData(
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender,
receiverContract,
erc1155Contract.address,
@@ -1667,13 +1640,9 @@ describe('ERC1155Proxy', () => {
it('should propagate revert reason from erc1155 contract failure', async () => {
// disable transfers
const shouldRejectTransfer = true;
await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(
shouldRejectTransfer,
{
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(shouldRejectTransfer, {
from: owner,
});
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);

View File

@@ -11,7 +11,7 @@ import {
TransactionHelper,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BigNumber, RawRevertError } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -45,7 +45,7 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
});
});
describe('withdrawTo()', () => {
describe('bridgeTransferFrom()', () => {
interface WithdrawToOpts {
toTokenAddress?: string;
fromTokenAddress?: string;
@@ -103,9 +103,9 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
_opts.toTokentransferRevertReason,
_opts.toTokenTransferReturnData,
);
// Call withdrawTo().
// Call bridgeTransferFrom().
const [result, { logs }] = await txHelper.getResultAndReceiptAsync(
testContract.withdrawTo,
testContract.bridgeTransferFrom,
// "to" token address
_opts.toTokenAddress,
// Random from address.
@@ -179,14 +179,14 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
return expect(tx).to.revertWith(opts.toTokentransferRevertReason);
});
it('fails if `toTokenAddress.transfer()` returns falsey', async () => {
it('fails if `toTokenAddress.transfer()` returns false', async () => {
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexLeftPad(0) });
const tx = withdrawToAsync(opts);
return expect(tx).to.revertWith('ERC20_TRANSFER_FAILED');
return expect(tx).to.revertWith(new RawRevertError(hexLeftPad(0)));
});
it('succeeds if `toTokenAddress.transfer()` returns truthy', async () => {
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(100) });
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(1) });
});
});
});

View File

@@ -1,3 +1,4 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import {
artifacts as erc20Artifacts,
@@ -22,7 +23,6 @@ import {
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
@@ -56,6 +56,7 @@ describe('Asset Transfer Proxies', () => {
let fromAddress: string;
let toAddress: string;
let devUtils: DevUtilsContract;
let erc20TokenA: DummyERC20TokenContract;
let erc20TokenB: DummyERC20TokenContract;
let erc721TokenA: DummyERC721TokenContract;
@@ -91,6 +92,7 @@ describe('Asset Transfer Proxies', () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5));
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
@@ -105,64 +107,24 @@ describe('Asset Transfer Proxies', () => {
);
// Configure ERC20Proxy
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
authorized,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
multiAssetProxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner });
// Configure ERC721Proxy
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
authorized,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
multiAssetProxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner });
// Configure ERC115Proxy
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
authorized,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
multiAssetProxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner });
// Configure MultiAssetProxy
await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
authorized,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
erc20Proxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
erc721Proxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
erc1155Proxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, { from: owner });
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address, { from: owner });
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc1155Proxy.address, { from: owner });
// Deploy and configure ERC20 tokens
const numDummyErc20ToDeploy = 2;
@@ -192,19 +154,13 @@ describe('Asset Transfer Proxies', () => {
);
await erc20Wrapper.setBalancesAndAllowancesAsync();
await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
fromAddress,
constants.INITIAL_ERC20_BALANCE,
{
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync(fromAddress, constants.INITIAL_ERC20_BALANCE, {
from: owner,
});
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
erc20Proxy.address,
constants.INITIAL_ERC20_ALLOWANCE,
{ from: fromAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await multipleReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
fromAddress,
@@ -212,13 +168,11 @@ describe('Asset Transfer Proxies', () => {
{
from: owner,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
await multipleReturnErc20Token.approve.awaitTransactionSuccessAsync(
erc20Proxy.address,
constants.INITIAL_ERC20_ALLOWANCE,
{ from: fromAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Deploy and configure ERC721 tokens and receiver
@@ -278,7 +232,7 @@ describe('Asset Transfer Proxies', () => {
describe('transferFrom', () => {
it('should successfully transfer tokens', async () => {
// Construct ERC20 asset data
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
// Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
@@ -308,7 +262,7 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer tokens that do not return a value', async () => {
// Construct ERC20 asset data
const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(noReturnErc20Token.address);
// Perform a transfer from fromAddress to toAddress
const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
@@ -337,7 +291,9 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer tokens and ignore extra assetData', async () => {
// Construct ERC20 asset data
const extraData = '0102030405060708';
const encodedAssetData = `${assetDataUtils.encodeERC20AssetData(erc20TokenA.address)}${extraData}`;
const encodedAssetData = `${await devUtils.encodeERC20AssetData.callAsync(
erc20TokenA.address,
)}${extraData}`;
// Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
@@ -367,7 +323,7 @@ describe('Asset Transfer Proxies', () => {
it('should do nothing if transferring 0 amount of a token', async () => {
// Construct ERC20 asset data
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
// Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(0);
@@ -397,7 +353,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if allowances are too low', async () => {
// Construct ERC20 asset data
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
// Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0);
const amount = new BigNumber(10);
@@ -407,12 +363,9 @@ describe('Asset Transfer Proxies', () => {
toAddress,
amount,
);
await erc20TokenA.approve.awaitTransactionSuccessAsync(
erc20Proxy.address,
allowance,
{ from: fromAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc20TokenA.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
from: fromAddress,
});
const erc20Balances = await erc20Wrapper.getBalancesAsync();
// Perform a transfer; expect this to fail.
await expectTransactionFailedAsync(
@@ -429,7 +382,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if allowances are too low and token does not return a value', async () => {
// Construct ERC20 asset data
const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(noReturnErc20Token.address);
// Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0);
const amount = new BigNumber(10);
@@ -439,12 +392,9 @@ describe('Asset Transfer Proxies', () => {
toAddress,
amount,
);
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
erc20Proxy.address,
allowance,
{ from: fromAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
from: fromAddress,
});
const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
// Perform a transfer; expect this to fail.
@@ -464,7 +414,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if caller is not authorized', async () => {
// Construct ERC20 asset data
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(10);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
@@ -488,7 +438,9 @@ describe('Asset Transfer Proxies', () => {
it('should revert if token returns more than 32 bytes', async () => {
// Construct ERC20 asset data
const encodedAssetData = assetDataUtils.encodeERC20AssetData(multipleReturnErc20Token.address);
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(
multipleReturnErc20Token.address,
);
const amount = new BigNumber(10);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
encodedAssetData,
@@ -535,7 +487,10 @@ describe('Asset Transfer Proxies', () => {
describe('transferFrom', () => {
it('should successfully transfer tokens', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -563,7 +518,7 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer tokens and ignore extra assetData', async () => {
// Construct ERC721 asset data
const extraData = '0102030405060708';
const encodedAssetData = `${assetDataUtils.encodeERC721AssetData(
const encodedAssetData = `${await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
)}${extraData}`;
@@ -593,7 +548,10 @@ describe('Asset Transfer Proxies', () => {
it('should not call onERC721Received when transferring to a smart contract', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -623,7 +581,10 @@ describe('Asset Transfer Proxies', () => {
it('should revert if transferring 0 amount of a token', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -649,7 +610,10 @@ describe('Asset Transfer Proxies', () => {
it('should revert if transferring > 1 amount of a token', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -675,24 +639,21 @@ describe('Asset Transfer Proxies', () => {
it('should revert if allowances are too low', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
expect(ownerFromAsset).to.be.equal(fromAddress);
// Remove blanket transfer approval for fromAddress.
await erc721TokenA.setApprovalForAll.awaitTransactionSuccessAsync(
erc721Proxy.address,
false,
{ from: fromAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc721TokenA.setApprovalForAll.awaitTransactionSuccessAsync(erc721Proxy.address, false, {
from: fromAddress,
});
// Remove token transfer approval for fromAddress.
await erc721TokenA.approve.awaitTransactionSuccessAsync(
constants.NULL_ADDRESS,
erc721AFromTokenId,
{ from: fromAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc721TokenA.approve.awaitTransactionSuccessAsync(constants.NULL_ADDRESS, erc721AFromTokenId, {
from: fromAddress,
});
// Perform a transfer; expect this to fail.
const amount = new BigNumber(1);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
@@ -715,7 +676,10 @@ describe('Asset Transfer Proxies', () => {
it('should revert if caller is not authorized', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -762,10 +726,10 @@ describe('Asset Transfer Proxies', () => {
it('should transfer a single ERC20 token', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -793,7 +757,7 @@ describe('Asset Transfer Proxies', () => {
it('should dispatch an ERC20 transfer when input amount is 0', async () => {
const inputAmount = constants.ZERO_AMOUNT;
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
@@ -824,11 +788,11 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -857,11 +821,11 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenB.address);
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -896,10 +860,13 @@ describe('Asset Transfer Proxies', () => {
it('should transfer a single ERC721 token', async () => {
const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc721Amount];
const nestedAssetData = [erc721AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -922,8 +889,11 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer multiple of the same ERC721 token', async () => {
const erc721Balances = await erc721Wrapper.getBalancesAsync();
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(
const erc721AssetData1 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const erc721AssetData2 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId2,
);
@@ -931,7 +901,7 @@ describe('Asset Transfer Proxies', () => {
const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -957,13 +927,19 @@ describe('Asset Transfer Proxies', () => {
expect(newOwnerFromAsset2).to.be.equal(toAddress);
});
it('should successfully transfer multiple different ERC721 tokens', async () => {
const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
const erc721AssetData1 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const erc721AssetData2 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenB.address,
erc721BFromTokenId,
);
const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1004,7 +980,7 @@ describe('Asset Transfer Proxies', () => {
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
@@ -1014,7 +990,7 @@ describe('Asset Transfer Proxies', () => {
const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1060,7 +1036,7 @@ describe('Asset Transfer Proxies', () => {
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
@@ -1070,7 +1046,7 @@ describe('Asset Transfer Proxies', () => {
const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1124,7 +1100,7 @@ describe('Asset Transfer Proxies', () => {
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
@@ -1134,7 +1110,7 @@ describe('Asset Transfer Proxies', () => {
const multiAssetAmount = new BigNumber(1);
const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1181,13 +1157,13 @@ describe('Asset Transfer Proxies', () => {
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData1 = assetDataUtils.encodeERC1155AssetData(
const erc1155AssetData1 = await devUtils.encodeERC1155AssetData.callAsync(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
const erc1155AssetData2 = assetDataUtils.encodeERC1155AssetData(
const erc1155AssetData2 = await devUtils.encodeERC1155AssetData.callAsync(
erc1155Contract2.address,
tokensToTransfer,
valuesToTransfer,
@@ -1197,7 +1173,7 @@ describe('Asset Transfer Proxies', () => {
const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier, valueMultiplier];
const nestedAssetData = [erc1155AssetData1, erc1155AssetData2];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1228,15 +1204,18 @@ describe('Asset Transfer Proxies', () => {
// setup test parameters
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const erc1155TokenHolders = [fromAddress, toAddress];
const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1);
const erc1155ValuesToTransfer = [new BigNumber(25)];
const erc1155Amount = new BigNumber(23);
const erc1155ReceiverCallbackData = '0x0102030405';
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
erc1155Contract.address,
erc1155TokensToTransfer,
erc1155ValuesToTransfer,
@@ -1244,7 +1223,7 @@ describe('Asset Transfer Proxies', () => {
);
const amounts = [erc20Amount, erc721Amount, erc1155Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1299,12 +1278,15 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1336,13 +1318,19 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer tokens and ignore extra assetData', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const extraData = '0102030405060708090001020304050607080900010203040506070809000102';
const assetData = `${assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`;
const assetData = `${await devUtils.encodeMultiAssetData.callAsync(
amounts,
nestedAssetData,
)}${extraData}`;
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1375,11 +1363,11 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(100);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenB.address);
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1415,19 +1403,25 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenB.address);
const erc721Amount = new BigNumber(1);
const erc721Balances = await erc721Wrapper.getBalancesAsync();
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1];
const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(
const erc721AssetData1 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const erc721AssetData2 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId2,
);
const erc721AssetData3 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
const erc721AssetData4 = assetDataUtils.encodeERC721AssetData(
const erc721AssetData3 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenB.address,
erc721BFromTokenId,
);
const erc721AssetData4 = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenB.address,
erc721BFromTokenId2,
);
@@ -1440,7 +1434,7 @@ describe('Asset Transfer Proxies', () => {
erc721AssetData3,
erc721AssetData4,
];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1492,13 +1486,16 @@ describe('Asset Transfer Proxies', () => {
it('should revert if a single transfer fails', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
// 2 is an invalid erc721 amount
const erc721Amount = new BigNumber(2);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1517,15 +1514,17 @@ describe('Asset Transfer Proxies', () => {
it('should revert if an AssetProxy is not registered', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const invalidProxyId = '0x12345678';
const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`;
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, invalidErc721AssetData];
// HACK: This is used to get around validation built into assetDataUtils
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1544,12 +1543,14 @@ describe('Asset Transfer Proxies', () => {
it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
// HACK: This is used to get around validation built into assetDataUtils
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1568,10 +1569,10 @@ describe('Asset Transfer Proxies', () => {
it('should revert if amounts multiplication results in an overflow', async () => {
const inputAmount = new BigNumber(2).pow(128);
const erc20Amount = new BigNumber(2).pow(128);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1590,13 +1591,12 @@ describe('Asset Transfer Proxies', () => {
it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = '0x123456';
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
// HACK: This is used to get around validation built into assetDataUtils
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1615,12 +1615,15 @@ describe('Asset Transfer Proxies', () => {
it('should revert if caller is not authorized', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1639,12 +1642,15 @@ describe('Asset Transfer Proxies', () => {
it('should revert if asset data overflows beyond the bounds of calldata', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1669,12 +1675,15 @@ describe('Asset Transfer Proxies', () => {
it('should revert if asset data resolves to a location beyond the bounds of calldata', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1700,12 +1709,15 @@ describe('Asset Transfer Proxies', () => {
// setup test parameters
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
const erc721Amount = new BigNumber(1);
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
erc721TokenA.address,
erc721AFromTokenId,
);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
const extraData = '01';
const assetDataWithExtraData = `${assetData}${extraData}`;
const badData = assetProxyInterface.transferFrom.getABIEncodedTransactionData(

View File

@@ -1,3 +1,4 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import {
chaiSetup,
constants,
@@ -8,7 +9,6 @@ import {
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
import * as chai from 'chai';
@@ -25,6 +25,7 @@ describe('StaticCallProxy', () => {
let fromAddress: string;
let toAddress: string;
let devUtils: DevUtilsContract;
let staticCallProxy: IAssetProxyContract;
let staticCallTarget: TestStaticCallTargetContract;
@@ -43,6 +44,7 @@ describe('StaticCallProxy', () => {
txDefaults,
artifacts,
);
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
staticCallProxy = new IAssetProxyContract(
staticCallProxyWithoutTransferFrom.address,
provider,
@@ -86,7 +88,7 @@ describe('StaticCallProxy', () => {
it('should revert if assetData lies outside the bounds of calldata', async () => {
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -114,9 +116,11 @@ describe('StaticCallProxy', () => {
it('should revert if the length of assetData is less than 100 bytes', async () => {
const staticCallData = constants.NULL_BYTES;
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
.slice(0, -128);
const assetData = (await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
)).slice(0, -128);
const assetDataByteLen = (assetData.length - 2) / 2;
expect((assetDataByteLen - 4) % 32).to.equal(0);
await expectTransactionFailedWithoutReasonAsync(
@@ -126,7 +130,7 @@ describe('StaticCallProxy', () => {
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -147,7 +151,7 @@ describe('StaticCallProxy', () => {
it('should revert if the callTarget attempts to write to state', async () => {
const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -159,7 +163,7 @@ describe('StaticCallProxy', () => {
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -173,7 +177,7 @@ describe('StaticCallProxy', () => {
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -186,7 +190,7 @@ describe('StaticCallProxy', () => {
it('should be successful if a function call with no inputs and no outputs is successful', async () => {
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -196,14 +200,18 @@ describe('StaticCallProxy', () => {
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
const staticCallData = '0x0102030405060708';
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash);
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
toAddress,
staticCallData,
expectedResultHash,
);
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
});
it('should be successful if a function call with one static input returns the correct value', async () => {
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -214,7 +222,7 @@ describe('StaticCallProxy', () => {
const dynamicInput = '0x0102030405060708';
const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,
@@ -237,7 +245,7 @@ describe('StaticCallProxy', () => {
const expectedResultHash = ethUtil.bufferToHex(
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
);
const assetData = assetDataUtils.encodeStaticCallAssetData(
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
staticCallTarget.address,
staticCallData,
expectedResultHash,

View File

@@ -0,0 +1,365 @@
import {
blockchainTests,
constants,
expect,
filterLogs,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
TransactionHelper,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import {
artifacts,
TestUniswapBridgeContract,
TestUniswapBridgeEthToTokenTransferInputEventArgs as EthToTokenTransferInputArgs,
TestUniswapBridgeEvents as ContractEvents,
TestUniswapBridgeTokenApproveEventArgs as TokenApproveArgs,
TestUniswapBridgeTokenToEthSwapInputEventArgs as TokenToEthSwapInputArgs,
TestUniswapBridgeTokenToTokenTransferInputEventArgs as TokenToTokenTransferInputArgs,
TestUniswapBridgeTokenTransferEventArgs as TokenTransferArgs,
TestUniswapBridgeWethDepositEventArgs as WethDepositArgs,
TestUniswapBridgeWethWithdrawEventArgs as WethWithdrawArgs,
} from '../src';
blockchainTests.resets('UniswapBridge unit tests', env => {
const txHelper = new TransactionHelper(env.web3Wrapper, artifacts);
let testContract: TestUniswapBridgeContract;
let wethTokenAddress: string;
before(async () => {
testContract = await TestUniswapBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestUniswapBridge,
env.provider,
env.txDefaults,
artifacts,
);
wethTokenAddress = await testContract.wethToken.callAsync();
});
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract.isValidSignature.callAsync(hexRandom(), hexRandom(_.random(0, 32)));
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
describe('bridgeTransferFrom()', () => {
interface WithdrawToOpts {
fromTokenAddress: string;
toTokenAddress: string;
fromTokenBalance: Numberish;
toAddress: string;
amount: Numberish;
exchangeRevertReason: string;
exchangeFillAmount: Numberish;
toTokenRevertReason: string;
fromTokenRevertReason: string;
}
function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts {
return {
fromTokenAddress: constants.NULL_ADDRESS,
toTokenAddress: constants.NULL_ADDRESS,
fromTokenBalance: getRandomInteger(1, 1e18),
toAddress: randomAddress(),
amount: getRandomInteger(1, 1e18),
exchangeRevertReason: '',
exchangeFillAmount: getRandomInteger(1, 1e18),
toTokenRevertReason: '',
fromTokenRevertReason: '',
...opts,
};
}
interface WithdrawToResult {
opts: WithdrawToOpts;
result: string;
logs: DecodedLogs;
blockTime: number;
}
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
const _opts = createWithdrawToOpts(opts);
// Create the "from" token and exchange.
[[_opts.fromTokenAddress]] = await txHelper.getResultAndReceiptAsync(
testContract.createTokenAndExchange,
_opts.fromTokenAddress,
_opts.exchangeRevertReason,
{ value: new BigNumber(_opts.exchangeFillAmount) },
);
// Create the "to" token and exchange.
[[_opts.toTokenAddress]] = await txHelper.getResultAndReceiptAsync(
testContract.createTokenAndExchange,
_opts.toTokenAddress,
_opts.exchangeRevertReason,
{ value: new BigNumber(_opts.exchangeFillAmount) },
);
await testContract.setTokenRevertReason.awaitTransactionSuccessAsync(
_opts.toTokenAddress,
_opts.toTokenRevertReason,
);
await testContract.setTokenRevertReason.awaitTransactionSuccessAsync(
_opts.fromTokenAddress,
_opts.fromTokenRevertReason,
);
// Set the token balance for the token we're converting from.
await testContract.setTokenBalance.awaitTransactionSuccessAsync(_opts.fromTokenAddress, {
value: new BigNumber(_opts.fromTokenBalance),
});
// Call bridgeTransferFrom().
const [result, receipt] = await txHelper.getResultAndReceiptAsync(
testContract.bridgeTransferFrom,
// The "to" token address.
_opts.toTokenAddress,
// The "from" address.
randomAddress(),
// The "to" address.
_opts.toAddress,
// The amount to transfer to "to"
new BigNumber(_opts.amount),
// ABI-encoded "from" token address.
hexLeftPad(_opts.fromTokenAddress),
);
return {
opts: _opts,
result,
logs: (receipt.logs as any) as DecodedLogs,
blockTime: await env.web3Wrapper.getBlockTimestampAsync(receipt.blockNumber),
};
}
async function getExchangeForTokenAsync(tokenAddress: string): Promise<string> {
return testContract.getExchange.callAsync(tokenAddress);
}
it('returns magic bytes on success', async () => {
const { result } = await withdrawToAsync();
expect(result).to.eq(AssetProxyId.ERC20Bridge);
});
it('just transfers tokens to `to` if the same tokens are in play', async () => {
const [[tokenAddress]] = await txHelper.getResultAndReceiptAsync(
testContract.createTokenAndExchange,
constants.NULL_ADDRESS,
'',
);
const { opts, result, logs } = await withdrawToAsync({
fromTokenAddress: tokenAddress,
toTokenAddress: tokenAddress,
});
expect(result).to.eq(AssetProxyId.ERC20Bridge);
const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer);
expect(transfers.length).to.eq(1);
expect(transfers[0].token).to.eq(tokenAddress);
expect(transfers[0].from).to.eq(testContract.address);
expect(transfers[0].to).to.eq(opts.toAddress);
expect(transfers[0].amount).to.bignumber.eq(opts.amount);
});
describe('token -> token', () => {
it('calls `IUniswapExchange.tokenToTokenTransferInput()', async () => {
const { opts, logs, blockTime } = await withdrawToAsync();
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
const calls = filterLogsToArguments<TokenToTokenTransferInputArgs>(
logs,
ContractEvents.TokenToTokenTransferInput,
);
expect(calls.length).to.eq(1);
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].deadline).to.bignumber.eq(blockTime);
expect(calls[0].recipient).to.eq(opts.toAddress);
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);
});
it('sets allowance for "from" token', async () => {
const { opts, logs } = await withdrawToAsync();
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
expect(approvals.length).to.eq(1);
expect(approvals[0].spender).to.eq(exchangeAddress);
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
});
it('sets allowance for "from" token on subsequent calls', async () => {
const { opts } = await withdrawToAsync();
const { logs } = await withdrawToAsync(opts);
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
expect(approvals.length).to.eq(1);
expect(approvals[0].spender).to.eq(exchangeAddress);
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
});
it('fails if "from" token does not exist', async () => {
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
randomAddress(),
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(randomAddress()),
);
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
});
it('fails if the exchange fails', async () => {
const revertReason = 'FOOBAR';
const tx = withdrawToAsync({
exchangeRevertReason: revertReason,
});
return expect(tx).to.revertWith(revertReason);
});
});
describe('token -> ETH', () => {
it('calls `IUniswapExchange.tokenToEthSwapInput()`, `WETH.deposit()`, then `transfer()`', async () => {
const { opts, logs, blockTime } = await withdrawToAsync({
toTokenAddress: wethTokenAddress,
});
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
let calls: any = filterLogs<TokenToEthSwapInputArgs>(logs, ContractEvents.TokenToEthSwapInput);
expect(calls.length).to.eq(1);
expect(calls[0].args.exchange).to.eq(exchangeAddress);
expect(calls[0].args.tokensSold).to.bignumber.eq(opts.fromTokenBalance);
expect(calls[0].args.minEthBought).to.bignumber.eq(opts.amount);
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
calls = filterLogs<WethDepositArgs>(
logs.slice(calls[0].logIndex as number),
ContractEvents.WethDeposit,
);
expect(calls.length).to.eq(1);
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
calls = filterLogs<TokenTransferArgs>(
logs.slice(calls[0].logIndex as number),
ContractEvents.TokenTransfer,
);
expect(calls.length).to.eq(1);
expect(calls[0].args.token).to.eq(opts.toTokenAddress);
expect(calls[0].args.from).to.eq(testContract.address);
expect(calls[0].args.to).to.eq(opts.toAddress);
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
});
it('sets allowance for "from" token', async () => {
const { opts, logs } = await withdrawToAsync({
toTokenAddress: wethTokenAddress,
});
const transfers = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
expect(transfers.length).to.eq(1);
expect(transfers[0].spender).to.eq(exchangeAddress);
expect(transfers[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
});
it('sets allowance for "from" token on subsequent calls', async () => {
const { opts } = await withdrawToAsync({
toTokenAddress: wethTokenAddress,
});
const { logs } = await withdrawToAsync(opts);
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
expect(approvals.length).to.eq(1);
expect(approvals[0].spender).to.eq(exchangeAddress);
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
});
it('fails if "from" token does not exist', async () => {
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
randomAddress(),
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(wethTokenAddress),
);
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
});
it('fails if `WETH.deposit()` fails', async () => {
const revertReason = 'FOOBAR';
const tx = withdrawToAsync({
toTokenAddress: wethTokenAddress,
toTokenRevertReason: revertReason,
});
return expect(tx).to.revertWith(revertReason);
});
it('fails if the exchange fails', async () => {
const revertReason = 'FOOBAR';
const tx = withdrawToAsync({
toTokenAddress: wethTokenAddress,
exchangeRevertReason: revertReason,
});
return expect(tx).to.revertWith(revertReason);
});
});
describe('ETH -> token', () => {
it('calls `WETH.withdraw()`, then `IUniswapExchange.ethToTokenTransferInput()`', async () => {
const { opts, logs, blockTime } = await withdrawToAsync({
fromTokenAddress: wethTokenAddress,
});
const exchangeAddress = await getExchangeForTokenAsync(opts.toTokenAddress);
let calls: any = filterLogs<WethWithdrawArgs>(logs, ContractEvents.WethWithdraw);
expect(calls.length).to.eq(1);
expect(calls[0].args.amount).to.bignumber.eq(opts.fromTokenBalance);
calls = filterLogs<EthToTokenTransferInputArgs>(
logs.slice(calls[0].logIndex as number),
ContractEvents.EthToTokenTransferInput,
);
expect(calls.length).to.eq(1);
expect(calls[0].args.exchange).to.eq(exchangeAddress);
expect(calls[0].args.minTokensBought).to.bignumber.eq(opts.amount);
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
expect(calls[0].args.recipient).to.eq(opts.toAddress);
});
it('does not set any allowance', async () => {
const { logs } = await withdrawToAsync({
fromTokenAddress: wethTokenAddress,
});
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
expect(approvals).to.be.empty('');
});
it('fails if "to" token does not exist', async () => {
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
wethTokenAddress,
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(randomAddress()),
);
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
});
it('fails if the `WETH.withdraw()` fails', async () => {
const revertReason = 'FOOBAR';
const tx = withdrawToAsync({
fromTokenAddress: wethTokenAddress,
fromTokenRevertReason: revertReason,
});
return expect(tx).to.revertWith(revertReason);
});
it('fails if the exchange fails', async () => {
const revertReason = 'FOOBAR';
const tx = withdrawToAsync({
fromTokenAddress: wethTokenAddress,
exchangeRevertReason: revertReason,
});
return expect(tx).to.revertWith(revertReason);
});
});
});
});

View File

@@ -1,3 +1,4 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import {
constants,
@@ -7,7 +8,6 @@ import {
LogDecoder,
txDefaults,
} from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
@@ -26,6 +26,7 @@ export class ERC1155ProxyWrapper {
private readonly _logDecoder: LogDecoder;
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
private readonly _assetProxyInterface: IAssetProxyContract;
private readonly _devUtils: DevUtilsContract;
private _proxyContract?: ERC1155ProxyContract;
private _proxyIdIfExists?: string;
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
@@ -37,6 +38,7 @@ export class ERC1155ProxyWrapper {
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
this._dummyTokenWrappers = [];
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress;
this._fungibleTokenIds = [];
@@ -95,7 +97,7 @@ export class ERC1155ProxyWrapper {
* @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return abi encoded tx data.
*/
public getTransferFromAbiEncodedTxData(
public async getTransferFromAbiEncodedTxDataAsync(
from: string,
to: string,
contractAddress: string,
@@ -105,11 +107,11 @@ export class ERC1155ProxyWrapper {
receiverCallbackData: string,
authorizedSender: string,
assetData_?: string,
): string {
): Promise<string> {
this._validateProxyContractExistsOrThrow();
const assetData =
assetData_ === undefined
? assetDataUtils.encodeERC1155AssetData(
? await this._devUtils.encodeERC1155AssetData.callAsync(
contractAddress,
tokensToTransfer,
valuesToTransfer,
@@ -169,7 +171,7 @@ export class ERC1155ProxyWrapper {
this._validateProxyContractExistsOrThrow();
const assetData =
assetData_ === undefined
? assetDataUtils.encodeERC1155AssetData(
? await this._devUtils.encodeERC1155AssetData.callAsync(
contractAddress,
tokensToTransfer,
valuesToTransfer,

View File

@@ -1,6 +1,6 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { ZeroExProvider } from 'ethereum-types';
import * as _ from 'lodash';
@@ -12,6 +12,7 @@ export class ERC20Wrapper {
private readonly _contractOwnerAddress: string;
private readonly _provider: ZeroExProvider;
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
private readonly _devUtils: DevUtilsContract;
private _proxyContract?: ERC20ProxyContract;
private _proxyIdIfExists?: string;
/**
@@ -26,6 +27,7 @@ export class ERC20Wrapper {
this._provider = provider;
this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress;
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
}
public async deployDummyTokensAsync(
numberToDeploy: number,
@@ -70,46 +72,39 @@ export class ERC20Wrapper {
tokenOwnerAddress,
constants.INITIAL_ERC20_BALANCE,
{ from: this._contractOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
(this._proxyContract as ERC20ProxyContract).address,
constants.INITIAL_ERC20_ALLOWANCE,
{ from: tokenOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
}
}
}
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
const tokenContract = this._getTokenContractFromAssetData(assetData);
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
return balance;
}
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(assetData);
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
await tokenContract.setBalance.awaitTransactionSuccessAsync(
userAddress,
amount,
{ from: this._contractOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
);
}
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
const tokenContract = this._getTokenContractFromAssetData(assetData);
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
return allowance;
}
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(assetData);
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
await tokenContract.approve.awaitTransactionSuccessAsync(
proxyAddress,
amount,
{ from: userAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await tokenContract.approve.awaitTransactionSuccessAsync(proxyAddress, amount, { from: userAddress });
}
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
this._validateDummyTokenContractsExistOrThrow();
@@ -151,9 +146,8 @@ export class ERC20Wrapper {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses;
}
private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
const tokenAddress = erc20ProxyData.tokenAddress;
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
const [proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData.callAsync(assetData); // tslint:disable-line:no-unused-variable
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
if (tokenContractIfExists === undefined) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);

View File

@@ -93,22 +93,14 @@ export class ERC721Wrapper {
): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
proxyAddress,
isApproved,
{ from: ownerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(proxyAddress, isApproved, {
from: ownerAddress,
});
}
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
await tokenContract.approve.awaitTransactionSuccessAsync(
to,
tokenId,
{ from: tokenOwner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await tokenContract.approve.awaitTransactionSuccessAsync(to, tokenId, { from: tokenOwner });
}
public async transferFromAsync(
tokenAddress: string,
@@ -117,31 +109,19 @@ export class ERC721Wrapper {
userAddress: string,
): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
await tokenContract.transferFrom.awaitTransactionSuccessAsync(
currentOwner,
userAddress,
tokenId,
{ from: currentOwner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await tokenContract.transferFrom.awaitTransactionSuccessAsync(currentOwner, userAddress, tokenId, {
from: currentOwner,
});
}
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
await tokenContract.mint.awaitTransactionSuccessAsync(
userAddress,
tokenId,
{ from: this._contractOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await tokenContract.mint.awaitTransactionSuccessAsync(userAddress, tokenId, {
from: this._contractOwnerAddress,
});
}
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
await tokenContract.burn.awaitTransactionSuccessAsync(
owner,
tokenId,
{ from: this._contractOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await tokenContract.burn.awaitTransactionSuccessAsync(owner, tokenId, { from: this._contractOwnerAddress });
}
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);

View File

@@ -14,7 +14,8 @@
"generated-artifacts/IAuthorizable.json",
"generated-artifacts/IERC20Bridge.json",
"generated-artifacts/IEth2Dai.json",
"generated-artifacts/IWallet.json",
"generated-artifacts/IUniswapExchange.json",
"generated-artifacts/IUniswapExchangeFactory.json",
"generated-artifacts/MixinAssetProxyDispatcher.json",
"generated-artifacts/MixinAuthorizable.json",
"generated-artifacts/MultiAssetProxy.json",
@@ -22,7 +23,9 @@
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestERC20Bridge.json",
"generated-artifacts/TestEth2DaiBridge.json",
"generated-artifacts/TestStaticCallTarget.json"
"generated-artifacts/TestStaticCallTarget.json",
"generated-artifacts/TestUniswapBridge.json",
"generated-artifacts/UniswapBridge.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@@ -1,4 +1,13 @@
[
{
"version": "2.1.0-beta.1",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1573159180
},
{
"version": "2.1.0-beta.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.0-beta.1 - _November 7, 2019_
* Dependencies updated
## v2.1.0-beta.0 - _October 3, 2019_
* Add chainId to domain separator (#1742)

View File

@@ -19,12 +19,15 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "./libs/LibCoordinatorApproval.sol";
import "./libs/LibCoordinatorRichErrors.sol";
import "./interfaces/ICoordinatorSignatureValidator.sol";
import "./interfaces/ICoordinatorApprovalVerifier.sol";
@@ -32,7 +35,7 @@ import "./interfaces/ICoordinatorApprovalVerifier.sol";
// solhint-disable avoid-tx-origin
contract MixinCoordinatorApprovalVerifier is
LibCoordinatorApproval,
LibZeroExTransaction,
LibEIP712ExchangeDomain,
ICoordinatorSignatureValidator,
ICoordinatorApprovalVerifier
{
@@ -44,13 +47,12 @@ contract MixinCoordinatorApprovalVerifier is
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
/// order in the transaction's Exchange calldata.
function assertValidCoordinatorApprovals(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
public
@@ -67,7 +69,6 @@ contract MixinCoordinatorApprovalVerifier is
orders,
txOrigin,
transactionSignature,
approvalExpirationTimeSeconds,
approvalSignatures
);
}
@@ -75,7 +76,7 @@ contract MixinCoordinatorApprovalVerifier is
/// @dev Decodes the orders from Exchange calldata representing any fill method.
/// @param data Exchange calldata representing a fill method.
/// @return The orders from the Exchange calldata.
/// @return orders The orders from the Exchange calldata.
function decodeOrdersFromFillData(bytes memory data)
public
pure
@@ -84,7 +85,6 @@ contract MixinCoordinatorApprovalVerifier is
bytes4 selector = data.readBytes4(0);
if (
selector == IExchange(address(0)).fillOrder.selector ||
selector == IExchange(address(0)).fillOrderNoThrow.selector ||
selector == IExchange(address(0)).fillOrKillOrder.selector
) {
// Decode single order
@@ -98,8 +98,10 @@ contract MixinCoordinatorApprovalVerifier is
selector == IExchange(address(0)).batchFillOrders.selector ||
selector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
selector == IExchange(address(0)).batchFillOrKillOrders.selector ||
selector == IExchange(address(0)).marketBuyOrders.selector ||
selector == IExchange(address(0)).marketSellOrders.selector
selector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
selector == IExchange(address(0)).marketBuyOrdersFillOrKill.selector ||
selector == IExchange(address(0)).marketSellOrdersNoThrow.selector ||
selector == IExchange(address(0)).marketSellOrdersFillOrKill.selector
) {
// Decode all orders
// solhint-disable indent
@@ -107,7 +109,10 @@ contract MixinCoordinatorApprovalVerifier is
data.slice(4, data.length),
(LibOrder.Order[])
);
} else if (selector == IExchange(address(0)).matchOrders.selector) {
} else if (
selector == IExchange(address(0)).matchOrders.selector ||
selector == IExchange(address(0)).matchOrdersWithMaximalFill.selector
) {
// Decode left and right orders
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
data.slice(4, data.length),
@@ -127,27 +132,24 @@ contract MixinCoordinatorApprovalVerifier is
/// @param orders Array of order structs containing order specifications.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
function _assertValidTransactionOrdersApproval(
LibZeroExTransaction.ZeroExTransaction memory transaction,
LibOrder.Order[] memory orders,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
internal
view
{
// Verify that Ethereum tx signer is the same as the approved txOrigin
require(
tx.origin == txOrigin,
"INVALID_ORIGIN"
);
if (tx.origin != txOrigin) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidOriginError(txOrigin));
}
// Hash 0x transaction
bytes32 transactionHash = getTransactionHash(transaction);
bytes32 transactionHash = LibZeroExTransaction.getTypedDataHash(transaction, EIP712_EXCHANGE_DOMAIN_HASH);
// Create empty list of approval signers
address[] memory approvalSignerAddresses = new address[](0);
@@ -155,21 +157,12 @@ contract MixinCoordinatorApprovalVerifier is
uint256 signaturesLength = approvalSignatures.length;
for (uint256 i = 0; i != signaturesLength; i++) {
// Create approval message
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
CoordinatorApproval memory approval = CoordinatorApproval({
txOrigin: txOrigin,
transactionHash: transactionHash,
transactionSignature: transactionSignature,
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
transactionSignature: transactionSignature
});
// Ensure approval has not expired
require(
// solhint-disable-next-line not-rely-on-time
currentApprovalExpirationTimeSeconds > block.timestamp,
"APPROVAL_EXPIRED"
);
// Hash approval message and recover signer address
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
@@ -191,10 +184,12 @@ contract MixinCoordinatorApprovalVerifier is
// Ensure feeRecipient of order has approved this 0x transaction
address approverAddress = orders[i].feeRecipientAddress;
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
require(
isOrderApproved,
"INVALID_APPROVAL_SIGNATURE"
);
if (!isOrderApproved) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidApprovalSignatureError(
transactionHash,
approverAddress
));
}
}
}
}

View File

@@ -20,41 +20,53 @@ pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-utils/contracts/src/Refundable.sol";
import "./libs/LibConstants.sol";
import "./interfaces/ICoordinatorCore.sol";
import "./interfaces/ICoordinatorApprovalVerifier.sol";
// solhint-disable no-empty-blocks
contract MixinCoordinatorCore is
Refundable,
LibConstants,
ICoordinatorApprovalVerifier,
ICoordinatorCore
{
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
/// @dev A payable fallback function that makes this contract "payable". This is necessary to allow
/// this contract to gracefully handle refunds from the Exchange.
function ()
external
payable
{}
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
/// each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
/// order in the transaction's Exchange calldata.
function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
public
payable
refundFinalBalance
{
// Validate that the 0x transaction has been approves by each feeRecipient
assertValidCoordinatorApprovals(
transaction,
txOrigin,
transactionSignature,
approvalExpirationTimeSeconds,
approvalSignatures
);
// Execute the transaction
EXCHANGE.executeTransaction(transaction, transactionSignature);
EXCHANGE.executeTransaction.value(msg.value)(transaction, transactionSignature);
}
}

View File

@@ -19,7 +19,9 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "./interfaces/ICoordinatorSignatureValidator.sol";
import "./libs/LibCoordinatorRichErrors.sol";
contract MixinSignatureValidator is
@@ -30,24 +32,32 @@ contract MixinSignatureValidator is
/// @dev Recovers the address of a signer given a hash and signature.
/// @param hash Any 32 byte hash.
/// @param signature Proof that the hash has been signed by signer.
/// @return signerAddress Address of the signer.
function getSignerAddress(bytes32 hash, bytes memory signature)
public
pure
returns (address signerAddress)
{
require(
signature.length > 0,
"LENGTH_GREATER_THAN_0_REQUIRED"
);
uint256 signatureLength = signature.length;
if (signatureLength == 0) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signature
));
}
// Pop last byte off of signature byte array.
uint8 signatureTypeRaw = uint8(signature.popLastByte());
uint8 signatureTypeRaw = uint8(signature[signature.length - 1]);
// Ensure signature is supported
require(
signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
"SIGNATURE_UNSUPPORTED"
);
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
hash,
signature
));
}
SignatureType signatureType = SignatureType(signatureTypeRaw);
@@ -57,25 +67,32 @@ contract MixinSignatureValidator is
// it an explicit option. This aids testing and analysis. It is
// also the initialization value for the enum type.
if (signatureType == SignatureType.Illegal) {
revert("SIGNATURE_ILLEGAL");
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
LibCoordinatorRichErrors.SignatureErrorCodes.ILLEGAL,
hash,
signature
));
// Always invalid signature.
// Like Illegal, this is always implicitly available and therefore
// offered explicitly. It can be implicitly created by providing
// a correctly formatted but incorrect signature.
} else if (signatureType == SignatureType.Invalid) {
require(
signature.length == 0,
"LENGTH_0_REQUIRED"
);
revert("SIGNATURE_INVALID");
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID,
hash,
signature
));
// Signature using EIP712
} else if (signatureType == SignatureType.EIP712) {
require(
signature.length == 65,
"LENGTH_65_REQUIRED"
);
if (signatureLength != 66) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signature
));
}
uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33);
@@ -89,10 +106,13 @@ contract MixinSignatureValidator is
// Signed using web3.eth_sign
} else if (signatureType == SignatureType.EthSign) {
require(
signature.length == 65,
"LENGTH_65_REQUIRED"
);
if (signatureLength != 66) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signature
));
}
uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33);
@@ -113,6 +133,10 @@ contract MixinSignatureValidator is
// that we currently support. In this case returning false
// may lead the caller to incorrectly believe that the
// signature was invalid.)
revert("SIGNATURE_UNSUPPORTED");
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
hash,
signature
));
}
}

View File

@@ -30,13 +30,12 @@ contract ICoordinatorApprovalVerifier {
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
/// order in the transaction's Exchange calldata.
function assertValidCoordinatorApprovals(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
public
@@ -44,7 +43,7 @@ contract ICoordinatorApprovalVerifier {
/// @dev Decodes the orders from Exchange calldata representing any fill method.
/// @param data Exchange calldata representing a fill method.
/// @return The orders from the Exchange calldata.
/// @return orders The orders from the Exchange calldata.
function decodeOrdersFromFillData(bytes memory data)
public
pure

View File

@@ -24,18 +24,19 @@ import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
contract ICoordinatorCore {
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
/// each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
/// order in the transaction's Exchange calldata.
function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
public;
public
payable;
}

View File

@@ -30,14 +30,14 @@ contract ICoordinatorSignatureValidator {
Wallet, // 0x04
Validator, // 0x05
PreSigned, // 0x06
OrderValidator, // 0x07
WalletOrderValidator, // 0x08
NSignatureTypes // 0x09, number of signature types. Always leave at end.
EIP1271Wallet, // 0x07
NSignatureTypes // 0x08, number of signature types. Always leave at end.
}
/// @dev Recovers the address of a signer given a hash and signature.
/// @param hash Any 32 byte hash.
/// @param signature Proof that the hash has been signed by signer.
/// @return signerAddress Address of the signer.
function getSignerAddress(bytes32 hash, bytes memory signature)
public
pure

View File

@@ -30,22 +30,24 @@ contract LibCoordinatorApproval is
// "CoordinatorApproval(",
// "address txOrigin,",
// "bytes32 transactionHash,",
// "bytes transactionSignature,",
// "uint256 approvalExpirationTimeSeconds",
// "bytes transactionSignature",
// ")"
// ));
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH =
0xa6511c04ca44625d50986f8c36bedc09366207a17b96e347094053a9f8507168;
struct CoordinatorApproval {
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
bytes32 transactionHash; // EIP712 hash of the transaction.
bytes transactionSignature; // Signature of the 0x transaction.
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the approval expires.
}
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage using the domain
/// separator of this contract.
/// @param approval Coordinator approval message containing the transaction hash, and transaction
/// signature.
/// @return approvalHash EIP712 hash of the Coordinator approval message with the domain
/// separator of this contract.
function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
public
view
@@ -55,9 +57,10 @@ contract LibCoordinatorApproval is
return approvalHash;
}
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage with no domain separator.
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
/// @return EIP712 hash of the Coordinator approval message with no domain separator.
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage with no domain separator.
/// @param approval Coordinator approval message containing the transaction hash, and transaction
// signature.
/// @return result EIP712 hash of the Coordinator approval message with no domain separator.
function _hashCoordinatorApproval(CoordinatorApproval memory approval)
internal
pure
@@ -67,7 +70,6 @@ contract LibCoordinatorApproval is
bytes memory transactionSignature = approval.transactionSignature;
address txOrigin = approval.txOrigin;
bytes32 transactionHash = approval.transactionHash;
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
// Assembly for more efficiently computing:
// keccak256(abi.encodePacked(
@@ -75,7 +77,6 @@ contract LibCoordinatorApproval is
// approval.txOrigin,
// approval.transactionHash,
// keccak256(approval.transactionSignature)
// approval.approvalExpirationTimeSeconds,
// ));
assembly {
@@ -89,9 +90,8 @@ contract LibCoordinatorApproval is
mstore(add(memPtr, 32), txOrigin) // txOrigin
mstore(add(memPtr, 64), transactionHash) // transactionHash
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
// Compute hash
result := keccak256(memPtr, 160)
result := keccak256(memPtr, 128)
}
return result;
}

View File

@@ -0,0 +1,87 @@
/*
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;
library LibCoordinatorRichErrors {
enum SignatureErrorCodes {
INVALID_LENGTH,
UNSUPPORTED,
ILLEGAL,
INVALID
}
// bytes4(keccak256("SignatureError(uint8,bytes32,bytes)"))
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
0x779c5223;
// bytes4(keccak256("InvalidOriginError(address)"))
bytes4 internal constant INVALID_ORIGIN_ERROR_SELECTOR =
0xa458d7ff;
// bytes4(keccak256("InvalidApprovalSignatureError(bytes32,address)"))
bytes4 internal constant INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR =
0xd789b640;
// solhint-disable func-name-mixedcase
function SignatureError(
SignatureErrorCodes errorCode,
bytes32 hash,
bytes memory signature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_ERROR_SELECTOR,
errorCode,
hash,
signature
);
}
function InvalidOriginError(
address expectedOrigin
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_ORIGIN_ERROR_SELECTOR,
expectedOrigin
);
}
function InvalidApprovalSignatureError(
bytes32 transactionHash,
address approverAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR,
transactionHash,
approverAddress
);
}
}

View File

@@ -21,15 +21,13 @@ pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
contract LibEIP712CoordinatorDomain is
LibEIP712
{
contract LibEIP712CoordinatorDomain {
// EIP712 Domain Name value for the Coordinator
string constant public EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
// EIP712 Domain Version value for the Coordinator
string constant public EIP712_COORDINATOR_DOMAIN_VERSION = "2.0.0";
string constant public EIP712_COORDINATOR_DOMAIN_VERSION = "3.0.0";
// Hash of the EIP712 Domain Separator data for the Coordinator
// solhint-disable-next-line var-name-mixedcase
@@ -43,7 +41,9 @@ contract LibEIP712CoordinatorDomain is
)
public
{
address verifyingContractAddress = verifyingContractAddressIfExists == address(0) ? address(this) : verifyingContractAddressIfExists;
address verifyingContractAddress = verifyingContractAddressIfExists == address(0)
? address(this)
: verifyingContractAddressIfExists;
EIP712_COORDINATOR_DOMAIN_HASH = LibEIP712.hashEIP712Domain(
EIP712_COORDINATOR_DOMAIN_NAME,
EIP712_COORDINATOR_DOMAIN_VERSION,
@@ -55,7 +55,7 @@ contract LibEIP712CoordinatorDomain is
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
/// of this contract.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to this EIP712 Domain.
/// @return result EIP712 hash applied to this EIP712 Domain.
function _hashEIP712CoordinatorMessage(bytes32 hashStruct)
internal
view

View File

@@ -29,7 +29,7 @@ contract MixinCoordinatorRegistryCore is
mapping (address => string) internal coordinatorEndpoints;
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
/// @param coordinatorEndpoint endpoint of the Coordinator.
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
address coordinatorOperator = msg.sender;
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
@@ -37,7 +37,8 @@ contract MixinCoordinatorRegistryCore is
}
/// @dev Gets the endpoint for a Coordinator.
/// @param coordinatorOperator operator of the Coordinator endpoint.
/// @param coordinatorOperator Operator of the Coordinator endpoint.
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
function getCoordinatorEndpoint(address coordinatorOperator)
external
view

View File

@@ -29,11 +29,12 @@ contract ICoordinatorRegistryCore
);
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
/// @param coordinatorEndpoint endpoint of the Coordinator.
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
/// @dev Gets the endpoint for a Coordinator.
/// @param coordinatorOperator operator of the Coordinator endpoint.
/// @param coordinatorOperator Operator of the Coordinator endpoint.
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
function getCoordinatorEndpoint(address coordinatorOperator)
external
view

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "2.1.0-beta.0",
"version": "2.1.0-beta.1",
"engines": {
"node": ">=6.12"
},
@@ -22,7 +22,7 @@
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
@@ -35,7 +35,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry).json",
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry|ICoordinatorApprovalVerifier|ICoordinatorCore|ICoordinatorRegistryCore|ICoordinatorSignatureValidator|LibConstants|LibCoordinatorApproval|LibCoordinatorRichErrors|LibEIP712CoordinatorDomain|MixinCoordinatorApprovalVerifier|MixinCoordinatorCore|MixinCoordinatorRegistryCore|MixinSignatureValidator).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -48,12 +48,17 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-asset-proxy": "^2.3.0-beta.1",
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
"@0x/contracts-erc20": "^2.3.0-beta.1",
"@0x/contracts-exchange": "^2.2.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/contracts-test-utils": "^3.2.0-beta.1",
"@0x/dev-utils": "^2.4.0-beta.1",
"@0x/order-utils": "^8.5.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -61,6 +66,7 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
@@ -71,20 +77,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-exchange": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"ethereumjs-util": "^5.1.1"
},
"publishConfig": {
"access": "public"

View File

@@ -7,7 +7,31 @@ import { ContractArtifact } from 'ethereum-types';
import * as Coordinator from '../generated-artifacts/Coordinator.json';
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
import * as ICoordinatorApprovalVerifier from '../generated-artifacts/ICoordinatorApprovalVerifier.json';
import * as ICoordinatorCore from '../generated-artifacts/ICoordinatorCore.json';
import * as ICoordinatorRegistryCore from '../generated-artifacts/ICoordinatorRegistryCore.json';
import * as ICoordinatorSignatureValidator from '../generated-artifacts/ICoordinatorSignatureValidator.json';
import * as LibConstants from '../generated-artifacts/LibConstants.json';
import * as LibCoordinatorApproval from '../generated-artifacts/LibCoordinatorApproval.json';
import * as LibCoordinatorRichErrors from '../generated-artifacts/LibCoordinatorRichErrors.json';
import * as LibEIP712CoordinatorDomain from '../generated-artifacts/LibEIP712CoordinatorDomain.json';
import * as MixinCoordinatorApprovalVerifier from '../generated-artifacts/MixinCoordinatorApprovalVerifier.json';
import * as MixinCoordinatorCore from '../generated-artifacts/MixinCoordinatorCore.json';
import * as MixinCoordinatorRegistryCore from '../generated-artifacts/MixinCoordinatorRegistryCore.json';
import * as MixinSignatureValidator from '../generated-artifacts/MixinSignatureValidator.json';
export const artifacts = {
Coordinator: Coordinator as ContractArtifact,
MixinCoordinatorApprovalVerifier: MixinCoordinatorApprovalVerifier as ContractArtifact,
MixinCoordinatorCore: MixinCoordinatorCore as ContractArtifact,
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
ICoordinatorApprovalVerifier: ICoordinatorApprovalVerifier as ContractArtifact,
ICoordinatorCore: ICoordinatorCore as ContractArtifact,
ICoordinatorSignatureValidator: ICoordinatorSignatureValidator as ContractArtifact,
LibConstants: LibConstants as ContractArtifact,
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
MixinCoordinatorRegistryCore: MixinCoordinatorRegistryCore as ContractArtifact,
ICoordinatorRegistryCore: ICoordinatorRegistryCore as ContractArtifact,
};

View File

@@ -5,3 +5,15 @@
*/
export * from '../generated-wrappers/coordinator';
export * from '../generated-wrappers/coordinator_registry';
export * from '../generated-wrappers/i_coordinator_approval_verifier';
export * from '../generated-wrappers/i_coordinator_core';
export * from '../generated-wrappers/i_coordinator_registry_core';
export * from '../generated-wrappers/i_coordinator_signature_validator';
export * from '../generated-wrappers/lib_constants';
export * from '../generated-wrappers/lib_coordinator_approval';
export * from '../generated-wrappers/lib_coordinator_rich_errors';
export * from '../generated-wrappers/lib_e_i_p712_coordinator_domain';
export * from '../generated-wrappers/mixin_coordinator_approval_verifier';
export * from '../generated-wrappers/mixin_coordinator_core';
export * from '../generated-wrappers/mixin_coordinator_registry_core';
export * from '../generated-wrappers/mixin_signature_validator';

View File

@@ -1,531 +0,0 @@
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import {
artifacts as exchangeArtifacts,
constants as exchangeConstants,
ExchangeCancelEventArgs,
ExchangeCancelUpToEventArgs,
ExchangeContract,
exchangeDataEncoder,
ExchangeFillEventArgs,
ExchangeFunctionName,
} from '@0x/contracts-exchange';
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
getLatestBlockTimestampAsync,
OrderFactory,
provider,
TransactionFactory,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { RevertReason } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
// tslint:disable:no-unnecessary-type-assertion
describe('Coordinator tests', () => {
let chainId: number;
let makerAddress: string;
let owner: string;
let takerAddress: string;
let feeRecipientAddress: string;
let erc20Proxy: ERC20ProxyContract;
let erc20TokenA: DummyERC20TokenContract;
let erc20TokenB: DummyERC20TokenContract;
let makerFeeToken: DummyERC20TokenContract;
let coordinatorContract: CoordinatorContract;
let exchange: ExchangeContract;
let erc20Wrapper: ERC20Wrapper;
let orderFactory: OrderFactory;
let takerTransactionFactory: TransactionFactory;
let makerTransactionFactory: TransactionFactory;
let approvalFactory: ApprovalFactory;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
chainId = await providerUtils.getChainIdAsync(provider);
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4));
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
erc20Proxy = await erc20Wrapper.deployProxyAsync();
const numDummyErc20ToDeploy = 3;
[erc20TokenA, erc20TokenB, makerFeeToken] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy,
constants.DUMMY_TOKEN_DECIMALS,
);
await erc20Wrapper.setBalancesAndAllowancesAsync();
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
provider,
txDefaults,
new BigNumber(chainId),
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator,
provider,
txDefaults,
artifacts,
exchange.address,
new BigNumber(chainId),
);
// Configure order defaults
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
senderAddress: coordinatorContract.address,
makerAddress,
feeRecipientAddress,
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
exchangeAddress: exchange.address,
chainId,
};
const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
const feeRecipientPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId);
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('single order fills', () => {
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
it(`${fnName} should fill the order with a signed approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress },
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it(`${fnName} should fill the order if called by approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
feeRecipientAddress,
transaction.signature,
[],
[],
{ from: feeRecipientAddress },
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it(`${fnName} should revert with no approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[],
[],
{
from: takerAddress,
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
},
),
RevertReason.InvalidApprovalSignature,
);
});
it(`${fnName} should revert with an invalid approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
{ from: takerAddress },
),
RevertReason.InvalidApprovalSignature,
);
});
it(`${fnName} should revert with an expired approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress },
),
RevertReason.ApprovalExpired,
);
});
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: owner },
),
RevertReason.InvalidOrigin,
);
});
}
});
describe('batch order fills', () => {
for (const fnName of [...exchangeConstants.MARKET_FILL_FN_NAMES, ...exchangeConstants.BATCH_FILL_FN_NAMES]) {
it(`${fnName} should fill the orders with a signed approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it(`${fnName} should fill the orders if called by approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
feeRecipientAddress,
transaction.signature,
[],
[],
{ from: feeRecipientAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it(`${fnName} should revert with an invalid approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
{ from: takerAddress },
),
RevertReason.InvalidApprovalSignature,
);
});
it(`${fnName} should revert with an expired approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress },
),
RevertReason.ApprovalExpired,
);
});
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: owner },
),
RevertReason.InvalidOrigin,
);
});
}
});
describe('cancels', () => {
it('cancelOrder call should be successful without an approval', async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it('batchCancelOrders call should be successful without an approval', async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it('cancelOrdersUpTo call should be successful without an approval', async () => {
const targetEpoch = constants.ZERO_AMOUNT;
const data = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetEpoch);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.orderSenderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(targetEpoch.plus(1));
});
});
});
// tslint:disable:max-file-line-count

View File

@@ -1,81 +1,72 @@
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import { blockchainTests, expect, verifyEvents } from '@0x/contracts-test-utils';
import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
import { artifacts, CoordinatorRegistryContract, CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
// tslint:disable:no-unnecessary-type-assertion
describe('Coordinator Registry tests', () => {
blockchainTests.resets('Coordinator Registry tests', env => {
let coordinatorRegistry: CoordinatorRegistryContract;
let coordinatorOperator: string;
const coordinatorEndpoint = 'http://sometec.0x.org';
const nilCoordinatorEndpoint = '';
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// setup accounts (skip owner)
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const accounts = await env.getAccountAddressesAsync();
[, coordinatorOperator] = accounts;
// deploy coordinator registry
coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider);
await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
artifacts.CoordinatorRegistry,
env.provider,
env.txDefaults,
artifacts,
);
});
describe('core', () => {
it('Should successfully set a Coordinator endpoint', async () => {
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(coordinatorEndpoint, {
from: coordinatorOperator,
});
const recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
coordinatorOperator,
);
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
});
it('Should successfully unset a Coordinator endpoint', async () => {
// set Coordinator endpoint
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(coordinatorEndpoint, {
from: coordinatorOperator,
});
let recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
coordinatorOperator,
);
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
// unset Coordinator endpoint
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, nilCoordinatorEndpoint);
recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(nilCoordinatorEndpoint, {
from: coordinatorOperator,
});
recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
coordinatorOperator,
);
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
});
it('Should emit an event when setting Coordinator endpoint', async () => {
// set Coordinator endpoint
const txReceipt = await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
coordinatorOperator,
const txReceipt = await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(
coordinatorEndpoint,
{
from: coordinatorOperator,
},
);
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
const recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
coordinatorOperator,
);
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
// validate event
expect(txReceipt.logs.length).to.be.equal(1);
const log = txReceipt.logs[0] as LogWithDecodedArgs<CoordinatorRegistryCoordinatorEndpointSetEventArgs>;
expect(log.args.coordinatorOperator).to.be.equal(coordinatorOperator);
expect(log.args.coordinatorEndpoint).to.be.equal(coordinatorEndpoint);
const expectedEvent: CoordinatorRegistryCoordinatorEndpointSetEventArgs = {
coordinatorOperator,
coordinatorEndpoint,
};
verifyEvents(txReceipt, [expectedEvent], 'CoordinatorEndpointSet');
});
});
});

View File

@@ -1,67 +1,32 @@
import { chaiSetup, constants, provider, randomAddress, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
import { transactionHashUtils } from '@0x/order-utils';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import { BigNumber } from '@0x/utils';
import { artifacts, CoordinatorContract, hashUtils } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Libs tests', () => {
blockchainTests.resets('Libs tests', env => {
let coordinatorContract: CoordinatorContract;
let chainId: number;
const exchangeAddress = randomAddress();
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
chainId = await providerUtils.getChainIdAsync(provider);
chainId = await env.getChainIdAsync();
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
exchangeAddress,
new BigNumber(chainId),
);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('getTransactionHash', () => {
it('should return the correct transaction hash', async () => {
const tx = {
salt: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0),
signerAddress: constants.NULL_ADDRESS,
data: '0x1234',
domain: {
verifyingContract: exchangeAddress,
chainId,
},
};
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
const txHash = await coordinatorContract.getTransactionHash.callAsync(tx);
expect(expectedTxHash).to.eq(txHash);
});
});
describe('getApprovalHash', () => {
it('should return the correct approval hash', async () => {
const signedTx = {
salt: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0),
salt: constants.ZERO_AMOUNT,
gasPrice: constants.ZERO_AMOUNT,
expirationTimeSeconds: constants.ZERO_AMOUNT,
signerAddress: constants.NULL_ADDRESS,
data: '0x1234',
signature: '0x5678',
@@ -70,20 +35,13 @@ describe('Libs tests', () => {
chainId,
},
};
const approvalExpirationTimeSeconds = new BigNumber(0);
const txOrigin = constants.NULL_ADDRESS;
const approval = {
txOrigin,
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
transactionSignature: signedTx.signature,
approvalExpirationTimeSeconds,
};
const expectedApprovalHash = hashUtils.getApprovalHashHex(
signedTx,
coordinatorContract.address,
txOrigin,
approvalExpirationTimeSeconds,
);
const expectedApprovalHash = hashUtils.getApprovalHashHex(signedTx, coordinatorContract.address, txOrigin);
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
expect(expectedApprovalHash).to.eq(approvalHash);
});

View File

@@ -1,29 +1,20 @@
import { constants as exchangeConstants, exchangeDataEncoder, ExchangeFunctionName } from '@0x/contracts-exchange';
import {
chaiSetup,
blockchainTests,
constants,
expectContractCallFailedAsync,
getLatestBlockTimestampAsync,
provider,
expect,
hexConcat,
hexSlice,
randomAddress,
TransactionFactory,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { transactionHashUtils } from '@0x/order-utils';
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
import { BigNumber, LibBytesRevertErrors, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import { CoordinatorRevertErrors, transactionHashUtils } from '@0x/order-utils';
import { SignatureType, SignedOrder } from '@0x/types';
import { BigNumber, LibBytesRevertErrors } from '@0x/utils';
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Mixins tests', () => {
blockchainTests.resets('Mixins tests', env => {
let chainId: number;
let transactionSignerAddress: string;
let approvalSignerAddress1: string;
@@ -36,23 +27,17 @@ describe('Mixins tests', () => {
const exchangeAddress = randomAddress();
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
chainId = await providerUtils.getChainIdAsync(provider);
chainId = await env.getChainIdAsync();
mixins = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator,
provider,
txDefaults,
env.provider,
env.txDefaults,
artifacts,
exchangeAddress,
new BigNumber(chainId),
);
const accounts = await web3Wrapper.getAvailableAddressesAsync();
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3);
const accounts = await env.getAccountAddressesAsync();
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts;
defaultOrder = {
makerAddress: constants.NULL_ADDRESS,
takerAddress: constants.NULL_ADDRESS,
@@ -79,12 +64,6 @@ describe('Mixins tests', () => {
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('getSignerAddress', () => {
it('should return the correct address using the EthSign signature type', async () => {
@@ -104,37 +83,62 @@ describe('Mixins tests', () => {
it('should revert with with the Illegal signature type', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex');
transaction.signature = `${transaction.signature.slice(
0,
transaction.signature.length - 2,
)}${illegalSignatureByte}`;
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.Illegal,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
RevertReason.SignatureIllegal,
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
new CoordinatorRevertErrors.SignatureError(
CoordinatorRevertErrors.SignatureErrorCodes.Illegal,
transactionHash,
transaction.signature,
),
);
});
it('should revert with with the Invalid signature type', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
transaction.signature = `0x${invalidSignatureByte}`;
transaction.signature = hexConcat(SignatureType.Invalid);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
RevertReason.SignatureInvalid,
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
new CoordinatorRevertErrors.SignatureError(
CoordinatorRevertErrors.SignatureErrorCodes.Invalid,
transactionHash,
transaction.signature,
),
);
});
it("should revert with with a signature type that doesn't exist", async () => {
it('should revert with with a signature type that equals `NSignatureTypes`', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const invalidSignatureByte = '04';
transaction.signature = `${transaction.signature.slice(
0,
transaction.signature.length - 2,
)}${invalidSignatureByte}`;
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.NSignatureTypes,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
RevertReason.SignatureUnsupported,
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
new CoordinatorRevertErrors.SignatureError(
CoordinatorRevertErrors.SignatureErrorCodes.Unsupported,
transactionHash,
transaction.signature,
),
);
});
it("should revert with with a signature type that isn't supported", async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.Wallet,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
new CoordinatorRevertErrors.SignatureError(
CoordinatorRevertErrors.SignatureErrorCodes.Unsupported,
transactionHash,
transaction.signature,
),
);
});
});
@@ -182,11 +186,21 @@ describe('Mixins tests', () => {
expect(orders).to.deep.eq(decodedSignedOrders);
});
}
for (const fnName of [
ExchangeFunctionName.CancelOrder,
ExchangeFunctionName.BatchCancelOrders,
ExchangeFunctionName.CancelOrdersUpTo,
]) {
for (const fnName of exchangeConstants.MATCH_ORDER_FN_NAMES) {
it(`should correctly decode the orders for ${fnName} data`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
const decodedSignedOrders = decodedOrders.map(order => ({
...order,
signature: constants.NULL_BYTES,
exchangeAddress: constants.NULL_ADDRESS,
chainId,
}));
expect(orders).to.deep.eq(decodedSignedOrders);
});
}
for (const fnName of exchangeConstants.CANCEL_ORDER_FN_NAMES) {
it(`should correctly decode the orders for ${fnName} data`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
@@ -214,27 +228,20 @@ describe('Mixins tests', () => {
describe('Single order approvals', () => {
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: transactionSignerAddress },
);
});
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1], expiration=[valid]`, async () => {
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1]`, async () => {
const order = {
...defaultOrder,
senderAddress: constants.NULL_ADDRESS,
@@ -242,23 +249,16 @@ describe('Mixins tests', () => {
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: transactionSignerAddress },
);
});
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
@@ -267,33 +267,25 @@ describe('Mixins tests', () => {
approvalSignerAddress1,
transaction.signature,
[],
[],
{
from: approvalSignerAddress1,
},
);
});
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: approvalSignerAddress1 },
);
});
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
@@ -302,81 +294,48 @@ describe('Mixins tests', () => {
approvalSignerAddress1,
transaction.signature,
[],
[],
{
from: approvalSignerAddress1,
},
);
});
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const signature = hexConcat(
hexSlice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
);
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
transaction.signature,
[signature],
{ from: transactionSignerAddress },
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
{ from: transactionSignerAddress },
),
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
);
});
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: transactionSignerAddress },
),
RevertReason.ApprovalExpired,
);
});
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: approvalSignerAddress2 },
),
RevertReason.InvalidOrigin,
transaction.signature,
[approval.signature],
{ from: approvalSignerAddress2 },
);
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
});
}
});
@@ -384,52 +343,38 @@ describe('Mixins tests', () => {
for (const fnName of [
...exchangeConstants.BATCH_FILL_FN_NAMES,
...exchangeConstants.MARKET_FILL_FN_NAMES,
ExchangeFunctionName.MatchOrders,
...exchangeConstants.MATCH_ORDER_FN_NAMES,
]) {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: transactionSignerAddress },
);
});
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
const orders = [defaultOrder, defaultOrder].map(order => ({
...order,
senderAddress: constants.NULL_ADDRESS,
}));
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: transactionSignerAddress },
);
});
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[]`, async () => {
const orders = [defaultOrder, defaultOrder].map(order => ({
...order,
senderAddress: constants.NULL_ADDRESS,
@@ -441,56 +386,37 @@ describe('Mixins tests', () => {
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
});
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, senderAddress: constants.NULL_ADDRESS }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: transactionSignerAddress },
);
});
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
[approval1.signature, approval2.signature],
{ from: transactionSignerAddress },
);
});
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
@@ -499,200 +425,125 @@ describe('Mixins tests', () => {
approvalSignerAddress1,
transaction.signature,
[],
[],
{ from: approvalSignerAddress1 },
);
});
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
{ from: approvalSignerAddress1 },
),
RevertReason.InvalidOrigin,
transaction.signature,
[approval2.signature],
{ from: approvalSignerAddress1 },
);
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
});
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
),
RevertReason.InvalidApprovalSignature,
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[],
{ from: transactionSignerAddress },
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
);
});
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const signature = hexConcat(
hexSlice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
);
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
transaction.signature,
[signature],
{ from: transactionSignerAddress },
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
{ from: transactionSignerAddress },
),
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
);
});
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
const approvalSignature2 = hexConcat(
hexSlice(approval2.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval2.signature, 6),
);
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
transaction.signature,
[approval1.signature, approvalSignature2],
{ from: transactionSignerAddress },
);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
[approval1.signature, approvalSignature2],
{ from: transactionSignerAddress },
),
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress2),
);
});
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
const approvalSignature2 = hexConcat(
hexSlice(approval2.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval2.signature, 6),
);
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approvalSignature2],
{ from: approvalSignerAddress1 },
),
RevertReason.InvalidApprovalSignature,
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[approvalSignature2],
{ from: approvalSignerAddress1 },
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress2),
);
});
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds1,
);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds2,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
[approval1.signature, approval2.signature],
{ from: transactionSignerAddress },
),
RevertReason.ApprovalExpired,
);
});
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
approvalSignerAddress1,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval2.signature],
{ from: approvalSignerAddress1 },
),
RevertReason.ApprovalExpired,
);
});
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
approvalExpirationTimeSeconds,
);
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval1.signature],
{ from: approvalSignerAddress2 },
),
RevertReason.InvalidOrigin,
transaction.signature,
[approval1.signature],
{ from: approvalSignerAddress2 },
);
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
});
}
});
@@ -706,7 +557,6 @@ describe('Mixins tests', () => {
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
});
@@ -719,7 +569,6 @@ describe('Mixins tests', () => {
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
});
@@ -731,7 +580,6 @@ describe('Mixins tests', () => {
transactionSignerAddress,
transaction.signature,
[],
[],
{ from: transactionSignerAddress },
);
});

View File

@@ -1,7 +1,5 @@
import { signingUtils } from '@0x/contracts-test-utils';
import { hexConcat, signingUtils } from '@0x/contracts-test-utils';
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import { hashUtils, SignedCoordinatorApproval } from './index';
@@ -17,21 +15,14 @@ export class ApprovalFactory {
public newSignedApproval(
transaction: SignedZeroExTransaction,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
signatureType: SignatureType = SignatureType.EthSign,
): SignedCoordinatorApproval {
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
transaction,
this._verifyingContractAddress,
txOrigin,
approvalExpirationTimeSeconds,
);
const approvalHashBuff = hashUtils.getApprovalHashBuffer(transaction, this._verifyingContractAddress, txOrigin);
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
const signedApproval = {
txOrigin,
transaction,
approvalExpirationTimeSeconds,
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
signature: hexConcat(signatureBuff),
};
return signedApproval;
}

View File

@@ -1,65 +0,0 @@
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
import { artifacts, CoordinatorRegistryContract } from '../../src';
export class CoordinatorRegistryWrapper {
private readonly _web3Wrapper: Web3Wrapper;
private readonly _provider: ZeroExProvider;
private readonly _logDecoder: LogDecoder;
private _coordinatorRegistryContract?: CoordinatorRegistryContract;
/**
* Instanitates an CoordinatorRegistryWrapper
* @param provider Web3 provider to use for all JSON RPC requests
* Instance of CoordinatorRegistryWrapper
*/
constructor(provider: ZeroExProvider) {
this._web3Wrapper = new Web3Wrapper(provider);
this._provider = provider;
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
}
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
artifacts.CoordinatorRegistry,
this._provider,
txDefaults,
artifacts,
);
if (this._coordinatorRegistryContract === undefined) {
throw new Error(`Failed to deploy Coordinator Registry contract.`);
}
return this._coordinatorRegistryContract;
}
public async setCoordinatorEndpointAsync(
coordinatorOperator: string,
coordinatorEndpoint: string,
): Promise<TransactionReceiptWithDecodedLogs> {
this._assertCoordinatorRegistryDeployed();
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
await (this
._coordinatorRegistryContract as CoordinatorRegistryContract).setCoordinatorEndpoint.sendTransactionAsync(
coordinatorEndpoint,
{
from: coordinatorOperator,
},
),
);
return txReceipt;
}
public async getCoordinatorEndpointAsync(coordinatorOperator: string): Promise<string> {
this._assertCoordinatorRegistryDeployed();
const coordinatorEndpoint = await (this
._coordinatorRegistryContract as CoordinatorRegistryContract).getCoordinatorEndpoint.callAsync(
coordinatorOperator,
);
return coordinatorEndpoint;
}
private _assertCoordinatorRegistryDeployed(): void {
if (this._coordinatorRegistryContract === undefined) {
throw new Error(
'The Coordinator Registry contract was not deployed through the CoordinatorRegistryWrapper. Call `deployCoordinatorRegistryAsync` to deploy.',
);
}
}
}

View File

@@ -1,33 +1,16 @@
import { hexConcat } from '@0x/contracts-test-utils';
import { eip712Utils } from '@0x/order-utils';
import { SignedZeroExTransaction } from '@0x/types';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as _ from 'lodash';
import { signTypedDataUtils } from '@0x/utils';
export const hashUtils = {
getApprovalHashBuffer(
transaction: SignedZeroExTransaction,
verifyingContract: string,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): Buffer {
const typedData = eip712Utils.createCoordinatorApprovalTypedData(
transaction,
verifyingContract,
txOrigin,
approvalExpirationTimeSeconds,
);
getApprovalHashBuffer(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): Buffer {
const typedData = eip712Utils.createCoordinatorApprovalTypedData(transaction, verifyingContract, txOrigin);
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
return hashBuffer;
},
getApprovalHashHex(
transaction: SignedZeroExTransaction,
verifyingContract: string,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): string {
const hashHex = `0x${hashUtils
.getApprovalHashBuffer(transaction, verifyingContract, txOrigin, approvalExpirationTimeSeconds)
.toString('hex')}`;
getApprovalHashHex(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): string {
const hashHex = hexConcat(hashUtils.getApprovalHashBuffer(transaction, verifyingContract, txOrigin));
return hashHex;
},
};

View File

@@ -1,10 +1,8 @@
import { SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
export interface CoordinatorApproval {
transaction: SignedZeroExTransaction;
txOrigin: string;
approvalExpirationTimeSeconds: BigNumber;
}
export interface SignedCoordinatorApproval extends CoordinatorApproval {

View File

@@ -2,6 +2,21 @@
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": ["generated-artifacts/Coordinator.json", "generated-artifacts/CoordinatorRegistry.json"],
"files": [
"generated-artifacts/Coordinator.json",
"generated-artifacts/CoordinatorRegistry.json",
"generated-artifacts/ICoordinatorApprovalVerifier.json",
"generated-artifacts/ICoordinatorCore.json",
"generated-artifacts/ICoordinatorRegistryCore.json",
"generated-artifacts/ICoordinatorSignatureValidator.json",
"generated-artifacts/LibConstants.json",
"generated-artifacts/LibCoordinatorApproval.json",
"generated-artifacts/LibCoordinatorRichErrors.json",
"generated-artifacts/LibEIP712CoordinatorDomain.json",
"generated-artifacts/MixinCoordinatorApprovalVerifier.json",
"generated-artifacts/MixinCoordinatorCore.json",
"generated-artifacts/MixinCoordinatorRegistryCore.json",
"generated-artifacts/MixinSignatureValidator.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@@ -1,4 +1,18 @@
[
{
"version": "0.1.0-beta.1",
"changes": [
{
"note": "Add `encodeStaticCallAssetData` and `decodeStaticCallAssetData` in LibAssetData",
"pr": 2034
},
{
"note": "Add `revertIfInvalidAssetData` in LibAssetData",
"pr": 2034
}
],
"timestamp": 1573159180
},
{
"version": "0.1.0-beta.0",
"changes": [

View File

@@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.1.0-beta.1 - _November 7, 2019_
* Add `encodeStaticCallAssetData` and `decodeStaticCallAssetData` in LibAssetData (#2034)
* Add `revertIfInvalidAssetData` in LibAssetData (#2034)
## v0.1.0-beta.0 - _October 3, 2019_
* Use built in selectors instead of hard coded constants (#2055)

View File

@@ -316,6 +316,29 @@ contract LibAssetData {
return (balances, allowances);
}
/// @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)
public
pure
returns (
bytes4 assetProxyId
)
{
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == IAssetData(address(0)).ERC20Token.selector ||
assetProxyId == IAssetData(address(0)).ERC721Token.selector ||
assetProxyId == IAssetData(address(0)).ERC1155Assets.selector ||
assetProxyId == IAssetData(address(0)).MultiAsset.selector ||
assetProxyId == IAssetData(address(0)).StaticCall.selector,
"WRONG_PROXY_ID"
);
return assetProxyId;
}
/// @dev Encode ERC-20 asset data into the format described in the AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-20 contract hosting the asset to be traded.
/// @return AssetProxy-compliant data describing the asset.
@@ -330,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 ERC-20 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
@@ -515,4 +538,75 @@ contract LibAssetData {
);
// solhint-enable indent
}
/// @dev Encode StaticCall asset data into the format described in the AssetProxy contract specification.
/// @param staticCallTargetAddress Target address of StaticCall.
/// @param staticCallData Data that will be passed to staticCallTargetAddress in the StaticCall.
/// @param expectedReturnDataHash Expected Keccak-256 hash of the StaticCall return data.
/// @return AssetProxy-compliant asset data describing the set of assets.
function encodeStaticCallAssetData(
address staticCallTargetAddress,
bytes memory staticCallData,
bytes32 expectedReturnDataHash
)
public
pure
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(
IAssetData(address(0)).StaticCall.selector,
staticCallTargetAddress,
staticCallData,
expectedReturnDataHash
);
return assetData;
}
/// @dev Decode StaticCall asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing a StaticCall asset
/// @return The StaticCall AssetProxy identifier, the target address of the StaticCAll, the data to be
/// passed to the target address, and the expected Keccak-256 hash of the static call return data.
function decodeStaticCallAssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address staticCallTargetAddress,
bytes memory staticCallData,
bytes32 expectedReturnDataHash
)
{
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == IAssetData(address(0)).StaticCall.selector,
"WRONG_PROXY_ID"
);
(staticCallTargetAddress, staticCallData, expectedReturnDataHash) = abi.decode(
assetData.slice(4, assetData.length),
(address, bytes, bytes32)
);
}
function revertIfInvalidAssetData(bytes memory assetData)
public
pure
{
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
decodeERC20AssetData(assetData);
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
decodeERC721AssetData(assetData);
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
decodeERC1155AssetData(assetData);
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
decodeMultiAssetData(assetData);
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
decodeStaticCallAssetData(assetData);
} else {
revert("WRONG_PROXY_ID");
}
}
}

View File

@@ -1,35 +1,22 @@
{
"name": "@0x/contracts-dev-utils",
"version": "0.1.0-beta.0",
"version": "0.1.0-beta.1",
"engines": {
"node": ">=6.12"
},
"description": "0x protocol specific utility contracts",
"main": "lib/src/index.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "yarn pre_build && tsc -b",
"test": "echo !!! Run tests in @0x/contracts-tests instead !!!",
"build:ci": "yarn build",
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"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": "UNLIMITED_CONTRACT_SIZE=true mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler:report:html": "istanbul report html && open coverage/index.html",
"coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test",
"contracts:gen": "contracts-gen",
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"quantify_bytecode": "echo EVM bytecode object lengths:;for i in ./generated-artifacts/*.json; do node -e \"console.log('$i\t' + (require('$i').compilerOutput.evm.bytecode.object.length - 2) / 2)\"; done",
@@ -49,21 +36,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/node": "*",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
"solhint": "^1.4.1",
@@ -72,21 +49,15 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc1155": "^1.2.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-exchange": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"ethereumjs-util": "^5.1.1"
"@0x/assert": "^2.2.0-beta.1",
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/contract-addresses": "^3.3.0-beta.2",
"@0x/json-schemas": "^4.1.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"ethers": "~4.0.4"
},
"publishConfig": {
"access": "public"

View File

@@ -1,10 +1,23 @@
[
{
"version": "1.2.0-beta.1",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1573159180
},
{
"version": "1.2.0-beta.0",
"changes": [
{
"note": "Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`.",
"pr": 1819
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1570135330

View File

@@ -5,9 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.2.0-beta.1 - _November 7, 2019_
* Dependencies updated
## v1.2.0-beta.0 - _October 3, 2019_
* Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`. (#1819)
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v1.1.15 - _September 17, 2019_

View File

@@ -18,7 +18,7 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibAddress.sol";
import "./interfaces/IERC1155.sol";
import "./interfaces/IERC1155Receiver.sol";
@@ -26,11 +26,11 @@ import "./MixinNonFungibleToken.sol";
contract ERC1155 is
SafeMath,
IERC1155,
MixinNonFungibleToken
{
using LibAddress for address;
using LibSafeMath for uint256;
// selectors for receiver callbacks
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
@@ -88,11 +88,11 @@ contract ERC1155 is
nfOwners[id] = to;
// You could keep balance of NF type in base type id like so:
// uint256 baseType = getNonFungibleBaseType(_id);
// balances[baseType][_from] = balances[baseType][_from]._safeSub(_value);
// balances[baseType][_to] = balances[baseType][_to]._safeAdd(_value);
// balances[baseType][_from] = balances[baseType][_from].safeSub(_value);
// balances[baseType][_to] = balances[baseType][_to].safeAdd(_value);
} else {
balances[id][from] = _safeSub(balances[id][from], value);
balances[id][to] = _safeAdd(balances[id][to], value);
balances[id][from] = balances[id][from].safeSub(value);
balances[id][to] = balances[id][to].safeAdd(value);
}
emit TransferSingle(msg.sender, from, to, id, value);
@@ -170,8 +170,8 @@ contract ERC1155 is
);
nfOwners[id] = to;
} else {
balances[id][from] = _safeSub(balances[id][from], value);
balances[id][to] = _safeAdd(balances[id][to], value);
balances[id][from] = balances[id][from].safeSub(value);
balances[id][to] = balances[id][to].safeAdd(value);
}
}
emit TransferBatch(msg.sender, from, to, ids, values);

View File

@@ -1,6 +1,6 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./ERC1155.sol";
import "./interfaces/IERC1155Mintable.sol";
@@ -11,6 +11,7 @@ contract ERC1155Mintable is
IERC1155Mintable,
ERC1155
{
using LibSafeMath for uint256;
/// token nonce
uint256 internal nonce;
@@ -37,7 +38,7 @@ contract ERC1155Mintable is
)
external
returns (uint256 type_)
{
{
// Store the type in the upper 128 bits
type_ = (++nonce << 128);
@@ -114,7 +115,7 @@ contract ERC1155Mintable is
uint256 quantity = quantities[i];
// Grant the items to the caller
balances[id][dst] = _safeAdd(quantity, balances[id][dst]);
balances[id][dst] = quantity.safeAdd(balances[id][dst]);
// Emit the Transfer/Mint event.
// the 0x0 source address implies a mint
@@ -172,7 +173,7 @@ contract ERC1155Mintable is
nfOwners[id] = dst;
// You could use base-type id to store NF type balances if you wish.
// balances[_type][dst] = quantity._safeAdd(balances[_type][dst]);
// balances[_type][dst] = quantity.safeAdd(balances[_type][dst]);
emit TransferSingle(msg.sender, address(0x0), dst, id, 1);
@@ -194,6 +195,6 @@ contract ERC1155Mintable is
// record the `maxIndex` of this nft type
// this allows us to mint more nft's of this type in a subsequent call.
maxIndex[type_] = _safeAdd(to.length, maxIndex[type_]);
maxIndex[type_] = to.length.safeAdd(maxIndex[type_]);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "1.2.0-beta.0",
"version": "1.2.0-beta.1",
"engines": {
"node": ">=6.12"
},
@@ -22,7 +22,7 @@
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
@@ -48,11 +48,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/dev-utils": "^2.4.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -70,14 +70,14 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/contracts-test-utils": "^3.2.0-beta.1",
"@0x/contracts-utils": "^3.3.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,4 @@
import { constants, LogDecoder } from '@0x/contracts-test-utils';
import { LogDecoder } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
@@ -100,7 +100,6 @@ export class Erc1155Wrapper {
beneficiaries,
tokenAmountsAsArray,
{ from: this._contractOwner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
}
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
@@ -114,12 +113,9 @@ export class Erc1155Wrapper {
// tslint:disable-next-line no-unnecessary-type-assertion
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
const token = createFungibleTokenLog.args.id;
await this._erc1155Contract.mintNonFungible.awaitTransactionSuccessAsync(
token,
beneficiaries,
{ from: this._contractOwner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await this._erc1155Contract.mintNonFungible.awaitTransactionSuccessAsync(token, beneficiaries, {
from: this._contractOwner,
});
const encodedNftIds: BigNumber[] = [];
const nftIdBegin = 1;
const nftIdEnd = beneficiaries.length + 1;

View File

@@ -1,9 +1,23 @@
[
{
"version": "2.3.0-beta.1",
"changes": [
{
"note": "Create `LibERC20Token`",
"pr": 2309
}
],
"timestamp": 1573159180
},
{
"version": "2.3.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1570135330

View File

@@ -5,9 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.3.0-beta.1 - _November 7, 2019_
* Create `LibERC20Token` (#2309)
## v2.3.0-beta.0 - _October 3, 2019_
* Dependencies updated
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v2.2.14 - _September 17, 2019_

View File

@@ -87,13 +87,13 @@ contract ERC20Token is
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
emit Transfer(
_from,
_to,
_value
);
return true;
}

View File

@@ -0,0 +1,119 @@
/*
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;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "../src/interfaces/IERC20Token.sol";
library LibERC20Token {
/// @dev Calls `IERC20Token(token).approve()`.
/// Reverts if `false` is returned or if the return
/// data length is nonzero and not 32 bytes.
/// @param token The address of the token contract.
/// @param spender The address that receives an allowance.
/// @param allowance The allowance to set.
function approve(
address token,
address spender,
uint256 allowance
)
internal
{
bytes memory callData = abi.encodeWithSelector(
IERC20Token(0).approve.selector,
spender,
allowance
);
_callWithOptionalBooleanResult(token, callData);
}
/// @dev Calls `IERC20Token(token).transfer()`.
/// Reverts if `false` is returned or if the return
/// data length is nonzero and not 32 bytes.
/// @param token The address of the token contract.
/// @param to The address that receives the tokens
/// @param amount Number of tokens to transfer.
function transfer(
address token,
address to,
uint256 amount
)
internal
{
bytes memory callData = abi.encodeWithSelector(
IERC20Token(0).transfer.selector,
to,
amount
);
_callWithOptionalBooleanResult(token, callData);
}
/// @dev Calls `IERC20Token(token).transferFrom()`.
/// Reverts if `false` is returned or if the return
/// data length is nonzero and not 32 bytes.
/// @param token The address of the token contract.
/// @param from The owner of the tokens.
/// @param to The address that receives the tokens
/// @param amount Number of tokens to transfer.
function transferFrom(
address token,
address from,
address to,
uint256 amount
)
internal
{
bytes memory callData = abi.encodeWithSelector(
IERC20Token(0).transferFrom.selector,
from,
to,
amount
);
_callWithOptionalBooleanResult(token, callData);
}
/// @dev Executes a call on address `target` with calldata `callData`
/// and asserts that either nothing was returned or a single boolean
/// was returned equal to `true`.
/// @param target The call target.
/// @param callData The abi-encoded call data.
function _callWithOptionalBooleanResult(
address target,
bytes memory callData
)
private
{
(bool didSucceed, bytes memory resultData) = target.call(callData);
if (didSucceed) {
if (resultData.length == 0) {
return;
}
if (resultData.length == 32) {
uint256 result = LibBytes.readUint256(resultData, 0);
if (result == 1) {
return;
}
}
}
LibRichErrors.rrevert(resultData);
}
}

View File

@@ -18,22 +18,23 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./UnlimitedAllowanceERC20Token.sol";
contract MintableERC20Token is
SafeMath,
contract MintableERC20Token is
UnlimitedAllowanceERC20Token
{
using LibSafeMath for uint256;
/// @dev Mints new tokens
/// @param _to Address of the beneficiary that will own the minted token
/// @param _value Amount of tokens to mint
function _mint(address _to, uint256 _value)
internal
{
balances[_to] = _safeAdd(_value, balances[_to]);
_totalSupply = _safeAdd(_totalSupply, _value);
balances[_to] = _value.safeAdd(balances[_to]);
_totalSupply = _totalSupply.safeAdd(_value);
emit Transfer(
address(0),
@@ -48,8 +49,8 @@ contract MintableERC20Token is
function _burn(address _owner, uint256 _value)
internal
{
balances[_owner] = _safeSub(balances[_owner], _value);
_totalSupply = _safeSub(_totalSupply, _value);
balances[_owner] = balances[_owner].safeSub(_value);
_totalSupply = _totalSupply.safeSub(_value);
emit Transfer(
_owner,

View File

@@ -18,14 +18,17 @@
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "../src/MintableERC20Token.sol";
contract DummyERC20Token is
contract DummyERC20Token is
Ownable,
MintableERC20Token
{
using LibSafeMath for uint256;
string public name;
string public symbol;
uint256 public decimals;
@@ -55,9 +58,9 @@ contract DummyERC20Token is
{
uint256 currBalance = balances[_target];
if (_value < currBalance) {
_totalSupply = _safeSub(_totalSupply, _safeSub(currBalance, _value));
_totalSupply = _totalSupply.safeSub(currBalance.safeSub(_value));
} else {
_totalSupply = _safeAdd(_totalSupply, _safeSub(_value, currBalance));
_totalSupply = _totalSupply.safeAdd(_value.safeSub(currBalance));
}
balances[_target] = _value;
}

View File

@@ -0,0 +1,72 @@
/*
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;
import "../src/LibERC20Token.sol";
import "./TestLibERC20TokenTarget.sol";
contract TestLibERC20Token {
TestLibERC20TokenTarget public target;
constructor() public {
target = new TestLibERC20TokenTarget();
}
function testApprove(
bool shouldRevert,
bytes calldata revertData,
bytes calldata returnData,
address spender,
uint256 allowance
)
external
{
target.setBehavior(shouldRevert, revertData, returnData);
LibERC20Token.approve(address(target), spender, allowance);
}
function testTransfer(
bool shouldRevert,
bytes calldata revertData,
bytes calldata returnData,
address to,
uint256 amount
)
external
{
target.setBehavior(shouldRevert, revertData, returnData);
LibERC20Token.transfer(address(target), to, amount);
}
function testTransferFrom(
bool shouldRevert,
bytes calldata revertData,
bytes calldata returnData,
address from,
address to,
uint256 amount
)
external
{
target.setBehavior(shouldRevert, revertData, returnData);
LibERC20Token.transferFrom(address(target), from, to, amount);
}
}

View File

@@ -0,0 +1,98 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
contract TestLibERC20TokenTarget {
event ApproveCalled(
address spender,
uint256 allowance
);
event TransferCalled(
address to,
uint256 amount
);
event TransferFromCalled(
address from,
address to,
uint256 amount
);
bool private _shouldRevert;
bytes private _revertData;
bytes private _returnData;
function setBehavior(
bool shouldRevert,
bytes calldata revertData,
bytes calldata returnData
)
external
{
_shouldRevert = shouldRevert;
_revertData = revertData;
_returnData = returnData;
}
function approve(
address spender,
uint256 allowance
)
external
returns (bool)
{
emit ApproveCalled(spender, allowance);
_execute();
}
function transfer(
address to,
uint256 amount
)
external
returns (bool)
{
emit TransferCalled(to, amount);
_execute();
}
function transferFrom(
address from,
address to,
uint256 amount
)
external
returns (bool)
{
emit TransferFromCalled(from, to, amount);
_execute();
}
function _execute() private view {
if (_shouldRevert) {
bytes memory revertData = _revertData;
assembly { revert(add(revertData, 0x20), mload(revertData)) }
}
bytes memory returnData = _returnData;
assembly { return(add(returnData, 0x20), mload(returnData)) }
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "2.3.0-beta.0",
"version": "2.3.0-beta.1",
"engines": {
"node": ">=6.12"
},
@@ -22,7 +22,7 @@
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
@@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IEtherToken|MintableERC20Token|UnlimitedAllowanceERC20Token|UntransferrableDummyERC20Token|WETH9|ZRXToken).json",
"abis": "./generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IEtherToken|LibERC20Token|MintableERC20Token|TestLibERC20Token|TestLibERC20TokenTarget|UnlimitedAllowanceERC20Token|UntransferrableDummyERC20Token|WETH9|ZRXToken).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -47,12 +47,12 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/contracts-test-utils": "^3.2.0-beta.1",
"@0x/dev-utils": "^2.4.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -69,13 +69,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/contracts-utils": "^3.3.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -11,13 +11,17 @@ import * as DummyNoReturnERC20Token from '../generated-artifacts/DummyNoReturnER
import * as ERC20Token from '../generated-artifacts/ERC20Token.json';
import * as IERC20Token from '../generated-artifacts/IERC20Token.json';
import * as IEtherToken from '../generated-artifacts/IEtherToken.json';
import * as LibERC20Token from '../generated-artifacts/LibERC20Token.json';
import * as MintableERC20Token from '../generated-artifacts/MintableERC20Token.json';
import * as TestLibERC20Token from '../generated-artifacts/TestLibERC20Token.json';
import * as TestLibERC20TokenTarget from '../generated-artifacts/TestLibERC20TokenTarget.json';
import * as UnlimitedAllowanceERC20Token from '../generated-artifacts/UnlimitedAllowanceERC20Token.json';
import * as UntransferrableDummyERC20Token from '../generated-artifacts/UntransferrableDummyERC20Token.json';
import * as WETH9 from '../generated-artifacts/WETH9.json';
import * as ZRXToken from '../generated-artifacts/ZRXToken.json';
export const artifacts = {
ERC20Token: ERC20Token as ContractArtifact,
LibERC20Token: LibERC20Token as ContractArtifact,
MintableERC20Token: MintableERC20Token as ContractArtifact,
UnlimitedAllowanceERC20Token: UnlimitedAllowanceERC20Token as ContractArtifact,
WETH9: WETH9 as ContractArtifact,
@@ -27,5 +31,7 @@ export const artifacts = {
DummyERC20Token: DummyERC20Token as ContractArtifact,
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
TestLibERC20Token: TestLibERC20Token as ContractArtifact,
TestLibERC20TokenTarget: TestLibERC20TokenTarget as ContractArtifact,
UntransferrableDummyERC20Token: UntransferrableDummyERC20Token as ContractArtifact,
};

View File

@@ -9,7 +9,10 @@ export * from '../generated-wrappers/dummy_no_return_erc20_token';
export * from '../generated-wrappers/erc20_token';
export * from '../generated-wrappers/i_erc20_token';
export * from '../generated-wrappers/i_ether_token';
export * from '../generated-wrappers/lib_erc20_token';
export * from '../generated-wrappers/mintable_erc20_token';
export * from '../generated-wrappers/test_lib_erc20_token';
export * from '../generated-wrappers/test_lib_erc20_token_target';
export * from '../generated-wrappers/unlimited_allowance_erc20_token';
export * from '../generated-wrappers/untransferrable_dummy_erc20_token';
export * from '../generated-wrappers/weth9';

View File

@@ -0,0 +1,419 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
hexLeftPad,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { RawRevertError, StringRevertError } from '@0x/utils';
import { artifacts, TestLibERC20TokenContract, TestLibERC20TokenTargetEvents } from '../src';
blockchainTests('LibERC20Token', env => {
let testContract: TestLibERC20TokenContract;
const REVERT_STRING = 'WHOOPSIE';
const ENCODED_TRUE = hexLeftPad(1);
const ENCODED_FALSE = hexLeftPad(0);
const ENCODED_TWO = hexLeftPad(2);
const ENCODED_SHORT_TRUE = hexLeftPad(2, 31);
const ENCODED_LONG_TRUE = hexLeftPad(2, 33);
before(async () => {
testContract = await TestLibERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.TestLibERC20Token,
env.provider,
env.txDefaults,
artifacts,
);
});
function encodeRevert(message: string): string {
return new StringRevertError(message).encode();
}
describe('approve()', () => {
it('calls the target with the correct arguments', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const { logs } = await testContract.testApprove.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
spender,
allowance,
);
expect(logs).to.be.length(1);
verifyEventsFromLogs(logs, [{ spender, allowance }], TestLibERC20TokenTargetEvents.ApproveCalled);
});
it('succeeds if the target returns true', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
await testContract.testApprove.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
spender,
allowance,
);
});
it('succeeds if the target returns nothing', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
await testContract.testApprove.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
constants.NULL_BYTES,
spender,
allowance,
);
});
it('fails if the target returns false', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_FALSE,
spender,
allowance,
);
const expectedError = new RawRevertError(ENCODED_FALSE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns nonzero and not true', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TWO,
spender,
allowance,
);
const expectedError = new RawRevertError(ENCODED_TWO);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns less than 32 bytes', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_SHORT_TRUE,
spender,
allowance,
);
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns greater than 32 bytes', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_LONG_TRUE,
spender,
allowance,
);
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target reverts', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
true,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
spender,
allowance,
);
return expect(tx).to.revertWith(REVERT_STRING);
});
it('fails if the target reverts with no data', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
true,
constants.NULL_BYTES,
ENCODED_TRUE,
spender,
allowance,
);
return expect(tx).to.be.rejectedWith('revert');
});
});
describe('transfer()', () => {
it('calls the target with the correct arguments', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const { logs } = await testContract.testTransfer.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
to,
amount,
);
expect(logs).to.be.length(1);
verifyEventsFromLogs(logs, [{ to, amount }], TestLibERC20TokenTargetEvents.TransferCalled);
});
it('succeeds if the target returns true', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract.testTransfer.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
to,
amount,
);
});
it('succeeds if the target returns nothing', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract.testTransfer.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
constants.NULL_BYTES,
to,
amount,
);
});
it('fails if the target returns false', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_FALSE,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_FALSE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns nonzero and not true', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TWO,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_TWO);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns less than 32 bytes', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_SHORT_TRUE,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns greater than 32 bytes', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_LONG_TRUE,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target reverts', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
true,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
to,
amount,
);
return expect(tx).to.revertWith(REVERT_STRING);
});
it('fails if the target reverts with no data', async () => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
true,
constants.NULL_BYTES,
ENCODED_TRUE,
to,
amount,
);
return expect(tx).to.be.rejectedWith('revert');
});
});
describe('transferFrom()', () => {
it('calls the target with the correct arguments', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const { logs } = await testContract.testTransferFrom.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
owner,
to,
amount,
);
expect(logs).to.be.length(1);
verifyEventsFromLogs(logs, [{ from: owner, to, amount }], TestLibERC20TokenTargetEvents.TransferFromCalled);
});
it('succeeds if the target returns true', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract.testTransferFrom.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
owner,
to,
amount,
);
});
it('succeeds if the target returns nothing', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract.testTransferFrom.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
constants.NULL_BYTES,
owner,
to,
amount,
);
});
it('fails if the target returns false', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_FALSE,
owner,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_FALSE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns nonzero and not true', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_TWO,
owner,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_TWO);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns less than 32 bytes', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_SHORT_TRUE,
owner,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target returns greater than 32 bytes', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
false,
encodeRevert(REVERT_STRING),
ENCODED_LONG_TRUE,
owner,
to,
amount,
);
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
return expect(tx).to.revertWith(expectedError);
});
it('fails if the target reverts', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
true,
encodeRevert(REVERT_STRING),
ENCODED_TRUE,
owner,
to,
amount,
);
return expect(tx).to.revertWith(REVERT_STRING);
});
it('fails if the target reverts with no data', async () => {
const owner = randomAddress();
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
true,
constants.NULL_BYTES,
ENCODED_TRUE,
owner,
to,
amount,
);
return expect(tx).to.be.rejectedWith('revert');
});
});
});

View File

@@ -9,7 +9,10 @@
"generated-artifacts/ERC20Token.json",
"generated-artifacts/IERC20Token.json",
"generated-artifacts/IEtherToken.json",
"generated-artifacts/LibERC20Token.json",
"generated-artifacts/MintableERC20Token.json",
"generated-artifacts/TestLibERC20Token.json",
"generated-artifacts/TestLibERC20TokenTarget.json",
"generated-artifacts/UnlimitedAllowanceERC20Token.json",
"generated-artifacts/UntransferrableDummyERC20Token.json",
"generated-artifacts/WETH9.json",

View File

@@ -1,9 +1,22 @@
[
{
"version": "2.2.0-beta.1",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1573159180
},
{
"version": "2.2.0-beta.0",
"changes": [
{
"note": "Dependencies updated"
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1570135330

View File

@@ -5,9 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.2.0-beta.1 - _November 7, 2019_
* Dependencies updated
## v2.2.0-beta.0 - _October 3, 2019_
* Dependencies updated
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v2.1.15 - _September 17, 2019_

View File

@@ -20,13 +20,14 @@ pragma solidity ^0.5.9;
import "./interfaces/IERC721Token.sol";
import "./interfaces/IERC721Receiver.sol";
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
contract ERC721Token is
IERC721Token,
SafeMath
IERC721Token
{
using LibSafeMath for uint256;
// Function selector for ERC721Receiver.onERC721Received
// 0x150b7a02
bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
@@ -163,7 +164,7 @@ contract ERC721Token is
_approved
);
}
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
@@ -223,9 +224,9 @@ contract ERC721Token is
}
owners[_tokenId] = _to;
balances[_from] = _safeSub(balances[_from], 1);
balances[_to] = _safeAdd(balances[_to], 1);
balances[_from] = balances[_from].safeSub(1);
balances[_to] = balances[_to].safeAdd(1);
emit Transfer(
_from,
_to,

View File

@@ -18,16 +18,19 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./ERC721Token.sol";
contract MintableERC721Token is
ERC721Token
{
using LibSafeMath for uint256;
/// @dev Function to mint a new token
/// Reverts if the given token ID already exists
/// @param _to Address of the beneficiary that will own the minted token
/// @param _tokenId ID of the token to be minted by the msg.sender
/// @param _tokenId ID of the token to be minted by the msg.sender
function _mint(address _to, uint256 _tokenId)
internal
{
@@ -43,7 +46,7 @@ contract MintableERC721Token is
);
owners[_tokenId] = _to;
balances[_to] = _safeAdd(balances[_to], 1);
balances[_to] = balances[_to].safeAdd(1);
emit Transfer(
address(0),
@@ -71,7 +74,7 @@ contract MintableERC721Token is
);
owners[_tokenId] = address(0);
balances[_owner] = _safeSub(balances[_owner], 1);
balances[_owner] = balances[_owner].safeSub(1);
emit Transfer(
_owner,

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "2.2.0-beta.0",
"version": "2.2.0-beta.1",
"engines": {
"node": ">=6.12"
},
@@ -22,7 +22,7 @@
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
@@ -48,12 +48,12 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/contracts-test-utils": "^3.2.0-beta.1",
"@0x/dev-utils": "^2.4.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -71,13 +71,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/contracts-utils": "^3.3.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,14 @@
[
{
"version": "3.1.0-beta.1",
"changes": [
{
"note": "Use `LibERC20Token` in `MixinAssets`",
"pr": 2309
}
],
"timestamp": 1573159180
},
{
"version": "3.1.0-beta.0",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.0-beta.1 - _November 7, 2019_
* Use `LibERC20Token` in `MixinAssets` (#2309)
## v3.1.0-beta.0 - _October 3, 2019_
* Dependencies updated

View File

@@ -22,6 +22,7 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
@@ -105,38 +106,8 @@ contract MixinAssets is
internal
{
address token = assetData.readAddress(16);
// Transfer tokens.
// We do a raw call so we can check the success separate
// from the return data.
(bool success, bytes memory returnData) = token.call(abi.encodeWithSelector(
ERC20_TRANSFER_SELECTOR,
msg.sender,
amount
));
if (!success) {
LibRichErrors.rrevert(LibForwarderRichErrors.TransferFailedError(returnData));
}
// Check return data.
// If there is no return data, we assume the token incorrectly
// does not return a bool. In this case we expect it to revert
// on failure, which was handled above.
// If the token does return data, we require that it is a single
// value that evaluates to true.
assembly {
if returndatasize {
success := 0
if eq(returndatasize, 32) {
// First 64 bytes of memory are reserved scratch space
returndatacopy(0, 0, 32)
success := mload(0)
}
}
}
if (!success) {
LibRichErrors.rrevert(LibForwarderRichErrors.TransferFailedError(returnData));
}
LibERC20Token.transfer(token, msg.sender, amount);
}
/// @dev Decodes ERC721 assetData and transfers given amount to sender.

View File

@@ -51,10 +51,6 @@ library LibForwarderRichErrors {
bytes4 internal constant OVERSPENT_WETH_ERROR_SELECTOR =
0xcdcbed5d;
// bytes4(keccak256("TransferFailedError(bytes)"))
bytes4 internal constant TRANSFER_FAILED_ERROR_SELECTOR =
0x5e7eb60f;
// bytes4(keccak256("DefaultFunctionWethContractOnlyError(address)"))
bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
0x08b18698;
@@ -160,19 +156,6 @@ library LibForwarderRichErrors {
);
}
function TransferFailedError(
bytes memory errorData
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
TRANSFER_FAILED_ERROR_SELECTOR,
errorData
);
}
function DefaultFunctionWethContractOnlyError(
address senderAddress
)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "3.1.0-beta.0",
"version": "3.1.0-beta.1",
"engines": {
"node": ">=6.12"
},
@@ -14,16 +14,15 @@
"build:ts": "tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"test": "echo !!! Tests have been relocated to @0x/contracts-integrations !!!",
"rebuild_and_test": "run-s build test",
"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",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html",
@@ -35,7 +34,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth|TestProtocolFeeCollector).json",
"abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -48,12 +47,12 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/contracts-test-utils": "^3.2.0-beta.1",
"@0x/dev-utils": "^2.4.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -71,19 +70,20 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
"@0x/contracts-erc20": "^2.3.0-beta.0",
"@0x/contracts-erc721": "^2.2.0-beta.0",
"@0x/contracts-exchange": "^2.2.0-beta.0",
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/contracts-asset-proxy": "^2.3.0-beta.1",
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
"@0x/contracts-erc20": "^2.3.0-beta.1",
"@0x/contracts-erc721": "^2.2.0-beta.1",
"@0x/contracts-exchange": "^2.2.0-beta.1",
"@0x/contracts-exchange-libs": "^3.1.0-beta.1",
"@0x/contracts-utils": "^3.3.0-beta.1",
"@0x/order-utils": "^8.5.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

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

View File

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

View File

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

View File

@@ -1,716 +0,0 @@
import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
import { artifacts as erc20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
import { artifacts as exchangeArtifacts, ExchangeContract, ExchangeWrapper } from '@0x/contracts-exchange';
import {
blockchainTests,
constants,
ContractName,
expect,
getLatestBlockTimestampAsync,
OrderFactory,
sendTransactionResult,
} from '@0x/contracts-test-utils';
import { assetDataUtils, ForwarderRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import {
artifacts,
ForwarderContract,
ForwarderTestFactory,
ForwarderWrapper,
TestProtocolFeeCollectorContract,
} from '../src';
const DECIMALS_DEFAULT = 18;
blockchainTests(ContractName.Forwarder, env => {
let owner: string;
let makerAddress: string;
let takerAddress: string;
let orderFeeRecipientAddress: string;
let forwarderFeeRecipientAddress: string;
let defaultMakerAssetAddress: string;
let weth: DummyERC20TokenContract;
let erc20Token: DummyERC20TokenContract;
let secondErc20Token: DummyERC20TokenContract;
let erc721Token: DummyERC721TokenContract;
let forwarderContract: ForwarderContract;
let wethContract: WETH9Contract;
let exchangeContract: ExchangeContract;
let protocolFeeCollector: TestProtocolFeeCollectorContract;
let forwarderWrapper: ForwarderWrapper;
let exchangeWrapper: ExchangeWrapper;
let erc20Wrapper: ERC20Wrapper;
let orderFactory: OrderFactory;
let forwarderTestFactory: ForwarderTestFactory;
let chainId: number;
let wethAssetData: string;
let erc721MakerAssetIds: BigNumber[];
const GAS_PRICE = new BigNumber(env.txDefaults.gasPrice || constants.DEFAULT_GAS_PRICE);
const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150);
const PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
before(async () => {
// Set up addresses
const accounts = await env.getAccountAddressesAsync();
const usedAddresses = ([
owner,
makerAddress,
takerAddress,
orderFeeRecipientAddress,
forwarderFeeRecipientAddress,
] = accounts);
// Set up Exchange
chainId = await env.getChainIdAsync();
exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
env.provider,
env.txDefaults,
{},
new BigNumber(chainId),
);
exchangeWrapper = new ExchangeWrapper(exchangeContract);
// Set up ERC20
erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
[erc20Token, secondErc20Token] = await erc20Wrapper.deployDummyTokensAsync(2, constants.DUMMY_TOKEN_DECIMALS);
const erc20Proxy = await erc20Wrapper.deployProxyAsync();
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
from: owner,
});
// Set up WETH
wethContract = await WETH9Contract.deployFrom0xArtifactAsync(
erc20Artifacts.WETH9,
env.provider,
env.txDefaults,
{},
);
weth = new DummyERC20TokenContract(wethContract.address, env.provider);
wethAssetData = assetDataUtils.encodeERC20AssetData(wethContract.address);
erc20Wrapper.addDummyTokenContract(weth);
await erc20Wrapper.setBalancesAndAllowancesAsync();
// Set up ERC721
const erc721Wrapper = new ERC721Wrapper(env.provider, usedAddresses, owner);
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
const erc721Proxy = await erc721Wrapper.deployProxyAsync();
await erc721Wrapper.setBalancesAndAllowancesAsync();
const erc721Balances = await erc721Wrapper.getBalancesAsync();
erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
from: owner,
});
// Set up Protocol Fee Collector
protocolFeeCollector = await TestProtocolFeeCollectorContract.deployFrom0xArtifactAsync(
artifacts.TestProtocolFeeCollector,
env.provider,
env.txDefaults,
{},
wethContract.address,
);
await exchangeContract.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(PROTOCOL_FEE_MULTIPLIER);
await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(
protocolFeeCollector.address,
);
erc20Wrapper.addTokenOwnerAddress(protocolFeeCollector.address);
// Set defaults
defaultMakerAssetAddress = erc20Token.address;
const defaultTakerAssetAddress = wethContract.address;
const defaultOrderParams = {
makerAddress,
feeRecipientAddress: orderFeeRecipientAddress,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(200, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, DECIMALS_DEFAULT),
makerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
takerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
exchangeAddress: exchangeContract.address,
chainId,
};
// Set up Forwarder
forwarderContract = await ForwarderContract.deployFrom0xArtifactAsync(
artifacts.Forwarder,
env.provider,
env.txDefaults,
{},
exchangeContract.address,
wethAssetData,
);
forwarderWrapper = new ForwarderWrapper(forwarderContract, env.provider);
await forwarderWrapper.approveMakerAssetProxyAsync(defaultOrderParams.makerAssetData, {
from: takerAddress,
});
erc20Wrapper.addTokenOwnerAddress(forwarderContract.address);
// Set up factories
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
forwarderTestFactory = new ForwarderTestFactory(
exchangeWrapper,
forwarderWrapper,
erc20Wrapper,
forwarderContract.address,
makerAddress,
takerAddress,
protocolFeeCollector.address,
orderFeeRecipientAddress,
forwarderFeeRecipientAddress,
weth.address,
GAS_PRICE,
PROTOCOL_FEE_MULTIPLIER,
);
});
blockchainTests.resets('constructor', () => {
it('should revert if assetProxy is unregistered', async () => {
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
env.provider,
env.txDefaults,
{},
new BigNumber(chainId),
);
const deployForwarder = (ForwarderContract.deployFrom0xArtifactAsync(
artifacts.Forwarder,
env.provider,
env.txDefaults,
{},
exchange.address,
wethAssetData,
) as any) as sendTransactionResult;
await expect(deployForwarder).to.revertWith(new ForwarderRevertErrors.UnregisteredAssetProxyError());
});
});
blockchainTests.resets('marketSellOrdersWithEth without extra fees', () => {
it('should fill a single order without a taker fee', async () => {
const orderWithoutFee = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([orderWithoutFee], 0.78, [erc20Token]);
});
it('should fill multiple orders without taker fees', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync();
const secondOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(285, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(21, DECIMALS_DEFAULT),
});
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketSellTestAsync(orders, 1.51, [erc20Token]);
});
it('should fill a single order with a percentage fee', async () => {
const orderWithPercentageFee = await orderFactory.newSignedOrderAsync({
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
await forwarderTestFactory.marketSellTestAsync([orderWithPercentageFee], 0.58, [erc20Token]);
});
it('should fill multiple orders with percentage fees', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync({
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
const secondOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(190, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(31, DECIMALS_DEFAULT),
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketSellTestAsync(orders, 1.34, [erc20Token]);
});
it('should fail to fill an order with a percentage fee if the asset proxy is not yet approved', async () => {
const unapprovedAsset = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
const order = await orderFactory.newSignedOrderAsync({
makerAssetData: unapprovedAsset,
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
takerFeeAssetData: unapprovedAsset,
});
const ethValue = order.takerAssetAmount;
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
// Execute test case
const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
value: ethValue,
from: takerAddress,
});
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalEthSpent = GAS_PRICE.times(tx.gasUsed);
// Validate test case
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
erc20Balances[makerAddress][defaultMakerAssetAddress],
);
expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
erc20Balances[takerAddress][defaultMakerAssetAddress],
);
expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
erc20Balances[makerAddress][weth.address],
);
expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should fill a single order with a WETH fee', async () => {
const orderWithWethFee = await orderFactory.newSignedOrderAsync({
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketSellTestAsync([orderWithWethFee], 0.13, [erc20Token]);
});
it('should fill multiple orders with WETH fees', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync({
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
const secondOrderWithWethFee = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(33, DECIMALS_DEFAULT),
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
const orders = [firstOrder, secondOrderWithWethFee];
await forwarderTestFactory.marketSellTestAsync(orders, 1.25, [erc20Token]);
});
it('should refund remaining ETH if amount is greater than takerAssetAmount', async () => {
const order = await orderFactory.newSignedOrderAsync();
const ethValue = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(2);
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
value: ethValue,
from: takerAddress,
});
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const totalEthSpent = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(GAS_PRICE.times(tx.gasUsed));
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
});
it('should fill orders with different makerAssetData', async () => {
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
const firstOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: firstOrderMakerAssetData,
});
const secondOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
const secondOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: secondOrderMakerAssetData,
});
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketSellTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
});
it('should fail to fill an order with a fee denominated in an asset other than makerAsset or WETH', async () => {
const makerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
const takerFeeAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
const order = await orderFactory.newSignedOrderAsync({
makerAssetData,
takerFeeAssetData,
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
});
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
revertError,
});
});
it('should fill a partially-filled order without a taker fee', async () => {
const order = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([order], 0.3, [erc20Token]);
await forwarderTestFactory.marketSellTestAsync([order], 0.8, [erc20Token]);
});
it('should skip over an order with an invalid maker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: constants.ZERO_AMOUNT,
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an order with an invalid taker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
takerAssetAmount: constants.ZERO_AMOUNT,
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an expired order', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
const expiredOrder = await orderFactory.newSignedOrderAsync({
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a fully filled order', async () => {
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder], 1, [erc20Token]);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a cancelled order', async () => {
const cancelledOrder = await orderFactory.newSignedOrderAsync();
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketSellTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
});
});
blockchainTests.resets('marketSellOrdersWithEth with extra fees', () => {
it('should fill the order and send fee to feeRecipient', async () => {
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(157, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(36, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketSellTestAsync([order], 0.67, [erc20Token], {
forwarderFeePercentage: new BigNumber(2),
});
});
it('should fail if the fee is set too high', async () => {
const order = await orderFactory.newSignedOrderAsync();
const forwarderFeePercentage = new BigNumber(6);
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage),
);
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
forwarderFeePercentage,
revertError,
});
});
});
blockchainTests.resets('marketBuyOrdersWithEth without extra fees', () => {
it('should buy the exact amount of makerAsset in a single order', async () => {
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(131, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(20, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.62, [erc20Token]);
});
it('should buy the exact amount of makerAsset in multiple orders', async () => {
const firstOrder = await orderFactory.newSignedOrderAsync();
const secondOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(77, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
});
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketBuyTestAsync(orders, 1.96, [erc20Token]);
});
it('should buy exactly makerAssetBuyAmount in orders with different makerAssetData', async () => {
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
const firstOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: firstOrderMakerAssetData,
});
const secondOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
const secondOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: secondOrderMakerAssetData,
});
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
const orders = [firstOrder, secondOrder];
await forwarderTestFactory.marketBuyTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
});
it('should buy the exact amount of makerAsset and return excess ETH', async () => {
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(80, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.57, [erc20Token], {
ethValueAdjustment: 2,
});
});
it('should buy the exact amount of makerAsset from a single order with a WETH fee', async () => {
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(79, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, DECIMALS_DEFAULT),
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.38, [erc20Token]);
});
it('should buy the exact amount of makerAsset from a single order with a percentage fee', async () => {
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(80, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, DECIMALS_DEFAULT),
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.52, [erc20Token]);
});
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount', async () => {
const order = await orderFactory.newSignedOrderAsync();
const revertError = new ForwarderRevertErrors.CompleteBuyFailedError(
order.makerAssetAmount.times(0.5),
constants.ZERO_AMOUNT,
);
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
ethValueAdjustment: -2,
revertError,
});
});
it('should buy an ERC721 asset from a single order', async () => {
const makerAssetId = erc721MakerAssetIds[0];
const erc721Order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: new BigNumber(1),
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketBuyTestAsync([erc721Order], 1, [erc721Token], {
makerAssetId,
});
});
it('should buy an ERC721 asset and pay a WETH fee', async () => {
const makerAssetId = erc721MakerAssetIds[0];
const erc721orderWithWethFee = await orderFactory.newSignedOrderAsync({
makerAssetAmount: new BigNumber(1),
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
takerFeeAssetData: wethAssetData,
});
await forwarderTestFactory.marketBuyTestAsync([erc721orderWithWethFee], 1, [erc721Token], {
makerAssetId,
});
});
it('should fail to fill an order with a fee denominated in an asset other than makerAsset or WETH', async () => {
const makerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
const takerFeeAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
const order = await orderFactory.newSignedOrderAsync({
makerAssetData,
takerFeeAssetData,
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
});
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
revertError,
});
});
it('should fill a partially-filled order without a taker fee', async () => {
const order = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([order], 0.3, [erc20Token]);
await forwarderTestFactory.marketBuyTestAsync([order], 0.8, [erc20Token]);
});
it('should skip over an order with an invalid maker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: constants.ZERO_AMOUNT,
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an order with an invalid taker asset amount', async () => {
const unfillableOrder = await orderFactory.newSignedOrderAsync({
takerAssetAmount: constants.ZERO_AMOUNT,
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over an expired order', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
const expiredOrder = await orderFactory.newSignedOrderAsync({
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
});
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a fully filled order', async () => {
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder], 1, [erc20Token]);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
});
it('should skip over a cancelled order', async () => {
const cancelledOrder = await orderFactory.newSignedOrderAsync();
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
const fillableOrder = await orderFactory.newSignedOrderAsync();
await forwarderTestFactory.marketBuyTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
});
it('Should buy slightly greater makerAsset when exchange rate is rounded', async () => {
// The 0x Protocol contracts round the exchange rate in favor of the Maker.
// In this case, the taker must round up how much they're going to spend, which
// in turn increases the amount of MakerAsset being purchased.
// Example:
// The taker wants to buy 5 units of the MakerAsset at a rate of 3M/2T.
// For every 2 units of TakerAsset, the taker will receive 3 units of MakerAsset.
// To purchase 5 units, the taker must spend 10/3 = 3.33 units of TakerAssset.
// However, the Taker can only spend whole units.
// Spending floor(10/3) = 3 units will yield a profit of Floor(3*3/2) = Floor(4.5) = 4 units of MakerAsset.
// Spending ceil(10/3) = 4 units will yield a profit of Floor(4*3/2) = 6 units of MakerAsset.
//
// The forwarding contract will opt for the second option, which overbuys, to ensure the taker
// receives at least the amount of MakerAsset they requested.
//
// Construct test case using values from example above
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: new BigNumber('30'),
takerAssetAmount: new BigNumber('20'),
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20Token.address),
takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
makerFee: new BigNumber(0),
takerFee: new BigNumber(0),
});
const desiredMakerAssetFillAmount = new BigNumber('5');
const makerAssetFillAmount = new BigNumber('6');
const primaryTakerAssetFillAmount = new BigNumber('4');
const ethValue = primaryTakerAssetFillAmount.plus(PROTOCOL_FEE);
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
// Execute test case
const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
value: ethValue,
from: takerAddress,
});
// Fetch end balances and construct expected outputs
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalEthSpent = ethValue.plus(GAS_PRICE.times(tx.gasUsed));
// Validate test case
expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
);
expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
);
expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
);
expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
it('Should buy slightly greater MakerAsset when exchange rate is rounded (Regression Test)', async () => {
// Disable protocol fees for regression test
await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(constants.NULL_ADDRESS);
// Order taken from a transaction on mainnet that failed due to a rounding error.
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: new BigNumber('268166666666666666666'),
takerAssetAmount: new BigNumber('219090625878836371'),
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20Token.address),
takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
makerFee: new BigNumber(0),
takerFee: new BigNumber(0),
});
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
// The taker will receive more than the desired amount of makerAsset due to rounding
const desiredMakerAssetFillAmount = new BigNumber('5000000000000000000');
const ethValue = new BigNumber('4084971271824171');
const makerAssetFillAmount = ethValue
.times(order.makerAssetAmount)
.dividedToIntegerBy(order.takerAssetAmount);
// Execute test case
const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
value: ethValue,
from: takerAddress,
});
// Fetch end balances and construct expected outputs
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
const newBalances = await erc20Wrapper.getBalancesAsync();
const primaryTakerAssetFillAmount = ethValue;
const totalEthSpent = primaryTakerAssetFillAmount.plus(GAS_PRICE.times(tx.gasUsed));
// Validate test case
expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
);
expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
);
expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
);
expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
});
blockchainTests.resets('marketBuyOrdersWithEth with extra fees', () => {
it('should buy the asset and send fee to feeRecipient', async () => {
const order = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(125, DECIMALS_DEFAULT),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
});
await forwarderTestFactory.marketBuyTestAsync([order], 0.33, [erc20Token], {
forwarderFeePercentage: new BigNumber(2),
});
});
it('should fail if the fee is set too high', async () => {
const order = await orderFactory.newSignedOrderAsync();
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, new BigNumber(6)),
);
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
forwarderFeePercentage: new BigNumber(6),
revertError,
});
});
it('should fail if there is not enough ETH remaining to pay the fee', async () => {
const order = await orderFactory.newSignedOrderAsync();
const forwarderFeePercentage = new BigNumber(2);
const ethFee = ForwarderTestFactory.getPercentageOfValue(
order.takerAssetAmount.times(0.5).plus(PROTOCOL_FEE),
forwarderFeePercentage,
);
const revertError = new ForwarderRevertErrors.InsufficientEthForFeeError(ethFee, ethFee.minus(1));
// -2 to compensate for the extra 1 wei added in ForwarderTestFactory to account for rounding
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
ethValueAdjustment: -2,
forwarderFeePercentage,
revertError,
});
});
});
});
// tslint:disable:max-file-line-count
// tslint:enable:no-unnecessary-type-assertion

View File

@@ -1,19 +0,0 @@
import { env, EnvVars } from '@0x/dev-utils';
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
import { providerUtils } from '@0x/utils';
before('start web3 provider', () => {
providerUtils.startProviderEngine(provider);
});
after('generate coverage report', async () => {
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
await coverageSubprovider.writeCoverageAsync();
}
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
await profilerSubprovider.writeProfilerOutputAsync();
}
provider.stop();
});

View File

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

View File

@@ -1,81 +0,0 @@
import { artifacts as erc20Artifacts } from '@0x/contracts-erc20';
import { artifacts as erc721Artifacts } from '@0x/contracts-erc721';
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
import { constants, LogDecoder, Web3ProviderEngine } from '@0x/contracts-test-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { TransactionReceiptWithDecodedLogs, TxDataPayable } from 'ethereum-types';
import * as _ from 'lodash';
import { ForwarderContract } from '../../generated-wrappers/forwarder';
import { artifacts } from '../../src/artifacts';
export class ForwarderWrapper {
private readonly _web3Wrapper: Web3Wrapper;
private readonly _forwarderContract: ForwarderContract;
private readonly _logDecoder: LogDecoder;
constructor(contractInstance: ForwarderContract, provider: Web3ProviderEngine) {
this._forwarderContract = contractInstance;
this._web3Wrapper = new Web3Wrapper(provider);
this._logDecoder = new LogDecoder(this._web3Wrapper, {
...artifacts,
...exchangeArtifacts,
...erc20Artifacts,
...erc721Artifacts,
});
}
public async marketSellOrdersWithEthAsync(
orders: SignedOrder[],
txData: TxDataPayable,
opts: { feePercentage?: BigNumber; feeRecipient?: string } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const feePercentage = opts.feePercentage === undefined ? constants.ZERO_AMOUNT : opts.feePercentage;
const feeRecipient = opts.feeRecipient === undefined ? constants.NULL_ADDRESS : opts.feeRecipient;
const txHash = await this._forwarderContract.marketSellOrdersWithEth.sendTransactionAsync(
orders,
orders.map(signedOrder => signedOrder.signature),
feePercentage,
feeRecipient,
txData,
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
public async marketBuyOrdersWithEthAsync(
orders: SignedOrder[],
makerAssetFillAmount: BigNumber,
txData: TxDataPayable,
opts: { feePercentage?: BigNumber; feeRecipient?: string } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const feePercentage = opts.feePercentage === undefined ? constants.ZERO_AMOUNT : opts.feePercentage;
const feeRecipient = opts.feeRecipient === undefined ? constants.NULL_ADDRESS : opts.feeRecipient;
const txHash = await this._forwarderContract.marketBuyOrdersWithEth.sendTransactionAsync(
orders,
makerAssetFillAmount,
orders.map(signedOrder => signedOrder.signature),
feePercentage,
feeRecipient,
txData,
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
public async withdrawAssetAsync(
assetData: string,
amount: BigNumber,
txData: TxDataPayable,
): Promise<TransactionReceiptWithDecodedLogs> {
const txHash = await this._forwarderContract.withdrawAsset.sendTransactionAsync(assetData, amount, txData);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
public async approveMakerAssetProxyAsync(
assetData: string,
txData: TxDataPayable,
): Promise<TransactionReceiptWithDecodedLogs> {
const txHash = await this._forwarderContract.approveMakerAssetProxy.sendTransactionAsync(assetData, txData);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
}

View File

@@ -1,2 +0,0 @@
export * from './forwarder_wrapper';
export * from './forwarder_test_factory';

View File

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

View File

@@ -1,4 +1,13 @@
[
{
"version": "3.1.0-beta.1",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1573159180
},
{
"version": "3.1.0-beta.0",
"changes": [
@@ -105,6 +114,10 @@
{
"note": "Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields.",
"pr": 2075
},
{
"note": "Move `IWallet.sol` from `asset-proxy` and `exchange` packages to here.",
"pr": 2233
}
],
"timestamp": 1570135330

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.0-beta.1 - _November 7, 2019_
* Dependencies updated
## v3.1.0-beta.0 - _October 3, 2019_
* Break up `LibEIP712` into reusable components (#1742)
@@ -33,6 +37,7 @@ CHANGELOG
* Remove `_hashEIP712ExchangeMessage` from `LibEIP712ExchangeDomain` (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields. (#2075)
* Move `IWallet.sol` from `asset-proxy` and `exchange` packages to here. (#2233)
## v3.0.8 - _September 17, 2019_

View File

@@ -85,9 +85,9 @@ library LibMath {
));
}
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// ceil(a / b) = floor((a + b - 1) / b)
// To implement `ceil(a / b)` using _safeDiv.
// To implement `ceil(a / b)` using safeDiv.
partialAmount = numerator.safeMul(target)
.safeAdd(denominator.safeSub(1))
.safeDiv(denominator);
@@ -127,9 +127,9 @@ library LibMath {
pure
returns (uint256 partialAmount)
{
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// ceil(a / b) = floor((a + b - 1) / b)
// To implement `ceil(a / b)` using _safeDiv.
// To implement `ceil(a / b)` using safeDiv.
partialAmount = numerator.safeMul(target)
.safeAdd(denominator.safeSub(1))
.safeDiv(denominator);

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "3.1.0-beta.0",
"version": "3.1.0-beta.1",
"engines": {
"node": ">=6.12"
},
@@ -22,7 +22,7 @@
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
@@ -35,7 +35,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json",
"abis": "./generated-artifacts/@(IWallet|LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -48,13 +48,13 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.3.0-beta.0",
"@0x/contracts-gen": "^1.1.0-beta.0",
"@0x/contracts-test-utils": "^3.2.0-beta.0",
"@0x/dev-utils": "^2.4.0-beta.0",
"@0x/sol-compiler": "^3.2.0-beta.0",
"@0x/subproviders": "^5.1.0-beta.0",
"@0x/tslint-config": "^3.0.1",
"@0x/abi-gen": "^4.4.0-beta.1",
"@0x/contracts-gen": "^1.1.0-beta.1",
"@0x/contracts-test-utils": "^3.2.0-beta.1",
"@0x/dev-utils": "^2.4.0-beta.1",
"@0x/sol-compiler": "^3.2.0-beta.1",
"@0x/subproviders": "^5.1.0-beta.1",
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -73,14 +73,14 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.0",
"@0x/contracts-utils": "^3.3.0-beta.0",
"@0x/order-utils": "^8.5.0-beta.0",
"@0x/types": "^2.5.0-beta.0",
"@0x/typescript-typings": "^4.4.0-beta.0",
"@0x/utils": "^4.6.0-beta.0",
"@0x/web3-wrapper": "^6.1.0-beta.0",
"ethereum-types": "^2.2.0-beta.0",
"@0x/base-contract": "^5.5.0-beta.1",
"@0x/contracts-utils": "^3.3.0-beta.1",
"@0x/order-utils": "^8.5.0-beta.1",
"@0x/types": "^2.5.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"ethereum-types": "^2.2.0-beta.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -5,6 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as IWallet from '../generated-artifacts/IWallet.json';
import * as LibEIP712ExchangeDomain from '../generated-artifacts/LibEIP712ExchangeDomain.json';
import * as LibExchangeRichErrors from '../generated-artifacts/LibExchangeRichErrors.json';
import * as LibFillResults from '../generated-artifacts/LibFillResults.json';
@@ -18,6 +19,7 @@ import * as TestLibMath from '../generated-artifacts/TestLibMath.json';
import * as TestLibOrder from '../generated-artifacts/TestLibOrder.json';
import * as TestLibZeroExTransaction from '../generated-artifacts/TestLibZeroExTransaction.json';
export const artifacts = {
IWallet: IWallet as ContractArtifact,
LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact,
LibExchangeRichErrors: LibExchangeRichErrors as ContractArtifact,
LibFillResults: LibFillResults as ContractArtifact,

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