Compare commits

..

91 Commits

Author SHA1 Message Date
Jacob Evans
4a133ca36f Publish
- @0x/contracts-asset-proxy@3.2.1
 - @0x/contracts-broker@1.1.0
 - @0x/contracts-coordinator@3.1.1
 - @0x/contracts-dev-utils@1.1.1
 - @0x/contracts-erc1155@2.1.1
 - @0x/contracts-erc20-bridge-sampler@1.4.0
 - @0x/contracts-erc20@3.1.1
 - @0x/contracts-erc721@3.1.1
 - @0x/contracts-exchange-forwarder@4.2.1
 - @0x/contracts-exchange-libs@4.3.1
 - @0x/contracts-exchange@3.2.1
 - @0x/contracts-extensions@6.1.1
 - @0x/contracts-integrations@2.4.0
 - @0x/contracts-multisig@4.1.1
 - @0x/contracts-staking@2.0.8
 - @0x/contracts-test-utils@5.1.5
 - @0x/contracts-utils@4.3.1
 - 0x.js@9.1.1
 - @0x/asset-swapper@4.2.0
 - @0x/contract-addresses@4.6.0
 - @0x/contract-artifacts@3.6.0
 - @0x/contract-wrappers-test@12.2.9
 - @0x/contract-wrappers@13.6.0
 - @0x/instant@1.0.46
 - @0x/migrations@6.2.0
 - @0x/order-utils@10.2.1
 - @0x/orderbook@2.2.1
2020-02-15 17:39:09 +11:00
Jacob Evans
f7252f919a Updated CHANGELOGS & MD docs 2020-02-15 17:38:47 +11:00
Jacob Evans
e05a03a842 Curve ERC20Bridge (#2480)
* Curve ERC20Bridge

* ERC20BridgeSampler Curve (#2483)

* ERC20Sampler Curve

* Use Bridge Sources for each Curve

* Support multiple versions of the Curve contract

* CHANGELOG and redeployed Curve (mainnet)

* Fix Market ops utils test

* Added Curve DAI USDC USDT TUSD

* Bump sampler gas limit default

* Decode the Curve in tests

* Disable Curve in Buy tests

* blockchainTests.fork.resets Curve and Sampler
2020-02-15 17:02:19 +11:00
mzhu25
dcce8276b8 Add decoders for broker and stop-limit data (#2484)
* Add decoders for broker and stop-limit data

* update changelogs

* Address comments
2020-02-14 17:38:43 -08:00
Alex Towle
fd47947e55 Merge pull request #2454 from 0xProject/fix/dev-utils/and-another-one
Another one...
2020-02-13 20:45:34 -08:00
Alex Towle
ae151df2eb Addressed amir's review feedback 2020-02-13 18:07:45 -08:00
Alex Towle
79de188683 Updated addresses again 2020-02-13 17:37:03 -08:00
Alex Towle
6e5c788e13 Added a test that fails for the old DevUtils 2020-02-13 17:37:03 -08:00
Alex Towle
f53606007d Updated DevUtils again 2020-02-13 17:37:03 -08:00
Greg Hysz
a4ac418bc9 Merge pull request #2479 from 0xProject/test/dydx-mainnet-tests/updateDeployment
Fixed dYdX Bridge Tests
2020-02-13 14:42:47 -08:00
Greg Hysen
a8c09d0bdb Updated contracts-integrations changelog. 2020-02-13 13:21:07 -08:00
Greg Hysen
871105a48a Fixed dYdX Bridge Tests 2020-02-13 13:19:26 -08:00
Fabio B
3b61129ade Update link to docs for web3-wrapper 2020-02-12 22:08:10 +01:00
mzhu25
f471c79b59 Chainlink stop-limit orders (#2473)
* Contracts for Chainlink stop-limit orders

* Tests and asset data utils

* Update contracts-integrations changelog

* Address comments

* Remove priceFreshness parameter

* Remove LibSafeMath

* fix typo

* Add ChainlinkStopLimit addresses to @0x/contract-addresses
2020-02-11 15:10:06 -08:00
Lawrence Forman
dfd9443f74 Merge pull request #2477 from 0xProject/feat/batched-sampler
ERC20BridgeSampler: batchCall()
2020-02-11 14:32:48 -07:00
Lawrence Forman
a36ff9e365 `@0x/asset-swapper: Address review comments. 2020-02-11 12:08:15 -07:00
Lawrence Forman
12e65bbf26 @0x/contracts-erc20-bridge-sampler: Fix failing tests. 2020-02-11 12:08:15 -07:00
Lawrence Forman
ab9841e60b @0x/contract-addresses: Update ERC20BridgeSampler addresses on mainnet and kovan. 2020-02-11 12:08:15 -07:00
Lawrence Forman
7a52f12e57 Rebase and update contract artifacts + wrappers. 2020-02-11 12:07:48 -07:00
Lawrence Forman
11fd4506ac @0x/asset-swapper: Fix failing asset-swapper test. 2020-02-11 12:07:48 -07:00
Lawrence Forman
0c9c68030e @0x/asset-swapper: Use batchCall() version of the ERC20BridgeSampler contract 2020-02-11 12:07:48 -07:00
Lawrence Forman
55d6eddbb2 @0x/contract-artifacts: Update IERC20BridgeSampler wrapper. 2020-02-11 12:07:48 -07:00
Lawrence Forman
8341e60edb @0x/contract-artifacts: Update ERC20BridgeSampler artifact. 2020-02-11 12:07:48 -07:00
Lawrence Forman
6273a1ca73 @0x/contracts-erc20-bridge-sampler: Remove wrapper functions and introduce batchCall(). 2020-02-11 12:07:48 -07:00
mzhu25
1b83ebdf89 Merge pull request #2469 from 0xProject/feature/broker/deployments
Broker-related updates to contract-artifacts, contract-wrappers, contract-addresses
2020-02-10 18:41:24 -08:00
Michael Zhu
fef7f0506f rebase 🙄 2020-02-10 17:38:16 -08:00
Michael Zhu
f44eb4e383 Update changelogs for Forwarder changes 2020-02-10 17:11:01 -08:00
Michael Zhu
05df485c4a Update Forwarder in contract-artifacts and contract-wrappers 2020-02-10 17:11:00 -08:00
Michael Zhu
44857c526b Remove DutchAuction and OrderValidator from python contract addresses 2020-02-10 17:10:36 -08:00
Michael Zhu
b8ad5d5d32 Update Forwarder addresses 2020-02-10 17:10:36 -08:00
Michael Zhu
e3e0d00e21 Add Broker and GodsUnchainedValidator to contract-wrappers 2020-02-10 17:10:14 -08:00
Michael Zhu
a9b1ea9690 Add Broker and GodsUnchainedValidator to contract-addresses 2020-02-10 17:09:34 -08:00
Michael Zhu
8e5dd0f8d9 Add Broker and GodsUnchainedValidator to contract-artifacts 2020-02-10 17:08:24 -08:00
Jacob Evans
5754c11e34 Publish
- @0x/contracts-asset-proxy@3.2.0
 - @0x/contracts-broker@1.0.2
 - @0x/contracts-coordinator@3.1.0
 - @0x/contracts-dev-utils@1.1.0
 - @0x/contracts-erc1155@2.1.0
 - @0x/contracts-erc20-bridge-sampler@1.3.0
 - @0x/contracts-erc20@3.1.0
 - @0x/contracts-erc721@3.1.0
 - @0x/contracts-exchange-forwarder@4.2.0
 - @0x/contracts-exchange-libs@4.3.0
 - @0x/contracts-exchange@3.2.0
 - @0x/contracts-extensions@6.1.0
 - @0x/contracts-integrations@2.3.0
 - @0x/contracts-multisig@4.1.0
 - @0x/contracts-staking@2.0.7
 - @0x/contracts-test-utils@5.1.4
 - @0x/contracts-utils@4.3.0
 - 0x.js@9.1.0
 - @0x/abi-gen@5.2.0
 - @0x/assert@3.0.6
 - @0x/asset-swapper@4.1.2
 - @0x/base-contract@6.2.0
 - @0x/connect@6.0.6
 - @0x/contract-addresses@4.5.0
 - @0x/contract-artifacts@3.5.0
 - @0x/contract-wrappers-test@12.2.8
 - @0x/contract-wrappers@13.5.0
 - @0x/contracts-gen@2.0.7
 - @0x/dev-utils@3.2.0
 - ethereum-types@3.1.0
 - @0x/instant@1.0.45
 - @0x/json-schemas@5.0.6
 - @0x/migrations@6.1.0
 - @0x/monorepo-scripts@1.0.50
 - @0x/order-utils@10.2.0
 - @0x/orderbook@2.2.0
 - @0x/sol-compiler@4.0.7
 - @0x/sol-coverage@4.0.7
 - @0x/sol-doc@3.1.4
 - @0x/sol-profiler@4.0.7
 - @0x/sol-resolver@3.0.3
 - @0x/sol-trace@3.0.7
 - @0x/sol-tracing-utils@7.0.7
 - @0x/sra-spec@3.0.6
 - @0x/subproviders@6.0.7
 - @0x/types@3.1.2
 - @0x/typescript-typings@5.0.2
 - @0x/utils@5.4.0
 - @0x/web3-wrapper@7.0.6
2020-02-09 09:37:34 +10:00
Jacob Evans
7bd88c1bb8 Updated CHANGELOGS & MD docs 2020-02-09 09:37:04 +10:00
Lawrence Forman
445b686c6c Merge pull request #2463 from 0xProject/feat/abi-gen/library-linking
Support contracts with unlinked libraries
2020-02-08 00:23:57 -05:00
Lawrence Forman
0cb7b75214 Merge pull request #2464 from 0xProject/feat/contracts/dev-utils/public-libraries-refactor
DevUtils refactor into public libraries
2020-02-07 23:53:16 -05:00
Lawrence Forman
a08399dfee regenerate artifacts and wrappers 2020-02-07 23:23:23 -05:00
Lawrence Forman
4016808fa4 Cherry pick DevUtils refactor code from #2456 2020-02-07 23:23:23 -05:00
Lawrence Forman
8635849977 Address review comments and regenerate contract-wrappers 2020-02-07 23:22:58 -05:00
Lawrence Forman
1a9ed4d4fe Add es2018.promises tsconfig lib target. 2020-02-07 23:22:58 -05:00
Lawrence Forman
e7b3246dd0 Cherry pick library linking code from #2456 2020-02-07 23:22:58 -05:00
Lawrence Forman
19589aec57 Cherry-pick changes from feat/dev-utils/dydx-bridge-validation 2020-02-07 23:22:58 -05:00
Lawrence Forman
4d33ff0417 Cherry-pick changes from feat/dev-utils/dydx-bridge-validation 2020-02-07 23:22:58 -05:00
Lawrence Forman
93a5ab5b33 Merge pull request #2462 from 0xProject/fix-and-feat/restore-contract-tests-and-refactors
Fix contracts tests + refactors
2020-02-07 23:16:50 -05:00
Lawrence Forman
59ada06cdf Fix linter errors. 2020-02-07 22:39:56 -05:00
Lawrence Forman
ae650849b0 Rebase and address review comments. 2020-02-07 22:29:32 -05:00
Lawrence Forman
3e8f9a6b53 Cherry-pick changes from feat/dev-utils/dydx-bridge-validation 2020-02-07 22:10:10 -05:00
Lawrence Forman
79362b0dba Merge pull request #2476 from 0xProject/fix/erc20-bridge-sampler/dev-utils-gas-stipend
ERC20BridgeSampler: Catch DevUtils reverts
2020-02-07 10:08:40 -05:00
Lawrence Forman
3e3df06d57 @0x/contracts-erc20-bridge-sampler: Fix catching invalid opcode from DevUtils
`@0x/contract-addresses`: Update `ERC20BridgeSampler` mainnet and kovan addresses.
2020-02-07 03:07:33 -05:00
Lawrence Forman
6b220eb1c5 Merge pull request #2474 from 0xProject/feat/eth2dai-bridge/market-migration
Update Eth2Dai address
2020-02-07 02:42:29 -05:00
Lawrence Forman
a1c61cae11 @0x/contract-addresses: Redeploy ERC20BridgeSampler 2020-02-07 02:10:19 -05:00
Jacob Evans
d48a917bf3 [erc20-bridge-sampler] gas stipend for DevUtils 2020-02-07 16:57:11 +10:00
Lawrence Forman
646b6dafb2 @0x/contract-addresses Redeploy Eth2DaiBridge and ERC20BridgeSampler. 2020-02-07 01:56:59 -05:00
Lawrence Forman
8d10f33a3f @0x/contracts-utils: Update Eth2Dai address. 2020-02-07 01:56:59 -05:00
Jacob Evans
5d603b2f80 Publish
- @0x/contracts-asset-proxy@3.1.3
 - @0x/contracts-broker@1.0.1
 - @0x/contracts-coordinator@3.0.6
 - @0x/contracts-dev-utils@1.0.6
 - @0x/contracts-erc1155@2.0.6
 - @0x/contracts-erc20-bridge-sampler@1.2.1
 - @0x/contracts-erc20@3.0.6
 - @0x/contracts-erc721@3.0.6
 - @0x/contracts-exchange-forwarder@4.1.0
 - @0x/contracts-exchange-libs@4.2.0
 - @0x/contracts-exchange@3.1.2
 - @0x/contracts-extensions@6.0.0
 - @0x/contracts-integrations@2.2.3
 - @0x/contracts-multisig@4.0.6
 - @0x/contracts-staking@2.0.6
 - @0x/contracts-test-utils@5.1.3
 - @0x/contracts-utils@4.2.1
 - 0x.js@9.0.7
 - @0x/abi-gen@5.1.2
 - @0x/assert@3.0.5
 - @0x/asset-swapper@4.1.1
 - @0x/base-contract@6.1.2
 - @0x/connect@6.0.5
 - @0x/contract-artifacts@3.4.1
 - @0x/contract-wrappers-test@12.2.7
 - @0x/contract-wrappers@13.4.2
 - @0x/contracts-gen@2.0.6
 - @0x/dev-utils@3.1.3
 - @0x/instant@1.0.44
 - @0x/json-schemas@5.0.5
 - @0x/migrations@6.0.2
 - @0x/monorepo-scripts@1.0.49
 - @0x/order-utils@10.1.3
 - @0x/orderbook@2.1.2
 - @0x/sol-compiler@4.0.6
 - @0x/sol-coverage@4.0.6
 - @0x/sol-doc@3.1.3
 - @0x/sol-profiler@4.0.6
 - @0x/sol-trace@3.0.6
 - @0x/sol-tracing-utils@7.0.6
 - @0x/sra-spec@3.0.5
 - @0x/subproviders@6.0.6
 - @0x/utils@5.3.0
 - @0x/web3-wrapper@7.0.5
2020-02-06 21:24:29 +10:00
Jacob Evans
0e1b08ff54 Updated CHANGELOGS & MD docs 2020-02-06 21:24:05 +10:00
David Sun
befc22d718 [FIX] asset-swapper liquidity breakdown (#2472)
* changed divided

* added changelog

* prettier

Co-authored-by: Jacob Evans <dekz@dekz.net>
2020-02-06 15:09:39 +10:00
Jacob Evans
4a62e80967 [asset-swapper] Bump sampler gas limit and allow for override (#2471)
* [asset-swapper] Bump sampler gas limit and allow for override

* Fix imports

* Set to 36e6
2020-02-06 12:31:28 +10:00
Jacob Evans
ee9ef9f2c1 [asset-swapper] prune before dummy order creation (#2470) 2020-02-06 07:08:31 +10:00
mzhu25
93dcb68437 Merge pull request #2455 from 0xProject/feature/broker/gods-unchained
`@0x/contracts-broker`: Property-based Gods Unchained orders
2020-02-04 10:53:54 -08:00
Michael Zhu
0691cc7909 rename _transferEthRefund -> _unwrapAndTransferEth 2020-02-04 10:13:07 -08:00
Michael Zhu
d82f34fe59 add weth-related integration tests 2020-02-04 10:13:07 -08:00
Michael Zhu
d2313b30af Address PR feedback and add support for affiliate fees 2020-02-04 10:13:07 -08:00
Michael Zhu
329719472a Move LibAssetDataTransfer and MixinWeth(Utils) to contracts-extensions 2020-02-04 10:13:02 -08:00
Michael Zhu
a2fcab47d4 skip coordinator client test 2020-02-04 10:08:25 -08:00
Michael Zhu
4ab5951c25 Add comments 2020-02-04 10:08:25 -08:00
Michael Zhu
403cabb201 more integrations tests 2020-02-04 10:08:25 -08:00
Michael Zhu
3da7c5d3e2 Move LibAssetDataTransfer from forwarder to exchange-libs package 2020-02-04 10:08:21 -08:00
Michael Zhu
c5e0de51aa Add Broker rich errors 2020-02-04 10:06:12 -08:00
Michael Zhu
c581f1bba4 Update Broker README 2020-02-04 10:06:12 -08:00
Michael Zhu
b8ac9c2edd lint 2020-02-04 10:06:10 -08:00
Michael Zhu
1027ee2481 Broker integrations tests 2020-02-04 10:05:17 -08:00
Michael Zhu
2f311f7821 GodsUnchainedValidator unit tests 2020-02-04 10:04:19 -08:00
Michael Zhu
a98c95b514 Broker contracts 2020-02-04 10:04:19 -08:00
Michael Zhu
5bbbae5b23 create contracts-broker package 2020-02-04 10:04:19 -08:00
Jacob Evans
f9c9b9f924 Prevent docs dir from being completely removed 2020-02-04 20:29:16 +10:00
Jacob Evans
5921208ea6 Publish
- @0x/contracts-asset-proxy@3.1.2
 - @0x/contracts-coordinator@3.0.5
 - @0x/contracts-dev-utils@1.0.5
 - @0x/contracts-erc1155@2.0.5
 - @0x/contracts-erc20-bridge-sampler@1.2.0
 - @0x/contracts-erc20@3.0.5
 - @0x/contracts-erc721@3.0.5
 - @0x/contracts-exchange-forwarder@4.0.5
 - @0x/contracts-exchange-libs@4.1.1
 - @0x/contracts-exchange@3.1.1
 - @0x/contracts-extensions@5.1.4
 - @0x/contracts-integrations@2.2.2
 - @0x/contracts-multisig@4.0.5
 - @0x/contracts-staking@2.0.5
 - @0x/contracts-test-utils@5.1.2
 - @0x/contracts-utils@4.2.0
 - 0x.js@9.0.6
 - @0x/abi-gen@5.1.1
 - @0x/asset-swapper@4.1.0
 - @0x/base-contract@6.1.1
 - @0x/contract-addresses@4.4.0
 - @0x/contract-wrappers-test@12.2.6
 - @0x/contract-wrappers@13.4.1
 - @0x/contracts-gen@2.0.5
 - @0x/dev-utils@3.1.2
 - @0x/instant@1.0.43
 - @0x/migrations@6.0.1
 - @0x/monorepo-scripts@1.0.48
 - @0x/order-utils@10.1.2
 - @0x/orderbook@2.1.1
 - @0x/sol-compiler@4.0.5
 - @0x/sol-coverage@4.0.5
 - @0x/sol-doc@3.1.2
 - @0x/sol-profiler@4.0.5
 - @0x/sol-trace@3.0.5
 - @0x/sol-tracing-utils@7.0.5
 - @0x/subproviders@6.0.5
2020-02-04 20:22:07 +10:00
Jacob Evans
f89c78abd1 Updated CHANGELOGS & MD docs 2020-02-04 20:21:45 +10:00
David Sun
74d3b9334c Add liquidity source breakdown to asset-swapper (#2465)
* add liquidity source breakdown to asset-swapper

* remove debug line

* use OptimizedMarketOrder metadata

* updated change-log

* prettier + lint

* bug fixes

* fixes

* Prettier

* Fix types

Co-authored-by: Jacob Evans <dekz@dekz.net>
2020-02-04 19:03:28 +10:00
Xianny
919fc66b9d Stop hardcoding contract addresses (#2461)
* stop hardcoding contract addresses

* update changelog

* export ContractAddresses for docs
2020-02-04 17:49:45 +10:00
Francesco Agosti
400fb5a5bb Merge pull request #2468 from 0xProject/feat/fix-test-publish
Bump maxBuffer?
2020-02-03 19:59:03 -08:00
fragosti
3bb4f9085c Add post-instal step 2020-02-03 18:13:31 -08:00
Jacob Evans
714c6cec3c Bump maxBuffer? 2020-02-03 17:50:32 -08:00
Lawrence Forman
cb69921202 ERC20BridgeSampler: Unlock full sampling on Kovan (#2459)
* `@0x/contracts-utils`: Update kovan addresses in `DeploymentConstants`

* `@0x/contracts-erc20-bridge-sampler`: Make source IDs static on all networks, not inherited from `DeploymentConstants`.

* `@0x/contract-addresses`: Update `ERC20BridgeSampler` addresses on mainnet and kovan.

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2020-01-31 11:27:28 -05:00
Jacob Evans
277a0adac9 [base-contract] throw string revert error (#2453) 2020-01-24 07:48:26 +10:00
Jacob Evans
02d14f504f Remove docs from sol-doc gitignore 2020-01-23 12:10:42 +10:00
Jacob Evans
1ab7664a60 Publish
- @0x/contracts-integrations@2.2.1
 - 0x.js@9.0.5
 - @0x/asset-swapper@4.0.1
 - @0x/instant@1.0.42
2020-01-23 11:58:27 +10:00
Jacob Evans
1be46ffb7e Updated CHANGELOGS & MD docs 2020-01-23 11:58:10 +10:00
Lawrence Forman
6ca52aed0d @0x/asset-swapper: Fix understimated protocol fee in worst case quote. (#2452) 2020-01-22 18:22:56 -05:00
Jacob Evans
74e20970e2 Create docs folder for sol-doc 2020-01-22 18:56:10 +10:00
407 changed files with 15790 additions and 12032 deletions

View File

@@ -47,7 +47,7 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- repo-{{ .Environment.CIRCLE_SHA1 }} - 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-tests @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-staking
test-exchange-ganache-3.0: test-exchange-ganache-3.0:
resource_class: medium+ resource_class: medium+
docker: docker:
@@ -77,7 +77,7 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- repo-{{ .Environment.CIRCLE_SHA1 }} - repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler - run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
# TODO(dorothy-zbornak): Re-enable after updating this package for # TODO(dorothy-zbornak): Re-enable after updating this package for
# 3.0. At that time, also remove exclusion from monorepo # 3.0. At that time, also remove exclusion from monorepo
# package.json's test script. # package.json's test script.

5
.gitignore vendored
View File

@@ -79,6 +79,8 @@ TODO.md
.vscode .vscode
# generated contract artifacts/ # generated contract artifacts/
contracts/broker/generated-artifacts/
contracts/broker/test/generated-artifacts/
contracts/erc20-bridge-sampler/generated-artifacts/ contracts/erc20-bridge-sampler/generated-artifacts/
contracts/erc20-bridge-sampler/test/generated-artifacts/ contracts/erc20-bridge-sampler/test/generated-artifacts/
contracts/integrations/generated-artifacts/ contracts/integrations/generated-artifacts/
@@ -113,6 +115,7 @@ packages/sol-tracing-utils/test/fixtures/artifacts/
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/ python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
# generated truffle contract artifacts/ # generated truffle contract artifacts/
contracts/broker/build/
contracts/erc20-bridge-sampler/build/ contracts/erc20-bridge-sampler/build/
contracts/staking/build/ contracts/staking/build/
contracts/coordinator/build/ contracts/coordinator/build/
@@ -129,6 +132,8 @@ contracts/exchange-forwarder/build/
contracts/dev-utils/build/ contracts/dev-utils/build/
# generated contract wrappers # generated contract wrappers
contracts/broker/generated-wrappers/
contracts/broker/test/generated-wrappers/
packages/python-contract-wrappers/generated/ packages/python-contract-wrappers/generated/
contracts/erc20-bridge-sampler/generated-wrappers/ contracts/erc20-bridge-sampler/generated-wrappers/
contracts/erc20-bridge-sampler/test/generated-wrappers/ contracts/erc20-bridge-sampler/test/generated-wrappers/

View File

@@ -1,5 +1,9 @@
lib lib
.nyc_output .nyc_output
/contracts/broker/generated-wrappers
/contracts/broker/test/generated-wrappers
/contracts/broker/generated-artifacts
/contracts/broker/test/generated-artifacts
/contracts/integrations/generated-wrappers /contracts/integrations/generated-wrappers
/contracts/integrations/test/generated-wrappers /contracts/integrations/test/generated-wrappers
/contracts/integrations/generated-artifacts /contracts/integrations/generated-artifacts

View File

@@ -1,4 +1,49 @@
[ [
{
"timestamp": 1581748629,
"version": "3.2.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.2.0",
"changes": [
{
"note": "Fix broken tests.",
"pr": 2462
},
{
"note": "Remove dependency on `@0x/contracts-dev-utils`",
"pr": 2462
},
{
"note": "Add asset data decoding functions",
"pr": 2462
}
],
"timestamp": 1581204851
},
{
"timestamp": 1580988106,
"version": "3.1.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1580811564,
"version": "3.1.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1579682890, "timestamp": 1579682890,
"version": "3.1.1", "version": "3.1.1",

View File

@@ -5,6 +5,24 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v3.2.1 - _February 15, 2020_
* Dependencies updated
## v3.2.0 - _February 8, 2020_
* Fix broken tests. (#2462)
* Remove dependency on `@0x/contracts-dev-utils` (#2462)
* Add asset data decoding functions (#2462)
## v3.1.3 - _February 6, 2020_
* Dependencies updated
## v3.1.2 - _February 4, 2020_
* Dependencies updated
## v3.1.1 - _January 22, 2020_ ## v3.1.1 - _January 22, 2020_
* Dependencies updated * Dependencies updated

View File

@@ -0,0 +1,108 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/ICurve.sol";
// solhint-disable not-rely-on-time
// solhint-disable space-after-comma
contract CurveBridge is
IERC20Bridge,
IWallet,
DeploymentConstants
{
/// @dev Callback for `ICurve`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
/// (DAI, USDC) to the Curve contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT).
/// @param to The recipient of the bought tokens.
/// @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 bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success)
{
// Decode the bridge data to get the Curve metadata.
(address curveAddress, int128 fromCoinIdx, int128 toCoinIdx, int128 version) = abi.decode(bridgeData, (address, int128, int128, int128));
ICurve exchange = ICurve(curveAddress);
address fromTokenAddress = exchange.underlying_coins(fromCoinIdx);
require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
// Try to sell all of this contract's `fromTokenAddress` token balance.
if (version == 0) {
exchange.exchange_underlying(
fromCoinIdx,
toCoinIdx,
// dx
IERC20Token(fromTokenAddress).balanceOf(address(this)),
// min dy
amount,
// expires
block.timestamp + 1
);
} else {
exchange.exchange_underlying(
fromCoinIdx,
toCoinIdx,
// dx
IERC20Token(fromTokenAddress).balanceOf(address(this)),
// min dy
amount
);
}
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
// Transfer the converted `toToken`s to `to`.
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
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 Magic success bytes, always.
function isValidSignature(
bytes32,
bytes calldata
)
external
view
returns (bytes4 magicValue)
{
return LEGACY_WALLET_MAGIC_VALUE;
}
}

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;
// solhint-disable func-name-mixedcase
interface ICurve {
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
/// This function exists on early versions of Curve (USDC/DAI)
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param sellAmount The amount of token being bought.
/// @param minBuyAmount The minimum buy amount of the token being bought.
/// @param deadline The time in seconds when this operation should expire.
function exchange_underlying(
int128 i,
int128 j,
uint256 sellAmount,
uint256 minBuyAmount,
uint256 deadline
)
external;
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
/// This function exists on later versions of Curve (USDC/DAI/USDT)
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param sellAmount The amount of token being bought.
/// @param minBuyAmount The minimum buy amount of the token being bought.
function exchange_underlying(
int128 i,
int128 j,
uint256 sellAmount,
uint256 minBuyAmount
)
external;
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param sellAmount The amount of token being bought.
function get_dy_underlying(
int128 i,
int128 j,
uint256 sellAmount
)
external
returns (uint256 dy);
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
/// This function exists on later versions of Curve (USDC/DAI/USDT)
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param buyAmount The amount of token being bought.
function get_dx_underlying(
int128 i,
int128 j,
uint256 buyAmount
)
external
returns (uint256 dx);
/// @dev Get the underlying token address from the token index
/// @param i The token index.
function underlying_coins(
int128 i
)
external
returns (address tokenAddress);
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-asset-proxy", "name": "@0x/contracts-asset-proxy",
"version": "3.1.1", "version": "3.2.1",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -38,7 +38,7 @@
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
}, },
"config": { "config": {
"abis": "./test/generated-artifacts/@(ChaiBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json", "abis": "./test/generated-artifacts/@(ChaiBridge|CurveBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {
@@ -51,15 +51,14 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.1.0", "@0x/abi-gen": "^5.2.0",
"@0x/contracts-gen": "^2.0.4", "@0x/contracts-gen": "^2.0.7",
"@0x/contracts-test-utils": "^5.1.1", "@0x/contracts-test-utils": "^5.1.5",
"@0x/contracts-utils": "^4.1.0", "@0x/contracts-utils": "^4.3.1",
"@0x/dev-utils": "^3.1.1", "@0x/dev-utils": "^3.2.0",
"@0x/sol-compiler": "^4.0.4", "@0x/sol-compiler": "^4.0.7",
"@0x/ts-doc-gen": "^0.0.22", "@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0", "@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -79,17 +78,17 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^6.1.0", "@0x/base-contract": "^6.2.0",
"@0x/contracts-dev-utils": "^1.0.4", "@0x/contracts-erc1155": "^2.1.1",
"@0x/contracts-erc1155": "^2.0.4", "@0x/contracts-erc20": "^3.1.1",
"@0x/contracts-erc20": "^3.0.4", "@0x/contracts-erc721": "^3.1.1",
"@0x/contracts-erc721": "^3.0.4", "@0x/contracts-exchange-libs": "^4.3.1",
"@0x/contracts-exchange-libs": "^4.1.0", "@0x/order-utils": "^10.2.1",
"@0x/order-utils": "^10.1.1", "@0x/types": "^3.1.2",
"@0x/typescript-typings": "^5.0.1", "@0x/typescript-typings": "^5.0.2",
"@0x/utils": "^5.2.0", "@0x/utils": "^5.4.0",
"@0x/web3-wrapper": "^7.0.4", "@0x/web3-wrapper": "^7.0.6",
"ethereum-types": "^3.0.0", "ethereum-types": "^3.1.0",
"lodash": "^4.17.11" "lodash": "^4.17.11"
}, },
"publishConfig": { "publishConfig": {

View File

@@ -6,6 +6,7 @@
import { ContractArtifact } from 'ethereum-types'; import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json'; import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
import * as CurveBridge from '../generated-artifacts/CurveBridge.json';
import * as DydxBridge from '../generated-artifacts/DydxBridge.json'; import * as DydxBridge from '../generated-artifacts/DydxBridge.json';
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json'; import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json'; import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
@@ -17,6 +18,7 @@ import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json'; import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json'; import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
import * as IChai from '../generated-artifacts/IChai.json'; import * as IChai from '../generated-artifacts/IChai.json';
import * as ICurve from '../generated-artifacts/ICurve.json';
import * as IDydx from '../generated-artifacts/IDydx.json'; import * as IDydx from '../generated-artifacts/IDydx.json';
import * as IDydxBridge from '../generated-artifacts/IDydxBridge.json'; import * as IDydxBridge from '../generated-artifacts/IDydxBridge.json';
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json'; import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
@@ -49,6 +51,7 @@ export const artifacts = {
MultiAssetProxy: MultiAssetProxy as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact, StaticCallProxy: StaticCallProxy as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact,
CurveBridge: CurveBridge as ContractArtifact,
DydxBridge: DydxBridge as ContractArtifact, DydxBridge: DydxBridge as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact, Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact, KyberBridge: KyberBridge as ContractArtifact,
@@ -58,6 +61,7 @@ export const artifacts = {
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact, IAuthorizable: IAuthorizable as ContractArtifact,
IChai: IChai as ContractArtifact, IChai: IChai as ContractArtifact,
ICurve: ICurve as ContractArtifact,
IDydx: IDydx as ContractArtifact, IDydx: IDydx as ContractArtifact,
IDydxBridge: IDydxBridge as ContractArtifact, IDydxBridge: IDydxBridge as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact, IERC20Bridge: IERC20Bridge as ContractArtifact,

View File

@@ -0,0 +1,112 @@
import { AssetProxyId } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import { IAssetDataContract } from './wrappers';
const assetDataIface = new IAssetDataContract('0x0000000000000000000000000000000000000000', { isEIP1193: true } as any);
/**
* Get the proxy ID from encoded asset data.
*/
export function getAssetDataProxyId(encoded: string): AssetProxyId {
// tslint:disable-next-line: no-unnecessary-type-assertion
return hexUtils.slice(encoded, 0, 4) as AssetProxyId;
}
/**
* Decode ERC20 asset data.
*/
export function decodeERC20AssetData(encoded: string): string {
return assetDataIface.getABIDecodedTransactionData<string>('ERC20Token', encoded);
}
/**
* Decode ERC721 asset data.
*/
export function decodeERC721AssetData(encoded: string): [string, BigNumber] {
return assetDataIface.getABIDecodedTransactionData<[string, BigNumber]>('ERC721Token', encoded);
}
/**
* Decode ERC1155 asset data.
*/
export function decodeERC1155AssetData(encoded: string): [string, BigNumber[], BigNumber[], string] {
return assetDataIface.getABIDecodedTransactionData<[string, BigNumber[], BigNumber[], string]>(
'ERC1155Assets',
encoded,
);
}
/**
* Decode MultiAsset asset data.
*/
export function decodeMultiAssetData(encoded: string): [BigNumber[], string[]] {
return assetDataIface.getABIDecodedTransactionData<[BigNumber[], string[]]>('MultiAsset', encoded);
}
/**
* Decode StaticCall asset data.
*/
export function decodeStaticCallAssetData(encoded: string): [string, string, string] {
return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('StaticCall', encoded);
}
/**
* Decode ERC20Bridge asset data.
*/
export function decodeERC20BridgeAssetData(encoded: string): [string, string, string] {
return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('ERC20Bridge', encoded);
}
/**
* Encode ERC20 asset data.
*/
export function encodeERC20AssetData(tokenAddress: string): string {
return assetDataIface.ERC20Token(tokenAddress).getABIEncodedTransactionData();
}
/**
* Encode ERC721 asset data.
*/
export function encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string {
return assetDataIface.ERC721Token(tokenAddress, tokenId).getABIEncodedTransactionData();
}
/**
* Encode ERC1155 asset data.
*/
export function encodeERC1155AssetData(
tokenAddress: string,
tokenIds: BigNumber[],
values: BigNumber[],
callbackData: string,
): string {
return assetDataIface.ERC1155Assets(tokenAddress, tokenIds, values, callbackData).getABIEncodedTransactionData();
}
/**
* Encode MultiAsset asset data.
*/
export function encodeMultiAssetData(values: BigNumber[], nestedAssetData: string[]): string {
return assetDataIface.MultiAsset(values, nestedAssetData).getABIEncodedTransactionData();
}
/**
* Encode StaticCall asset data.
*/
export function encodeStaticCallAssetData(
staticCallTargetAddress: string,
staticCallData: string,
expectedReturnDataHash: string,
): string {
return assetDataIface
.StaticCall(staticCallTargetAddress, staticCallData, expectedReturnDataHash)
.getABIEncodedTransactionData();
}
/**
* Encode ERC20Bridge asset data.
*/
export function encodeERC20BridgeAssetData(tokenAddress: string, bridgeAddress: string, bridgeData: string): string {
return assetDataIface.ERC20Bridge(tokenAddress, bridgeAddress, bridgeData).getABIEncodedTransactionData();
}

View File

@@ -5,7 +5,7 @@ export enum DydxBridgeActionType {
Withdraw, Withdraw,
} }
export interface DydxBrigeAction { export interface DydxBridgeAction {
actionType: DydxBridgeActionType; actionType: DydxBridgeActionType;
accountId: BigNumber; accountId: BigNumber;
marketId: BigNumber; marketId: BigNumber;
@@ -15,7 +15,7 @@ export interface DydxBrigeAction {
export interface DydxBridgeData { export interface DydxBridgeData {
accountNumbers: BigNumber[]; accountNumbers: BigNumber[];
actions: DydxBrigeAction[]; actions: DydxBridgeAction[];
} }
export const dydxBridgeDataEncoder = AbiEncoder.create([ export const dydxBridgeDataEncoder = AbiEncoder.create([

View File

@@ -1,4 +1,3 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import { import {
constants, constants,
@@ -15,7 +14,7 @@ import * as _ from 'lodash';
import { artifacts } from './artifacts'; import { artifacts } from './artifacts';
import { ERC1155ProxyContract, IAssetProxyContract } from './wrappers'; import { ERC1155ProxyContract, IAssetDataContract, IAssetProxyContract } from './wrappers';
export class ERC1155ProxyWrapper { export class ERC1155ProxyWrapper {
private readonly _tokenOwnerAddresses: string[]; private readonly _tokenOwnerAddresses: string[];
@@ -28,7 +27,7 @@ export class ERC1155ProxyWrapper {
private readonly _logDecoder: LogDecoder; private readonly _logDecoder: LogDecoder;
private readonly _dummyTokenWrappers: Erc1155Wrapper[]; private readonly _dummyTokenWrappers: Erc1155Wrapper[];
private readonly _assetProxyInterface: IAssetProxyContract; private readonly _assetProxyInterface: IAssetProxyContract;
private readonly _devUtils: DevUtilsContract; private readonly _assetDataInterface: IAssetDataContract;
private _proxyContract?: ERC1155ProxyContract; private _proxyContract?: ERC1155ProxyContract;
private _proxyIdIfExists?: string; private _proxyIdIfExists?: string;
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} }; private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
@@ -40,7 +39,7 @@ export class ERC1155ProxyWrapper {
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts); this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
this._dummyTokenWrappers = []; this._dummyTokenWrappers = [];
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider); this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider); this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
this._tokenOwnerAddresses = tokenOwnerAddresses; this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress; this._contractOwnerAddress = contractOwnerAddress;
this._fungibleTokenIds = []; this._fungibleTokenIds = [];
@@ -113,9 +112,9 @@ export class ERC1155ProxyWrapper {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
const assetData = const assetData =
assetData_ === undefined assetData_ === undefined
? await this._devUtils ? this._assetDataInterface
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData) .ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
.callAsync() .getABIEncodedTransactionData()
: assetData_; : assetData_;
const data = this._assetProxyInterface const data = this._assetProxyInterface
.transferFrom(assetData, from, to, valueMultiplier) .transferFrom(assetData, from, to, valueMultiplier)
@@ -167,9 +166,9 @@ export class ERC1155ProxyWrapper {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
const assetData = const assetData =
assetData_ === undefined assetData_ === undefined
? await this._devUtils ? this._assetDataInterface
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData) .ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
.callAsync() .getABIEncodedTransactionData()
: assetData_; : assetData_;
const data = this._assetProxyInterface const data = this._assetProxyInterface
.transferFrom(assetData, from, to, valueMultiplier) .transferFrom(assetData, from, to, valueMultiplier)

View File

@@ -1,4 +1,3 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils'; import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
@@ -7,14 +6,14 @@ import * as _ from 'lodash';
import { artifacts } from './artifacts'; import { artifacts } from './artifacts';
import { ERC20ProxyContract } from './wrappers'; import { ERC20ProxyContract, IAssetDataContract } from './wrappers';
export class ERC20Wrapper { export class ERC20Wrapper {
private readonly _tokenOwnerAddresses: string[]; private readonly _tokenOwnerAddresses: string[];
private readonly _contractOwnerAddress: string; private readonly _contractOwnerAddress: string;
private readonly _provider: ZeroExProvider; private readonly _provider: ZeroExProvider;
private readonly _dummyTokenContracts: DummyERC20TokenContract[]; private readonly _dummyTokenContracts: DummyERC20TokenContract[];
private readonly _devUtils: DevUtilsContract; private readonly _assetDataInterface: IAssetDataContract;
private _proxyContract?: ERC20ProxyContract; private _proxyContract?: ERC20ProxyContract;
private _proxyIdIfExists?: string; private _proxyIdIfExists?: string;
/** /**
@@ -29,7 +28,7 @@ export class ERC20Wrapper {
this._provider = provider; this._provider = provider;
this._tokenOwnerAddresses = tokenOwnerAddresses; this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress; this._contractOwnerAddress = contractOwnerAddress;
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider); this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
} }
public async deployDummyTokensAsync( public async deployDummyTokensAsync(
numberToDeploy: number, numberToDeploy: number,
@@ -145,7 +144,7 @@ export class ERC20Wrapper {
return tokenAddresses; return tokenAddresses;
} }
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> { private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
const [proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData(assetData).callAsync(); // tslint:disable-line:no-unused-variable const tokenAddress = this._assetDataInterface.getABIDecodedTransactionData<string>('ERC20Token', assetData); // tslint:disable-line:no-unused-variable
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
if (tokenContractIfExists === undefined) { if (tokenContractIfExists === undefined) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);

View File

@@ -24,6 +24,7 @@ export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
export { DummyERC20TokenContract } from '@0x/contracts-erc20'; export { DummyERC20TokenContract } from '@0x/contracts-erc20';
export { DummyERC721TokenContract } from '@0x/contracts-erc721'; export { DummyERC721TokenContract } from '@0x/contracts-erc721';
export { AssetProxyId } from '@0x/types';
export { export {
ERC1155HoldingsByOwner, ERC1155HoldingsByOwner,
ERC20BalancesByOwner, ERC20BalancesByOwner,
@@ -54,6 +55,7 @@ export {
OutputField, OutputField,
ParamDescription, ParamDescription,
EvmBytecodeOutput, EvmBytecodeOutput,
EvmBytecodeOutputLinkReferences,
AbiDefinition, AbiDefinition,
FunctionAbi, FunctionAbi,
EventAbi, EventAbi,
@@ -67,4 +69,21 @@ export {
TupleDataItem, TupleDataItem,
StateMutability, StateMutability,
} from 'ethereum-types'; } from 'ethereum-types';
export {
decodeERC1155AssetData,
decodeERC20AssetData,
decodeERC20BridgeAssetData,
decodeERC721AssetData,
decodeMultiAssetData,
decodeStaticCallAssetData,
encodeERC1155AssetData,
encodeERC20AssetData,
encodeERC20BridgeAssetData,
encodeERC721AssetData,
encodeMultiAssetData,
encodeStaticCallAssetData,
getAssetDataProxyId,
} from './asset_data';
export * from './dydx_bridge_encoder'; export * from './dydx_bridge_encoder';

View File

@@ -4,6 +4,7 @@
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
export * from '../generated-wrappers/chai_bridge'; export * from '../generated-wrappers/chai_bridge';
export * from '../generated-wrappers/curve_bridge';
export * from '../generated-wrappers/dydx_bridge'; export * from '../generated-wrappers/dydx_bridge';
export * from '../generated-wrappers/erc1155_proxy'; export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_bridge_proxy'; export * from '../generated-wrappers/erc20_bridge_proxy';
@@ -15,6 +16,7 @@ export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/i_asset_proxy_dispatcher'; export * from '../generated-wrappers/i_asset_proxy_dispatcher';
export * from '../generated-wrappers/i_authorizable'; export * from '../generated-wrappers/i_authorizable';
export * from '../generated-wrappers/i_chai'; export * from '../generated-wrappers/i_chai';
export * from '../generated-wrappers/i_curve';
export * from '../generated-wrappers/i_dydx'; export * from '../generated-wrappers/i_dydx';
export * from '../generated-wrappers/i_dydx_bridge'; export * from '../generated-wrappers/i_dydx_bridge';
export * from '../generated-wrappers/i_erc20_bridge'; export * from '../generated-wrappers/i_erc20_bridge';

View File

@@ -6,6 +6,7 @@
import { ContractArtifact } from 'ethereum-types'; import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json'; import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json';
import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json'; import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json';
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json'; import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json'; import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
@@ -17,6 +18,7 @@ import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json'; import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json'; import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
import * as IChai from '../test/generated-artifacts/IChai.json'; import * as IChai from '../test/generated-artifacts/IChai.json';
import * as ICurve from '../test/generated-artifacts/ICurve.json';
import * as IDydx from '../test/generated-artifacts/IDydx.json'; import * as IDydx from '../test/generated-artifacts/IDydx.json';
import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.json'; import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.json';
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json'; import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
@@ -49,6 +51,7 @@ export const artifacts = {
MultiAssetProxy: MultiAssetProxy as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact, StaticCallProxy: StaticCallProxy as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact,
CurveBridge: CurveBridge as ContractArtifact,
DydxBridge: DydxBridge as ContractArtifact, DydxBridge: DydxBridge as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact, Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact, KyberBridge: KyberBridge as ContractArtifact,
@@ -58,6 +61,7 @@ export const artifacts = {
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact, IAuthorizable: IAuthorizable as ContractArtifact,
IChai: IChai as ContractArtifact, IChai: IChai as ContractArtifact,
ICurve: ICurve as ContractArtifact,
IDydx: IDydx as ContractArtifact, IDydx: IDydx as ContractArtifact,
IDydxBridge: IDydxBridge as ContractArtifact, IDydxBridge: IDydxBridge as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact, IERC20Bridge: IERC20Bridge as ContractArtifact,

View File

@@ -1,35 +1,20 @@
import { chaiSetup, expectTransactionFailedAsync, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; import { blockchainTests, expect, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types'; import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { artifacts } from './artifacts'; import { artifacts } from './artifacts';
import { MixinAuthorizableContract } from './wrappers'; import { MixinAuthorizableContract } from './wrappers';
chaiSetup.configure(); blockchainTests.resets('Authorizable', () => {
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Authorizable', () => {
let owner: string; let owner: string;
let notOwner: string; let notOwner: string;
let address: string; let address: string;
let authorizable: MixinAuthorizableContract; let authorizable: MixinAuthorizableContract;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => { before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync(); const accounts = await web3Wrapper.getAvailableAddressesAsync();
[owner, address, notOwner] = _.slice(accounts, 0, 3); [owner, address, notOwner] = accounts.slice(0, 3);
authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync( authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
artifacts.MixinAuthorizable, artifacts.MixinAuthorizable,
provider, provider,
@@ -38,20 +23,10 @@ describe('Authorizable', () => {
); );
}); });
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('addAuthorizedAddress', () => { describe('addAuthorizedAddress', () => {
it('should revert if not called by owner', async () => { it('should revert if not called by owner', async () => {
await expectTransactionFailedAsync( const tx = authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner });
authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner }), return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
RevertReason.OnlyContractOwner,
);
}); });
it('should allow owner to add an authorized address', async () => { it('should allow owner to add an authorized address', async () => {
@@ -62,20 +37,16 @@ describe('Authorizable', () => {
it('should revert if owner attempts to authorize a duplicate address', async () => { it('should revert if owner attempts to authorize a duplicate address', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
return expectTransactionFailedAsync( const tx = authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner });
authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner }), return expect(tx).to.revertWith(RevertReason.TargetAlreadyAuthorized);
RevertReason.TargetAlreadyAuthorized,
);
}); });
}); });
describe('removeAuthorizedAddress', () => { describe('removeAuthorizedAddress', () => {
it('should revert if not called by owner', async () => { it('should revert if not called by owner', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
await expectTransactionFailedAsync( const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner });
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner }), return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
RevertReason.OnlyContractOwner,
);
}); });
it('should allow owner to remove an authorized address', async () => { it('should allow owner to remove an authorized address', async () => {
@@ -86,12 +57,8 @@ describe('Authorizable', () => {
}); });
it('should revert if owner attempts to remove an address that is not authorized', async () => { it('should revert if owner attempts to remove an address that is not authorized', async () => {
return expectTransactionFailedAsync( const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: owner });
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized);
from: owner,
}),
RevertReason.TargetNotAuthorized,
);
}); });
}); });
@@ -99,33 +66,27 @@ describe('Authorizable', () => {
it('should revert if not called by owner', async () => { it('should revert if not called by owner', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
const index = new BigNumber(0); const index = new BigNumber(0);
await expectTransactionFailedAsync( const tx = authorizable
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({ .removeAuthorizedAddressAtIndex(address, index)
from: notOwner, .sendTransactionAsync({ from: notOwner });
}), return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
RevertReason.OnlyContractOwner,
);
}); });
it('should revert if index is >= authorities.length', async () => { it('should revert if index is >= authorities.length', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
const index = new BigNumber(1); const index = new BigNumber(1);
return expectTransactionFailedAsync( const tx = authorizable
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({ .removeAuthorizedAddressAtIndex(address, index)
from: owner, .sendTransactionAsync({ from: owner });
}), return expect(tx).to.revertWith(RevertReason.IndexOutOfBounds);
RevertReason.IndexOutOfBounds,
);
}); });
it('should revert if owner attempts to remove an address that is not authorized', async () => { it('should revert if owner attempts to remove an address that is not authorized', async () => {
const index = new BigNumber(0); const index = new BigNumber(0);
return expectTransactionFailedAsync( const tx = authorizable
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({ .removeAuthorizedAddressAtIndex(address, index)
from: owner, .sendTransactionAsync({ from: owner });
}), return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized);
RevertReason.TargetNotAuthorized,
);
}); });
it('should revert if address at index does not match target', async () => { it('should revert if address at index does not match target', async () => {
@@ -134,12 +95,10 @@ describe('Authorizable', () => {
await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner });
await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner });
const address1Index = new BigNumber(0); const address1Index = new BigNumber(0);
return expectTransactionFailedAsync( const tx = authorizable
authorizable.removeAuthorizedAddressAtIndex(address2, address1Index).sendTransactionAsync({ .removeAuthorizedAddressAtIndex(address2, address1Index)
from: owner, .sendTransactionAsync({ from: owner });
}), return expect(tx).to.revertWith(RevertReason.AuthorizedAddressMismatch);
RevertReason.AuthorizedAddressMismatch,
);
}); });
it('should allow owner to remove an authorized address', async () => { it('should allow owner to remove an authorized address', async () => {

View File

@@ -1,4 +1,3 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { import {
artifacts as erc1155Artifacts, artifacts as erc1155Artifacts,
DummyERC1155ReceiverBatchTokenReceivedEventArgs, DummyERC1155ReceiverBatchTokenReceivedEventArgs,
@@ -63,8 +62,8 @@ describe('ERC1155Proxy', () => {
// tokens // tokens
let fungibleTokens: BigNumber[]; let fungibleTokens: BigNumber[];
let nonFungibleTokensOwnedBySpender: BigNumber[]; let nonFungibleTokensOwnedBySpender: BigNumber[];
// devUtils for encoding and decoding assetData // IAssetData for encoding and decoding assetData
let devUtils: DevUtilsContract; let assetDataContract: IAssetDataContract;
// tests // tests
before(async () => { before(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@@ -101,8 +100,8 @@ describe('ERC1155Proxy', () => {
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0]; tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender); nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
}); });
// set up devUtils // set up assetDataContract
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, { from: owner }); assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider, { from: owner });
}); });
beforeEach(async () => { beforeEach(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@@ -638,14 +637,9 @@ describe('ERC1155Proxy', () => {
return value.times(valueMultiplier); return value.times(valueMultiplier);
}); });
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
const extraData = '0102030405060708091001020304050607080910010203040506070809100102'; const extraData = '0102030405060708091001020304050607080910010203040506070809100102';
const assetDataWithExtraData = `${assetData}${extraData}`; const assetDataWithExtraData = `${assetData}${extraData}`;
// check balances before transfer // check balances before transfer
@@ -745,8 +739,7 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = tokensToTransfer; const valuesToTransfer = tokensToTransfer;
const valueMultiplier = new BigNumber(2); const valueMultiplier = new BigNumber(2);
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/encodeERC1155AssetData) does not use optimized encoding // hand encode optimized assetData because our tooling (based on LibAssetData.sol/ERC1155Assets) does not use optimized encoding
const assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider);
const selector = assetDataContract.getSelector('ERC1155Assets'); const selector = assetDataContract.getSelector('ERC1155Assets');
const assetDataWithoutContractAddress = const assetDataWithoutContractAddress =
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000'; '0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
@@ -857,14 +850,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)]; const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
const valueMultiplier = new BigNumber(2); const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer` // create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = await devUtils const generatedAssetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// remove the function selector and contract address from check, as these change on each test // remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74; const offsetToTokenIds = 74;
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds); const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
@@ -983,14 +971,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)]; const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
const valueMultiplier = new BigNumber(2); const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer` // create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = await devUtils const generatedAssetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// remove the function selector and contract address from check, as these change on each test // remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74; const offsetToTokenIds = 74;
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds); const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
@@ -1048,14 +1031,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1097,14 +1075,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1150,14 +1123,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1203,14 +1171,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1256,14 +1219,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1310,14 +1268,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1359,14 +1312,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1412,14 +1360,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1461,14 +1404,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1514,14 +1452,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync( const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender, spender,
receiverContract, receiverContract,
@@ -1547,14 +1480,9 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataContract
.encodeERC1155AssetData( .ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
erc1155ContractAddress, .getABIEncodedTransactionData();
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync( const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender, spender,
receiverContract, receiverContract,

View File

@@ -1,4 +1,3 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import { import {
artifacts as erc20Artifacts, artifacts as erc20Artifacts,
@@ -29,19 +28,24 @@ import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types'; import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import {
encodeERC1155AssetData,
encodeERC20AssetData,
encodeERC721AssetData,
encodeMultiAssetData,
} from '../src/asset_data';
import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper'; import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper';
import { ERC20Wrapper } from '../src/erc20_wrapper'; import { ERC20Wrapper } from '../src/erc20_wrapper';
import { ERC721Wrapper } from '../src/erc721_wrapper'; import { ERC721Wrapper } from '../src/erc721_wrapper';
import { ERC1155ProxyContract, ERC20ProxyContract, ERC721ProxyContract } from '../src/wrappers'; import { ERC1155ProxyContract, ERC20ProxyContract, ERC721ProxyContract } from '../src/wrappers';
import { artifacts } from './artifacts'; import { artifacts } from './artifacts';
import { IAssetDataContract, IAssetProxyContract, MultiAssetProxyContract } from './wrappers'; import { IAssetProxyContract, MultiAssetProxyContract } from './wrappers';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
const assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider); const assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
const assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
// tslint:disable:no-unnecessary-type-assertion // tslint:disable:no-unnecessary-type-assertion
describe('Asset Transfer Proxies', () => { describe('Asset Transfer Proxies', () => {
@@ -51,7 +55,6 @@ describe('Asset Transfer Proxies', () => {
let fromAddress: string; let fromAddress: string;
let toAddress: string; let toAddress: string;
let devUtils: DevUtilsContract;
let erc20TokenA: DummyERC20TokenContract; let erc20TokenA: DummyERC20TokenContract;
let erc20TokenB: DummyERC20TokenContract; let erc20TokenB: DummyERC20TokenContract;
let erc721TokenA: DummyERC721TokenContract; let erc721TokenA: DummyERC721TokenContract;
@@ -87,7 +90,6 @@ describe('Asset Transfer Proxies', () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync(); const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5)); const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5));
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
@@ -221,7 +223,7 @@ describe('Asset Transfer Proxies', () => {
describe('transferFrom', () => { describe('transferFrom', () => {
it('should successfully transfer tokens', async () => { it('should successfully transfer tokens', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const encodedAssetData = encodeERC20AssetData(erc20TokenA.address);
// Perform a transfer from fromAddress to toAddress // Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync(); const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10); const amount = new BigNumber(10);
@@ -248,7 +250,7 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer tokens that do not return a value', async () => { it('should successfully transfer tokens that do not return a value', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(noReturnErc20Token.address).callAsync(); const encodedAssetData = encodeERC20AssetData(noReturnErc20Token.address);
// Perform a transfer from fromAddress to toAddress // Perform a transfer from fromAddress to toAddress
const initialFromBalance = await noReturnErc20Token.balanceOf(fromAddress).callAsync(); const initialFromBalance = await noReturnErc20Token.balanceOf(fromAddress).callAsync();
const initialToBalance = await noReturnErc20Token.balanceOf(toAddress).callAsync(); const initialToBalance = await noReturnErc20Token.balanceOf(toAddress).callAsync();
@@ -274,9 +276,7 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer tokens and ignore extra assetData', async () => { it('should successfully transfer tokens and ignore extra assetData', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const extraData = '0102030405060708'; const extraData = '0102030405060708';
const encodedAssetData = `${await devUtils const encodedAssetData = `${encodeERC20AssetData(erc20TokenA.address)}${extraData}`;
.encodeERC20AssetData(erc20TokenA.address)
.callAsync()}${extraData}`;
// Perform a transfer from fromAddress to toAddress // Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync(); const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10); const amount = new BigNumber(10);
@@ -303,7 +303,7 @@ describe('Asset Transfer Proxies', () => {
it('should do nothing if transferring 0 amount of a token', async () => { it('should do nothing if transferring 0 amount of a token', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const encodedAssetData = encodeERC20AssetData(erc20TokenA.address);
// Perform a transfer from fromAddress to toAddress // Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync(); const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(0); const amount = new BigNumber(0);
@@ -330,7 +330,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if allowances are too low', async () => { it('should revert if allowances are too low', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const encodedAssetData = encodeERC20AssetData(erc20TokenA.address);
// Create allowance less than transfer amount. Set allowance on proxy. // Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0); const allowance = new BigNumber(0);
const amount = new BigNumber(10); const amount = new BigNumber(10);
@@ -356,7 +356,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if allowances are too low and token does not return a value', async () => { it('should revert if allowances are too low and token does not return a value', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(noReturnErc20Token.address).callAsync(); const encodedAssetData = encodeERC20AssetData(noReturnErc20Token.address);
// Create allowance less than transfer amount. Set allowance on proxy. // Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0); const allowance = new BigNumber(0);
const amount = new BigNumber(10); const amount = new BigNumber(10);
@@ -385,7 +385,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if caller is not authorized', async () => { it('should revert if caller is not authorized', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const encodedAssetData = encodeERC20AssetData(erc20TokenA.address);
// Perform a transfer from fromAddress to toAddress // Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(10); const amount = new BigNumber(10);
const data = assetProxyInterface const data = assetProxyInterface
@@ -406,9 +406,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if token returns more than 32 bytes', async () => { it('should revert if token returns more than 32 bytes', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
const encodedAssetData = await devUtils const encodedAssetData = encodeERC20AssetData(multipleReturnErc20Token.address);
.encodeERC20AssetData(multipleReturnErc20Token.address)
.callAsync();
const amount = new BigNumber(10); const amount = new BigNumber(10);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount) .transferFrom(encodedAssetData, fromAddress, toAddress, amount)
@@ -452,9 +450,7 @@ describe('Asset Transfer Proxies', () => {
describe('transferFrom', () => { describe('transferFrom', () => {
it('should successfully transfer tokens', async () => { it('should successfully transfer tokens', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data
const encodedAssetData = await devUtils const encodedAssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition // Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress); expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -479,9 +475,10 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer tokens and ignore extra assetData', async () => { it('should successfully transfer tokens and ignore extra assetData', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data
const extraData = '0102030405060708'; const extraData = '0102030405060708';
const encodedAssetData = `${await devUtils const encodedAssetData = `${encodeERC721AssetData(
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId) erc721TokenA.address,
.callAsync()}${extraData}`; erc721AFromTokenId,
)}${extraData}`;
// Verify pre-condition // Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress); expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -505,9 +502,7 @@ describe('Asset Transfer Proxies', () => {
it('should not call onERC721Received when transferring to a smart contract', async () => { it('should not call onERC721Received when transferring to a smart contract', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data
const encodedAssetData = await devUtils const encodedAssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition // Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress); expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -534,9 +529,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if transferring 0 amount of a token', async () => { it('should revert if transferring 0 amount of a token', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data
const encodedAssetData = await devUtils const encodedAssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition // Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress); expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -559,9 +552,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if transferring > 1 amount of a token', async () => { it('should revert if transferring > 1 amount of a token', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data
const encodedAssetData = await devUtils const encodedAssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition // Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress); expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -584,9 +575,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if allowances are too low', async () => { it('should revert if allowances are too low', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data
const encodedAssetData = await devUtils const encodedAssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition // Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress); expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -617,9 +606,7 @@ describe('Asset Transfer Proxies', () => {
it('should revert if caller is not authorized', async () => { it('should revert if caller is not authorized', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data
const encodedAssetData = await devUtils const encodedAssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition // Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress); expect(ownerFromAsset).to.be.equal(fromAddress);
@@ -663,10 +650,10 @@ describe('Asset Transfer Proxies', () => {
it('should transfer a single ERC20 token', async () => { it('should transfer a single ERC20 token', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const amounts = [erc20Amount]; const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData]; const nestedAssetData = [erc20AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -691,12 +678,10 @@ describe('Asset Transfer Proxies', () => {
it('should dispatch an ERC20 transfer when input amount is 0', async () => { it('should dispatch an ERC20 transfer when input amount is 0', async () => {
const inputAmount = constants.ZERO_AMOUNT; const inputAmount = constants.ZERO_AMOUNT;
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const amounts = [erc20Amount]; const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData]; const nestedAssetData = [erc20AssetData];
const assetData = assetDataInterface const assetData = encodeMultiAssetData(amounts, nestedAssetData);
.MultiAsset(amounts, nestedAssetData)
.getABIEncodedTransactionData();
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -721,11 +706,11 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10); const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20); const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData1 = encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData2 = encodeERC20AssetData(erc20TokenA.address);
const amounts = [erc20Amount1, erc20Amount2]; const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -751,11 +736,11 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10); const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20); const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData1 = encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenB.address).callAsync(); const erc20AssetData2 = encodeERC20AssetData(erc20TokenB.address);
const amounts = [erc20Amount1, erc20Amount2]; const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -787,12 +772,10 @@ describe('Asset Transfer Proxies', () => {
it('should transfer a single ERC721 token', async () => { it('should transfer a single ERC721 token', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc721Amount]; const amounts = [erc721Amount];
const nestedAssetData = [erc721AssetData]; const nestedAssetData = [erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -812,17 +795,13 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer multiple of the same ERC721 token', async () => { it('should successfully transfer multiple of the same ERC721 token', async () => {
const erc721Balances = await erc721Wrapper.getBalancesAsync(); const erc721Balances = await erc721Wrapper.getBalancesAsync();
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1]; const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
const erc721AssetData1 = await devUtils const erc721AssetData1 = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId) const erc721AssetData2 = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId2);
.callAsync();
const erc721AssetData2 = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId2)
.callAsync();
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount]; const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2]; const nestedAssetData = [erc721AssetData1, erc721AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -845,17 +824,13 @@ describe('Asset Transfer Proxies', () => {
expect(newOwnerFromAsset2).to.be.equal(toAddress); expect(newOwnerFromAsset2).to.be.equal(toAddress);
}); });
it('should successfully transfer multiple different ERC721 tokens', async () => { it('should successfully transfer multiple different ERC721 tokens', async () => {
const erc721AssetData1 = await devUtils const erc721AssetData1 = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId) const erc721AssetData2 = encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
.callAsync();
const erc721AssetData2 = await devUtils
.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId)
.callAsync();
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount]; const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2]; const nestedAssetData = [erc721AssetData1, erc721AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -893,19 +868,17 @@ describe('Asset Transfer Proxies', () => {
]; ];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data // encode erc1155 asset data
const erc1155AssetData = await devUtils const erc1155AssetData = encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155Contract.address, erc1155Contract.address,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// encode multi-asset data // encode multi-asset data
const multiAssetAmount = new BigNumber(5); const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier]; const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData]; const nestedAssetData = [erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount) .transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -948,19 +921,17 @@ describe('Asset Transfer Proxies', () => {
]; ];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data // encode erc1155 asset data
const erc1155AssetData = await devUtils const erc1155AssetData = encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155Contract.address, erc1155Contract.address,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// encode multi-asset data // encode multi-asset data
const multiAssetAmount = new BigNumber(5); const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier]; const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData]; const nestedAssetData = [erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount) .transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1011,19 +982,17 @@ describe('Asset Transfer Proxies', () => {
]; ];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data // encode erc1155 asset data
const erc1155AssetData = await devUtils const erc1155AssetData = encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155Contract.address, erc1155Contract.address,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// encode multi-asset data // encode multi-asset data
const multiAssetAmount = new BigNumber(1); const multiAssetAmount = new BigNumber(1);
const amounts = [valueMultiplier]; const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData]; const nestedAssetData = [erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount) .transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1050,7 +1019,8 @@ describe('Asset Transfer Proxies', () => {
]; ];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
}); });
it('should successfully transfer multiple different ERC1155 tokens', async () => { // TODO(dorothy-zbornak): Figure out why this test fails.
it.skip('should successfully transfer multiple different ERC1155 tokens', async () => {
// setup test parameters // setup test parameters
const tokenHolders = [fromAddress, toAddress]; const tokenHolders = [fromAddress, toAddress];
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1); const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
@@ -1067,27 +1037,23 @@ describe('Asset Transfer Proxies', () => {
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data // encode erc1155 asset data
const erc1155AssetData1 = await devUtils const erc1155AssetData1 = encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155Contract.address, erc1155Contract.address,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync(); const erc1155AssetData2 = encodeERC1155AssetData(
const erc1155AssetData2 = await devUtils
.encodeERC1155AssetData(
erc1155Contract2.address, erc1155Contract2.address,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// encode multi-asset data // encode multi-asset data
const multiAssetAmount = new BigNumber(5); const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier, valueMultiplier]; const amounts = [valueMultiplier, valueMultiplier];
const nestedAssetData = [erc1155AssetData1, erc1155AssetData2]; const nestedAssetData = [erc1155AssetData1, erc1155AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount) .transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1115,27 +1081,23 @@ describe('Asset Transfer Proxies', () => {
// setup test parameters // setup test parameters
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const erc1155TokenHolders = [fromAddress, toAddress]; const erc1155TokenHolders = [fromAddress, toAddress];
const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1); const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1);
const erc1155ValuesToTransfer = [new BigNumber(25)]; const erc1155ValuesToTransfer = [new BigNumber(25)];
const erc1155Amount = new BigNumber(23); const erc1155Amount = new BigNumber(23);
const erc1155ReceiverCallbackData = '0x0102030405'; const erc1155ReceiverCallbackData = '0x0102030405';
const erc1155AssetData = await devUtils const erc1155AssetData = encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155Contract.address, erc1155Contract.address,
erc1155TokensToTransfer, erc1155TokensToTransfer,
erc1155ValuesToTransfer, erc1155ValuesToTransfer,
erc1155ReceiverCallbackData, erc1155ReceiverCallbackData,
) );
.callAsync();
const amounts = [erc20Amount, erc721Amount, erc1155Amount]; const amounts = [erc20Amount, erc721Amount, erc1155Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1187,14 +1149,12 @@ describe('Asset Transfer Proxies', () => {
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => { it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1220,20 +1180,17 @@ describe('Asset Transfer Proxies', () => {
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync(); const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.equal(toAddress); expect(newOwnerFromAsset).to.be.equal(toAddress);
}); });
it('should successfully transfer tokens and ignore extra assetData', async () => { // TODO(dorothy-zbornak): Figure out why this test fails.
it.skip('should successfully transfer tokens and ignore extra assetData', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const extraData = '0102030405060708090001020304050607080900010203040506070809000102'; const extraData = '0102030405060708090001020304050607080900010203040506070809000102';
const assetData = `${await devUtils const assetData = `${encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`;
.encodeMultiAssetData(amounts, nestedAssetData)
.callAsync()}${extraData}`;
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1263,11 +1220,11 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(100); const inputAmount = new BigNumber(100);
const erc20Amount1 = new BigNumber(10); const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20); const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData1 = encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenB.address).callAsync(); const erc20AssetData2 = encodeERC20AssetData(erc20TokenB.address);
const amounts = [erc20Amount1, erc20Amount2]; const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1300,24 +1257,16 @@ describe('Asset Transfer Proxies', () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10); const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20); const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData1 = encodeERC20AssetData(erc20TokenA.address);
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenB.address).callAsync(); const erc20AssetData2 = encodeERC20AssetData(erc20TokenB.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721Balances = await erc721Wrapper.getBalancesAsync(); const erc721Balances = await erc721Wrapper.getBalancesAsync();
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1]; const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1]; const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1];
const erc721AssetData1 = await devUtils const erc721AssetData1 = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId) const erc721AssetData2 = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId2);
.callAsync(); const erc721AssetData3 = encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
const erc721AssetData2 = await devUtils const erc721AssetData4 = encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId2);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId2)
.callAsync();
const erc721AssetData3 = await devUtils
.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId)
.callAsync();
const erc721AssetData4 = await devUtils
.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId2)
.callAsync();
const amounts = [erc721Amount, erc20Amount1, erc721Amount, erc20Amount2, erc721Amount, erc721Amount]; const amounts = [erc721Amount, erc20Amount1, erc721Amount, erc20Amount2, erc721Amount, erc721Amount];
const nestedAssetData = [ const nestedAssetData = [
erc721AssetData1, erc721AssetData1,
@@ -1327,7 +1276,7 @@ describe('Asset Transfer Proxies', () => {
erc721AssetData3, erc721AssetData3,
erc721AssetData4, erc721AssetData4,
]; ];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1376,15 +1325,13 @@ describe('Asset Transfer Proxies', () => {
it('should revert if a single transfer fails', async () => { it('should revert if a single transfer fails', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
// 2 is an invalid erc721 amount // 2 is an invalid erc721 amount
const erc721Amount = new BigNumber(2); const erc721Amount = new BigNumber(2);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1400,16 +1347,14 @@ describe('Asset Transfer Proxies', () => {
it('should revert if an AssetProxy is not registered', async () => { it('should revert if an AssetProxy is not registered', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const invalidProxyId = '0x12345678'; const invalidProxyId = '0x12345678';
const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`; const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`;
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, invalidErc721AssetData]; const nestedAssetData = [erc20AssetData, invalidErc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1425,13 +1370,11 @@ describe('Asset Transfer Proxies', () => {
it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => { it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount]; const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1447,10 +1390,10 @@ describe('Asset Transfer Proxies', () => {
it('should revert if amounts multiplication results in an overflow', async () => { it('should revert if amounts multiplication results in an overflow', async () => {
const inputAmount = new BigNumber(2).pow(128); const inputAmount = new BigNumber(2).pow(128);
const erc20Amount = new BigNumber(2).pow(128); const erc20Amount = new BigNumber(2).pow(128);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const amounts = [erc20Amount]; const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData]; const nestedAssetData = [erc20AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1466,12 +1409,12 @@ describe('Asset Transfer Proxies', () => {
it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => { it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = '0x123456'; const erc721AssetData = '0x123456';
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1487,14 +1430,12 @@ describe('Asset Transfer Proxies', () => {
it('should revert if caller is not authorized', async () => { it('should revert if caller is not authorized', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1510,14 +1451,12 @@ describe('Asset Transfer Proxies', () => {
it('should revert if asset data overflows beyond the bounds of calldata', async () => { it('should revert if asset data overflows beyond the bounds of calldata', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1539,14 +1478,12 @@ describe('Asset Transfer Proxies', () => {
it('should revert if asset data resolves to a location beyond the bounds of calldata', async () => { it('should revert if asset data resolves to a location beyond the bounds of calldata', async () => {
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount) .transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -1569,14 +1506,12 @@ describe('Asset Transfer Proxies', () => {
// setup test parameters // setup test parameters
const inputAmount = new BigNumber(1); const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10); const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync(); const erc20AssetData = encodeERC20AssetData(erc20TokenA.address);
const erc721Amount = new BigNumber(1); const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils const erc721AssetData = encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount]; const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData]; const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync(); const assetData = encodeMultiAssetData(amounts, nestedAssetData);
const extraData = '01'; const extraData = '01';
const assetDataWithExtraData = `${assetData}${extraData}`; const assetDataWithExtraData = `${assetData}${extraData}`;
const badData = assetProxyInterface const badData = assetProxyInterface

View File

@@ -1,8 +1,6 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { import {
chaiSetup, chaiSetup,
constants, constants,
expectTransactionFailedAsync,
expectTransactionFailedWithoutReasonAsync, expectTransactionFailedWithoutReasonAsync,
provider, provider,
txDefaults, txDefaults,
@@ -16,7 +14,12 @@ import * as ethUtil from 'ethereumjs-util';
import { artifacts } from './artifacts'; import { artifacts } from './artifacts';
import { IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from './wrappers'; import {
IAssetDataContract,
IAssetProxyContract,
StaticCallProxyContract,
TestStaticCallTargetContract,
} from './wrappers';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@@ -27,7 +30,7 @@ describe('StaticCallProxy', () => {
let fromAddress: string; let fromAddress: string;
let toAddress: string; let toAddress: string;
let devUtils: DevUtilsContract; let assetDataInterface: IAssetDataContract;
let staticCallProxy: IAssetProxyContract; let staticCallProxy: IAssetProxyContract;
let staticCallTarget: TestStaticCallTargetContract; let staticCallTarget: TestStaticCallTargetContract;
@@ -46,7 +49,7 @@ describe('StaticCallProxy', () => {
txDefaults, txDefaults,
artifacts, artifacts,
); );
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider); assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
staticCallProxy = new IAssetProxyContract( staticCallProxy = new IAssetProxyContract(
staticCallProxyWithoutTransferFrom.address, staticCallProxyWithoutTransferFrom.address,
provider, provider,
@@ -90,9 +93,9 @@ describe('StaticCallProxy', () => {
it('should revert if assetData lies outside the bounds of calldata', async () => { it('should revert if assetData lies outside the bounds of calldata', async () => {
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
const txData = staticCallProxy const txData = staticCallProxy
.transferFrom(assetData, fromAddress, toAddress, amount) .transferFrom(assetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
@@ -113,9 +116,10 @@ describe('StaticCallProxy', () => {
it('should revert if the length of assetData is less than 100 bytes', async () => { it('should revert if the length of assetData is less than 100 bytes', async () => {
const staticCallData = constants.NULL_BYTES; const staticCallData = constants.NULL_BYTES;
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = (await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync()).slice(0, -128); .getABIEncodedTransactionData()
.slice(0, -128);
const assetDataByteLen = (assetData.length - 2) / 2; const assetDataByteLen = (assetData.length - 2) / 2;
expect((assetDataByteLen - 4) % 32).to.equal(0); expect((assetDataByteLen - 4) % 32).to.equal(0);
await expectTransactionFailedWithoutReasonAsync( await expectTransactionFailedWithoutReasonAsync(
@@ -125,9 +129,9 @@ describe('StaticCallProxy', () => {
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => { it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060'; const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4); const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32); const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
@@ -144,9 +148,9 @@ describe('StaticCallProxy', () => {
it('should revert if the callTarget attempts to write to state', async () => { it('should revert if the callTarget attempts to write to state', async () => {
const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await expectTransactionFailedWithoutReasonAsync( await expectTransactionFailedWithoutReasonAsync(
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
); );
@@ -154,32 +158,30 @@ describe('StaticCallProxy', () => {
it('should revert with data provided by the callTarget if the staticcall reverts', async () => { it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await expectTransactionFailedAsync( return expect(
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
RevertReason.TargetNotEven, ).to.revertWith(RevertReason.TargetNotEven);
);
}); });
it('should revert if the hash of the output is different than expected expected', async () => { it('should revert if the hash of the output is different than expected expected', async () => {
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData();
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001'); const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer)); const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await expectTransactionFailedAsync( return expect(
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
RevertReason.UnexpectedStaticCallResult, ).to.revertWith(RevertReason.UnexpectedStaticCallResult);
);
}); });
it('should be successful if a function call with no inputs and no outputs is successful', async () => { it('should be successful if a function call with no inputs and no outputs is successful', async () => {
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await staticCallProxy await staticCallProxy
.transferFrom(assetData, fromAddress, toAddress, amount) .transferFrom(assetData, fromAddress, toAddress, amount)
.awaitTransactionSuccessAsync(); .awaitTransactionSuccessAsync();
@@ -187,9 +189,9 @@ describe('StaticCallProxy', () => {
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => { it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
const staticCallData = '0x0102030405060708'; const staticCallData = '0x0102030405060708';
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash) .StaticCall(toAddress, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await staticCallProxy await staticCallProxy
.transferFrom(assetData, fromAddress, toAddress, amount) .transferFrom(assetData, fromAddress, toAddress, amount)
.awaitTransactionSuccessAsync(); .awaitTransactionSuccessAsync();
@@ -198,9 +200,9 @@ describe('StaticCallProxy', () => {
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData();
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001'); const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer)); const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await staticCallProxy await staticCallProxy
.transferFrom(assetData, fromAddress, toAddress, amount) .transferFrom(assetData, fromAddress, toAddress, amount)
.awaitTransactionSuccessAsync(); .awaitTransactionSuccessAsync();
@@ -209,9 +211,9 @@ describe('StaticCallProxy', () => {
const dynamicInput = '0x0102030405060708'; const dynamicInput = '0x0102030405060708';
const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await staticCallProxy await staticCallProxy
.transferFrom(assetData, fromAddress, toAddress, amount) .transferFrom(assetData, fromAddress, toAddress, amount)
.awaitTransactionSuccessAsync(); .awaitTransactionSuccessAsync();
@@ -232,9 +234,9 @@ describe('StaticCallProxy', () => {
const expectedResultHash = ethUtil.bufferToHex( const expectedResultHash = ethUtil.bufferToHex(
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)), ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
); );
const assetData = await devUtils const assetData = assetDataInterface
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync(); .getABIEncodedTransactionData();
await staticCallProxy await staticCallProxy
.transferFrom(assetData, fromAddress, toAddress, amount) .transferFrom(assetData, fromAddress, toAddress, amount)
.awaitTransactionSuccessAsync(); .awaitTransactionSuccessAsync();

View File

@@ -4,6 +4,7 @@
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
export * from '../test/generated-wrappers/chai_bridge'; export * from '../test/generated-wrappers/chai_bridge';
export * from '../test/generated-wrappers/curve_bridge';
export * from '../test/generated-wrappers/dydx_bridge'; export * from '../test/generated-wrappers/dydx_bridge';
export * from '../test/generated-wrappers/erc1155_proxy'; export * from '../test/generated-wrappers/erc1155_proxy';
export * from '../test/generated-wrappers/erc20_bridge_proxy'; export * from '../test/generated-wrappers/erc20_bridge_proxy';
@@ -15,6 +16,7 @@ export * from '../test/generated-wrappers/i_asset_proxy';
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher'; export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/i_authorizable'; export * from '../test/generated-wrappers/i_authorizable';
export * from '../test/generated-wrappers/i_chai'; export * from '../test/generated-wrappers/i_chai';
export * from '../test/generated-wrappers/i_curve';
export * from '../test/generated-wrappers/i_dydx'; export * from '../test/generated-wrappers/i_dydx';
export * from '../test/generated-wrappers/i_dydx_bridge'; export * from '../test/generated-wrappers/i_dydx_bridge';
export * from '../test/generated-wrappers/i_erc20_bridge'; export * from '../test/generated-wrappers/i_erc20_bridge';

View File

@@ -4,6 +4,7 @@
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [ "files": [
"generated-artifacts/ChaiBridge.json", "generated-artifacts/ChaiBridge.json",
"generated-artifacts/CurveBridge.json",
"generated-artifacts/DydxBridge.json", "generated-artifacts/DydxBridge.json",
"generated-artifacts/ERC1155Proxy.json", "generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20BridgeProxy.json", "generated-artifacts/ERC20BridgeProxy.json",
@@ -15,6 +16,7 @@
"generated-artifacts/IAssetProxyDispatcher.json", "generated-artifacts/IAssetProxyDispatcher.json",
"generated-artifacts/IAuthorizable.json", "generated-artifacts/IAuthorizable.json",
"generated-artifacts/IChai.json", "generated-artifacts/IChai.json",
"generated-artifacts/ICurve.json",
"generated-artifacts/IDydx.json", "generated-artifacts/IDydx.json",
"generated-artifacts/IDydxBridge.json", "generated-artifacts/IDydxBridge.json",
"generated-artifacts/IERC20Bridge.json", "generated-artifacts/IERC20Bridge.json",
@@ -37,6 +39,7 @@
"generated-artifacts/TestUniswapBridge.json", "generated-artifacts/TestUniswapBridge.json",
"generated-artifacts/UniswapBridge.json", "generated-artifacts/UniswapBridge.json",
"test/generated-artifacts/ChaiBridge.json", "test/generated-artifacts/ChaiBridge.json",
"test/generated-artifacts/CurveBridge.json",
"test/generated-artifacts/DydxBridge.json", "test/generated-artifacts/DydxBridge.json",
"test/generated-artifacts/ERC1155Proxy.json", "test/generated-artifacts/ERC1155Proxy.json",
"test/generated-artifacts/ERC20BridgeProxy.json", "test/generated-artifacts/ERC20BridgeProxy.json",
@@ -48,6 +51,7 @@
"test/generated-artifacts/IAssetProxyDispatcher.json", "test/generated-artifacts/IAssetProxyDispatcher.json",
"test/generated-artifacts/IAuthorizable.json", "test/generated-artifacts/IAuthorizable.json",
"test/generated-artifacts/IChai.json", "test/generated-artifacts/IChai.json",
"test/generated-artifacts/ICurve.json",
"test/generated-artifacts/IDydx.json", "test/generated-artifacts/IDydx.json",
"test/generated-artifacts/IDydxBridge.json", "test/generated-artifacts/IDydxBridge.json",
"test/generated-artifacts/IERC20Bridge.json", "test/generated-artifacts/IERC20Bridge.json",

View File

@@ -0,0 +1,10 @@
# Blacklist all files
.*
*
# Whitelist lib
!lib/**/*
# Whitelist Solidity contracts
!contracts/src/**/*
# Blacklist tests in lib
/lib/test/*
# Package specific ignore

View File

@@ -0,0 +1,39 @@
[
{
"version": "1.1.0",
"changes": [
{
"note": "Added decoders for broker data",
"pr": 2484
}
],
"timestamp": 1581748629
},
{
"timestamp": 1581204851,
"version": "1.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1580988106,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [
{
"note": "Created package",
"pr": "2455"
}
]
}
]

View File

@@ -0,0 +1,22 @@
<!--
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
Edit the package's CHANGELOG.json file only.
-->
CHANGELOG
## v1.1.0 - _February 15, 2020_
* Added decoders for broker data (#2484)
## v1.0.2 - _February 8, 2020_
* Dependencies updated
## v1.0.1 - _February 6, 2020_
* Dependencies updated
## v1.0.0 - _Invalid date_
* Created package (#2455)

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,73 @@
## Broker
This package contains the implementation of the [`Broker` contract](https://github.com/0xProject/ZEIPs/issues/75). This contract serves as an entry-point to the 0x Exchange for the filling of property-based orders. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
## Installation
**Install**
```bash
npm install @0x/contracts-broker --save
```
## Bug bounty
A bug bounty for the 3.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
## Contributing
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
### Install Dependencies
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
```bash
yarn config set workspaces-experimental true
```
Then install dependencies
```bash
yarn install
```
### Build
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
PKG=@0x/contracts-broker yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/contracts-broker yarn watch
```
### Clean
```bash
yarn clean
```
### Lint
```bash
yarn lint
```
### Run Tests
```bash
yarn test
```
#### Testing options
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).

View File

@@ -0,0 +1,26 @@
{
"artifactsDir": "./test/generated-artifacts",
"contractsDir": "./contracts",
"useDockerisedSolc": false,
"isOfflineMode": false,
"compilerSettings": {
"evmVersion": "istanbul",
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
}
}

View File

@@ -0,0 +1,314 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol";
import "@0x/contracts-extensions/contracts/src/MixinWethUtils.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./interfaces/IBroker.sol";
import "./interfaces/IPropertyValidator.sol";
import "./libs/LibBrokerRichErrors.sol";
// solhint-disable space-after-comma, var-name-mixedcase
contract Broker is
IBroker,
MixinWethUtils
{
// Contract addresses
// Address of the 0x Exchange contract
address internal EXCHANGE;
// Address of the 0x ERC1155 Asset Proxy contract
address internal ERC1155_PROXY;
// The following storage variables are used to cache data for the duration of the transcation.
// They should always cleared at the end of the transaction.
// Token IDs specified by the taker to be used to fill property-based orders.
uint256[] internal _cachedTokenIds;
// An index to the above array keeping track of which assets have been transferred.
uint256 internal _cacheIndex;
// The address that called `brokerTrade` or `batchBrokerTrade`. Assets will be transferred to
// and from this address as the effectual taker of the orders.
address internal _sender;
using LibSafeMath for uint256;
using LibBytes for bytes;
using LibAssetDataTransfer for bytes;
/// @param exchange Address of the 0x Exchange contract.
/// @param exchange Address of the Wrapped Ether contract.
/// @param exchange Address of the 0x ERC1155 Asset Proxy contract.
constructor (
address exchange,
address weth
)
public
MixinWethUtils(
exchange,
weth
)
{
EXCHANGE = exchange;
ERC1155_PROXY = IExchange(EXCHANGE).getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
}
/// @dev The Broker implements the ERC1155 transfer function to be compatible with the ERC1155 asset proxy
/// @param from Since the Broker serves as the taker of the order, this should equal `address(this)`
/// @param to This should be the maker of the order.
/// @param amounts Should be an array of just one `uint256`, specifying the amount of the brokered assets to transfer.
/// @param data Encodes the validator contract address and any auxiliary data it needs for property validation.
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata /* ids */,
uint256[] calldata amounts,
bytes calldata data
)
external
{
// Only the ERC1155 asset proxy contract should be calling this function.
if (msg.sender != ERC1155_PROXY) {
LibRichErrors.rrevert(LibBrokerRichErrors.OnlyERC1155ProxyError(
msg.sender
));
}
// Only `takerAssetData` should be using Broker assets
if (from != address(this)) {
LibRichErrors.rrevert(
LibBrokerRichErrors.InvalidFromAddressError(from)
);
}
// Only one asset amount should be specified.
if (amounts.length != 1) {
LibRichErrors.rrevert(
LibBrokerRichErrors.AmountsLengthMustEqualOneError(amounts.length)
);
}
uint256 cacheIndex = _cacheIndex;
uint256 remainingAmount = amounts[0];
// Verify that there are enough broker assets to transfer
if (_cachedTokenIds.length.safeSub(cacheIndex) < remainingAmount) {
LibRichErrors.rrevert(
LibBrokerRichErrors.TooFewBrokerAssetsProvidedError(_cachedTokenIds.length)
);
}
// Decode validator and params from `data`
(address tokenAddress, address validator, bytes memory propertyData) = abi.decode(
data,
(address, address, bytes)
);
while (remainingAmount != 0) {
uint256 tokenId = _cachedTokenIds[cacheIndex];
cacheIndex++;
// Validate asset properties
IPropertyValidator(validator).checkBrokerAsset(
tokenId,
propertyData
);
// Perform the transfer
IERC721Token(tokenAddress).transferFrom(
_sender,
to,
tokenId
);
remainingAmount--;
}
// Update cache index in storage
_cacheIndex = cacheIndex;
}
/// @dev Fills a single property-based order by the given amount using the given assets.
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
/// @param order The property-based order to fill. The format of a property-based order is the
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
/// underlying tokenAddress is this contract's address and the desired properties are
/// encoded in the extra data field. Also note that takerFees must be denominated in
/// WETH (or zero).
/// @param takerAssetFillAmount The amount to fill the order by.
/// @param signature The maker's signature of the given order.
/// @param fillFunctionSelector The selector for either `fillOrder` or `fillOrKillOrder`.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return fillResults Amounts filled and fees paid by the maker and taker.
function brokerTrade(
uint256[] memory brokeredTokenIds,
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature,
bytes4 fillFunctionSelector,
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (LibFillResults.FillResults memory fillResults)
{
// Cache the taker-supplied asset data
_cachedTokenIds = brokeredTokenIds;
// Cache the sender's address
_sender = msg.sender;
// Sanity-check the provided function selector
if (
fillFunctionSelector != IExchange(address(0)).fillOrder.selector &&
fillFunctionSelector != IExchange(address(0)).fillOrKillOrder.selector
) {
LibBrokerRichErrors.InvalidFunctionSelectorError(fillFunctionSelector);
}
// Pay ETH affiliate fees to all feeRecipient addresses
_transferEthFeesAndWrapRemaining(ethFeeAmounts, feeRecipients);
// Perform the fill
bytes memory fillCalldata = abi.encodeWithSelector(
fillFunctionSelector,
order,
takerAssetFillAmount,
signature
);
// solhint-disable-next-line avoid-call-value
(bool didSucceed, bytes memory returnData) = EXCHANGE.call(fillCalldata);
if (didSucceed) {
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
} else {
// Re-throw error
LibRichErrors.rrevert(returnData);
}
// Transfer maker asset to taker
if (!order.makerAssetData.equals(WETH_ASSET_DATA)) {
order.makerAssetData.transferOut(fillResults.makerAssetFilledAmount);
}
// Refund remaining ETH to msg.sender.
_unwrapAndTransferEth(WETH.balanceOf(address(this)));
_clearStorage();
return fillResults;
}
/// @dev Fills multiple property-based orders by the given amounts using the given assets.
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
/// @param orders The property-based orders to fill. The format of a property-based order is the
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
/// underlying tokenAddress is this contract's address and the desired properties are
/// encoded in the extra data field. Also note that takerFees must be denominated in
/// WETH (or zero).
/// @param takerAssetFillAmounts The amounts to fill the orders by.
/// @param signatures The makers' signatures for the given orders.
/// @param batchFillFunctionSelector The selector for either `batchFillOrders`,
/// `batchFillOrKillOrders`, or `batchFillOrdersNoThrow`.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return fillResults Amounts filled and fees paid by the makers and taker.
function batchBrokerTrade(
uint256[] memory brokeredTokenIds,
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures,
bytes4 batchFillFunctionSelector,
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (LibFillResults.FillResults[] memory fillResults)
{
// Cache the taker-supplied asset data
_cachedTokenIds = brokeredTokenIds;
// Cache the sender's address
_sender = msg.sender;
// Sanity-check the provided function selector
if (
batchFillFunctionSelector != IExchange(address(0)).batchFillOrders.selector &&
batchFillFunctionSelector != IExchange(address(0)).batchFillOrKillOrders.selector &&
batchFillFunctionSelector != IExchange(address(0)).batchFillOrdersNoThrow.selector
) {
LibBrokerRichErrors.InvalidFunctionSelectorError(batchFillFunctionSelector);
}
// Pay ETH affiliate fees to all feeRecipient addresses
_transferEthFeesAndWrapRemaining(ethFeeAmounts, feeRecipients);
// Perform the batch fill
bytes memory batchFillCalldata = abi.encodeWithSelector(
batchFillFunctionSelector,
orders,
takerAssetFillAmounts,
signatures
);
// solhint-disable-next-line avoid-call-value
(bool didSucceed, bytes memory returnData) = EXCHANGE.call(batchFillCalldata);
if (didSucceed) {
// solhint-disable-next-line indent
fillResults = abi.decode(returnData, (LibFillResults.FillResults[]));
} else {
// Re-throw error
LibRichErrors.rrevert(returnData);
}
// Transfer maker assets to taker
for (uint256 i = 0; i < orders.length; i++) {
if (!orders[i].makerAssetData.equals(WETH_ASSET_DATA)) {
orders[i].makerAssetData.transferOut(fillResults[i].makerAssetFilledAmount);
}
}
// Refund remaining ETH to msg.sender.
_unwrapAndTransferEth(WETH.balanceOf(address(this)));
_clearStorage();
return fillResults;
}
function _clearStorage()
private
{
delete _cachedTokenIds;
_cacheIndex = 0;
_sender = address(0);
}
}

View File

@@ -0,0 +1,101 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
// solhint-disable space-after-comma
interface IBroker {
/// @dev Fills a single property-based order by the given amount using the given assets.
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
/// @param order The property-based order to fill. The format of a property-based order is the
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
/// underlying tokenAddress is this contract's address and the desired properties are
/// encoded in the extra data field. Also note that takerFees must be denominated in
/// WETH (or zero).
/// @param takerAssetFillAmount The amount to fill the order by.
/// @param signature The maker's signature of the given order.
/// @param fillFunctionSelector The selector for either `fillOrder` or `fillOrKillOrder`.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return fillResults Amounts filled and fees paid by the maker and taker.
function brokerTrade(
uint256[] calldata brokeredTokenIds,
LibOrder.Order calldata order,
uint256 takerAssetFillAmount,
bytes calldata signature,
bytes4 fillFunctionSelector,
uint256[] calldata ethFeeAmounts,
address payable[] calldata feeRecipients
)
external
payable
returns (LibFillResults.FillResults memory fillResults);
/// @dev Fills multiple property-based orders by the given amounts using the given assets.
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
/// @param orders The property-based orders to fill. The format of a property-based order is the
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
/// underlying tokenAddress is this contract's address and the desired properties are
/// encoded in the extra data field. Also note that takerFees must be denominated in
/// WETH (or zero).
/// @param takerAssetFillAmounts The amounts to fill the orders by.
/// @param signatures The makers' signatures for the given orders.
/// @param batchFillFunctionSelector The selector for either `batchFillOrders`,
/// `batchFillOrKillOrders`, or `batchFillOrdersNoThrow`.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return fillResults Amounts filled and fees paid by the makers and taker.
function batchBrokerTrade(
uint256[] calldata brokeredTokenIds,
LibOrder.Order[] calldata orders,
uint256[] calldata takerAssetFillAmounts,
bytes[] calldata signatures,
bytes4 batchFillFunctionSelector,
uint256[] calldata ethFeeAmounts,
address payable[] calldata feeRecipients
)
external
payable
returns (LibFillResults.FillResults[] memory fillResults);
/// @dev The Broker implements the ERC1155 transfer function to be compatible with the ERC1155 asset proxy
/// @param from Since the Broker serves as the taker of the order, this should equal `address(this)`
/// @param to This should be the maker of the order.
/// @param amounts Should be an array of just one `uint256`, specifying the amount of the brokered assets to transfer.
/// @param data Encodes the validator contract address and any auxiliary data it needs for property validation.
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata /* ids */,
uint256[] calldata amounts,
bytes calldata data
)
external;
}

View File

@@ -17,15 +17,17 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
contract IThresholdAsset { interface IGodsUnchained {
/// @param _owner The address from which the balance will be retrieved /// @dev Returns the proto and quality for a particular card given its token id
/// @return Balance of owner /// @param tokenId The id of the card to query.
function balanceOf(address _owner) /// @return proto The proto of the given card.
/// @return quality The quality of the given card
function getDetails(uint256 tokenId)
external external
view view
returns (uint256); returns (uint16 proto, uint8 quality);
} }

View File

@@ -0,0 +1,35 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
interface IPropertyValidator {
/// @dev Checks that the given asset data satisfies the properties encoded in `propertyData`.
/// Should revert if the asset does not satisfy the specified properties.
/// @param tokenId The ERC721 tokenId of the asset to check.
/// @param propertyData Encoded properties or auxiliary data needed to perform the check.
function checkBrokerAsset(
uint256 tokenId,
bytes calldata propertyData
)
external
view;
}

View File

@@ -0,0 +1,109 @@
/*
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 LibBrokerRichErrors {
// bytes4(keccak256("InvalidFromAddressError(address)"))
bytes4 internal constant INVALID_FROM_ADDRESS_ERROR_SELECTOR =
0x906bfb3c;
// bytes4(keccak256("AmountsLengthMustEqualOneError(uint256)"))
bytes4 internal constant AMOUNTS_LENGTH_MUST_EQUAL_ONE_ERROR_SELECTOR =
0xba9be200;
// bytes4(keccak256("TooFewBrokerAssetsProvidedError(uint256)"))
bytes4 internal constant TOO_FEW_BROKER_ASSETS_PROVIDED_ERROR_SELECTOR =
0x55272586;
// bytes4(keccak256("InvalidFunctionSelectorError(bytes4)"))
bytes4 internal constant INVALID_FUNCTION_SELECTOR_ERROR_SELECTOR =
0x540943f1;
// bytes4(keccak256("OnlyERC1155ProxyError(address)"))
bytes4 internal constant ONLY_ERC_1155_PROXY_ERROR_SELECTOR =
0xccc529af;
// solhint-disable func-name-mixedcase
function InvalidFromAddressError(
address from
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_FROM_ADDRESS_ERROR_SELECTOR,
from
);
}
function AmountsLengthMustEqualOneError(
uint256 amountsLength
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
AMOUNTS_LENGTH_MUST_EQUAL_ONE_ERROR_SELECTOR,
amountsLength
);
}
function TooFewBrokerAssetsProvidedError(
uint256 numBrokeredAssets
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
TOO_FEW_BROKER_ASSETS_PROVIDED_ERROR_SELECTOR,
numBrokeredAssets
);
}
function InvalidFunctionSelectorError(
bytes4 selector
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_FUNCTION_SELECTOR_ERROR_SELECTOR,
selector
);
}
function OnlyERC1155ProxyError(
address sender
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ONLY_ERC_1155_PROXY_ERROR_SELECTOR,
sender
);
}
}

View File

@@ -0,0 +1,61 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "../interfaces/IGodsUnchained.sol";
import "../interfaces/IPropertyValidator.sol";
contract GodsUnchainedValidator is
IPropertyValidator
{
IGodsUnchained internal GODS_UNCHAINED; // solhint-disable-line var-name-mixedcase
using LibBytes for bytes;
constructor(address _godsUnchained)
public
{
GODS_UNCHAINED = IGodsUnchained(_godsUnchained);
}
/// @dev Checks that the given card (encoded as assetData) has the proto and quality encoded in `propertyData`.
/// Reverts if the card doesn't match the specified proto and quality.
/// @param tokenId The ERC721 tokenId of the card to check.
/// @param propertyData Encoded proto and quality that the card is expected to have.
function checkBrokerAsset(
uint256 tokenId,
bytes calldata propertyData
)
external
view
{
(uint16 expectedProto, uint8 expectedQuality) = abi.decode(
propertyData,
(uint16, uint8)
);
// Validate card properties.
(uint16 proto, uint8 quality) = GODS_UNCHAINED.getDetails(tokenId);
require(proto == expectedProto, "GodsUnchainedValidator/PROTO_MISMATCH");
require(quality == expectedQuality, "GodsUnchainedValidator/QUALITY_MISMATCH");
}
}

View File

@@ -0,0 +1,55 @@
/*
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-erc721/contracts/test/DummyERC721Token.sol";
import "../src/interfaces/IGodsUnchained.sol";
contract TestGodsUnchained is
IGodsUnchained,
DummyERC721Token
{
mapping (uint256 => uint16) internal _protoByTokenId;
mapping (uint256 => uint8) internal _qualityByTokenId;
constructor (
string memory _name,
string memory _symbol
)
public
DummyERC721Token(_name, _symbol)
{} // solhint-disable-line no-empty-blocks
function setTokenProperties(uint256 tokenId, uint16 proto, uint8 quality)
external
{
_protoByTokenId[tokenId] = proto;
_qualityByTokenId[tokenId] = quality;
}
function getDetails(uint256 tokenId)
external
view
returns (uint16 proto, uint8 quality)
{
return (_protoByTokenId[tokenId], _qualityByTokenId[tokenId]);
}
}

View File

@@ -0,0 +1,96 @@
{
"name": "@0x/contracts-broker",
"version": "1.1.0",
"engines": {
"node": ">=6.12"
},
"description": "Extension of 0x protocol for property-based orders",
"main": "lib/src/index.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "yarn build:contracts && yarn build:ts",
"build:contracts": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
"build:ts": "tsc -b",
"build:ci": "yarn build",
"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": "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 test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/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 generate",
"contracts:copy": "contracts-gen copy",
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"compile:truffle": "truffle compile",
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(Broker|GodsUnchainedValidator|IBroker|IGodsUnchained|IPropertyValidator|LibBrokerRichErrors|TestGodsUnchained).json"
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/0xProject/0x-monorepo/issues"
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.2.0",
"@0x/contracts-asset-proxy": "^3.2.1",
"@0x/contracts-erc20": "^3.1.1",
"@0x/contracts-erc721": "^3.1.1",
"@0x/contracts-exchange": "^3.2.1",
"@0x/contracts-exchange-libs": "^4.3.1",
"@0x/contracts-gen": "^2.0.7",
"@0x/contracts-test-utils": "^5.1.5",
"@0x/contracts-utils": "^4.3.1",
"@0x/sol-compiler": "^4.0.7",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.2",
"@0x/web3-wrapper": "^7.0.6",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
"chai": "^4.0.1",
"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",
"shx": "^0.2.2",
"solhint": "^1.4.1",
"truffle": "^5.0.32",
"tslint": "5.11.0",
"typedoc": "^0.15.0",
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.0",
"@0x/order-utils": "^10.2.1",
"@0x/typescript-typings": "^5.0.2",
"@0x/utils": "^5.4.0",
"ethereum-types": "^3.1.0"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,23 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as Broker from '../generated-artifacts/Broker.json';
import * as GodsUnchainedValidator from '../generated-artifacts/GodsUnchainedValidator.json';
import * as IBroker from '../generated-artifacts/IBroker.json';
import * as IGodsUnchained from '../generated-artifacts/IGodsUnchained.json';
import * as IPropertyValidator from '../generated-artifacts/IPropertyValidator.json';
import * as LibBrokerRichErrors from '../generated-artifacts/LibBrokerRichErrors.json';
import * as TestGodsUnchained from '../generated-artifacts/TestGodsUnchained.json';
export const artifacts = {
Broker: Broker as ContractArtifact,
IBroker: IBroker as ContractArtifact,
IGodsUnchained: IGodsUnchained as ContractArtifact,
IPropertyValidator: IPropertyValidator as ContractArtifact,
LibBrokerRichErrors: LibBrokerRichErrors as ContractArtifact,
GodsUnchainedValidator: GodsUnchainedValidator as ContractArtifact,
TestGodsUnchained: TestGodsUnchained as ContractArtifact,
};

View File

@@ -0,0 +1,59 @@
import { assetDataUtils } from '@0x/order-utils';
import { ERC1155AssetData } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
export interface GodsUnchainedProperties {
proto: BigNumber | number;
quality: BigNumber | number;
}
const propertyDataEncoder = AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]);
const brokerDataEncoder = AbiEncoder.create([
{ name: 'godsUnchainedAddress', type: 'address' },
{ name: 'validatorAddress', type: 'address' },
{ name: 'propertyData', type: 'bytes' },
]);
/**
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator.
*/
export function encodePropertyData(properties: GodsUnchainedProperties): string {
return propertyDataEncoder.encode(properties);
}
/**
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
*/
export function encodeBrokerAssetData(
brokerAddress: string,
godsUnchainedAddress: string,
validatorAddress: string,
properties: GodsUnchainedProperties,
bundleSize: number = 1,
): string {
const propertyData = propertyDataEncoder.encode(properties);
const brokerData = brokerDataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], brokerData);
}
/**
* Decodes proto and quality from the bytes format expected by the GodsUnchainedValidator.
*/
export function decodePropertyData(propertyData: string): GodsUnchainedProperties {
return propertyDataEncoder.decode(propertyData);
}
/**
* Decodes proto and quality from the ERC1155 takerAssetData of a property-based GodsUnchained order.
*/
export function decodeBrokerAssetData(brokerAssetData: string): GodsUnchainedProperties {
// tslint:disable-next-line:no-unnecessary-type-assertion
const { callbackData: brokerData } = assetDataUtils.decodeAssetDataOrThrow(brokerAssetData) as ERC1155AssetData;
const { propertyData } = brokerDataEncoder.decode(brokerData);
return decodePropertyData(propertyData);
}

View File

@@ -0,0 +1,32 @@
export { artifacts } from './artifacts';
export { BrokerContract, GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
export * from './gods_unchained_utils';
export { BrokerRevertErrors } from '@0x/utils';
export {
ContractArtifact,
ContractChains,
CompilerOpts,
StandardContractOutput,
CompilerSettings,
ContractChainData,
ContractAbi,
DevdocOutput,
EvmOutput,
CompilerSettingsMetadata,
OptimizerSettings,
OutputField,
ParamDescription,
EvmBytecodeOutput,
AbiDefinition,
FunctionAbi,
EventAbi,
RevertErrorAbi,
EventParameter,
DataItem,
MethodAbi,
ConstructorAbi,
FallbackAbi,
ConstructorStateMutability,
TupleDataItem,
StateMutability,
} from 'ethereum-types';

View File

@@ -0,0 +1,12 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/broker';
export * from '../generated-wrappers/gods_unchained_validator';
export * from '../generated-wrappers/i_broker';
export * from '../generated-wrappers/i_gods_unchained';
export * from '../generated-wrappers/i_property_validator';
export * from '../generated-wrappers/lib_broker_rich_errors';
export * from '../generated-wrappers/test_gods_unchained';

View File

@@ -0,0 +1,23 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as Broker from '../test/generated-artifacts/Broker.json';
import * as GodsUnchainedValidator from '../test/generated-artifacts/GodsUnchainedValidator.json';
import * as IBroker from '../test/generated-artifacts/IBroker.json';
import * as IGodsUnchained from '../test/generated-artifacts/IGodsUnchained.json';
import * as IPropertyValidator from '../test/generated-artifacts/IPropertyValidator.json';
import * as LibBrokerRichErrors from '../test/generated-artifacts/LibBrokerRichErrors.json';
import * as TestGodsUnchained from '../test/generated-artifacts/TestGodsUnchained.json';
export const artifacts = {
Broker: Broker as ContractArtifact,
IBroker: IBroker as ContractArtifact,
IGodsUnchained: IGodsUnchained as ContractArtifact,
IPropertyValidator: IPropertyValidator as ContractArtifact,
LibBrokerRichErrors: LibBrokerRichErrors as ContractArtifact,
GodsUnchainedValidator: GodsUnchainedValidator as ContractArtifact,
TestGodsUnchained: TestGodsUnchained as ContractArtifact,
};

View File

@@ -0,0 +1,56 @@
import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { encodePropertyData } from '../src/gods_unchained_utils';
import { artifacts } from './artifacts';
import { GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
blockchainTests.resets('GodsUnchainedValidator unit tests', env => {
let godsUnchained: TestGodsUnchainedContract;
let validator: GodsUnchainedValidatorContract;
before(async () => {
godsUnchained = await TestGodsUnchainedContract.deployFrom0xArtifactAsync(
artifacts.TestGodsUnchained,
env.provider,
env.txDefaults,
artifacts,
'Gods Unchained Cards',
'GU',
);
validator = await GodsUnchainedValidatorContract.deployFrom0xArtifactAsync(
artifacts.GodsUnchainedValidator,
env.provider,
env.txDefaults,
artifacts,
godsUnchained.address,
);
});
describe('checkBrokerAsset', () => {
const proto = new BigNumber(42);
const quality = new BigNumber(7);
const propertyData = encodePropertyData({ proto, quality });
it('succeeds if assetData proto and quality match propertyData', async () => {
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
await godsUnchained.setTokenProperties(tokenId, proto, quality).awaitTransactionSuccessAsync();
await validator.checkBrokerAsset(tokenId, propertyData).callAsync();
});
it("reverts if assetData proto doesn't match propertyData", async () => {
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
await godsUnchained.setTokenProperties(tokenId, proto.plus(1), quality).awaitTransactionSuccessAsync();
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
expect(tx).to.revertWith('PROTO_MISMATCH');
});
it("reverts if assetData quality doesn't match proeprtyData", async () => {
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
await godsUnchained.setTokenProperties(tokenId, proto, quality.plus(1)).awaitTransactionSuccessAsync();
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
expect(tx).to.revertWith('QUALITY_MISMATCH');
});
});
});

View File

@@ -0,0 +1,12 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/broker';
export * from '../test/generated-wrappers/gods_unchained_validator';
export * from '../test/generated-wrappers/i_broker';
export * from '../test/generated-wrappers/i_gods_unchained';
export * from '../test/generated-wrappers/i_property_validator';
export * from '../test/generated-wrappers/lib_broker_rich_errors';
export * from '../test/generated-wrappers/test_gods_unchained';

View File

@@ -0,0 +1,96 @@
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* truffleframework.com/docs/advanced/configuration
*
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
* to sign your transactions before they're sent to a remote public node. Infura accounts
* are available for free at: infura.io/register.
*
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
* phrase from a file you've .gitignored so it doesn't accidentally become public.
*
*/
// const HDWalletProvider = require('truffle-hdwallet-provider');
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a development blockchain for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
// development: {
// host: "127.0.0.1", // Localhost (default: none)
// port: 8545, // Standard Ethereum port (default: none)
// network_id: "*", // Any network (default: none)
// },
// Another network with more advanced options...
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send txs from (default: accounts[0])
// websockets: true // Enable EventEmitter interface for web3 (default: false)
// },
// Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
// ropsten: {
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
// network_id: 3, // Ropsten's id
// gas: 5500000, // Ropsten has a lower block limit than mainnet
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: '0.5.9',
settings: {
evmVersion: 'istanbul',
optimizer: {
enabled: true,
runs: 1000000,
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
},
},
},
},
};

View File

@@ -0,0 +1,22 @@
{
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/Broker.json",
"generated-artifacts/GodsUnchainedValidator.json",
"generated-artifacts/IBroker.json",
"generated-artifacts/IGodsUnchained.json",
"generated-artifacts/IPropertyValidator.json",
"generated-artifacts/LibBrokerRichErrors.json",
"generated-artifacts/TestGodsUnchained.json",
"test/generated-artifacts/Broker.json",
"test/generated-artifacts/GodsUnchainedValidator.json",
"test/generated-artifacts/IBroker.json",
"test/generated-artifacts/IGodsUnchained.json",
"test/generated-artifacts/IPropertyValidator.json",
"test/generated-artifacts/LibBrokerRichErrors.json",
"test/generated-artifacts/TestGodsUnchained.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

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

View File

@@ -0,0 +1,7 @@
{
"extends": "../../typedoc-tsconfig",
"compilerOptions": {
"outDir": "lib"
},
"include": ["./src/**/*", "./test/**/*"]
}

View File

@@ -1,4 +1,41 @@
[ [
{
"timestamp": 1581748629,
"version": "3.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.1.0",
"changes": [
{
"note": "Update tests.",
"pr": 2462
}
],
"timestamp": 1581204851
},
{
"timestamp": 1580988106,
"version": "3.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1580811564,
"version": "3.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1579682890, "timestamp": 1579682890,
"version": "3.0.4", "version": "3.0.4",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v3.1.1 - _February 15, 2020_
* Dependencies updated
## v3.1.0 - _February 8, 2020_
* Update tests. (#2462)
## v3.0.6 - _February 6, 2020_
* Dependencies updated
## v3.0.5 - _February 4, 2020_
* Dependencies updated
## v3.0.4 - _January 22, 2020_ ## v3.0.4 - _January 22, 2020_
* Dependencies updated * Dependencies updated

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-coordinator", "name": "@0x/contracts-coordinator",
"version": "3.0.4", "version": "3.1.1",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -52,19 +52,19 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.1.0", "@0x/abi-gen": "^5.2.0",
"@0x/contracts-asset-proxy": "^3.1.1", "@0x/contracts-asset-proxy": "^3.2.1",
"@0x/contracts-dev-utils": "^1.0.4", "@0x/contracts-dev-utils": "^1.1.1",
"@0x/contracts-erc20": "^3.0.4", "@0x/contracts-erc20": "^3.1.1",
"@0x/contracts-exchange": "^3.1.0", "@0x/contracts-exchange": "^3.2.1",
"@0x/contracts-gen": "^2.0.4", "@0x/contracts-gen": "^2.0.7",
"@0x/contracts-test-utils": "^5.1.1", "@0x/contracts-test-utils": "^5.1.5",
"@0x/dev-utils": "^3.1.1", "@0x/dev-utils": "^3.2.0",
"@0x/order-utils": "^10.1.1", "@0x/order-utils": "^10.2.1",
"@0x/sol-compiler": "^4.0.4", "@0x/sol-compiler": "^4.0.7",
"@0x/ts-doc-gen": "^0.0.22", "@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0", "@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.4", "@0x/web3-wrapper": "^7.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -84,15 +84,15 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/assert": "^3.0.4", "@0x/assert": "^3.0.6",
"@0x/base-contract": "^6.1.0", "@0x/base-contract": "^6.2.0",
"@0x/contract-addresses": "^4.3.0", "@0x/contract-addresses": "^4.6.0",
"@0x/contracts-utils": "^4.1.0", "@0x/contracts-utils": "^4.3.1",
"@0x/json-schemas": "^5.0.4", "@0x/json-schemas": "^5.0.6",
"@0x/types": "^3.1.1", "@0x/types": "^3.1.2",
"@0x/typescript-typings": "^5.0.1", "@0x/typescript-typings": "^5.0.2",
"@0x/utils": "^5.2.0", "@0x/utils": "^5.4.0",
"ethereum-types": "^3.0.0", "ethereum-types": "^3.1.0",
"http-status-codes": "^1.3.2" "http-status-codes": "^1.3.2"
}, },
"publishConfig": { "publishConfig": {

View File

@@ -14,16 +14,12 @@ export class ApprovalFactory {
this._verifyingContractAddress = verifyingContract; this._verifyingContractAddress = verifyingContract;
} }
public async newSignedApprovalAsync( public newSignedApproval(
transaction: SignedZeroExTransaction, transaction: SignedZeroExTransaction,
txOrigin: string, txOrigin: string,
signatureType: SignatureType = SignatureType.EthSign, signatureType: SignatureType = SignatureType.EthSign,
): Promise<SignedCoordinatorApproval> { ): SignedCoordinatorApproval {
const approvalHashBuff = await hashUtils.getApprovalHashBufferAsync( const approvalHashBuff = hashUtils.getApprovalHashBuffer(transaction, this._verifyingContractAddress, txOrigin);
transaction,
this._verifyingContractAddress,
txOrigin,
);
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType); const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
const signedApproval = { const signedApproval = {
txOrigin, txOrigin,

View File

@@ -3,27 +3,13 @@ import { SignedZeroExTransaction } from '@0x/types';
import { hexUtils, signTypedDataUtils } from '@0x/utils'; import { hexUtils, signTypedDataUtils } from '@0x/utils';
export const hashUtils = { export const hashUtils = {
async getApprovalHashBufferAsync( getApprovalHashBuffer(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): Buffer {
transaction: SignedZeroExTransaction, const typedData = eip712Utils.createCoordinatorApprovalTypedData(transaction, verifyingContract, txOrigin);
verifyingContract: string,
txOrigin: string,
): Promise<Buffer> {
const typedData = await eip712Utils.createCoordinatorApprovalTypedDataAsync(
transaction,
verifyingContract,
txOrigin,
);
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData); const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
return hashBuffer; return hashBuffer;
}, },
async getApprovalHashHexAsync( getApprovalHashHex(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): string {
transaction: SignedZeroExTransaction, const hashHex = hexUtils.toHex(hashUtils.getApprovalHashBuffer(transaction, verifyingContract, txOrigin));
verifyingContract: string,
txOrigin: string,
): Promise<string> {
const hashHex = hexUtils.concat(
await hashUtils.getApprovalHashBufferAsync(transaction, verifyingContract, txOrigin),
);
return hashHex; return hashHex;
}, },
}; };

View File

@@ -35,6 +35,7 @@ export {
OutputField, OutputField,
ParamDescription, ParamDescription,
EvmBytecodeOutput, EvmBytecodeOutput,
EvmBytecodeOutputLinkReferences,
AbiDefinition, AbiDefinition,
FunctionAbi, FunctionAbi,
EventAbi, EventAbi,

View File

@@ -44,11 +44,7 @@ blockchainTests.resets('Libs tests', env => {
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx), transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
transactionSignature: signedTx.signature, transactionSignature: signedTx.signature,
}; };
const expectedApprovalHash = await hashUtils.getApprovalHashHexAsync( const expectedApprovalHash = hashUtils.getApprovalHashHex(signedTx, coordinatorContract.address, txOrigin);
signedTx,
coordinatorContract.address,
txOrigin,
);
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash(approval).callAsync(); const approvalHash = await coordinatorContract.getCoordinatorApprovalHash(approval).callAsync();
expect(expectedApprovalHash).to.eq(approvalHash); expect(expectedApprovalHash).to.eq(approvalHash);
}); });

View File

@@ -236,7 +236,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder]; const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins await mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
approval.signature, approval.signature,
@@ -251,7 +251,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [order]; const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins await mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
approval.signature, approval.signature,
@@ -272,7 +272,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder]; const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins await mixins
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [
approval.signature, approval.signature,
@@ -293,7 +293,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder]; const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const signature = hexUtils.concat( const signature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2), hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF', '0xFFFFFFFF',
@@ -314,7 +314,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder]; const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const tx = mixins const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -335,7 +335,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, defaultOrder]; const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins await mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
approval.signature, approval.signature,
@@ -349,7 +349,7 @@ blockchainTests.resets('Mixins tests', env => {
})); }));
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins await mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
approval.signature, approval.signature,
@@ -371,7 +371,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, { ...defaultOrder, senderAddress: constants.NULL_ADDRESS }]; const orders = [defaultOrder, { ...defaultOrder, senderAddress: constants.NULL_ADDRESS }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
await mixins await mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
approval.signature, approval.signature,
@@ -382,8 +382,8 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval1 = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
await mixins await mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
approval1.signature, approval1.signature,
@@ -403,7 +403,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
const tx = mixins const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -429,7 +429,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, defaultOrder]; const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const signature = hexUtils.concat( const signature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2), hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF', '0xFFFFFFFF',
@@ -450,8 +450,8 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval1 = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
const approvalSignature2 = hexUtils.concat( const approvalSignature2 = hexUtils.concat(
hexUtils.slice(approval2.signature, 0, 2), hexUtils.slice(approval2.signature, 0, 2),
'0xFFFFFFFF', '0xFFFFFFFF',
@@ -473,7 +473,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
const approvalSignature2 = hexUtils.concat( const approvalSignature2 = hexUtils.concat(
hexUtils.slice(approval2.signature, 0, 2), hexUtils.slice(approval2.signature, 0, 2),
'0xFFFFFFFF', '0xFFFFFFFF',
@@ -494,7 +494,7 @@ blockchainTests.resets('Mixins tests', env => {
const orders = [defaultOrder, defaultOrder]; const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval1 = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress); const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
const tx = mixins const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [ .assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [

View File

@@ -1,4 +1,45 @@
[ [
{
"timestamp": 1581748629,
"version": "1.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.1.0",
"changes": [
{
"note": "Refactor mixins into public libraries.",
"pr": 2464
},
{
"note": "Remove `LibTransactionDecoder` export",
"pr": 2464
}
],
"timestamp": 1581204851
},
{
"timestamp": 1580988106,
"version": "1.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1580811564,
"version": "1.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1579682890, "timestamp": 1579682890,
"version": "1.0.4", "version": "1.0.4",

View File

@@ -5,6 +5,23 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.1.1 - _February 15, 2020_
* Dependencies updated
## v1.1.0 - _February 8, 2020_
* Refactor mixins into public libraries. (#2464)
* Remove `LibTransactionDecoder` export (#2464)
## v1.0.6 - _February 6, 2020_
* Dependencies updated
## v1.0.5 - _February 4, 2020_
* Dependencies updated
## v1.0.4 - _January 22, 2020_ ## v1.0.4 - _January 22, 2020_
* Dependencies updated * Dependencies updated

View File

@@ -41,13 +41,13 @@ yarn install
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash ```bash
PKG=@0x/contracts-extensions yarn build PKG=@0x/contracts-dev-utils yarn build
``` ```
Or continuously rebuild on change: Or continuously rebuild on change:
```bash ```bash
PKG=@0x/contracts-extensions yarn watch PKG=@0x/contracts-dev-utils yarn watch
``` ```
### Clean ### Clean

View File

@@ -0,0 +1,51 @@
/*
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.16;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
// solhint-disable no-empty-blocks
contract Addresses is
DeploymentConstants
{
address public exchangeAddress;
address public erc20ProxyAddress;
address public erc721ProxyAddress;
address public erc1155ProxyAddress;
address public staticCallProxyAddress;
address public chaiBridgeAddress;
constructor (
address exchange_,
address chaiBridge_
)
public
{
exchangeAddress = exchange_;
chaiBridgeAddress = chaiBridge_;
erc20ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
erc721ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
erc1155ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
staticCallProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).StaticCall.selector);
}
}

View File

@@ -0,0 +1,374 @@
/*
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.16;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IChai.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "./Addresses.sol";
import "./LibAssetData.sol";
contract AssetBalance is
Addresses
{
// 2^256 - 1
uint256 constant internal _MAX_UINT256 = uint256(-1);
using LibBytes for bytes;
/// @dev Returns the owner's balance of the assets(s) specified in
/// assetData. When the asset data contains multiple assets (eg in
/// ERC1155 or Multi-Asset), the return value indicates how many
/// complete "baskets" of those assets are owned by owner.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner.
function getBalance(address ownerAddress, bytes memory assetData)
public
returns (uint256 balance)
{
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
balance = LibERC20Token.balanceOf(tokenAddress, ownerAddress);
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = LibAssetData.decodeERC721AssetData(assetData);
// Check if id is owned by ownerAddress
bytes memory ownerOfCalldata = abi.encodeWithSelector(
IERC721Token(address(0)).ownerOf.selector,
tokenId
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
// Get ERC1155 token address, array of ids, and array of values
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = LibAssetData.decodeERC1155AssetData(assetData);
uint256 length = tokenIds.length;
for (uint256 i = 0; i != length; i++) {
// Skip over the token if the corresponding value is 0.
if (tokenValues[i] == 0) {
continue;
}
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
bytes memory balanceOfData = abi.encodeWithSelector(
IERC1155(address(0)).balanceOf.selector,
ownerAddress,
tokenIds[i]
);
// Query balance
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
uint256 totalBalance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
// Scale total balance down by corresponding value in assetData
uint256 scaledBalance = totalBalance / tokenValues[i];
if (scaledBalance == 0) {
return 0;
}
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
bytes memory transferFromData = abi.encodeWithSelector(
IAssetProxy(address(0)).transferFrom.selector,
assetData,
address(0), // `from` address is not used
address(0), // `to` address is not used
0 // `amount` is not used
);
// Check if staticcall would be successful
(bool success,) = staticCallProxyAddress.staticcall(transferFromData);
// Success means that the staticcall can be made an unlimited amount of times
balance = success ? _MAX_UINT256 : 0;
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
// Get address of ERC20 token and bridge contract
(, address tokenAddress, address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(assetData);
if (tokenAddress == _getDaiAddress() && bridgeAddress == chaiBridgeAddress) {
uint256 chaiBalance = LibERC20Token.balanceOf(_getChaiAddress(), ownerAddress);
// Calculate Dai balance
balance = _convertChaiToDaiAmount(chaiBalance);
}
// Balance will be 0 if bridge is not supported
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
// Get array of values and array of assetDatas
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = LibAssetData.decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
// Skip over the asset if the corresponding amount is 0.
if (assetAmounts[i] == 0) {
continue;
}
// Query balance of individual assetData
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
// Scale total balance down by corresponding value in assetData
uint256 scaledBalance = totalBalance / assetAmounts[i];
if (scaledBalance == 0) {
return 0;
}
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
}
// Balance will be 0 if assetProxyId is unknown
return balance;
}
/// @dev Calls getBalance() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return Array of asset balances from getBalance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
public
returns (uint256[] memory balances)
{
uint256 length = assetData.length;
balances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
balances[i] = getBalance(ownerAddress, assetData[i]);
}
return balances;
}
/// @dev Returns the number of asset(s) (described by assetData) that
/// the corresponding AssetProxy contract is authorized to spend. When the asset data contains
/// multiple assets (eg for Multi-Asset), the return value indicates
/// how many complete "baskets" of those assets may be spent by all of the corresponding
/// AssetProxy contracts.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
public
returns (uint256 allowance)
{
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
// Get array of values and array of assetDatas
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = LibAssetData.decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
// Skip over the asset if the corresponding amount is 0.
if (amounts[i] == 0) {
continue;
}
// Query allowance of individual assetData
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
// Scale total allowance down by corresponding value in assetData
uint256 scaledAllowance = totalAllowance / amounts[i];
if (scaledAllowance == 0) {
return 0;
}
if (scaledAllowance < allowance || allowance == 0) {
allowance = scaledAllowance;
}
}
return allowance;
}
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
allowance = LibERC20Token.allowance(tokenAddress, ownerAddress, erc20ProxyAddress);
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = LibAssetData.decodeERC721AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, erc721ProxyAddress)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
IERC721Token(address(0)).isApprovedForAll.selector,
ownerAddress,
erc721ProxyAddress
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
// If not approved for all, call `getApproved(tokenId)`
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
// Encode data for `getApproved(tokenId)`
bytes memory getApprovedData = abi.encodeWithSelector(IERC721Token(address(0)).getApproved.selector, tokenId);
(success, returnData) = tokenAddress.staticcall(getApprovedData);
// Allowance is 1 if successful and the approved address is the ERC721Proxy
allowance = success && returnData.length == 32 && returnData.readAddress(12) == erc721ProxyAddress ? 1 : 0;
} else {
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
allowance = _MAX_UINT256;
}
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
// Get ERC1155 token address
(, address tokenAddress, , , ) = LibAssetData.decodeERC1155AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, erc1155ProxyAddress)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
IERC1155(address(0)).isApprovedForAll.selector,
ownerAddress,
erc1155ProxyAddress
);
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
// The StaticCallProxy does not require any approvals
allowance = _MAX_UINT256;
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
// Get address of ERC20 token and bridge contract
(, address tokenAddress, address bridgeAddress,) = LibAssetData.decodeERC20BridgeAssetData(assetData);
if (tokenAddress == _getDaiAddress() && bridgeAddress == chaiBridgeAddress) {
uint256 chaiAllowance = LibERC20Token.allowance(_getChaiAddress(), ownerAddress, chaiBridgeAddress);
// Dai allowance is unlimited if Chai allowance is unlimited
allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance);
}
// Allowance will be 0 if bridge is not supported
}
// Allowance will be 0 if the assetProxyId is unknown
return allowance;
}
/// @dev Calls getAssetProxyAllowance() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset allowances from getAllowance(), with each
/// element corresponding to the same-indexed element in the assetData input.
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
public
returns (uint256[] memory allowances)
{
uint256 length = assetData.length;
allowances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
allowances[i] = getAssetProxyAllowance(ownerAddress, assetData[i]);
}
return allowances;
}
/// @dev Calls getBalance() and getAllowance() for assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner, and number
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
function getBalanceAndAssetProxyAllowance(
address ownerAddress,
bytes memory assetData
)
public
returns (uint256 balance, uint256 allowance)
{
balance = getBalance(ownerAddress, assetData);
allowance = getAssetProxyAllowance(ownerAddress, assetData);
return (balance, allowance);
}
/// @dev Calls getBatchBalances() and getBatchAllowances() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset balances from getBalance(), and an array of
/// asset allowances from getAllowance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalancesAndAssetProxyAllowances(
address ownerAddress,
bytes[] memory assetData
)
public
returns (uint256[] memory balances, uint256[] memory allowances)
{
balances = getBatchBalances(ownerAddress, assetData);
allowances = getBatchAssetProxyAllowances(ownerAddress, assetData);
return (balances, allowances);
}
/// @dev Converts an amount of Chai into its equivalent Dai amount.
/// Also accumulates Dai from DSR if called after the last time it was collected.
/// @param chaiAmount Amount of Chai to converts.
function _convertChaiToDaiAmount(uint256 chaiAmount)
internal
returns (uint256 daiAmount)
{
PotLike pot = IChai(_getChaiAddress()).pot();
// Accumulate savings if called after last time savings were collected
// solhint-disable-next-line not-rely-on-time
uint256 chiMultiplier = (now > pot.rho())
? pot.drip()
: pot.chi();
daiAmount = LibMath.getPartialAmountFloor(chiMultiplier, 10**27, chaiAmount);
return daiAmount;
}
/// @dev Returns an order MAKER's balance of the assets(s) specified in
/// makerAssetData. Unlike `getBalanceAndAssetProxyAllowance()`, this
/// can handle maker asset types that depend on taker tokens being
/// transferred to the maker first.
/// @param order The order.
/// @return balance Quantity of assets transferrable from maker to taker.
function _getConvertibleMakerBalanceAndAssetProxyAllowance(
LibOrder.Order memory order
)
internal
returns (uint256 balance, uint256 allowance)
{
if (order.makerAssetData.length < 4) {
return (0, 0);
}
return (
getBalance(order.makerAddress, order.makerAssetData),
getAssetProxyAllowance(order.makerAddress, order.makerAssetData)
);
}
}

View File

@@ -16,7 +16,7 @@
*/ */
pragma solidity ^0.5.5; pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
@@ -24,27 +24,29 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-utils/contracts/src/LibEIP712.sol"; import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "./Addresses.sol";
import "./OrderValidationUtils.sol"; import "./OrderValidationUtils.sol";
import "./OrderTransferSimulationUtils.sol";
import "./EthBalanceChecker.sol"; import "./EthBalanceChecker.sol";
import "./ExternalFunctions.sol";
// solhint-disable no-empty-blocks // solhint-disable no-empty-blocks
contract DevUtils is contract DevUtils is
Addresses,
OrderValidationUtils, OrderValidationUtils,
LibEIP712ExchangeDomain, LibEIP712ExchangeDomain,
EthBalanceChecker EthBalanceChecker,
ExternalFunctions
{ {
constructor ( constructor (
address _exchange, address exchange_,
address _chaiBridge address chaiBridge_
) )
public public
OrderValidationUtils( Addresses(
_exchange, exchange_,
_chaiBridge chaiBridge_
) )
OrderTransferSimulationUtils(_exchange)
LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants
{} {}

View File

@@ -16,7 +16,7 @@
*/ */
pragma solidity ^0.5.5; pragma solidity ^0.5.16;
contract EthBalanceChecker { contract EthBalanceChecker {

View File

@@ -0,0 +1,322 @@
/*
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.16;
pragma experimental ABIEncoderV2;
import "./Addresses.sol";
import "./LibAssetData.sol";
import "./LibTransactionDecoder.sol";
import "./LibOrderTransferSimulation.sol";
contract ExternalFunctions is
Addresses
{
/// @dev Decodes the call data for an Exchange contract method call.
/// @param transactionData ABI-encoded calldata for an Exchange
/// contract method call.
/// @return The name of the function called, and the parameters it was
/// given. For single-order fills and cancels, the arrays will have
/// just one element.
function decodeZeroExTransactionData(bytes memory transactionData)
public
pure
returns(
string memory functionName,
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
{
return LibTransactionDecoder.decodeZeroExTransactionData(transactionData);
}
/// @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
)
{
return LibAssetData.decodeAssetProxyId(assetData);
}
/// @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.
function encodeERC20AssetData(address tokenAddress)
public
pure
returns (bytes memory assetData)
{
return LibAssetData.encodeERC20AssetData(tokenAddress);
}
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
/// @return The AssetProxy identifier, and the address of the ERC-20
/// contract hosting this asset.
function decodeERC20AssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address tokenAddress
)
{
return LibAssetData.decodeERC20AssetData(assetData);
}
/// @dev Encode ERC-721 asset data into the format described in the AssetProxy specification.
/// @param tokenAddress The address of the ERC-721 contract hosting the asset to be traded.
/// @param tokenId The identifier of the specific asset to be traded.
/// @return AssetProxy-compliant asset data describing the asset.
function encodeERC721AssetData(address tokenAddress, uint256 tokenId)
public
pure
returns (bytes memory assetData)
{
return LibAssetData.encodeERC721AssetData(tokenAddress, tokenId);
}
/// @dev Decode ERC-721 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-721 asset.
/// @return The ERC-721 AssetProxy identifier, the address of the ERC-721
/// contract hosting this asset, and the identifier of the specific
/// asset to be traded.
function decodeERC721AssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address tokenAddress,
uint256 tokenId
)
{
return LibAssetData.decodeERC721AssetData(assetData);
}
/// @dev Encode ERC-1155 asset data into the format described in the AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-1155 contract hosting the asset(s) to be traded.
/// @param tokenIds The identifiers of the specific assets to be traded.
/// @param tokenValues The amounts of each asset to be traded.
/// @param callbackData Data to be passed to receiving contracts when a transfer is performed.
/// @return AssetProxy-compliant asset data describing the set of assets.
function encodeERC1155AssetData(
address tokenAddress,
uint256[] memory tokenIds,
uint256[] memory tokenValues,
bytes memory callbackData
)
public
pure
returns (bytes memory assetData)
{
return LibAssetData.encodeERC1155AssetData(
tokenAddress,
tokenIds,
tokenValues,
callbackData
);
}
/// @dev Decode ERC-1155 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-1155 set of assets.
/// @return The ERC-1155 AssetProxy identifier, the address of the ERC-1155
/// contract hosting the assets, an array of the identifiers of the
/// assets to be traded, an array of asset amounts to be traded, and
/// callback data. Each element of the arrays corresponds to the
/// same-indexed element of the other array. Return values specified as
/// `memory` are returned as pointers to locations within the memory of
/// the input parameter `assetData`.
function decodeERC1155AssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address tokenAddress,
uint256[] memory tokenIds,
uint256[] memory tokenValues,
bytes memory callbackData
)
{
return LibAssetData.decodeERC1155AssetData(assetData);
}
/// @dev Encode data for multiple assets, per the AssetProxy contract specification.
/// @param amounts The amounts of each asset to be traded.
/// @param nestedAssetData AssetProxy-compliant data describing each asset to be traded.
/// @return AssetProxy-compliant data describing the set of assets.
function encodeMultiAssetData(uint256[] memory amounts, bytes[] memory nestedAssetData)
public
pure
returns (bytes memory assetData)
{
return LibAssetData.encodeMultiAssetData(amounts, nestedAssetData);
}
/// @dev Decode multi-asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant data describing a multi-asset basket.
/// @return The Multi-Asset AssetProxy identifier, an array of the amounts
/// of the assets to be traded, and an array of the
/// AssetProxy-compliant data describing each asset to be traded. Each
/// element of the arrays corresponds to the same-indexed element of the other array.
function decodeMultiAssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
uint256[] memory amounts,
bytes[] memory nestedAssetData
)
{
return LibAssetData.decodeMultiAssetData(assetData);
}
/// @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)
{
return LibAssetData.encodeStaticCallAssetData(
staticCallTargetAddress,
staticCallData,
expectedReturnDataHash
);
}
/// @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
)
{
return LibAssetData.decodeStaticCallAssetData(assetData);
}
/// @dev Decode ERC20Bridge asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC20Bridge asset
/// @return The ERC20BridgeProxy identifier, the address of the ERC20 token to transfer, the address
/// of the bridge contract, and extra data to be passed to the bridge contract.
function decodeERC20BridgeAssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address tokenAddress,
address bridgeAddress,
bytes memory bridgeData
)
{
return LibAssetData.decodeERC20BridgeAssetData(assetData);
}
/// @dev Reverts if assetData is not of a valid format for its given proxy id.
/// @param assetData AssetProxy compliant asset data.
function revertIfInvalidAssetData(bytes memory assetData)
public
pure
{
return LibAssetData.revertIfInvalidAssetData(assetData);
}
/// @dev Simulates the maker transfers within an order and returns the index of the first failed transfer.
/// @param order The order to simulate transfers for.
/// @param takerAddress The address of the taker that will fill the order.
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
function getSimulatedOrderMakerTransferResults(
LibOrder.Order memory order,
address takerAddress,
uint256 takerAssetFillAmount
)
public
returns (LibOrderTransferSimulation.OrderTransferResults orderTransferResults)
{
return LibOrderTransferSimulation.getSimulatedOrderMakerTransferResults(
exchangeAddress,
order,
takerAddress,
takerAssetFillAmount
);
}
/// @dev Simulates all of the transfers within an order and returns the index of the first failed transfer.
/// @param order The order to simulate transfers for.
/// @param takerAddress The address of the taker that will fill the order.
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
function getSimulatedOrderTransferResults(
LibOrder.Order memory order,
address takerAddress,
uint256 takerAssetFillAmount
)
public
returns (LibOrderTransferSimulation.OrderTransferResults orderTransferResults)
{
return LibOrderTransferSimulation.getSimulatedOrderTransferResults(
exchangeAddress,
order,
takerAddress,
takerAssetFillAmount
);
}
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
/// @param orders Array of orders to individually simulate transfers for.
/// @param takerAddresses Array of addresses of takers that will fill each order.
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
function getSimulatedOrdersTransferResults(
LibOrder.Order[] memory orders,
address[] memory takerAddresses,
uint256[] memory takerAssetFillAmounts
)
public
returns (LibOrderTransferSimulation.OrderTransferResults[] memory orderTransferResults)
{
return LibOrderTransferSimulation.getSimulatedOrdersTransferResults(
exchangeAddress,
orders,
takerAddresses,
takerAssetFillAmounts
);
}
}

View File

@@ -16,357 +16,17 @@
*/ */
pragma solidity ^0.5.5; pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IChai.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
contract LibAssetData is library LibAssetData {
DeploymentConstants
{
// 2^256 - 1
uint256 constant internal _MAX_UINT256 = uint256(-1);
using LibBytes for bytes; using LibBytes for bytes;
// solhint-disable var-name-mixedcase
IExchange internal _EXCHANGE;
address internal _ERC20_PROXY_ADDRESS;
address internal _ERC721_PROXY_ADDRESS;
address internal _ERC1155_PROXY_ADDRESS;
address internal _STATIC_CALL_PROXY_ADDRESS;
address internal _CHAI_BRIDGE_ADDRESS;
// solhint-enable var-name-mixedcase
constructor (
address _exchange,
address _chaiBridge
)
public
{
_EXCHANGE = IExchange(_exchange);
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.selector);
_CHAI_BRIDGE_ADDRESS = _chaiBridge;
}
/// @dev Returns the owner's balance of the assets(s) specified in
/// assetData. When the asset data contains multiple assets (eg in
/// ERC1155 or Multi-Asset), the return value indicates how many
/// complete "baskets" of those assets are owned by owner.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner.
function getBalance(address ownerAddress, bytes memory assetData)
public
returns (uint256 balance)
{
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
balance = _erc20BalanceOf(tokenAddress, ownerAddress);
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
// Check if id is owned by ownerAddress
bytes memory ownerOfCalldata = abi.encodeWithSelector(
IERC721Token(address(0)).ownerOf.selector,
tokenId
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
// Get ERC1155 token address, array of ids, and array of values
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
uint256 length = tokenIds.length;
for (uint256 i = 0; i != length; i++) {
// Skip over the token if the corresponding value is 0.
if (tokenValues[i] == 0) {
continue;
}
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
bytes memory balanceOfData = abi.encodeWithSelector(
IERC1155(address(0)).balanceOf.selector,
ownerAddress,
tokenIds[i]
);
// Query balance
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
uint256 totalBalance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
// Scale total balance down by corresponding value in assetData
uint256 scaledBalance = totalBalance / tokenValues[i];
if (scaledBalance == 0) {
return 0;
}
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
bytes memory transferFromData = abi.encodeWithSelector(
IAssetProxy(address(0)).transferFrom.selector,
assetData,
address(0), // `from` address is not used
address(0), // `to` address is not used
0 // `amount` is not used
);
// Check if staticcall would be successful
(bool success,) = _STATIC_CALL_PROXY_ADDRESS.staticcall(transferFromData);
// Success means that the staticcall can be made an unlimited amount of times
balance = success ? _MAX_UINT256 : 0;
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
// Get address of ERC20 token and bridge contract
(, address tokenAddress, address bridgeAddress,) = decodeERC20BridgeAssetData(assetData);
if (tokenAddress == _getDaiAddress() && bridgeAddress == _CHAI_BRIDGE_ADDRESS) {
uint256 chaiBalance = _erc20BalanceOf(_getChaiAddress(), ownerAddress);
// Calculate Dai balance
balance = _convertChaiToDaiAmount(chaiBalance);
}
// Balance will be 0 if bridge is not supported
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
// Get array of values and array of assetDatas
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
// Skip over the asset if the corresponding amount is 0.
if (assetAmounts[i] == 0) {
continue;
}
// Query balance of individual assetData
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
// Scale total balance down by corresponding value in assetData
uint256 scaledBalance = totalBalance / assetAmounts[i];
if (scaledBalance == 0) {
return 0;
}
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
}
// Balance will be 0 if assetProxyId is unknown
return balance;
}
/// @dev Calls getBalance() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return Array of asset balances from getBalance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
public
returns (uint256[] memory balances)
{
uint256 length = assetData.length;
balances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
balances[i] = getBalance(ownerAddress, assetData[i]);
}
return balances;
}
/// @dev Returns the number of asset(s) (described by assetData) that
/// the corresponding AssetProxy contract is authorized to spend. When the asset data contains
/// multiple assets (eg for Multi-Asset), the return value indicates
/// how many complete "baskets" of those assets may be spent by all of the corresponding
/// AssetProxy contracts.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
public
returns (uint256 allowance)
{
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
// Get array of values and array of assetDatas
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
// Skip over the asset if the corresponding amount is 0.
if (amounts[i] == 0) {
continue;
}
// Query allowance of individual assetData
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
// Scale total allowance down by corresponding value in assetData
uint256 scaledAllowance = totalAllowance / amounts[i];
if (scaledAllowance == 0) {
return 0;
}
if (scaledAllowance < allowance || allowance == 0) {
allowance = scaledAllowance;
}
}
return allowance;
}
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
// Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)`
bytes memory allowanceData = abi.encodeWithSelector(
IERC20Token(address(0)).allowance.selector,
ownerAddress,
_ERC20_PROXY_ADDRESS
);
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
IERC721Token(address(0)).isApprovedForAll.selector,
ownerAddress,
_ERC721_PROXY_ADDRESS
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
// If not approved for all, call `getApproved(tokenId)`
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
// Encode data for `getApproved(tokenId)`
bytes memory getApprovedData = abi.encodeWithSelector(IERC721Token(address(0)).getApproved.selector, tokenId);
(success, returnData) = tokenAddress.staticcall(getApprovedData);
// Allowance is 1 if successful and the approved address is the ERC721Proxy
allowance = success && returnData.length == 32 && returnData.readAddress(12) == _ERC721_PROXY_ADDRESS ? 1 : 0;
} else {
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
allowance = _MAX_UINT256;
}
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
// Get ERC1155 token address
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
IERC1155(address(0)).isApprovedForAll.selector,
ownerAddress,
_ERC1155_PROXY_ADDRESS
);
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
// The StaticCallProxy does not require any approvals
allowance = _MAX_UINT256;
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
// Get address of ERC20 token and bridge contract
(, address tokenAddress, address bridgeAddress,) = decodeERC20BridgeAssetData(assetData);
if (tokenAddress == _getDaiAddress() && bridgeAddress == _CHAI_BRIDGE_ADDRESS) {
bytes memory allowanceData = abi.encodeWithSelector(
IERC20Token(address(0)).allowance.selector,
ownerAddress,
_CHAI_BRIDGE_ADDRESS
);
(bool success, bytes memory returnData) = _getChaiAddress().staticcall(allowanceData);
uint256 chaiAllowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
// Dai allowance is unlimited if Chai allowance is unlimited
allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance);
}
// Allowance will be 0 if bridge is not supported
}
// Allowance will be 0 if the assetProxyId is unknown
return allowance;
}
/// @dev Calls getAssetProxyAllowance() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset allowances from getAllowance(), with each
/// element corresponding to the same-indexed element in the assetData input.
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
public
returns (uint256[] memory allowances)
{
uint256 length = assetData.length;
allowances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
allowances[i] = getAssetProxyAllowance(ownerAddress, assetData[i]);
}
return allowances;
}
/// @dev Calls getBalance() and getAllowance() for assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner, and number
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
public
returns (uint256 balance, uint256 allowance)
{
balance = getBalance(ownerAddress, assetData);
allowance = getAssetProxyAllowance(ownerAddress, assetData);
return (balance, allowance);
}
/// @dev Calls getBatchBalances() and getBatchAllowances() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset balances from getBalance(), and an array of
/// asset allowances from getAllowance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
public
returns (uint256[] memory balances, uint256[] memory allowances)
{
balances = getBatchBalances(ownerAddress, assetData);
allowances = getBatchAssetProxyAllowances(ownerAddress, assetData);
return (balances, allowances);
}
/// @dev Decode AssetProxy identifier /// @dev Decode AssetProxy identifier
/// @param assetData AssetProxy-compliant asset data describing an ERC-20, ERC-721, ERC1155, or MultiAsset asset. /// @param assetData AssetProxy-compliant asset data describing an ERC-20, ERC-721, ERC1155, or MultiAsset asset.
/// @return The AssetProxy identifier /// @return The AssetProxy identifier
@@ -691,44 +351,4 @@ contract LibAssetData is
revert("WRONG_PROXY_ID"); revert("WRONG_PROXY_ID");
} }
} }
/// @dev Queries balance of an ERC20 token. Returns 0 if call was unsuccessful.
/// @param tokenAddress Address of ERC20 token.
/// @param ownerAddress Address of owner of ERC20 token.
/// @return balance ERC20 token balance of owner.
function _erc20BalanceOf(
address tokenAddress,
address ownerAddress
)
internal
view
returns (uint256 balance)
{
// Encode data for `balanceOf(ownerAddress)`
bytes memory balanceOfData = abi.encodeWithSelector(
IERC20Token(address(0)).balanceOf.selector,
ownerAddress
);
// Query balance
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
return balance;
}
/// @dev Converts an amount of Chai into its equivalent Dai amount.
/// Also accumulates Dai from DSR if called after the last time it was collected.
/// @param chaiAmount Amount of Chai to converts.
function _convertChaiToDaiAmount(uint256 chaiAmount)
internal
returns (uint256 daiAmount)
{
PotLike pot = IChai(_getChaiAddress()).pot();
// Accumulate savings if called after last time savings were collected
uint256 chiMultiplier = (now > pot.rho())
? pot.drip()
: pot.chi();
daiAmount = LibMath.getPartialAmountFloor(chiMultiplier, 10**27, chaiAmount);
return daiAmount;
}
} }

View File

@@ -0,0 +1,227 @@
/*
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.16;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange/contracts/src/libs/LibExchangeRichErrorDecoder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
library LibOrderTransferSimulation {
using LibBytes for bytes;
enum OrderTransferResults {
TakerAssetDataFailed, // Transfer of takerAsset failed
MakerAssetDataFailed, // Transfer of makerAsset failed
TakerFeeAssetDataFailed, // Transfer of takerFeeAsset failed
MakerFeeAssetDataFailed, // Transfer of makerFeeAsset failed
TransfersSuccessful // All transfers in the order were successful
}
// NOTE(jalextowle): This is a random address that we use to avoid issues that addresses like `address(1)`
// may cause later.
address constant internal UNUSED_ADDRESS = address(0x377f698C4c287018D09b516F415317aEC5919332);
// keccak256(abi.encodeWithSignature("Error(string)", "TRANSFERS_SUCCESSFUL"));
bytes32 constant internal _TRANSFERS_SUCCESSFUL_RESULT_HASH = 0xf43f26ea5a94b478394a975e856464913dc1a8a1ca70939d974aa7c238aa0ce0;
/// @dev Simulates the maker transfers within an order and returns the index of the first failed transfer.
/// @param order The order to simulate transfers for.
/// @param takerAddress The address of the taker that will fill the order.
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
function getSimulatedOrderMakerTransferResults(
address exchange,
LibOrder.Order memory order,
address takerAddress,
uint256 takerAssetFillAmount
)
public
returns (OrderTransferResults orderTransferResults)
{
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResults(
order,
takerAssetFillAmount,
IExchange(exchange).protocolFeeMultiplier(),
tx.gasprice
);
bytes[] memory assetData = new bytes[](2);
address[] memory fromAddresses = new address[](2);
address[] memory toAddresses = new address[](2);
uint256[] memory amounts = new uint256[](2);
// Transfer `makerAsset` from maker to taker
assetData[0] = order.makerAssetData;
fromAddresses[0] = order.makerAddress;
toAddresses[0] = takerAddress == address(0) ? UNUSED_ADDRESS : takerAddress;
amounts[0] = fillResults.makerAssetFilledAmount;
// Transfer `makerFeeAsset` from maker to feeRecipient
assetData[1] = order.makerFeeAssetData;
fromAddresses[1] = order.makerAddress;
toAddresses[1] = order.feeRecipientAddress == address(0) ? UNUSED_ADDRESS : order.feeRecipientAddress;
amounts[1] = fillResults.makerFeePaid;
return _simulateTransferFromCalls(
exchange,
assetData,
fromAddresses,
toAddresses,
amounts
);
}
/// @dev Simulates all of the transfers within an order and returns the index of the first failed transfer.
/// @param order The order to simulate transfers for.
/// @param takerAddress The address of the taker that will fill the order.
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
function getSimulatedOrderTransferResults(
address exchange,
LibOrder.Order memory order,
address takerAddress,
uint256 takerAssetFillAmount
)
public
returns (OrderTransferResults orderTransferResults)
{
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResults(
order,
takerAssetFillAmount,
IExchange(exchange).protocolFeeMultiplier(),
tx.gasprice
);
// Create input arrays
bytes[] memory assetData = new bytes[](4);
address[] memory fromAddresses = new address[](4);
address[] memory toAddresses = new address[](4);
uint256[] memory amounts = new uint256[](4);
// Transfer `takerAsset` from taker to maker
assetData[0] = order.takerAssetData;
fromAddresses[0] = takerAddress;
toAddresses[0] = order.makerAddress;
amounts[0] = takerAssetFillAmount;
// Transfer `makerAsset` from maker to taker
assetData[1] = order.makerAssetData;
fromAddresses[1] = order.makerAddress;
toAddresses[1] = takerAddress == address(0) ? UNUSED_ADDRESS : takerAddress;
amounts[1] = fillResults.makerAssetFilledAmount;
// Transfer `takerFeeAsset` from taker to feeRecipient
assetData[2] = order.takerFeeAssetData;
fromAddresses[2] = takerAddress;
toAddresses[2] = order.feeRecipientAddress == address(0) ? UNUSED_ADDRESS : order.feeRecipientAddress;
amounts[2] = fillResults.takerFeePaid;
// Transfer `makerFeeAsset` from maker to feeRecipient
assetData[3] = order.makerFeeAssetData;
fromAddresses[3] = order.makerAddress;
toAddresses[3] = order.feeRecipientAddress == address(0) ? UNUSED_ADDRESS : order.feeRecipientAddress;
amounts[3] = fillResults.makerFeePaid;
return _simulateTransferFromCalls(
exchange,
assetData,
fromAddresses,
toAddresses,
amounts
);
}
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
/// @param orders Array of orders to individually simulate transfers for.
/// @param takerAddresses Array of addresses of takers that will fill each order.
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
function getSimulatedOrdersTransferResults(
address exchange,
LibOrder.Order[] memory orders,
address[] memory takerAddresses,
uint256[] memory takerAssetFillAmounts
)
public
returns (OrderTransferResults[] memory orderTransferResults)
{
uint256 length = orders.length;
orderTransferResults = new OrderTransferResults[](length);
for (uint256 i = 0; i != length; i++) {
orderTransferResults[i] = getSimulatedOrderTransferResults(
exchange,
orders[i],
takerAddresses[i],
takerAssetFillAmounts[i]
);
}
return orderTransferResults;
}
/// @dev Makes the simulation call with information about the transfers and processes
/// the returndata.
/// @param assetData The assetdata to use to make transfers.
/// @param fromAddresses The addresses to transfer funds.
/// @param toAddresses The addresses that will receive funds
/// @param amounts The amounts involved in the transfer.
function _simulateTransferFromCalls(
address exchange,
bytes[] memory assetData,
address[] memory fromAddresses,
address[] memory toAddresses,
uint256[] memory amounts
)
private
returns (OrderTransferResults orderTransferResults)
{
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
assetData,
fromAddresses,
toAddresses,
amounts
);
// Perform call and catch revert
(, bytes memory returnData) = address(exchange).call(simulateDispatchTransferFromCallsData);
bytes4 selector = returnData.readBytes4(0);
if (selector == LibExchangeRichErrors.AssetProxyDispatchErrorSelector()) {
// Decode AssetProxyDispatchError and return index of failed transfer
(, bytes32 failedTransferIndex,) = LibExchangeRichErrorDecoder.decodeAssetProxyDispatchError(returnData);
return OrderTransferResults(uint8(uint256(failedTransferIndex)));
} else if (selector == LibExchangeRichErrors.AssetProxyTransferErrorSelector()) {
// Decode AssetProxyTransferError and return index of failed transfer
(bytes32 failedTransferIndex, ,) = LibExchangeRichErrorDecoder.decodeAssetProxyTransferError(returnData);
return OrderTransferResults(uint8(uint256(failedTransferIndex)));
} else if (keccak256(returnData) == _TRANSFERS_SUCCESSFUL_RESULT_HASH) {
// All transfers were successful
return OrderTransferResults.TransfersSuccessful;
} else {
revert("UNKNOWN_RETURN_DATA");
}
}
}

View File

@@ -16,7 +16,7 @@
*/ */
pragma solidity ^0.5.5; pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
@@ -24,7 +24,7 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
contract LibTransactionDecoder { library LibTransactionDecoder {
using LibBytes for bytes; using LibBytes for bytes;

View File

@@ -28,9 +28,8 @@ import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
contract OrderTransferSimulationUtils is contract OrderTransferSimulationUtils {
LibExchangeRichErrorDecoder
{
using LibBytes for bytes; using LibBytes for bytes;
enum OrderTransferResults { enum OrderTransferResults {
@@ -216,11 +215,13 @@ contract OrderTransferSimulationUtils is
bytes4 selector = returnData.readBytes4(0); bytes4 selector = returnData.readBytes4(0);
if (selector == LibExchangeRichErrors.AssetProxyDispatchErrorSelector()) { if (selector == LibExchangeRichErrors.AssetProxyDispatchErrorSelector()) {
// Decode AssetProxyDispatchError and return index of failed transfer // Decode AssetProxyDispatchError and return index of failed transfer
(, bytes32 failedTransferIndex,) = decodeAssetProxyDispatchError(returnData); (, bytes32 failedTransferIndex,) = LibExchangeRichErrorDecoder
.decodeAssetProxyDispatchError(returnData);
return OrderTransferResults(uint8(uint256(failedTransferIndex))); return OrderTransferResults(uint8(uint256(failedTransferIndex)));
} else if (selector == LibExchangeRichErrors.AssetProxyTransferErrorSelector()) { } else if (selector == LibExchangeRichErrors.AssetProxyTransferErrorSelector()) {
// Decode AssetProxyTransferError and return index of failed transfer // Decode AssetProxyTransferError and return index of failed transfer
(bytes32 failedTransferIndex, ,) = decodeAssetProxyTransferError(returnData); (bytes32 failedTransferIndex, ,) = LibExchangeRichErrorDecoder
.decodeAssetProxyTransferError(returnData);
return OrderTransferResults(uint8(uint256(failedTransferIndex))); return OrderTransferResults(uint8(uint256(failedTransferIndex)));
} else if (keccak256(returnData) == _TRANSFERS_SUCCESSFUL_RESULT_HASH) { } else if (keccak256(returnData) == _TRANSFERS_SUCCESSFUL_RESULT_HASH) {
// All transfers were successful // All transfers were successful

View File

@@ -16,36 +16,26 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./Addresses.sol";
import "./AssetBalance.sol";
import "./LibAssetData.sol"; import "./LibAssetData.sol";
import "./OrderTransferSimulationUtils.sol"; import "./LibOrderTransferSimulation.sol";
contract OrderValidationUtils is contract OrderValidationUtils is
LibAssetData, Addresses,
OrderTransferSimulationUtils AssetBalance
{ {
using LibBytes for bytes; using LibBytes for bytes;
using LibSafeMath for uint256; using LibSafeMath for uint256;
constructor (
address _exchange,
address _chaiBridge
)
public
LibAssetData(
_exchange,
_chaiBridge
)
{}
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable. /// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
/// @param order The order structure. /// @param order The order structure.
/// @param signature Signature provided by maker that proves the order's authenticity. /// @param signature Signature provided by maker that proves the order's authenticity.
@@ -65,23 +55,22 @@ contract OrderValidationUtils is
) )
{ {
// Get info specific to order // Get info specific to order
orderInfo = _EXCHANGE.getOrderInfo(order); orderInfo = IExchange(exchangeAddress).getOrderInfo(order);
// Validate the maker's signature // Validate the maker's signature
address makerAddress = order.makerAddress; address makerAddress = order.makerAddress;
isValidSignature = _EXCHANGE.isValidOrderSignature( isValidSignature = IExchange(exchangeAddress).isValidOrderSignature(
order, order,
signature signature
); );
// Get the transferable amount of the `makerAsset` // Get the transferable amount of the `makerAsset`
uint256 transferableMakerAssetAmount = getTransferableAssetAmount(makerAddress, order.makerAssetData); uint256 transferableMakerAssetAmount = _getTransferableConvertedMakerAssetAmount(
order
);
// Assign to stack variables to reduce redundant mloads/sloads // Get the amount of `takerAsset` that is transferable to maker given the
uint256 takerAssetAmount = order.takerAssetAmount; // transferability of `makerAsset`, `makerFeeAsset`,
uint256 makerFee = order.makerFee;
// Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset`, `makerFeeAsset`,
// and the total amounts specified in the order // and the total amounts specified in the order
uint256 transferableTakerAssetAmount; uint256 transferableTakerAssetAmount;
if (order.makerAssetData.equals(order.makerFeeAssetData)) { if (order.makerAssetData.equals(order.makerFeeAssetData)) {
@@ -89,32 +78,35 @@ contract OrderValidationUtils is
// transferableMakerAssetAmount / (makerAssetAmount + makerFee) // transferableMakerAssetAmount / (makerAssetAmount + makerFee)
transferableTakerAssetAmount = LibMath.getPartialAmountFloor( transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount, transferableMakerAssetAmount,
order.makerAssetAmount.safeAdd(makerFee), order.makerAssetAmount.safeAdd(order.makerFee),
takerAssetAmount order.takerAssetAmount
); );
} else { } else {
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount) // If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
if (makerFee == 0) { if (order.makerFee == 0) {
transferableTakerAssetAmount = LibMath.getPartialAmountFloor( transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount, transferableMakerAssetAmount,
order.makerAssetAmount, order.makerAssetAmount,
takerAssetAmount order.takerAssetAmount
); );
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of // If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee) // (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
} else { } else {
// Get the transferable amount of the `makerFeeAsset` // Get the transferable amount of the `makerFeeAsset`
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, order.makerFeeAssetData); uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(
makerAddress,
order.makerFeeAssetData
);
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor( uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount, transferableMakerAssetAmount,
order.makerAssetAmount, order.makerAssetAmount,
takerAssetAmount order.takerAssetAmount
); );
uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor( uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor(
transferableMakerFeeAssetAmount, transferableMakerFeeAssetAmount,
makerFee, order.makerFee,
takerAssetAmount order.takerAssetAmount
); );
transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount); transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
} }
@@ -122,25 +114,18 @@ contract OrderValidationUtils is
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount` // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
fillableTakerAssetAmount = LibSafeMath.min256( fillableTakerAssetAmount = LibSafeMath.min256(
takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount), order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount),
transferableTakerAssetAmount transferableTakerAssetAmount
); );
// Execute the maker transfers. // Ensure that all of the asset data is valid. Fee asset data only needs
fillableTakerAssetAmount = getSimulatedOrderMakerTransferResults( // to be valid if the fees are nonzero.
order, if (!_areOrderAssetDatasValid(order)) {
order.takerAddress,
fillableTakerAssetAmount
) == OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
if (!_isAssetDataValid(order.takerAssetData)) {
fillableTakerAssetAmount = 0;
}
if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) {
fillableTakerAssetAmount = 0; fillableTakerAssetAmount = 0;
} }
// If the order is not fillable, then the fillable taker asset amount is
// zero by definition.
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) { if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
fillableTakerAssetAmount = 0; fillableTakerAssetAmount = 0;
} }
@@ -181,7 +166,7 @@ contract OrderValidationUtils is
return (ordersInfo, fillableTakerAssetAmounts, isValidSignature); return (ordersInfo, fillableTakerAssetAmounts, isValidSignature);
} }
/// @dev Gets the amount of an asset transferable by the owner. /// @dev Gets the amount of an asset transferable by the maker of an order.
/// @param ownerAddress Address of the owner of the asset. /// @param ownerAddress Address of the owner of the asset.
/// @param assetData Description of tokens, per the AssetProxy contract specification. /// @param assetData Description of tokens, per the AssetProxy contract specification.
/// @return The amount of the asset tranferable by the owner. /// @return The amount of the asset tranferable by the owner.
@@ -193,11 +178,45 @@ contract OrderValidationUtils is
public public
returns (uint256 transferableAssetAmount) returns (uint256 transferableAssetAmount)
{ {
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData); (uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(
ownerAddress,
assetData
);
transferableAssetAmount = LibSafeMath.min256(balance, allowance); transferableAssetAmount = LibSafeMath.min256(balance, allowance);
return transferableAssetAmount; return transferableAssetAmount;
} }
/// @dev Gets the amount of an asset transferable by the maker of an order.
/// Similar to `getTransferableAssetAmount()`, but can handle maker asset
/// types that depend on taker assets being transferred first (e.g., Dydx bridge).
/// @param order The order.
/// @return transferableAssetAmount Amount of maker asset that can be transferred.
function _getTransferableConvertedMakerAssetAmount(
LibOrder.Order memory order
)
internal
returns (uint256 transferableAssetAmount)
{
(uint256 balance, uint256 allowance) = _getConvertibleMakerBalanceAndAssetProxyAllowance(order);
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
return transferableAssetAmount;
}
/// @dev Checks that the asset data contained in a ZeroEx is valid and returns
/// a boolean that indicates whether or not the asset data was found to be valid.
/// @param order A ZeroEx order to validate.
/// @return The validatity of the asset data.
function _areOrderAssetDatasValid(LibOrder.Order memory order)
internal
pure
returns (bool)
{
return _isAssetDataValid(order.makerAssetData) &&
(order.makerFee == 0 || _isAssetDataValid(order.makerFeeAssetData)) &&
_isAssetDataValid(order.takerAssetData) &&
(order.takerFee == 0 || _isAssetDataValid(order.takerFeeAssetData));
}
/// @dev This function handles the edge cases around taker validation. This function /// @dev This function handles the edge cases around taker validation. This function
/// currently attempts to find duplicate ERC721 token's in the taker /// currently attempts to find duplicate ERC721 token's in the taker
/// multiAssetData. /// multiAssetData.
@@ -221,7 +240,8 @@ contract OrderValidationUtils is
} }
// Get array of values and array of assetDatas // Get array of values and array of assetDatas
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData); (, , bytes[] memory nestedAssetData) =
LibAssetData.decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length; uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) { for (uint256 i = 0; i != length; i++) {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-dev-utils", "name": "@0x/contracts-dev-utils",
"version": "1.0.4", "version": "1.1.1",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -8,7 +8,7 @@
"main": "lib/src/index.js", "main": "lib/src/index.js",
"scripts": { "scripts": {
"build": "yarn pre_build && tsc -b", "build": "yarn pre_build && tsc -b",
"test": "yarn assert_deployable && echo !!! Tests are run via @0x/contracts-integrations !!!", "test": "yarn assert_deployable",
"assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"", "assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"",
"build:ci": "yarn build", "build:ci": "yarn build",
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy", "pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy",
@@ -27,8 +27,8 @@
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
}, },
"config": { "config": {
"publicInterfaceContracts": "DevUtils,LibAssetData,LibTransactionDecoder", "publicInterfaceContracts": "DevUtils,LibAssetData,LibOrderTransferSimulation,LibTransactionDecoder",
"abis": "./test/generated-artifacts/@(DevUtils|EthBalanceChecker|LibAssetData|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils).json", "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {
@@ -41,14 +41,19 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.1.0", "@0x/abi-gen": "^5.2.0",
"@0x/assert": "^3.0.4", "@0x/assert": "^3.0.6",
"@0x/contracts-gen": "^2.0.4", "@0x/contracts-asset-proxy": "^3.2.1",
"@0x/sol-compiler": "^4.0.4", "@0x/contracts-erc20": "^3.1.1",
"@0x/contracts-gen": "^2.0.7",
"@0x/contracts-test-utils": "^5.1.5",
"@0x/sol-compiler": "^4.0.7",
"@0x/ts-doc-gen": "^0.0.22", "@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0", "@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.2",
"@0x/utils": "^5.4.0",
"@types/node": "*", "@types/node": "*",
"ethereum-types": "^3.0.0", "ethereum-types": "^3.1.0",
"ethers": "~4.0.4", "ethers": "~4.0.4",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"shx": "^0.2.2", "shx": "^0.2.2",
@@ -59,7 +64,7 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^6.1.0" "@0x/base-contract": "^6.2.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@@ -7,9 +7,11 @@ import { ContractArtifact } from 'ethereum-types';
import * as DevUtils from '../generated-artifacts/DevUtils.json'; import * as DevUtils from '../generated-artifacts/DevUtils.json';
import * as LibAssetData from '../generated-artifacts/LibAssetData.json'; import * as LibAssetData from '../generated-artifacts/LibAssetData.json';
import * as LibOrderTransferSimulation from '../generated-artifacts/LibOrderTransferSimulation.json';
import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json'; import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json';
export const artifacts = { export const artifacts = {
DevUtils: DevUtils as ContractArtifact, DevUtils: DevUtils as ContractArtifact,
LibAssetData: LibAssetData as ContractArtifact, LibAssetData: LibAssetData as ContractArtifact,
LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact,
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
}; };

View File

@@ -1,5 +1,5 @@
export { artifacts } from './artifacts'; export { artifacts } from './artifacts';
export { DevUtilsContract, LibAssetDataContract, LibTransactionDecoderContract } from './wrappers'; export { DevUtilsContract } from './wrappers';
export { export {
ContractArtifact, ContractArtifact,
ContractChains, ContractChains,
@@ -15,6 +15,7 @@ export {
OutputField, OutputField,
ParamDescription, ParamDescription,
EvmBytecodeOutput, EvmBytecodeOutput,
EvmBytecodeOutputLinkReferences,
AbiDefinition, AbiDefinition,
FunctionAbi, FunctionAbi,
EventAbi, EventAbi,

View File

@@ -5,4 +5,5 @@
*/ */
export * from '../generated-wrappers/dev_utils'; export * from '../generated-wrappers/dev_utils';
export * from '../generated-wrappers/lib_asset_data'; export * from '../generated-wrappers/lib_asset_data';
export * from '../generated-wrappers/lib_order_transfer_simulation';
export * from '../generated-wrappers/lib_transaction_decoder'; export * from '../generated-wrappers/lib_transaction_decoder';

View File

@@ -5,16 +5,24 @@
*/ */
import { ContractArtifact } from 'ethereum-types'; import { ContractArtifact } from 'ethereum-types';
import * as Addresses from '../test/generated-artifacts/Addresses.json';
import * as AssetBalance from '../test/generated-artifacts/AssetBalance.json';
import * as DevUtils from '../test/generated-artifacts/DevUtils.json'; import * as DevUtils from '../test/generated-artifacts/DevUtils.json';
import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json'; import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json';
import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.json';
import * as LibAssetData from '../test/generated-artifacts/LibAssetData.json'; import * as LibAssetData from '../test/generated-artifacts/LibAssetData.json';
import * as LibOrderTransferSimulation from '../test/generated-artifacts/LibOrderTransferSimulation.json';
import * as LibTransactionDecoder from '../test/generated-artifacts/LibTransactionDecoder.json'; import * as LibTransactionDecoder from '../test/generated-artifacts/LibTransactionDecoder.json';
import * as OrderTransferSimulationUtils from '../test/generated-artifacts/OrderTransferSimulationUtils.json'; import * as OrderTransferSimulationUtils from '../test/generated-artifacts/OrderTransferSimulationUtils.json';
import * as OrderValidationUtils from '../test/generated-artifacts/OrderValidationUtils.json'; import * as OrderValidationUtils from '../test/generated-artifacts/OrderValidationUtils.json';
export const artifacts = { export const artifacts = {
Addresses: Addresses as ContractArtifact,
AssetBalance: AssetBalance as ContractArtifact,
DevUtils: DevUtils as ContractArtifact, DevUtils: DevUtils as ContractArtifact,
EthBalanceChecker: EthBalanceChecker as ContractArtifact, EthBalanceChecker: EthBalanceChecker as ContractArtifact,
ExternalFunctions: ExternalFunctions as ContractArtifact,
LibAssetData: LibAssetData as ContractArtifact, LibAssetData: LibAssetData as ContractArtifact,
LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact,
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact, OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact,
OrderValidationUtils: OrderValidationUtils as ContractArtifact, OrderValidationUtils: OrderValidationUtils as ContractArtifact,

View File

@@ -3,9 +3,13 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually. * Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
export * from '../test/generated-wrappers/addresses';
export * from '../test/generated-wrappers/asset_balance';
export * from '../test/generated-wrappers/dev_utils'; export * from '../test/generated-wrappers/dev_utils';
export * from '../test/generated-wrappers/eth_balance_checker'; export * from '../test/generated-wrappers/eth_balance_checker';
export * from '../test/generated-wrappers/external_functions';
export * from '../test/generated-wrappers/lib_asset_data'; export * from '../test/generated-wrappers/lib_asset_data';
export * from '../test/generated-wrappers/lib_order_transfer_simulation';
export * from '../test/generated-wrappers/lib_transaction_decoder'; export * from '../test/generated-wrappers/lib_transaction_decoder';
export * from '../test/generated-wrappers/order_transfer_simulation_utils'; export * from '../test/generated-wrappers/order_transfer_simulation_utils';
export * from '../test/generated-wrappers/order_validation_utils'; export * from '../test/generated-wrappers/order_validation_utils';

View File

@@ -5,10 +5,15 @@
"files": [ "files": [
"generated-artifacts/DevUtils.json", "generated-artifacts/DevUtils.json",
"generated-artifacts/LibAssetData.json", "generated-artifacts/LibAssetData.json",
"generated-artifacts/LibOrderTransferSimulation.json",
"generated-artifacts/LibTransactionDecoder.json", "generated-artifacts/LibTransactionDecoder.json",
"test/generated-artifacts/Addresses.json",
"test/generated-artifacts/AssetBalance.json",
"test/generated-artifacts/DevUtils.json", "test/generated-artifacts/DevUtils.json",
"test/generated-artifacts/EthBalanceChecker.json", "test/generated-artifacts/EthBalanceChecker.json",
"test/generated-artifacts/ExternalFunctions.json",
"test/generated-artifacts/LibAssetData.json", "test/generated-artifacts/LibAssetData.json",
"test/generated-artifacts/LibOrderTransferSimulation.json",
"test/generated-artifacts/LibTransactionDecoder.json", "test/generated-artifacts/LibTransactionDecoder.json",
"test/generated-artifacts/OrderTransferSimulationUtils.json", "test/generated-artifacts/OrderTransferSimulationUtils.json",
"test/generated-artifacts/OrderValidationUtils.json" "test/generated-artifacts/OrderValidationUtils.json"

View File

@@ -1,4 +1,41 @@
[ [
{
"timestamp": 1581748629,
"version": "2.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.1.0",
"changes": [
{
"note": "Fix broken tests",
"pr": 2462
}
],
"timestamp": 1581204851
},
{
"timestamp": 1580988106,
"version": "2.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1580811564,
"version": "2.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1579682890, "timestamp": 1579682890,
"version": "2.0.4", "version": "2.0.4",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v2.1.1 - _February 15, 2020_
* Dependencies updated
## v2.1.0 - _February 8, 2020_
* Fix broken tests (#2462)
## v2.0.6 - _February 6, 2020_
* Dependencies updated
## v2.0.5 - _February 4, 2020_
* Dependencies updated
## v2.0.4 - _January 22, 2020_ ## v2.0.4 - _January 22, 2020_
* Dependencies updated * Dependencies updated

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-erc1155", "name": "@0x/contracts-erc1155",
"version": "2.0.4", "version": "2.1.1",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -52,15 +52,15 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.1.0", "@0x/abi-gen": "^5.2.0",
"@0x/contracts-gen": "^2.0.4", "@0x/contracts-gen": "^2.0.7",
"@0x/contracts-utils": "^4.1.0", "@0x/contracts-utils": "^4.3.1",
"@0x/dev-utils": "^3.1.1", "@0x/dev-utils": "^3.2.0",
"@0x/sol-compiler": "^4.0.4", "@0x/sol-compiler": "^4.0.7",
"@0x/ts-doc-gen": "^0.0.22", "@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0", "@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1", "@0x/types": "^3.1.2",
"@0x/typescript-typings": "^5.0.1", "@0x/typescript-typings": "^5.0.2",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -68,7 +68,7 @@
"chai-as-promised": "^7.1.0", "chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0", "chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"ethereum-types": "^3.0.0", "ethereum-types": "^3.1.0",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
@@ -80,10 +80,10 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^6.1.0", "@0x/base-contract": "^6.2.0",
"@0x/contracts-test-utils": "^5.1.1", "@0x/contracts-test-utils": "^5.1.5",
"@0x/utils": "^5.2.0", "@0x/utils": "^5.4.0",
"@0x/web3-wrapper": "^7.0.4", "@0x/web3-wrapper": "^7.0.6",
"lodash": "^4.17.11" "lodash": "^4.17.11"
}, },
"publishConfig": { "publishConfig": {

View File

@@ -27,6 +27,7 @@ export {
OutputField, OutputField,
ParamDescription, ParamDescription,
EvmBytecodeOutput, EvmBytecodeOutput,
EvmBytecodeOutputLinkReferences,
AbiDefinition, AbiDefinition,
FunctionAbi, FunctionAbi,
EventAbi, EventAbi,

View File

@@ -1,11 +1,4 @@
import { import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
chaiSetup,
constants,
expectTransactionFailedAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils'; import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types'; import { RevertReason } from '@0x/types';
@@ -193,12 +186,11 @@ describe('ERC1155Token', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
// execute transfer // execute transfer
await expectTransactionFailedAsync( return expect(
erc1155Contract erc1155Contract
.safeTransferFrom(spender, receiver, tokenToTransfer, valueToTransfer, receiverCallbackData) .safeTransferFrom(spender, receiver, tokenToTransfer, valueToTransfer, receiverCallbackData)
.sendTransactionAsync({ from: spender }), .awaitTransactionSuccessAsync({ from: spender }),
RevertReason.TransferRejected, ).to.revertWith(RevertReason.TransferRejected);
);
}); });
}); });
describe('batchSafeTransferFrom', () => { describe('batchSafeTransferFrom', () => {
@@ -359,12 +351,11 @@ describe('ERC1155Token', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
// execute transfer // execute transfer
await expectTransactionFailedAsync( return expect(
erc1155Contract erc1155Contract
.safeBatchTransferFrom(spender, receiver, tokensToTransfer, valuesToTransfer, receiverCallbackData) .safeBatchTransferFrom(spender, receiver, tokensToTransfer, valuesToTransfer, receiverCallbackData)
.sendTransactionAsync({ from: spender }), .awaitTransactionSuccessAsync({ from: spender }),
RevertReason.TransferRejected, ).to.revertWith(RevertReason.TransferRejected);
);
}); });
}); });
describe('setApprovalForAll', () => { describe('setApprovalForAll', () => {
@@ -409,12 +400,11 @@ describe('ERC1155Token', () => {
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer // execute transfer
await expectTransactionFailedAsync( return expect(
erc1155Contract erc1155Contract
.safeTransferFrom(spender, receiver, tokenToTransfer, valueToTransfer, receiverCallbackData) .safeTransferFrom(spender, receiver, tokenToTransfer, valueToTransfer, receiverCallbackData)
.sendTransactionAsync({ from: delegatedSpender }), .awaitTransactionSuccessAsync({ from: delegatedSpender }),
RevertReason.InsufficientAllowance, ).to.revertWith(RevertReason.InsufficientAllowance);
);
}); });
it('should transfer token via safeBatchTransferFrom if called by approved account', async () => { it('should transfer token via safeBatchTransferFrom if called by approved account', async () => {
// set approval // set approval
@@ -457,12 +447,11 @@ describe('ERC1155Token', () => {
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer // execute transfer
await expectTransactionFailedAsync( return expect(
erc1155Contract erc1155Contract
.safeBatchTransferFrom(spender, receiver, tokensToTransfer, valuesToTransfer, receiverCallbackData) .safeBatchTransferFrom(spender, receiver, tokensToTransfer, valuesToTransfer, receiverCallbackData)
.sendTransactionAsync({ from: delegatedSpender }), .awaitTransactionSuccessAsync({ from: delegatedSpender }),
RevertReason.InsufficientAllowance, ).to.revertWith(RevertReason.InsufficientAllowance);
);
}); });
}); });
}); });

View File

@@ -1,4 +1,47 @@
[ [
{
"version": "1.4.0",
"changes": [
{
"note": "Added Curve contract sampling",
"pr": 2483
}
],
"timestamp": 1581748629
},
{
"version": "1.3.0",
"changes": [
{
"note": "Catch reverts to `DevUtils` calls",
"pr": 2476
},
{
"note": "Remove wrapper functions and introduce `batchCall()`",
"pr": 2477
}
],
"timestamp": 1581204851
},
{
"timestamp": 1580988106,
"version": "1.2.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.2.0",
"changes": [
{
"note": "Make source IDs static on all networks, not inherited from `DeploymentConstants`.",
"pr": 2459
}
],
"timestamp": 1580811564
},
{ {
"version": "1.1.0", "version": "1.1.0",
"changes": [ "changes": [

View File

@@ -5,6 +5,23 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.4.0 - _February 15, 2020_
* Added Curve contract sampling (#2483)
## v1.3.0 - _February 8, 2020_
* Catch reverts to `DevUtils` calls (#2476)
* Remove wrapper functions and introduce `batchCall()` (#2477)
## v1.2.1 - _February 6, 2020_
* Dependencies updated
## v1.2.0 - _February 4, 2020_
* Make source IDs static on all networks, not inherited from `DeploymentConstants`. (#2459)
## v1.1.0 - _January 22, 2020_ ## v1.1.0 - _January 22, 2020_
* Add batch functions to query quotes (#2427) * Add batch functions to query quotes (#2427)

View File

@@ -21,7 +21,6 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
@@ -30,151 +29,41 @@ import "./IERC20BridgeSampler.sol";
import "./IEth2Dai.sol"; import "./IEth2Dai.sol";
import "./IKyberNetwork.sol"; import "./IKyberNetwork.sol";
import "./IUniswapExchangeQuotes.sol"; import "./IUniswapExchangeQuotes.sol";
import "./ICurve.sol";
contract ERC20BridgeSampler is contract ERC20BridgeSampler is
IERC20BridgeSampler, IERC20BridgeSampler,
DeploymentConstants DeploymentConstants
{ {
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)")); /// @dev Gas limit for DevUtils calls.
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 1500e3; uint256 constant internal DEV_UTILS_CALL_GAS = 500e3; // 500k
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3; /// @dev Gas limit for Kyber calls.
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 1000e3; uint256 constant internal KYBER_CALL_GAS = 1500e3; // 1.5m
/// @dev Gas limit for Uniswap calls.
uint256 constant internal UNISWAP_CALL_GAS = 150e3; // 150k
/// @dev Base gas limit for Eth2Dai calls.
uint256 constant internal ETH2DAI_CALL_GAS = 1000e3; // 1m
/// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
uint256 constant internal CURVE_CALL_GAS = 600e3; // 600k
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once. /// @dev Call multiple public functions on this contract in a single transaction.
/// @param orders Batches of Native orders to query. /// @param callDatas ABI-encoded call data for each function call.
/// @param orderSignatures Batches of Signatures for each respective order in `orders`. /// @return callResults ABI-encoded results data for each call.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw. function batchCall(bytes[] calldata callDatas)
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample. external
/// @return ordersAndSamples How much taker asset can be filled
/// by each order in `orders`. Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryBatchOrdersAndSampleSells(
LibOrder.Order[][] memory orders,
bytes[][] memory orderSignatures,
address[] memory sources,
uint256[][] memory takerTokenAmounts
)
public
view view
returns ( returns (bytes[] memory callResults)
OrdersAndSample[] memory ordersAndSamples
)
{ {
ordersAndSamples = new OrdersAndSample[](orders.length); callResults = new bytes[](callDatas.length);
for (uint256 i = 0; i != orders.length; i++) { for (uint256 i = 0; i != callDatas.length; ++i) {
( (bool didSucceed, bytes memory resultData) = address(this).staticcall(callDatas[i]);
uint256[] memory orderFillableAssetAmounts, if (!didSucceed) {
uint256[][] memory tokenAmountsBySource assembly { revert(add(resultData, 0x20), mload(resultData)) }
) = queryOrdersAndSampleSells(orders[i], orderSignatures[i], sources, takerTokenAmounts[i]);
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
} }
callResults[i] = resultData;
} }
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
/// @param orders Batches of Native orders to query.
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
/// @return ordersAndSamples How much taker asset can be filled
/// by each order in `orders`. Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function queryBatchOrdersAndSampleBuys(
LibOrder.Order[][] memory orders,
bytes[][] memory orderSignatures,
address[] memory sources,
uint256[][] memory makerTokenAmounts
)
public
view
returns (
OrdersAndSample[] memory ordersAndSamples
)
{
ordersAndSamples = new OrdersAndSample[](orders.length);
for (uint256 i = 0; i != orders.length; i++) {
(
uint256[] memory orderFillableAssetAmounts,
uint256[][] memory tokenAmountsBySource
) = queryOrdersAndSampleBuys(orders[i], orderSignatures[i], sources, makerTokenAmounts[i]);
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
}
}
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
/// by each order in `orders`.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleSells(
LibOrder.Order[] memory orders,
bytes[] memory orderSignatures,
address[] memory sources,
uint256[] memory takerTokenAmounts
)
public
view
returns (
uint256[] memory orderFillableTakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
)
{
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
orderFillableTakerAssetAmounts = getOrderFillableTakerAssetAmounts(
orders,
orderSignatures
);
makerTokenAmountsBySource = sampleSells(
sources,
_assetDataToTokenAddress(orders[0].takerAssetData),
_assetDataToTokenAddress(orders[0].makerAssetData),
takerTokenAmounts
);
}
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
/// by each order in `orders`.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleBuys(
LibOrder.Order[] memory orders,
bytes[] memory orderSignatures,
address[] memory sources,
uint256[] memory makerTokenAmounts
)
public
view
returns (
uint256[] memory orderFillableMakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
)
{
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
orderFillableMakerAssetAmounts = getOrderFillableMakerAssetAmounts(
orders,
orderSignatures
);
makerTokenAmountsBySource = sampleBuys(
sources,
_assetDataToTokenAddress(orders[0].takerAssetData),
_assetDataToTokenAddress(orders[0].makerAssetData),
makerTokenAmounts
);
} }
/// @dev Queries the fillable taker asset amounts of native orders. /// @dev Queries the fillable taker asset amounts of native orders.
@@ -201,13 +90,28 @@ contract ERC20BridgeSampler is
orderFillableTakerAssetAmounts[i] = 0; orderFillableTakerAssetAmounts[i] = 0;
continue; continue;
} }
// solhint-disable indent
(bool didSucceed, bytes memory resultData) =
_getDevUtilsAddress()
.staticcall
.gas(DEV_UTILS_CALL_GAS)
(abi.encodeWithSelector(
IDevUtils(_getDevUtilsAddress()).getOrderRelevantState.selector,
orders[i],
orderSignatures[i]
));
// solhint-enable indent
if (!didSucceed) {
orderFillableTakerAssetAmounts[i] = 0;
continue;
}
( (
LibOrder.OrderInfo memory orderInfo, LibOrder.OrderInfo memory orderInfo,
uint256 fillableTakerAssetAmount, uint256 fillableTakerAssetAmount,
bool isValidSignature bool isValidSignature
) = IDevUtils(_getDevUtilsAddress()).getOrderRelevantState( ) = abi.decode(
orders[i], resultData,
orderSignatures[i] (LibOrder.OrderInfo, uint256, bool)
); );
// The fillable amount is zero if the order is not fillable or if the // The fillable amount is zero if the order is not fillable or if the
// signature is invalid. // signature is invalid.
@@ -251,66 +155,6 @@ contract ERC20BridgeSampler is
} }
} }
/// @dev Sample sell quotes on multiple DEXes at once.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function sampleSells(
address[] memory sources,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[][] memory makerTokenAmountsBySource)
{
uint256 numSources = sources.length;
makerTokenAmountsBySource = new uint256[][](numSources);
for (uint256 i = 0; i < numSources; i++) {
makerTokenAmountsBySource[i] = _sampleSellSource(
sources[i],
takerToken,
makerToken,
takerTokenAmounts
);
}
}
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function sampleBuys(
address[] memory sources,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[][] memory takerTokenAmountsBySource)
{
uint256 numSources = sources.length;
takerTokenAmountsBySource = new uint256[][](numSources);
for (uint256 i = 0; i < numSources; i++) {
takerTokenAmountsBySource[i] = _sampleBuySource(
sources[i],
takerToken,
makerToken,
makerTokenAmounts
);
}
}
/// @dev Sample sell quotes from Kyber. /// @dev Sample sell quotes from Kyber.
/// @param takerToken Address of the taker token (what to sell). /// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy). /// @param makerToken Address of the maker token (what to buy).
@@ -335,7 +179,7 @@ contract ERC20BridgeSampler is
makerTokenAmounts = new uint256[](numSamples); makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) { for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) = (bool didSucceed, bytes memory resultData) =
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_SAMPLE_CALL_GAS)( _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)(
abi.encodeWithSelector( abi.encodeWithSelector(
IKyberNetwork(0).getExpectedRate.selector, IKyberNetwork(0).getExpectedRate.selector,
_takerToken, _takerToken,
@@ -377,7 +221,7 @@ contract ERC20BridgeSampler is
makerTokenAmounts = new uint256[](numSamples); makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) { for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) = (bool didSucceed, bytes memory resultData) =
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)( _getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
abi.encodeWithSelector( abi.encodeWithSelector(
IEth2Dai(0).getBuyAmount.selector, IEth2Dai(0).getBuyAmount.selector,
makerToken, makerToken,
@@ -414,7 +258,7 @@ contract ERC20BridgeSampler is
takerTokenAmounts = new uint256[](numSamples); takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) { for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) = (bool didSucceed, bytes memory resultData) =
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)( _getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
abi.encodeWithSelector( abi.encodeWithSelector(
IEth2Dai(0).getPayAmount.selector, IEth2Dai(0).getPayAmount.selector,
takerToken, takerToken,
@@ -549,6 +393,44 @@ contract ERC20BridgeSampler is
} }
} }
/// @dev Sample sell quotes from Curve.
/// @param curveAddress Address of the Curve contract.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromCurve(
address curveAddress,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
curveAddress.staticcall.gas(CURVE_CALL_GAS)(
abi.encodeWithSelector(
ICurve(0).get_dy_underlying.selector,
fromTokenIdx,
toTokenIdx,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
} else {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}
/// @dev Overridable way to get token decimals. /// @dev Overridable way to get token decimals.
/// @param tokenAddress Address of the token. /// @param tokenAddress Address of the token.
/// @return decimals The decimal places for the token. /// @return decimals The decimal places for the token.
@@ -580,7 +462,7 @@ contract ERC20BridgeSampler is
} }
bytes memory resultData; bytes memory resultData;
(didSucceed, resultData) = (didSucceed, resultData) =
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)( uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)(
abi.encodeWithSelector( abi.encodeWithSelector(
functionSelector, functionSelector,
inputAmount inputAmount
@@ -590,59 +472,6 @@ contract ERC20BridgeSampler is
} }
} }
/// @dev Samples a supported sell source, defined by its address.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function _sampleSellSource(
address source,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
private
view
returns (uint256[] memory makerTokenAmounts)
{
if (source == _getEth2DaiAddress()) {
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
}
if (source == _getUniswapExchangeFactoryAddress()) {
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
}
if (source == _getKyberNetworkProxyAddress()) {
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
}
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
}
/// @dev Samples a supported buy source, defined by its address.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function _sampleBuySource(
address source,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
private
view
returns (uint256[] memory takerTokenAmounts)
{
if (source == _getEth2DaiAddress()) {
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
}
if (source == _getUniswapExchangeFactoryAddress()) {
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
}
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
}
/// @dev Retrive an existing Uniswap exchange contract. /// @dev Retrive an existing Uniswap exchange contract.
/// Throws if the exchange does not exist. /// Throws if the exchange does not exist.
/// @param tokenAddress Address of the token contract. /// @param tokenAddress Address of the token contract.
@@ -658,23 +487,9 @@ contract ERC20BridgeSampler is
); );
} }
/// @dev Extract the token address from ERC20 proxy asset data. /// @dev Assert that the tokens in a trade pair are valid.
/// @param assetData ERC20 asset data. /// @param makerToken Address of the maker token.
/// @return tokenAddress The decoded token address. /// @param takerToken Address of the taker token.
function _assetDataToTokenAddress(bytes memory assetData)
private
pure
returns (address tokenAddress)
{
require(assetData.length == 36, "ERC20BridgeSampler/INVALID_ASSET_DATA");
bytes4 selector;
assembly {
selector := and(mload(add(assetData, 0x20)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
tokenAddress := mload(add(assetData, 0x24))
}
require(selector == ERC20_PROXY_ID, "ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY");
}
function _assertValidPair(address makerToken, address takerToken) function _assertValidPair(address makerToken, address takerToken)
private private
pure pure

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;
// solhint-disable func-name-mixedcase
interface ICurve {
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
/// This function exists on early versions of Curve (USDC/DAI)
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param sellAmount The amount of token being bought.
/// @param minBuyAmount The minimum buy amount of the token being bought.
/// @param deadline The time in seconds when this operation should expire.
function exchange_underlying(
int128 i,
int128 j,
uint256 sellAmount,
uint256 minBuyAmount,
uint256 deadline
)
external;
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
/// This function exists on later versions of Curve (USDC/DAI/USDT)
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param sellAmount The amount of token being bought.
/// @param minBuyAmount The minimum buy amount of the token being bought.
function exchange_underlying(
int128 i,
int128 j,
uint256 sellAmount,
uint256 minBuyAmount
)
external;
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param sellAmount The amount of token being bought.
function get_dy_underlying(
int128 i,
int128 j,
uint256 sellAmount
)
external
returns (uint256 dy);
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
/// This function exists on later versions of Curve (USDC/DAI/USDT)
/// @param i The token index being sold.
/// @param j The token index being bought.
/// @param buyAmount The amount of token being bought.
function get_dx_underlying(
int128 i,
int128 j,
uint256 buyAmount
)
external
returns (uint256 dx);
/// @dev Get the underlying token address from the token index
/// @param i The token index.
function underlying_coins(
int128 i
)
external
returns (address tokenAddress);
}

View File

@@ -23,98 +23,14 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
interface IERC20BridgeSampler { interface IERC20BridgeSampler {
struct OrdersAndSample {
uint256[] orderFillableAssetAmounts;
uint256[][] tokenAmountsBySource;
}
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once. /// @dev Call multiple public functions on this contract in a single transaction.
/// @param orders Batches of Native orders to query. /// @param callDatas ABI-encoded call data for each function call.
/// @param orderSignatures Batches of Signatures for each respective order in `orders`. /// @return callResults ABI-encoded results data for each call.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw. function batchCall(bytes[] calldata callDatas)
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
/// @return ordersAndSamples How much taker asset can be filled
/// by each order in `orders`. Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryBatchOrdersAndSampleSells(
LibOrder.Order[][] calldata orders,
bytes[][] calldata orderSignatures,
address[] calldata sources,
uint256[][] calldata takerTokenAmounts
)
external external
view view
returns ( returns (bytes[] memory callResults);
OrdersAndSample[] memory ordersAndSamples
);
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
/// @param orders Batches of Native orders to query.
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
/// @return ordersAndSamples How much taker asset can be filled
/// by each order in `orders`. Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index
function queryBatchOrdersAndSampleBuys(
LibOrder.Order[][] calldata orders,
bytes[][] calldata orderSignatures,
address[] calldata sources,
uint256[][] calldata makerTokenAmounts
)
external
view
returns (
OrdersAndSample[] memory ordersAndSamples
);
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
/// by each order in `orders`.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleSells(
LibOrder.Order[] calldata orders,
bytes[] calldata orderSignatures,
address[] calldata sources,
uint256[] calldata takerTokenAmounts
)
external
view
returns (
uint256[] memory orderFillableTakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
);
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param orderSignatures Signatures for each respective order in `orders`.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
/// by each order in `orders`.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleBuys(
LibOrder.Order[] calldata orders,
bytes[] calldata orderSignatures,
address[] calldata sources,
uint256[] calldata makerTokenAmounts
)
external
view
returns (
uint256[] memory orderFillableMakerAssetAmounts,
uint256[][] memory makerTokenAmountsBySource
);
/// @dev Queries the fillable taker asset amounts of native orders. /// @dev Queries the fillable taker asset amounts of native orders.
/// @param orders Native orders to query. /// @param orders Native orders to query.
@@ -142,39 +58,95 @@ interface IERC20BridgeSampler {
view view
returns (uint256[] memory orderFillableMakerAssetAmounts); returns (uint256[] memory orderFillableMakerAssetAmounts);
/// @dev Sample sell quotes on multiple DEXes at once. /// @dev Sample sell quotes from Kyber.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell). /// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy). /// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample. /// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at /// @return makerTokenAmounts Maker amounts bought at each taker token
/// each taker token amount. First indexed by source index, then sample /// amount.
/// index. function sampleSellsFromKyberNetwork(
function sampleSells(
address[] calldata sources,
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] calldata takerTokenAmounts uint256[] calldata takerTokenAmounts
) )
external external
view view
returns (uint256[][] memory makerTokenAmountsBySource); returns (uint256[] memory makerTokenAmounts);
/// @dev Query native orders and sample buy quotes on multiple DEXes at once. /// @dev Sample sell quotes from Eth2Dai/Oasis.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell). /// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy). /// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample. /// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at /// @return makerTokenAmounts Maker amounts bought at each taker token
/// each maker token amount. First indexed by source index, then sample /// amount.
/// index. function sampleSellsFromEth2Dai(
function sampleBuys( address takerToken,
address[] calldata sources, address makerToken,
uint256[] calldata takerTokenAmounts
)
external
view
returns (uint256[] memory makerTokenAmounts);
/// @dev Sample sell quotes from Uniswap.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromUniswap(
address takerToken,
address makerToken,
uint256[] calldata takerTokenAmounts
)
external
view
returns (uint256[] memory makerTokenAmounts);
/// @dev Sample buy quotes from Uniswap.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromUniswap(
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] calldata makerTokenAmounts uint256[] calldata makerTokenAmounts
) )
external external
view view
returns (uint256[][] memory takerTokenAmountsBySource); returns (uint256[] memory takerTokenAmounts);
/// @dev Sample buy quotes from Eth2Dai/Oasis.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Maker token sell amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromEth2Dai(
address takerToken,
address makerToken,
uint256[] calldata makerTokenAmounts
)
external
view
returns (uint256[] memory takerTokenAmounts);
/// @dev Sample sell quotes from Curve.
/// @param curveAddress Address of the Curve contract.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromCurve(
address curveAddress,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] calldata takerTokenAmounts
)
external
view
returns (uint256[] memory makerTokenAmounts);
} }

View File

@@ -327,7 +327,6 @@ contract TestERC20BridgeSampler is
bytes memory bytes memory
) )
public public
view
returns ( returns (
LibOrder.OrderInfo memory orderInfo, LibOrder.OrderInfo memory orderInfo,
uint256 fillableTakerAssetAmount, uint256 fillableTakerAssetAmount,

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-erc20-bridge-sampler", "name": "@0x/contracts-erc20-bridge-sampler",
"version": "1.1.0", "version": "1.4.0",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -38,7 +38,7 @@
"config": { "config": {
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler", "publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json" "abis": "./test/generated-artifacts/@(ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -50,18 +50,18 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.1.0", "@0x/abi-gen": "^5.2.0",
"@0x/contracts-asset-proxy": "^3.1.1", "@0x/contracts-asset-proxy": "^3.2.1",
"@0x/contracts-erc20": "^3.0.4", "@0x/contracts-erc20": "^3.1.1",
"@0x/contracts-exchange": "^3.1.0", "@0x/contracts-exchange": "^3.2.1",
"@0x/contracts-exchange-libs": "^4.1.0", "@0x/contracts-exchange-libs": "^4.3.1",
"@0x/contracts-gen": "^2.0.4", "@0x/contracts-gen": "^2.0.7",
"@0x/contracts-test-utils": "^5.1.1", "@0x/contracts-test-utils": "^5.1.5",
"@0x/contracts-utils": "^4.1.0", "@0x/contracts-utils": "^4.3.1",
"@0x/dev-utils": "^3.1.1", "@0x/dev-utils": "^3.2.0",
"@0x/sol-compiler": "^4.0.4", "@0x/sol-compiler": "^4.0.7",
"@0x/tslint-config": "^4.0.0", "@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.4", "@0x/web3-wrapper": "^7.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -79,11 +79,11 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^6.1.0", "@0x/base-contract": "^6.2.0",
"@0x/types": "^3.1.1", "@0x/types": "^3.1.2",
"@0x/typescript-typings": "^5.0.1", "@0x/typescript-typings": "^5.0.2",
"@0x/utils": "^5.2.0", "@0x/utils": "^5.4.0",
"ethereum-types": "^3.0.0", "ethereum-types": "^3.1.0",
"lodash": "^4.17.11" "lodash": "^4.17.11"
}, },
"publishConfig": { "publishConfig": {

View File

@@ -6,6 +6,7 @@
import { ContractArtifact } from 'ethereum-types'; import { ContractArtifact } from 'ethereum-types';
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json'; import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
import * as ICurve from '../test/generated-artifacts/ICurve.json';
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json'; import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json'; import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json'; import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
@@ -14,6 +15,7 @@ import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExc
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json'; import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
export const artifacts = { export const artifacts = {
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact, ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
ICurve: ICurve as ContractArtifact,
IDevUtils: IDevUtils as ContractArtifact, IDevUtils: IDevUtils as ContractArtifact,
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact, IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact, IEth2Dai: IEth2Dai as ContractArtifact,

View File

@@ -15,7 +15,6 @@ import { TestERC20BridgeSamplerContract } from './wrappers';
blockchainTests('erc20-bridge-sampler', env => { blockchainTests('erc20-bridge-sampler', env => {
let testContract: TestERC20BridgeSamplerContract; let testContract: TestERC20BridgeSamplerContract;
let allSources: { [name: string]: string };
const RATE_DENOMINATOR = constants.ONE_ETHER; const RATE_DENOMINATOR = constants.ONE_ETHER;
const MIN_RATE = new BigNumber('0.01'); const MIN_RATE = new BigNumber('0.01');
const MAX_RATE = new BigNumber('100'); const MAX_RATE = new BigNumber('100');
@@ -26,14 +25,6 @@ blockchainTests('erc20-bridge-sampler', env => {
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7'; const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab'; const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
const ERC20_PROXY_ID = '0xf47261b0'; const ERC20_PROXY_ID = '0xf47261b0';
const INVALID_ASSET_PROXY_ASSET_DATA = hexUtils.concat('0xf47261b1', hexUtils.leftPad(randomAddress()));
const INVALID_ASSET_DATA = hexUtils.random(37);
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
const UNSUPPORTED_SOURCE_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_SOURCE';
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR'; const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
const MAKER_TOKEN = randomAddress(); const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress(); const TAKER_TOKEN = randomAddress();
@@ -45,14 +36,6 @@ blockchainTests('erc20-bridge-sampler', env => {
env.txDefaults, env.txDefaults,
{}, {},
); );
allSources = _.zipObject(
['Uniswap', 'Eth2Dai', 'Kyber'],
[
await testContract.uniswap().callAsync(),
await testContract.eth2Dai().callAsync(),
await testContract.kyber().callAsync(),
],
);
}); });
function getPackedHash(...args: string[]): string { function getPackedHash(...args: string[]): string {
@@ -194,7 +177,7 @@ blockchainTests('erc20-bridge-sampler', env => {
} }
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber { function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
const hash = getPackedHash(hexUtils.toHex(order.salt, 32)); const hash = getPackedHash(hexUtils.leftPad(order.salt));
const orderStatus = new BigNumber(hash).mod(100).toNumber() > 90 ? 5 : 3; const orderStatus = new BigNumber(hash).mod(100).toNumber() > 90 ? 5 : 3;
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber(); const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
if (orderStatus !== 3 || !isValidSignature) { if (orderStatus !== 3 || !isValidSignature) {
@@ -328,329 +311,6 @@ blockchainTests('erc20-bridge-sampler', env => {
}); });
}); });
describe('queryOrdersAndSampleSells()', () => {
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns expected fillable amounts for each order', async () => {
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
const [orderInfos] = await testContract
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
ORDERS,
SIGNATURES,
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
});
describe('queryOrdersAndSampleBuys()', () => {
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns expected fillable amounts for each order', async () => {
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
const [orderInfos] = await testContract
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
ORDERS,
SIGNATURES,
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws if kyber is passed in as a source', async () => {
const sources = [...BUY_SOURCES, 'Kyber'];
const tx = testContract
.queryOrdersAndSampleBuys(
ORDERS,
SIGNATURES,
sources.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
ORDERS.map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
ORDERS.map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
SIGNATURES,
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
});
describe('sampleSells()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns empty quotes with no sample amounts', async () => {
const emptyQuotes = _.times(SELL_SOURCES.length, () => []);
const quotes = await testContract
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq(emptyQuotes);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
const quotes = await testContract
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can return quotes for some sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const sources = _.sampleSize(SELL_SOURCES, 1);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
const quotes = await testContract
.sampleSells(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.sampleSells(
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
TAKER_TOKEN,
MAKER_TOKEN,
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
});
describe('sampleBuys()', () => {
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns empty quotes with no sample amounts', async () => {
const emptyQuotes = _.times(BUY_SOURCES.length, () => []);
const quotes = await testContract
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq(emptyQuotes);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
const quotes = await testContract
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can return quotes for some sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const sources = _.sampleSize(BUY_SOURCES, 1);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
const quotes = await testContract
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.sampleBuys(
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
TAKER_TOKEN,
MAKER_TOKEN,
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws if kyber is passed in as a source', async () => {
const sources = [...BUY_SOURCES, 'Kyber'];
const tx = testContract
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, getSampleAmounts(MAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
});
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => { blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
before(async () => { before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
@@ -1055,4 +715,65 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
}); });
describe('batchCall()', () => {
it('can call one function', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
const calls = [
testContract.getOrderFillableTakerAssetAmounts(orders, signatures).getABIEncodedTransactionData(),
];
const r = await testContract.batchCall(calls).callAsync();
expect(r).to.be.length(1);
const actual = testContract.getABIDecodedReturnData<BigNumber[]>('getOrderFillableTakerAssetAmounts', r[0]);
expect(actual).to.deep.eq(expected);
});
it('can call two functions', async () => {
const numOrders = _.random(1, 10);
const orders = _.times(2, () => createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders));
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
const expecteds = [
orders[0].map(getDeterministicFillableTakerAssetAmount),
orders[1].map(getDeterministicFillableMakerAssetAmount),
];
const calls = [
testContract.getOrderFillableTakerAssetAmounts(orders[0], signatures).getABIEncodedTransactionData(),
testContract.getOrderFillableMakerAssetAmounts(orders[1], signatures).getABIEncodedTransactionData(),
];
const r = await testContract.batchCall(calls).callAsync();
expect(r).to.be.length(2);
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0])).to.deep.eq(
expecteds[0],
);
expect(testContract.getABIDecodedReturnData('getOrderFillableMakerAssetAmounts', r[1])).to.deep.eq(
expecteds[1],
);
});
it('can make recursive calls', async () => {
const numOrders = _.random(1, 10);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders);
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
let r = await testContract
.batchCall([
testContract
.batchCall([
testContract
.getOrderFillableTakerAssetAmounts(orders, signatures)
.getABIEncodedTransactionData(),
])
.getABIEncodedTransactionData(),
])
.callAsync();
expect(r).to.be.length(1);
r = testContract.getABIDecodedReturnData<string[]>('batchCall', r[0]);
expect(r).to.be.length(1);
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0])).to.deep.eq(
expected,
);
});
});
}); });

View File

@@ -4,6 +4,7 @@
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
export * from '../test/generated-wrappers/erc20_bridge_sampler'; export * from '../test/generated-wrappers/erc20_bridge_sampler';
export * from '../test/generated-wrappers/i_curve';
export * from '../test/generated-wrappers/i_dev_utils'; export * from '../test/generated-wrappers/i_dev_utils';
export * from '../test/generated-wrappers/i_erc20_bridge_sampler'; export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
export * from '../test/generated-wrappers/i_eth2_dai'; export * from '../test/generated-wrappers/i_eth2_dai';

View File

@@ -6,6 +6,7 @@
"generated-artifacts/ERC20BridgeSampler.json", "generated-artifacts/ERC20BridgeSampler.json",
"generated-artifacts/IERC20BridgeSampler.json", "generated-artifacts/IERC20BridgeSampler.json",
"test/generated-artifacts/ERC20BridgeSampler.json", "test/generated-artifacts/ERC20BridgeSampler.json",
"test/generated-artifacts/ICurve.json",
"test/generated-artifacts/IDevUtils.json", "test/generated-artifacts/IDevUtils.json",
"test/generated-artifacts/IERC20BridgeSampler.json", "test/generated-artifacts/IERC20BridgeSampler.json",
"test/generated-artifacts/IEth2Dai.json", "test/generated-artifacts/IEth2Dai.json",

View File

@@ -1,4 +1,45 @@
[ [
{
"timestamp": 1581748629,
"version": "3.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.1.0",
"changes": [
{
"note": "Add `allowance()` and `balanceOf()` to `LibERC20Token`",
"pr": 2464
},
{
"note": "Fix broken tests",
"pr": 2456
}
],
"timestamp": 1581204851
},
{
"timestamp": 1580988106,
"version": "3.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1580811564,
"version": "3.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1579682890, "timestamp": 1579682890,
"version": "3.0.4", "version": "3.0.4",

View File

@@ -5,6 +5,23 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v3.1.1 - _February 15, 2020_
* Dependencies updated
## v3.1.0 - _February 8, 2020_
* Add `allowance()` and `balanceOf()` to `LibERC20Token` (#2464)
* Fix broken tests (#2456)
## v3.0.6 - _February 6, 2020_
* Dependencies updated
## v3.0.5 - _February 4, 2020_
* Dependencies updated
## v3.0.4 - _January 22, 2020_ ## v3.0.4 - _January 22, 2020_
* Dependencies updated * Dependencies updated

View File

@@ -94,7 +94,8 @@ library LibERC20Token {
/// @dev Retrieves the number of decimals for a token. /// @dev Retrieves the number of decimals for a token.
/// Returns `18` if the call reverts. /// Returns `18` if the call reverts.
/// @return The number of decimals places for the token. /// @param token The address of the token contract.
/// @return tokenDecimals The number of decimals places for the token.
function decimals(address token) function decimals(address token)
internal internal
view view
@@ -107,6 +108,50 @@ library LibERC20Token {
} }
} }
/// @dev Retrieves the allowance for a token, owner, and spender.
/// Returns `0` if the call reverts.
/// @param token The address of the token contract.
/// @param owner The owner of the tokens.
/// @param spender The address the spender.
/// @return allowance The allowance for a token, owner, and spender.
function allowance(address token, address owner, address spender)
internal
view
returns (uint256 allowance_)
{
(bool didSucceed, bytes memory resultData) = token.staticcall(
abi.encodeWithSelector(
IERC20Token(0).allowance.selector,
owner,
spender
)
);
if (didSucceed && resultData.length == 32) {
allowance_ = LibBytes.readUint256(resultData, 0);
}
}
/// @dev Retrieves the balance for a token owner.
/// Returns `0` if the call reverts.
/// @param token The address of the token contract.
/// @param owner The owner of the tokens.
/// @return balance The token balance of an owner.
function balanceOf(address token, address owner)
internal
view
returns (uint256 balance)
{
(bool didSucceed, bytes memory resultData) = token.staticcall(
abi.encodeWithSelector(
IERC20Token(0).balanceOf.selector,
owner
)
);
if (didSucceed && resultData.length == 32) {
balance = LibBytes.readUint256(resultData, 0);
}
}
/// @dev Executes a call on address `target` with calldata `callData` /// @dev Executes a call on address `target` with calldata `callData`
/// and asserts that either nothing was returned or a single boolean /// and asserts that either nothing was returned or a single boolean
/// was returned equal to `true`. /// was returned equal to `true`.

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-erc20", "name": "@0x/contracts-erc20",
"version": "3.0.4", "version": "3.1.1",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -51,18 +51,18 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.1.0", "@0x/abi-gen": "^5.2.0",
"@0x/contracts-gen": "^2.0.4", "@0x/contracts-gen": "^2.0.7",
"@0x/contracts-test-utils": "^5.1.1", "@0x/contracts-test-utils": "^5.1.5",
"@0x/contracts-utils": "^4.1.0", "@0x/contracts-utils": "^4.3.1",
"@0x/dev-utils": "^3.1.1", "@0x/dev-utils": "^3.2.0",
"@0x/sol-compiler": "^4.0.4", "@0x/sol-compiler": "^4.0.7",
"@0x/ts-doc-gen": "^0.0.22", "@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0", "@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1", "@0x/types": "^3.1.2",
"@0x/typescript-typings": "^5.0.1", "@0x/typescript-typings": "^5.0.2",
"@0x/utils": "^5.2.0", "@0x/utils": "^5.4.0",
"@0x/web3-wrapper": "^7.0.4", "@0x/web3-wrapper": "^7.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -70,7 +70,7 @@
"chai-as-promised": "^7.1.0", "chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0", "chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"ethereum-types": "^3.0.0", "ethereum-types": "^3.1.0",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
"mocha": "^6.2.0", "mocha": "^6.2.0",
@@ -82,7 +82,7 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^6.1.0" "@0x/base-contract": "^6.2.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@@ -30,6 +30,7 @@ export {
OutputField, OutputField,
ParamDescription, ParamDescription,
EvmBytecodeOutput, EvmBytecodeOutput,
EvmBytecodeOutputLinkReferences,
AbiDefinition, AbiDefinition,
FunctionAbi, FunctionAbi,
EventAbi, EventAbi,

View File

@@ -1,11 +1,4 @@
import { import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
chaiSetup,
constants,
expectContractCallFailedAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types'; import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
@@ -60,8 +53,7 @@ describe('UnlimitedAllowanceToken', () => {
it('should revert if owner has insufficient balance', async () => { it('should revert if owner has insufficient balance', async () => {
const ownerBalance = await token.balanceOf(owner).callAsync(); const ownerBalance = await token.balanceOf(owner).callAsync();
const amountToTransfer = ownerBalance.plus(1); const amountToTransfer = ownerBalance.plus(1);
return expectContractCallFailedAsync( return expect(token.transfer(spender, amountToTransfer).callAsync({ from: owner })).to.revertWith(
token.transfer(spender, amountToTransfer).callAsync({ from: owner }),
RevertReason.Erc20InsufficientBalance, RevertReason.Erc20InsufficientBalance,
); );
}); });
@@ -99,12 +91,11 @@ describe('UnlimitedAllowanceToken', () => {
await token.approve(spender, amountToTransfer).sendTransactionAsync({ from: owner }), await token.approve(spender, amountToTransfer).sendTransactionAsync({ from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
return expectContractCallFailedAsync( return expect(
token.transferFrom(owner, spender, amountToTransfer).callAsync({ token.transferFrom(owner, spender, amountToTransfer).callAsync({
from: spender, from: spender,
}), }),
RevertReason.Erc20InsufficientBalance, ).to.revertWith(RevertReason.Erc20InsufficientBalance);
);
}); });
it('should revert if spender has insufficient allowance', async () => { it('should revert if spender has insufficient allowance', async () => {
@@ -115,12 +106,11 @@ describe('UnlimitedAllowanceToken', () => {
const isSpenderAllowanceInsufficient = spenderAllowance.comparedTo(amountToTransfer) < 0; const isSpenderAllowanceInsufficient = spenderAllowance.comparedTo(amountToTransfer) < 0;
expect(isSpenderAllowanceInsufficient).to.be.true(); expect(isSpenderAllowanceInsufficient).to.be.true();
return expectContractCallFailedAsync( return expect(
token.transferFrom(owner, spender, amountToTransfer).callAsync({ token.transferFrom(owner, spender, amountToTransfer).callAsync({
from: spender, from: spender,
}), }),
RevertReason.Erc20InsufficientAllowance, ).to.revertWith(RevertReason.Erc20InsufficientAllowance);
);
}); });
it('should return true on a 0 value transfer', async () => { it('should return true on a 0 value transfer', async () => {

View File

@@ -1,4 +1,41 @@
[ [
{
"timestamp": 1581748629,
"version": "3.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.1.0",
"changes": [
{
"note": "Fix broken tests",
"pr": 2462
}
],
"timestamp": 1581204851
},
{
"timestamp": 1580988106,
"version": "3.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1580811564,
"version": "3.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1579682890, "timestamp": 1579682890,
"version": "3.0.4", "version": "3.0.4",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v3.1.1 - _February 15, 2020_
* Dependencies updated
## v3.1.0 - _February 8, 2020_
* Fix broken tests (#2462)
## v3.0.6 - _February 6, 2020_
* Dependencies updated
## v3.0.5 - _February 4, 2020_
* Dependencies updated
## v3.0.4 - _January 22, 2020_ ## v3.0.4 - _January 22, 2020_
* Dependencies updated * Dependencies updated

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-erc721", "name": "@0x/contracts-erc721",
"version": "3.0.4", "version": "3.1.1",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -52,18 +52,18 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^5.1.0", "@0x/abi-gen": "^5.2.0",
"@0x/contracts-gen": "^2.0.4", "@0x/contracts-gen": "^2.0.7",
"@0x/contracts-test-utils": "^5.1.1", "@0x/contracts-test-utils": "^5.1.5",
"@0x/contracts-utils": "^4.1.0", "@0x/contracts-utils": "^4.3.1",
"@0x/dev-utils": "^3.1.1", "@0x/dev-utils": "^3.2.0",
"@0x/sol-compiler": "^4.0.4", "@0x/sol-compiler": "^4.0.7",
"@0x/ts-doc-gen": "^0.0.22", "@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^4.0.0", "@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.1", "@0x/types": "^3.1.2",
"@0x/typescript-typings": "^5.0.1", "@0x/typescript-typings": "^5.0.2",
"@0x/utils": "^5.2.0", "@0x/utils": "^5.4.0",
"@0x/web3-wrapper": "^7.0.4", "@0x/web3-wrapper": "^7.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -71,7 +71,7 @@
"chai-as-promised": "^7.1.0", "chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0", "chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"ethereum-types": "^3.0.0", "ethereum-types": "^3.1.0",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
"mocha": "^6.2.0", "mocha": "^6.2.0",
@@ -84,7 +84,7 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^6.1.0" "@0x/base-contract": "^6.2.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

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