Compare commits
272 Commits
@0x/utils@
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
d4c37ecfa3 | ||
|
3f9fd7c060 | ||
|
0490ef5900 | ||
|
fa36c91bd6 | ||
|
5b8f294aaf | ||
|
aad75840d4 | ||
|
31faef7030 | ||
|
941877a05a | ||
|
4fd4c1e8e1 | ||
|
7cd27cd9c8 | ||
|
54dbef2b4f | ||
|
05424c9f33 | ||
|
d29ed6cd49 | ||
|
8c3abf3473 | ||
|
384114d3c7 | ||
|
e8be5a0a8f | ||
|
98c6fa10e6 | ||
|
30d0bdec07 | ||
|
7024f29865 | ||
|
29ebed9514 | ||
|
c0260bc44a | ||
|
6992bff0e0 | ||
|
6957e6e8f7 | ||
|
31dbca7efc | ||
|
82b6dad1ba | ||
|
5063c17e6b | ||
|
3fff3d9c60 | ||
|
2675833b0d | ||
|
939a5b477a | ||
|
dc57e7a5b3 | ||
|
a401b8f475 | ||
|
b008fabdac | ||
|
caf286b8cb | ||
|
aea278c022 | ||
|
4e20fe1602 | ||
|
d3ab612a89 | ||
|
a8720806f1 | ||
|
5aac4c2e5d | ||
|
6fa645aab9 | ||
|
842ea4645b | ||
|
e1cfbcd4f6 | ||
|
6f17ff55fa | ||
|
d88af4dfa6 | ||
|
bb0ba21e92 | ||
|
f0753c8e58 | ||
|
4c55004b08 | ||
|
ae24119c09 | ||
|
b8753d8f20 | ||
|
bab368b956 | ||
|
22bc1fb21e | ||
|
63ba764de8 | ||
|
6a7530d741 | ||
|
eb9bf7c4f9 | ||
|
b6571d0ca3 | ||
|
9207d1c680 | ||
|
f783c9bb25 | ||
|
df5786deda | ||
|
de971e6c46 | ||
|
2a6ed0c96e | ||
|
9ec380777a | ||
|
8916d0d367 | ||
|
1e5648111e | ||
|
ae51cfe8b9 | ||
|
05ef250ab4 | ||
|
38e4871f32 | ||
|
a86ba7af2e | ||
|
5704afc54c | ||
|
55c4fc9aca | ||
|
888c17353b | ||
|
054c0e91a3 | ||
|
4dfb610eba | ||
|
008eb8dd8b | ||
|
2882c4bb89 | ||
|
4473851f5b | ||
|
15d9e2d3d5 | ||
|
d5d9df383e | ||
|
243a04b756 | ||
|
b77dcbd39b | ||
|
ad5d4bdfc5 | ||
|
54c17b0068 | ||
|
616907eff8 | ||
|
afaabb3673 | ||
|
1c1f625352 | ||
|
d3f45d2148 | ||
|
7326dbd108 | ||
|
4bf311a282 | ||
|
7f5a3f12ca | ||
|
6a9b71466d | ||
|
70e550a25f | ||
|
1328882ab6 | ||
|
b378a0608d | ||
|
e28c6d6f9c | ||
|
f57f29e426 | ||
|
f2857452e3 | ||
|
af04c294b9 | ||
|
65c8630534 | ||
|
667c22169f | ||
|
c53edf6bd9 | ||
|
b601220845 | ||
|
e77a608f45 | ||
|
2822e77716 | ||
|
98227928af | ||
|
430afbdc80 | ||
|
dfdb48ce7d | ||
|
257d1b2b52 | ||
|
07200437b6 | ||
|
ebfa00d555 | ||
|
421f555d57 | ||
|
4b60d941cc | ||
|
60d24ada62 | ||
|
a1c121e2fe | ||
|
b3775a3ca5 | ||
|
5d36c97a46 | ||
|
4bdd412c15 | ||
|
46e3bcd6b9 | ||
|
68a85ddf90 | ||
|
6eba9273cb | ||
|
9de151cc14 | ||
|
b8c9a5dd1f | ||
|
4dfb3507c4 | ||
|
0cac2d407b | ||
|
b3106cd932 | ||
|
c4a467fa96 | ||
|
cf7afbe7f6 | ||
|
a8d58aeac4 | ||
|
28209e9413 | ||
|
68ed56f2d9 | ||
|
0c03747807 | ||
|
dfe7ecbb5b | ||
|
3039d8edfa | ||
|
7c850cc082 | ||
|
0e07ee3d81 | ||
|
14b820f2c1 | ||
|
fa6db9411b | ||
|
644f54bfba | ||
|
755c4da8cc | ||
|
483c77fba8 | ||
|
22af796302 | ||
|
a70931ffbf | ||
|
2f7dd177c1 | ||
|
d35a053efd | ||
|
87cc1f9415 | ||
|
e7bb524362 | ||
|
9e03e1c742 | ||
|
6fbfcef1fa | ||
|
9471510086 | ||
|
56320468fd | ||
|
53a70bbffb | ||
|
4f2e547bd6 | ||
|
af82fa7b62 | ||
|
6bd1a5ab80 | ||
|
93568f6fd0 | ||
|
1a3bf81c19 | ||
|
2eeabe3998 | ||
|
b1d86a7a2d | ||
|
6cfc9ba47b | ||
|
350474540b | ||
|
1136e58de7 | ||
|
558ce4713c | ||
|
69c6f50dfb | ||
|
babe01321c | ||
|
ff61dc4391 | ||
|
4da6ede9d0 | ||
|
0b1bab873e | ||
|
6c60341ce8 | ||
|
e1c1878130 | ||
|
fea5a39740 | ||
|
9f2221a885 | ||
|
977cd2505e | ||
|
e3f85a7c0f | ||
|
d81339722e | ||
|
f5c09e02de | ||
|
5e102526ec | ||
|
3c2efd4b67 | ||
|
7bb93a7c32 | ||
|
ac8f4ae2f9 | ||
|
1ff49188b1 | ||
|
2c329023c2 | ||
|
4457a300bf | ||
|
397496aff0 | ||
|
7458fe0d81 | ||
|
453c81f634 | ||
|
eebce4b54d | ||
|
f7976e18f1 | ||
|
d951fe9988 | ||
|
fd4da78075 | ||
|
a025ae3f54 | ||
|
a75ba0d903 | ||
|
99318ae2ba | ||
|
8369bcb605 | ||
|
338cc69034 | ||
|
4526c52fe8 | ||
|
81f9bda502 | ||
|
46bc5463ca | ||
|
bb346537ba | ||
|
3b5f0d5c30 | ||
|
808ce969d9 | ||
|
16f8339f3c | ||
|
25d68c3904 | ||
|
77ad8e1a80 | ||
|
9e8c18075a | ||
|
4278cdfd29 | ||
|
142c2bd0f0 | ||
|
13ee8686bb | ||
|
5c06df2635 | ||
|
d69bf76341 | ||
|
d0d1b295b4 | ||
|
e450191548 | ||
|
3aee83f3d8 | ||
|
3610a2bc8d | ||
|
ab559d4620 | ||
|
15d308d4c5 | ||
|
e7ea66afb5 | ||
|
d9a1d8bde6 | ||
|
9ac4486403 | ||
|
428afabaa3 | ||
|
9162189fa6 | ||
|
154ca9b760 | ||
|
9fcead3973 | ||
|
2fd9d0359c | ||
|
4cf9e030a2 | ||
|
5570d14179 | ||
|
fd3c546994 | ||
|
e81ae05df9 | ||
|
cb394f3a1c | ||
|
03ed057ff6 | ||
|
807290ff38 | ||
|
40a4b4fa7a | ||
|
b7d2ad3651 | ||
|
faac286f70 | ||
|
c6c7f6f907 | ||
|
ccd0da58cb | ||
|
500e5f1b5d | ||
|
ebb6177271 | ||
|
951c256980 | ||
|
a134ef03dd | ||
|
f21a9d16d0 | ||
|
42c3bb00ec | ||
|
0d0fcfe49a | ||
|
3f0db92be6 | ||
|
eb212de70e | ||
|
8fbdef2a1d | ||
|
e6594cecce | ||
|
85ed5d27f5 | ||
|
6ac9e11245 | ||
|
9fbd809344 | ||
|
cf65d4a909 | ||
|
079f627b34 | ||
|
f6c6cbc343 | ||
|
c6cdea77b6 | ||
|
3191de68b8 | ||
|
f3da56773e | ||
|
5197758579 | ||
|
d48af7c4c2 | ||
|
bbafe0fc46 | ||
|
9a91f917e0 | ||
|
1f681f02ae | ||
|
778a86e7eb | ||
|
6eb923d22f | ||
|
e602afcd5f | ||
|
bf1115d417 | ||
|
f5d668af31 | ||
|
62373f969c | ||
|
30f2e3b606 | ||
|
7d3d997083 | ||
|
3955e2c84a | ||
|
6a6c41df26 | ||
|
1dfd2aec50 | ||
|
8885f543ae | ||
|
8804e6c2ca | ||
|
88b7c214f7 | ||
|
9a7ccc20e8 |
@@ -47,11 +47,12 @@ jobs:
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-libs
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc20
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc721
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc1155
|
||||
- run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||
- run: yarn wsrun test:circleci @0x/contracts-asset-proxy
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
||||
- run: yarn wsrun test:circleci @0x/contracts-tec
|
||||
- run: yarn wsrun test:circleci @0x/contracts-coordinator
|
||||
test-contracts-geth:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
@@ -68,11 +69,12 @@ jobs:
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-libs
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc20
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc721
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc1155
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-extensions
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-asset-proxy
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-tec
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-coordinator
|
||||
test-publish:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -83,7 +83,7 @@ packages/react-docs/example/public/bundle*
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# generated contract artifacts/
|
||||
contracts/tec/generated-artifacts/
|
||||
contracts/coordinator/generated-artifacts/
|
||||
contracts/exchange/generated-artifacts/
|
||||
contracts/asset-proxy/generated-artifacts/
|
||||
contracts/multisig/generated-artifacts/
|
||||
@@ -91,6 +91,7 @@ contracts/utils/generated-artifacts/
|
||||
contracts/exchange-libs/generated-artifacts/
|
||||
contracts/erc20/generated-artifacts/
|
||||
contracts/erc721/generated-artifacts/
|
||||
contracts/erc1155/generated-artifacts/
|
||||
contracts/extensions/generated-artifacts/
|
||||
contracts/exchange-forwarder/generated-artifacts/
|
||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||
@@ -98,7 +99,7 @@ packages/metacoin/artifacts/
|
||||
|
||||
# generated contract wrappers
|
||||
packages/abi-gen-wrappers/wrappers
|
||||
contracts/tec/generated-wrappers/
|
||||
contracts/coordinator/generated-wrappers/
|
||||
contracts/exchange/generated-wrappers/
|
||||
contracts/asset-proxy/generated-wrappers/
|
||||
contracts/multisig/generated-wrappers/
|
||||
@@ -106,6 +107,7 @@ contracts/utils/generated-wrappers/
|
||||
contracts/exchange-libs/generated-wrappers/
|
||||
contracts/erc20/generated-wrappers/
|
||||
contracts/erc721/generated-wrappers/
|
||||
contracts/erc1155/generated-wrappers/
|
||||
contracts/extensions/generated-wrappers/
|
||||
contracts/exchange-forwarder/generated-wrappers/
|
||||
packages/metacoin/src/contract_wrappers
|
||||
|
@@ -1,7 +1,7 @@
|
||||
lib
|
||||
.nyc_output
|
||||
/contracts/tec/generated-wrappers
|
||||
/contracts/tec/generated-artifacts
|
||||
/contracts/coordinator/generated-wrappers
|
||||
/contracts/coordinator/generated-artifacts
|
||||
/contracts/exchange/generated-wrappers
|
||||
/contracts/exchange/generated-artifacts
|
||||
/contracts/asset-proxy/generated-wrappers
|
||||
@@ -16,6 +16,8 @@ lib
|
||||
/contracts/erc20/generated-artifacts
|
||||
/contracts/erc721/generated-wrappers
|
||||
/contracts/erc721/generated-artifacts
|
||||
/contracts/erc1155/generated-wrappers
|
||||
/contracts/erc1155/generated-artifacts
|
||||
/contracts/extensions/generated-wrappers
|
||||
/contracts/extensions/generated-artifacts
|
||||
/contracts/exchange-forwarder/generated-wrappers
|
||||
|
@@ -33,6 +33,8 @@ packages/subproviders/ @fabioberger @dekz
|
||||
packages/verdaccio/ @albrow
|
||||
packages/web3-wrapper/ @LogvinovLeon @fabioberger
|
||||
python-packages/ @feuGeneA
|
||||
packages/utils/ @hysz
|
||||
|
||||
# Protocol/smart contracts
|
||||
contracts/core/test/ @albrow
|
||||
contracts/ @abandeali1 @hysz
|
||||
|
@@ -34,11 +34,14 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
||||
|
||||
### Solidity Packages
|
||||
|
||||
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
|
||||
|
||||
| Package | Version | Description |
|
||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
|
||||
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
||||
| [`@0x/contracts-erc721`](/contracts/erc721) | [](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
|
||||
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
|
||||
| [`@0x/contracts-exchange`](/contracts/exchange) | [](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
|
||||
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
|
||||
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
|
||||
@@ -46,7 +49,7 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
||||
| [`@0x/contracts-multisig`](/contracts/multisig) | [](https://www.npmjs.com/package/@0x/contracts-multisig) | Various implementations of multisignature wallets, including the [`AssetProxyOwner`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxyowner) contract that has permissions to upgrade the protocol |
|
||||
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | Typescript/Javascript shared utilities used for testing contracts |
|
||||
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
||||
| [`@0x/contracts-tec`](/contracts/tec) | [](https://www.npmjs.com/package/@0x/contracts-tec) | A contract that allows users to execute 0x transactions with permission from a TEC (Trade Execution Coordinator) |
|
||||
| [`@0x/contracts-coordinator`](/contracts/coordinator) | [](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator |
|
||||
|
||||
### Typescript/Javascript Packages
|
||||
|
||||
|
12
contracts/README.md
Normal file
12
contracts/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
#### Deployed Contract Packages
|
||||
|
||||
| Contract | Package | Version | Git Tag |
|
||||
| --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2) | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2) |
|
||||
| ERC20Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| ERC721Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| Exchange | [`@0x/contracts-exchange`](/contracts/exchange) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1) | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1) |
|
||||
| DutchAuction | [`@0x/contracts-extensions`](/contracts/extensions) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2) | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2) |
|
||||
| Forwarder | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) |
|
||||
| MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| ZRXToken | [`@0x/contracts-erc20`](/contracts/erc20) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1) | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1) |
|
@@ -1,4 +1,44 @@
|
||||
[
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Do not reexport external dependencies",
|
||||
"pr": 1682
|
||||
},
|
||||
{
|
||||
"note": "Add ERC1155Proxy",
|
||||
"pr": 1661
|
||||
},
|
||||
{
|
||||
"note": "Bumped solidity version to ^0.5.5",
|
||||
"pr": 1701
|
||||
},
|
||||
{
|
||||
"note": "Integration testing for ERC1155Proxy",
|
||||
"pr": 1673
|
||||
}
|
||||
],
|
||||
"timestamp": 1553091633
|
||||
},
|
||||
{
|
||||
"timestamp": 1551479279,
|
||||
"version": "1.0.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1551299797,
|
||||
"version": "1.0.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1551220833,
|
||||
"version": "1.0.7",
|
||||
|
@@ -5,6 +5,21 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.0 - _March 20, 2019_
|
||||
|
||||
* Do not reexport external dependencies (#1682)
|
||||
* Add ERC1155Proxy (#1661)
|
||||
* Bumped solidity version to ^0.5.5 (#1701)
|
||||
* Integration testing for ERC1155Proxy (#1673)
|
||||
|
||||
## v1.0.9 - _March 1, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.8 - _February 27, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.7 - _February 26, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -4,7 +4,12 @@
|
||||
"useDockerisedSolc": true,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
@@ -18,11 +23,7 @@
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"@0x/contracts-erc20/contracts/test/DummyERC20Token.sol",
|
||||
"@0x/contracts-erc20/contracts/test/DummyMultipleReturnERC20Token.sol",
|
||||
"@0x/contracts-erc20/contracts/test/DummyNoReturnERC20Token.sol",
|
||||
"@0x/contracts-erc721/contracts/test/DummyERC721Receiver.sol",
|
||||
"@0x/contracts-erc721/contracts/test/DummyERC721Token.sol",
|
||||
"src/ERC1155Proxy.sol",
|
||||
"src/ERC20Proxy.sol",
|
||||
"src/ERC721Proxy.sol",
|
||||
"src/MixinAuthorizable.sol",
|
||||
|
267
contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
Normal file
267
contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract ERC1155Proxy is
|
||||
MixinAuthorizable
|
||||
{
|
||||
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Token(address,uint256[],uint256[],bytes)"));
|
||||
|
||||
function ()
|
||||
external
|
||||
{
|
||||
// Input calldata to this function is encoded as follows:
|
||||
// -- TABLE #1 --
|
||||
// | Area | Offset (**) | Length | Contents |
|
||||
// |----------|-------------|-------------|---------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | (see below) | assetData Contents |
|
||||
//
|
||||
//
|
||||
// Asset data is encoded as follows:
|
||||
// -- TABLE #2 --
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|-------------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | assetProxyId |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. address of ERC1155 contract |
|
||||
// | | 36 | | 2. offset to ids (*) |
|
||||
// | | 68 | | 3. offset to values (*) |
|
||||
// | | 100 | | 4. offset to data (*) |
|
||||
// | Data | | | ids: |
|
||||
// | | 132 | 32 | 1. ids Length |
|
||||
// | | 164 | a | 2. ids Contents |
|
||||
// | | | | values: |
|
||||
// | | 164 + a | 32 | 1. values Length |
|
||||
// | | 196 + a | b | 2. values Contents |
|
||||
// | | | | data |
|
||||
// | | 196 + a + b | 32 | 1. data Length |
|
||||
// | | 228 + a + b | c | 2. data Contents |
|
||||
//
|
||||
//
|
||||
// Calldata for target ERC155 asset is encoded for safeBatchTransferFrom:
|
||||
// -- TABLE #3 --
|
||||
// | Area | Offset (**) | Length | Contents |
|
||||
// |----------|-------------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | safeBatchTransferFrom selector |
|
||||
// | Params | | 5 * 32 | function parameters: |
|
||||
// | | 4 | | 1. from address |
|
||||
// | | 36 | | 2. to address |
|
||||
// | | 68 | | 3. offset to ids (*) |
|
||||
// | | 100 | | 4. offset to values (*) |
|
||||
// | | 132 | | 5. offset to data (*) |
|
||||
// | Data | | | ids: |
|
||||
// | | 164 | 32 | 1. ids Length |
|
||||
// | | 196 | a | 2. ids Contents |
|
||||
// | | | | values: |
|
||||
// | | 196 + a | 32 | 1. values Length |
|
||||
// | | 228 + a | b | 2. values Contents |
|
||||
// | | | | data |
|
||||
// | | 228 + a + b | 32 | 1. data Length |
|
||||
// | | 260 + a + b | c | 2. data Contents |
|
||||
//
|
||||
//
|
||||
// (*): offset is computed from start of function parameters, so offset
|
||||
// by an additional 4 bytes in the calldata.
|
||||
//
|
||||
// (**): the `Offset` column is computed assuming no calldata compression;
|
||||
// offsets in the Data Area are dynamic and should be evaluated in
|
||||
// real-time.
|
||||
//
|
||||
// WARNING: The ABIv2 specification allows additional padding between
|
||||
// the Params and Data section. This will result in a larger
|
||||
// offset to assetData.
|
||||
//
|
||||
// Note: Table #1 and Table #2 exists in Calldata. We construct Table #3 in memory.
|
||||
//
|
||||
//
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// `transferFrom` will be called with the following parameters:
|
||||
// assetData Encoded byte array.
|
||||
// from Address to transfer asset from.
|
||||
// to Address to transfer asset to.
|
||||
// amount Amount of asset to transfer.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
||||
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
||||
mstore(0, caller)
|
||||
mstore(32, authorized_slot)
|
||||
|
||||
// Revert if authorized[msg.sender] == false
|
||||
if iszero(sload(keccak256(0, 64))) {
|
||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Construct Table #3 in memory, starting at memory offset 0.
|
||||
// The algorithm below maps asset data from Table #1 and Table #2 to Table #3, while
|
||||
// scaling the `values` (Table #2) by `amount` (Table #1). Once Table #3 has
|
||||
// been constructed in memory, the destination erc1155 contract is called using this
|
||||
// as its calldata. This process is divided into four steps, below.
|
||||
|
||||
////////// STEP 1/4 //////////
|
||||
// Map relevant fields from assetData (Table #2) into memory (Table #3)
|
||||
// The Contents column of Table #2 is the same as Table #3,
|
||||
// beginning from parameter 3 - `offset to ids (*)`
|
||||
// The offsets in these rows are offset by 32 bytes in Table #3.
|
||||
// Strategy:
|
||||
// 1. Copy the assetData into memory at offset 32
|
||||
// 2. Increment by 32 the offsets to `ids`, `values`, and `data`
|
||||
|
||||
// Load offset to `assetData`
|
||||
let assetDataOffset := calldataload(4)
|
||||
|
||||
// Load length in bytes of `assetData`, computed by:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
let assetDataLength := calldataload(add(4, assetDataOffset))
|
||||
|
||||
// This corresponds to the beginning of the Data Area for Table #3.
|
||||
// Computed by:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
// + 32 (length of assetData)
|
||||
calldatacopy(
|
||||
32, // aligned such that "offset to ids" is at the correct location for Table #3
|
||||
add(36, assetDataOffset), // beginning of asset data contents
|
||||
assetDataLength // length of asset data
|
||||
)
|
||||
|
||||
// Increment by 32 the offsets to `ids`, `values`, and `data`
|
||||
mstore(68, add(mload(68), 32))
|
||||
mstore(100, add(mload(100), 32))
|
||||
mstore(132, add(mload(132), 32))
|
||||
|
||||
// Record the address of the destination erc1155 asset for later.
|
||||
let assetAddress := and(
|
||||
mload(36),
|
||||
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
|
||||
)
|
||||
|
||||
////////// STEP 2/4 //////////
|
||||
let amount := calldataload(100)
|
||||
let valuesOffset := add(mload(100), 4) // add 4 for calldata offset
|
||||
let valuesLengthInBytes := mul(
|
||||
mload(valuesOffset),
|
||||
32
|
||||
)
|
||||
let valuesBegin := add(valuesOffset, 32)
|
||||
let valuesEnd := add(valuesBegin, valuesLengthInBytes)
|
||||
for { let tokenValueOffset := valuesBegin }
|
||||
lt(tokenValueOffset, valuesEnd)
|
||||
{ tokenValueOffset := add(tokenValueOffset, 32) }
|
||||
{
|
||||
// Load token value and generate scaled value
|
||||
let tokenValue := mload(tokenValueOffset)
|
||||
let scaledTokenValue := mul(tokenValue, amount)
|
||||
|
||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
||||
if iszero(or(
|
||||
iszero(amount),
|
||||
eq(div(scaledTokenValue, amount), tokenValue)
|
||||
)) {
|
||||
// Revert with `Error("UINT256_OVERFLOW")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// There was no overflow, update `tokenValue` with its scaled counterpart
|
||||
mstore(tokenValueOffset, scaledTokenValue)
|
||||
}
|
||||
|
||||
////////// STEP 3/4 //////////
|
||||
// Store the safeBatchTransferFrom function selector,
|
||||
// and copy `from`/`to` fields from Table #1 to Table #3.
|
||||
|
||||
// The function selector is computed using:
|
||||
// bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"))
|
||||
mstore(0, 0x2eb2c2d600000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// Copy `from` and `to` fields from Table #1 to Table #3
|
||||
calldatacopy(
|
||||
4, // aligned such that `from` and `to` are at the correct location for Table #3
|
||||
36, // beginning of `from` field from Table #1
|
||||
64 // 32 bytes for `from` + 32 bytes for `to` field
|
||||
)
|
||||
|
||||
////////// STEP 4/4 //////////
|
||||
// Call into the destination erc1155 contract using as calldata Table #3 (constructed in-memory above)
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
assetAddress, // call address of erc1155 asset
|
||||
0, // don't send any ETH
|
||||
0, // pointer to start of input
|
||||
add(assetDataLength, 32), // length of input (Table #3) is 32 bytes longer than `assetData` (Table #2)
|
||||
0, // write output over memory that won't be reused
|
||||
0 // don't copy output to memory
|
||||
)
|
||||
|
||||
// Revert with reason given by AssetProxy if `transferFrom` call failed
|
||||
if iszero(success) {
|
||||
returndatacopy(
|
||||
0, // copy to memory at 0
|
||||
0, // copy from return data at 0
|
||||
returndatasize() // copy all return data
|
||||
)
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
|
||||
// Return if call was successful
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAssetProxyDispatcher.sol";
|
||||
@@ -28,7 +28,7 @@ contract MixinAssetProxyDispatcher is
|
||||
MAssetProxyDispatcher
|
||||
{
|
||||
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
||||
mapping (bytes4 => IAssetProxy) public assetProxies;
|
||||
mapping (bytes4 => address) public assetProxies;
|
||||
|
||||
/// @dev Registers an asset proxy to its asset proxy id.
|
||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
||||
@@ -37,10 +37,8 @@ contract MixinAssetProxyDispatcher is
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
IAssetProxy assetProxyContract = IAssetProxy(assetProxy);
|
||||
|
||||
// Ensure that no asset proxy exists with current id.
|
||||
bytes4 assetProxyId = assetProxyContract.getProxyId();
|
||||
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
|
||||
address currentAssetProxy = assetProxies[assetProxyId];
|
||||
require(
|
||||
currentAssetProxy == address(0),
|
||||
@@ -48,7 +46,7 @@ contract MixinAssetProxyDispatcher is
|
||||
);
|
||||
|
||||
// Add asset proxy and log registration.
|
||||
assetProxies[assetProxyId] = assetProxyContract;
|
||||
assetProxies[assetProxyId] = assetProxy;
|
||||
emit AssetProxyRegistered(
|
||||
assetProxyId,
|
||||
assetProxy
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAuthorizable.sol";
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAssetProxyDispatcher.sol";
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
// solhint-disable
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ interface IAssetData {
|
||||
external;
|
||||
|
||||
function MultiAsset(
|
||||
uint256[] amounts,
|
||||
bytes[] nestedAssetData
|
||||
uint256[] calldata amounts,
|
||||
bytes[] calldata nestedAssetData
|
||||
)
|
||||
external;
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./IAuthorizable.sol";
|
||||
|
||||
@@ -30,7 +30,7 @@ contract IAssetProxy is
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
function transferFrom(
|
||||
bytes assetData,
|
||||
bytes calldata assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract IAssetProxyDispatcher {
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/IAssetProxyDispatcher.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/IAuthorizable.sol";
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "1.0.7",
|
||||
"version": "2.0.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
|
||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -46,11 +46,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^2.0.5",
|
||||
"@0x/contracts-gen": "^1.0.4",
|
||||
"@0x/contracts-test-utils": "^3.0.6",
|
||||
"@0x/dev-utils": "^2.1.2",
|
||||
"@0x/sol-compiler": "^3.1.1",
|
||||
"@0x/abi-gen": "^2.0.7",
|
||||
"@0x/contracts-gen": "^1.0.6",
|
||||
"@0x/contracts-test-utils": "^3.1.0",
|
||||
"@0x/dev-utils": "^2.1.4",
|
||||
"@0x/sol-compiler": "^3.1.4",
|
||||
"@0x/tslint-config": "^3.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
@@ -67,16 +67,17 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.0.1",
|
||||
"@0x/contracts-erc20": "^1.0.7",
|
||||
"@0x/contracts-erc721": "^1.0.7",
|
||||
"@0x/contracts-utils": "2.0.1",
|
||||
"@0x/order-utils": "^7.0.1",
|
||||
"@0x/types": "^2.1.1",
|
||||
"@0x/typescript-typings": "^4.1.0",
|
||||
"@0x/utils": "^4.2.1",
|
||||
"@0x/web3-wrapper": "^6.0.1",
|
||||
"ethereum-types": "^2.1.0",
|
||||
"@0x/base-contract": "^5.0.3",
|
||||
"@0x/contracts-erc1155": "^1.0.1",
|
||||
"@0x/contracts-erc20": "^2.0.0",
|
||||
"@0x/contracts-erc721": "^2.0.0",
|
||||
"@0x/contracts-utils": "^3.0.0",
|
||||
"@0x/order-utils": "^7.1.0",
|
||||
"@0x/types": "^2.2.0",
|
||||
"@0x/typescript-typings": "^4.2.0",
|
||||
"@0x/utils": "^4.2.3",
|
||||
"@0x/web3-wrapper": "^6.0.3",
|
||||
"ethereum-types": "^2.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -5,11 +5,7 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyERC20Token from '../generated-artifacts/DummyERC20Token.json';
|
||||
import * as DummyERC721Receiver from '../generated-artifacts/DummyERC721Receiver.json';
|
||||
import * as DummyERC721Token from '../generated-artifacts/DummyERC721Token.json';
|
||||
import * as DummyMultipleReturnERC20Token from '../generated-artifacts/DummyMultipleReturnERC20Token.json';
|
||||
import * as DummyNoReturnERC20Token from '../generated-artifacts/DummyNoReturnERC20Token.json';
|
||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||
@@ -18,13 +14,9 @@ import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
export const artifacts = {
|
||||
DummyERC20Token: DummyERC20Token as ContractArtifact,
|
||||
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
|
||||
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
|
||||
DummyERC721Receiver: DummyERC721Receiver as ContractArtifact,
|
||||
DummyERC721Token: DummyERC721Token as ContractArtifact,
|
||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
|
@@ -3,11 +3,7 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/dummy_erc20_token';
|
||||
export * from '../generated-wrappers/dummy_erc721_receiver';
|
||||
export * from '../generated-wrappers/dummy_erc721_token';
|
||||
export * from '../generated-wrappers/dummy_multiple_return_erc20_token';
|
||||
export * from '../generated-wrappers/dummy_no_return_erc20_token';
|
||||
export * from '../generated-wrappers/erc1155_proxy';
|
||||
export * from '../generated-wrappers/erc20_proxy';
|
||||
export * from '../generated-wrappers/erc721_proxy';
|
||||
export * from '../generated-wrappers/i_asset_data';
|
||||
|
834
contracts/asset-proxy/test/erc1155_proxy.ts
Normal file
834
contracts/asset-proxy/test/erc1155_proxy.ts
Normal file
@@ -0,0 +1,834 @@
|
||||
import {
|
||||
artifacts as erc1155Artifacts,
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||
DummyERC1155ReceiverContract,
|
||||
ERC1155MintableContract,
|
||||
Erc1155Wrapper,
|
||||
} from '@0x/contracts-erc1155';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
expectTransactionFailedWithoutReasonAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ERC1155ProxyWrapper, ERC721ProxyContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('ERC1155Proxy', () => {
|
||||
// constant values used in transfer tests
|
||||
const nftOwnerBalance = new BigNumber(1);
|
||||
const nftNotOwnerBalance = new BigNumber(0);
|
||||
const spenderInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
const receiverInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
const receiverContractInitialFungibleBalance = new BigNumber(0);
|
||||
const fungibleValueToTransferSmall = spenderInitialFungibleBalance.div(100);
|
||||
const fungibleValueToTransferLarge = spenderInitialFungibleBalance.div(4);
|
||||
const valueMultiplierSmall = new BigNumber(2);
|
||||
const valueMultiplierNft = new BigNumber(1);
|
||||
const nonFungibleValueToTransfer = nftOwnerBalance;
|
||||
const receiverCallbackData = '0x01020304';
|
||||
// addresses
|
||||
let owner: string;
|
||||
let notAuthorized: string;
|
||||
let authorized: string;
|
||||
let spender: string;
|
||||
let receiver: string;
|
||||
let receiverContract: string;
|
||||
// contracts & wrappers
|
||||
let erc1155Proxy: ERC721ProxyContract;
|
||||
let erc1155Receiver: DummyERC1155ReceiverContract;
|
||||
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
||||
let erc1155Contract: ERC1155MintableContract;
|
||||
let erc1155Wrapper: Erc1155Wrapper;
|
||||
// tokens
|
||||
let fungibleTokens: BigNumber[];
|
||||
let nonFungibleTokensOwnedBySpender: BigNumber[];
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
/// deploy & configure ERC1155Proxy
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(erc1155Proxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// deploy & configure ERC1155 tokens and receiver
|
||||
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||
erc1155Contract = erc1155Wrapper.getContract();
|
||||
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.DummyERC1155Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
receiverContract = erc1155Receiver.address;
|
||||
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
|
||||
fungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
|
||||
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
|
||||
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
|
||||
nonFungibleTokensOwnedBySpender = [];
|
||||
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
|
||||
const nonFungibleTokenAsString = nonFungibleToken.toString();
|
||||
const nonFungibleTokenHeldBySpender =
|
||||
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||
});
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('general', () => {
|
||||
it('should revert if undefined function is called', async () => {
|
||||
const undefinedSelector = '0x01020304';
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
web3Wrapper.sendTransactionAsync({
|
||||
from: owner,
|
||||
to: erc1155Proxy.address,
|
||||
value: constants.ZERO_AMOUNT,
|
||||
data: undefinedSelector,
|
||||
}),
|
||||
);
|
||||
});
|
||||
it('should have an id of 0x9645780d', async () => {
|
||||
const proxyId = await erc1155Proxy.getProxyId.callAsync();
|
||||
// proxy computed using -- bytes4(keccak256("erc1155Token(address,uint256[],uint256[],bytes)"));
|
||||
const expectedProxyId = '0x9645780d';
|
||||
expect(proxyId).to.equal(expectedProxyId);
|
||||
});
|
||||
});
|
||||
describe('transferFrom', () => {
|
||||
it('should successfully transfer value for a single, fungible token', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier);
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(totalValueTransferred),
|
||||
receiverInitialFungibleBalance.plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value for the same fungible token several times', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleTokens[0];
|
||||
const tokensToTransfer = [tokenToTransfer, tokenToTransfer, tokenToTransfer];
|
||||
const valuesToTransfer = [
|
||||
fungibleValueToTransferSmall.plus(10),
|
||||
fungibleValueToTransferSmall.plus(20),
|
||||
fungibleValueToTransferSmall.plus(30),
|
||||
];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
let totalValueTransferred = _.reduce(valuesToTransfer, (sum: BigNumber, value: BigNumber) => {
|
||||
return sum.plus(value);
|
||||
}) as BigNumber;
|
||||
totalValueTransferred = totalValueTransferred.times(valueMultiplier);
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(totalValueTransferred),
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value for several fungible tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 3);
|
||||
const valuesToTransfer = [
|
||||
fungibleValueToTransferSmall.plus(10),
|
||||
fungibleValueToTransferSmall.plus(20),
|
||||
fungibleValueToTransferSmall.plus(30),
|
||||
];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(totalValuesTransferred[0]),
|
||||
spenderInitialFungibleBalance.minus(totalValuesTransferred[1]),
|
||||
spenderInitialFungibleBalance.minus(totalValuesTransferred[2]),
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(totalValuesTransferred[0]),
|
||||
receiverInitialFungibleBalance.plus(totalValuesTransferred[1]),
|
||||
receiverInitialFungibleBalance.plus(totalValuesTransferred[2]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer a non-fungible token', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple non-fungible tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
|
||||
const valuesToTransfer = [
|
||||
nonFungibleValueToTransfer,
|
||||
nonFungibleValueToTransfer,
|
||||
nonFungibleValueToTransfer,
|
||||
];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value for a combination of several fungible/non-fungible tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const fungibleTokensToTransfer = fungibleTokens.slice(0, 3);
|
||||
const nonFungibleTokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 2);
|
||||
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
|
||||
const valuesToTransfer = [
|
||||
fungibleValueToTransferLarge,
|
||||
fungibleValueToTransferSmall,
|
||||
fungibleValueToTransferSmall,
|
||||
nonFungibleValueToTransfer,
|
||||
nonFungibleValueToTransfer,
|
||||
];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
|
||||
expectedInitialBalances[3].minus(totalValuesTransferred[3]),
|
||||
expectedInitialBalances[4].minus(totalValuesTransferred[4]),
|
||||
// receiver
|
||||
expectedInitialBalances[5].plus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[6].plus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[7].plus(totalValuesTransferred[2]),
|
||||
expectedInitialBalances[8].plus(totalValuesTransferred[3]),
|
||||
expectedInitialBalances[9].plus(totalValuesTransferred[4]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value to a smart contract and trigger its callback', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check receiver log ignored extra asset data
|
||||
expect(txReceipt.logs.length).to.be.equal(2);
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs
|
||||
>;
|
||||
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
|
||||
expect(receiverLog.args.from).to.be.equal(spender);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
|
||||
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
|
||||
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is NULL', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const nullReceiverCallbackData = '0x';
|
||||
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
nullReceiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check receiver log ignored extra asset data
|
||||
expect(txReceipt.logs.length).to.be.equal(2);
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs
|
||||
>;
|
||||
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
|
||||
expect(receiverLog.args.from).to.be.equal(spender);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
|
||||
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
|
||||
expect(receiverLog.args.data).to.be.deep.equal(nullReceiverCallbackData);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value and ignore extra assetData', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const extraData = '0102030405060708';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
extraData,
|
||||
);
|
||||
// check receiver log ignored extra asset data
|
||||
expect(txReceipt.logs.length).to.be.equal(2);
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs
|
||||
>;
|
||||
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
|
||||
expect(receiverLog.args.from).to.be.equal(spender);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
|
||||
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
|
||||
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer nothing if value is zero', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(0)];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer nothing if value multiplier is zero', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = new BigNumber(0);
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer nothing if there are no tokens in asset data', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer: BigNumber[] = [];
|
||||
const valuesToTransfer: BigNumber[] = [];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should propagate revert reason from erc1155 contract failure', async () => {
|
||||
// disable transfers
|
||||
const shouldRejectTransfer = true;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.TransferRejected,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring the same non-fungible token more than once', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const nftToTransfer = nonFungibleTokensOwnedBySpender[0];
|
||||
const tokensToTransfer = [nftToTransfer, nftToTransfer];
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer, nonFungibleValueToTransfer];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.NFTNotOwnedByFromAddress,
|
||||
);
|
||||
});
|
||||
it('should revert if there is a multiplication overflow', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
|
||||
const maxUintValue = new BigNumber(2).pow(256).minus(1);
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer, maxUintValue, nonFungibleValueToTransfer];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.Uint256Overflow,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.AmountEqualToOneRequired,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring > 1 instances of a non-fungible token (`valuesToTransfer` field >1)', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(2)];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.AmountEqualToOneRequired,
|
||||
);
|
||||
});
|
||||
it('should revert if sender balance is insufficient', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valueGreaterThanSpenderBalance = spenderInitialFungibleBalance.plus(1);
|
||||
const valuesToTransfer = [valueGreaterThanSpenderBalance];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.Uint256Underflow,
|
||||
);
|
||||
});
|
||||
it('should revert if sender allowance is insufficient', async () => {
|
||||
// dremove allowance for ERC1155 proxy
|
||||
const wrapper = erc1155ProxyWrapper.getContractWrapper(erc1155Contract.address);
|
||||
const isApproved = false;
|
||||
await wrapper.setApprovalForAllAsync(spender, erc1155Proxy.address, isApproved);
|
||||
const isApprovedActualValue = await wrapper.isApprovedForAllAsync(spender, erc1155Proxy.address);
|
||||
expect(isApprovedActualValue).to.be.equal(isApproved);
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.InsufficientAllowance,
|
||||
);
|
||||
});
|
||||
it('should revert if caller is not authorized', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
notAuthorized,
|
||||
),
|
||||
RevertReason.SenderNotAuthorized,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:enable:no-unnecessary-type-assertion
|
||||
// tslint:disable:max-file-line-count
|
@@ -1,3 +1,16 @@
|
||||
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
artifacts as erc20Artifacts,
|
||||
DummyERC20TokenContract,
|
||||
DummyERC20TokenTransferEventArgs,
|
||||
DummyMultipleReturnERC20TokenContract,
|
||||
DummyNoReturnERC20TokenContract,
|
||||
} from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as erc721Artifacts,
|
||||
DummyERC721ReceiverContract,
|
||||
DummyERC721TokenContract,
|
||||
} from '@0x/contracts-erc721';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
@@ -18,12 +31,7 @@ import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
DummyERC20TokenContract,
|
||||
DummyERC20TokenTransferEventArgs,
|
||||
DummyERC721ReceiverContract,
|
||||
DummyERC721TokenContract,
|
||||
DummyMultipleReturnERC20TokenContract,
|
||||
DummyNoReturnERC20TokenContract,
|
||||
ERC1155ProxyWrapper,
|
||||
ERC20ProxyContract,
|
||||
ERC20Wrapper,
|
||||
ERC721ProxyContract,
|
||||
@@ -71,6 +79,15 @@ describe('Asset Transfer Proxies', () => {
|
||||
let erc721AFromTokenId: BigNumber;
|
||||
let erc721BFromTokenId: BigNumber;
|
||||
|
||||
let erc1155Proxy: ERC721ProxyContract;
|
||||
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
||||
let erc1155Contract: ERC1155MintableContract;
|
||||
let erc1155Contract2: ERC1155MintableContract;
|
||||
let erc1155Wrapper: Erc1155Wrapper;
|
||||
let erc1155Wrapper2: Erc1155Wrapper;
|
||||
let erc1155FungibleTokens: BigNumber[];
|
||||
let erc1155NonFungibleTokensOwnedBySpender: BigNumber[];
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
@@ -121,6 +138,22 @@ describe('Asset Transfer Proxies', () => {
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Configure ERC115Proxy
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Configure MultiAssetProxy
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
||||
@@ -140,6 +173,12 @@ describe('Asset Transfer Proxies', () => {
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc1155Proxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Deploy and configure ERC20 tokens
|
||||
const numDummyErc20ToDeploy = 2;
|
||||
@@ -148,7 +187,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
constants.DUMMY_TOKEN_DECIMALS,
|
||||
);
|
||||
noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyNoReturnERC20Token,
|
||||
erc20Artifacts.DummyNoReturnERC20Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
@@ -157,7 +196,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||
);
|
||||
multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyMultipleReturnERC20Token,
|
||||
erc20Artifacts.DummyMultipleReturnERC20Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
@@ -198,7 +237,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
// Deploy and configure ERC721 tokens and receiver
|
||||
[erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
|
||||
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Receiver,
|
||||
erc721Artifacts.DummyERC721Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
@@ -207,6 +246,22 @@ describe('Asset Transfer Proxies', () => {
|
||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||
erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0];
|
||||
erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0];
|
||||
|
||||
// Deploy & configure ERC1155 tokens and receiver
|
||||
[erc1155Wrapper, erc1155Wrapper2] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||
erc1155Contract = erc1155Wrapper.getContract();
|
||||
erc1155Contract2 = erc1155Wrapper2.getContract();
|
||||
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
|
||||
erc1155FungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
|
||||
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
|
||||
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
|
||||
erc1155NonFungibleTokensOwnedBySpender = [];
|
||||
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
|
||||
const nonFungibleTokenAsString = nonFungibleToken.toString();
|
||||
const nonFungibleTokenHeldBySpender =
|
||||
tokenBalances.nonFungible[fromAddress][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||
erc1155NonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||
});
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -562,7 +617,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
erc721Receiver.address,
|
||||
amount,
|
||||
);
|
||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
||||
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc721Artifacts });
|
||||
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: erc721Proxy.address,
|
||||
@@ -754,7 +809,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
inputAmount,
|
||||
);
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
||||
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc20Artifacts });
|
||||
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
@@ -938,6 +993,314 @@ describe('Asset Transfer Proxies', () => {
|
||||
expect(newOwnerFromAsset1).to.be.equal(toAddress);
|
||||
expect(newOwnerFromAsset2).to.be.equal(toAddress);
|
||||
});
|
||||
it('should transfer a fungible ERC1155 token', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(25)];
|
||||
const valueMultiplier = new BigNumber(23);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValueTransferred),
|
||||
// to
|
||||
expectedInitialBalances[1].plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple fungible tokens of the same ERC1155 contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const tokensToTransfer = erc1155FungibleTokens.slice(0, 3);
|
||||
const valuesToTransfer = [new BigNumber(25), new BigNumber(35), new BigNumber(45)];
|
||||
const valueMultiplier = new BigNumber(23);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier).times(multiAssetAmount);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
|
||||
// to
|
||||
expectedInitialBalances[3].plus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[4].plus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[5].plus(totalValuesTransferred[2]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple fungible/non-fungible tokens of the same ERC1155 contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const fungibleTokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const nonFungibleTokensToTransfer = erc1155NonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
|
||||
const valuesToTransfer = [new BigNumber(25), new BigNumber(1)];
|
||||
const valueMultiplier = new BigNumber(1);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const nftOwnerBalance = new BigNumber(1);
|
||||
const nftNotOwnerBalance = new BigNumber(0);
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
nftOwnerBalance,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(1);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier).times(multiAssetAmount);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||
// to
|
||||
expectedInitialBalances[2].plus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[3].plus(totalValuesTransferred[1]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple different ERC1155 tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(25)];
|
||||
const valueMultiplier = new BigNumber(23);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData1 = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const erc1155AssetData2 = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract2.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier, valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData1, erc1155AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValueTransferred),
|
||||
// to
|
||||
expectedInitialBalances[1].plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer a combination of ERC20, ERC721, and ERC1155 tokens', async () => {
|
||||
// setup test parameters
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc1155TokenHolders = [fromAddress, toAddress];
|
||||
const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const erc1155ValuesToTransfer = [new BigNumber(25)];
|
||||
const erc1155Amount = new BigNumber(23);
|
||||
const erc1155ReceiverCallbackData = '0x0102030405';
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
erc1155TokensToTransfer,
|
||||
erc1155ValuesToTransfer,
|
||||
erc1155ReceiverCallbackData,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount, erc1155Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
inputAmount,
|
||||
);
|
||||
// check balances before transfer
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
const erc1155ExpectedInitialBalances = [
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(
|
||||
erc1155TokenHolders,
|
||||
erc1155TokensToTransfer,
|
||||
erc1155ExpectedInitialBalances,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
gas: 1000000,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances after transfer
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
const totalAmount = inputAmount.times(erc20Amount);
|
||||
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
|
||||
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
|
||||
);
|
||||
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
|
||||
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
|
||||
);
|
||||
const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(newOwnerFromAsset).to.be.equal(toAddress);
|
||||
const erc1155TotalValueTransferred = erc1155ValuesToTransfer[0].times(erc1155Amount).times(inputAmount);
|
||||
const expectedFinalBalances = [
|
||||
erc1155ExpectedInitialBalances[0].minus(erc1155TotalValueTransferred),
|
||||
erc1155ExpectedInitialBalances[1].plus(erc1155TotalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(
|
||||
erc1155TokenHolders,
|
||||
erc1155TokensToTransfer,
|
||||
expectedFinalBalances,
|
||||
);
|
||||
});
|
||||
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
|
383
contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
Normal file
383
contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
Normal file
@@ -0,0 +1,383 @@
|
||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
constants,
|
||||
ERC1155FungibleHoldingsByOwner,
|
||||
ERC1155HoldingsByOwner,
|
||||
ERC1155NonFungibleHoldingsByOwner,
|
||||
LogDecoder,
|
||||
txDefaults,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
|
||||
|
||||
export class ERC1155ProxyWrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _fungibleTokenIds: string[];
|
||||
private readonly _nonFungibleTokenIds: string[];
|
||||
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: Provider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||
private _proxyContract?: ERC1155ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||
|
||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||
this._dummyTokenWrappers = [];
|
||||
this._assetProxyInterface = new IAssetProxyContract(
|
||||
artifacts.IAssetProxy.compilerOutput.abi,
|
||||
constants.NULL_ADDRESS,
|
||||
provider,
|
||||
);
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._fungibleTokenIds = [];
|
||||
this._nonFungibleTokenIds = [];
|
||||
this._nfts = [];
|
||||
}
|
||||
/**
|
||||
* @dev Deploys dummy ERC1155 contracts
|
||||
* @return An array of ERC1155 wrappers; one for each deployed contract.
|
||||
*/
|
||||
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
|
||||
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.ERC1155Mintable,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||
}
|
||||
return this._dummyTokenWrappers;
|
||||
}
|
||||
/**
|
||||
* @dev Deploys the ERC1155 proxy
|
||||
* @return Deployed ERC1155 proxy contract instance
|
||||
*/
|
||||
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
|
||||
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC1155Proxy,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
/**
|
||||
* @dev Gets the ERC1155 proxy id
|
||||
*/
|
||||
public getProxyId(): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
return this._proxyIdIfExists as string;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return tranasction hash.
|
||||
*/
|
||||
public async transferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
extraData?: string,
|
||||
): Promise<string> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
let encodedAssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
if (!_.isUndefined(extraData)) {
|
||||
encodedAssetData = `${encodedAssetData}${extraData}`;
|
||||
}
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
encodedAssetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data,
|
||||
from: authorizedSender,
|
||||
});
|
||||
return txHash;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return tranasction receipt with decoded logs.
|
||||
*/
|
||||
public async transferFromWithLogsAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
extraData?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this.transferFromAsync(
|
||||
from,
|
||||
to,
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorizedSender,
|
||||
extraData,
|
||||
),
|
||||
);
|
||||
return txReceipt;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: {},
|
||||
nonFungible: {},
|
||||
};
|
||||
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
// Set balances accordingly
|
||||
for (const dummyWrapper of this._dummyTokenWrappers) {
|
||||
const dummyAddress = dummyWrapper.getContract().address;
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
|
||||
// Create a fungible token
|
||||
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
|
||||
this._tokenOwnerAddresses,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._fungibleTokenIds.push(tokenIdAsString);
|
||||
// Mint tokens for each owner for this token
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress])) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Non-fungible tokens
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
|
||||
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._nonFungibleTokenIds.push(tokenIdAsString);
|
||||
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
|
||||
}
|
||||
this._nfts.push({ id: nftIds[i], tokenId });
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: fungibleHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return this._initialTokenIdsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateBalancesAndAllowancesSetOrThrow();
|
||||
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
|
||||
const tokenContract = dummyTokenWrapper.getContract();
|
||||
const tokenAddress = tokenContract.address;
|
||||
// Construct batch balance call
|
||||
const tokenOwners: string[] = [];
|
||||
const tokenIds: BigNumber[] = [];
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(new BigNumber(tokenId));
|
||||
}
|
||||
for (const nft of this._nfts) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(nft.id);
|
||||
}
|
||||
}
|
||||
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
|
||||
// Parse out balances into fungible / non-fungible token holdings
|
||||
let i = 0;
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// Fungible tokens
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress])) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
|
||||
}
|
||||
// Non-fungible tokens
|
||||
for (const nft of this._nfts) {
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
if (
|
||||
_.isUndefined(
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()],
|
||||
)
|
||||
) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
|
||||
}
|
||||
const isOwner = balances[i++];
|
||||
if (isOwner.isEqualTo(1)) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
|
||||
nft.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const holdingsByOwner = {
|
||||
fungible: tokenHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return holdingsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
|
||||
* @param userAddress owner of ERC1155 tokens.
|
||||
* @param contractAddress address of ERC1155 contract.
|
||||
* @return True iff the proxy is approved for all. False otherwise.
|
||||
*/
|
||||
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
||||
return didApproveAll;
|
||||
}
|
||||
public getFungibleTokenIds(): BigNumber[] {
|
||||
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return fungibleTokenIds;
|
||||
}
|
||||
public getNonFungibleTokenIds(): BigNumber[] {
|
||||
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return nonFungibleTokenIds;
|
||||
}
|
||||
public getTokenOwnerAddresses(): string[] {
|
||||
return this._tokenOwnerAddresses;
|
||||
}
|
||||
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
|
||||
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
|
||||
return wrapper.getContract().address === contractAddress;
|
||||
});
|
||||
if (_.isUndefined(tokenWrapper)) {
|
||||
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenWrapper;
|
||||
}
|
||||
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
|
||||
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
|
||||
if (_.isUndefined(tokenContractIfExists)) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenContractIfExists.getContract();
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (_.isUndefined(this._dummyTokenWrappers)) {
|
||||
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (_.isUndefined(this._proxyContract)) {
|
||||
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
||||
if (
|
||||
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
|
||||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
|
||||
) {
|
||||
throw new Error(
|
||||
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
@@ -5,7 +6,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, DummyERC20TokenContract, ERC20ProxyContract } from '../../src';
|
||||
import { artifacts, ERC20ProxyContract } from '../../src';
|
||||
|
||||
export class ERC20Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
@@ -36,7 +37,7 @@ export class ERC20Wrapper {
|
||||
for (let i = 0; i < numberToDeploy; i++) {
|
||||
this._dummyTokenContracts.push(
|
||||
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC20Token,
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
@@ -5,7 +6,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, DummyERC721TokenContract, ERC721ProxyContract } from '../../src';
|
||||
import { artifacts, ERC721ProxyContract } from '../../src';
|
||||
|
||||
export class ERC721Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
@@ -28,7 +29,7 @@ export class ERC721Wrapper {
|
||||
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
|
||||
this._dummyTokenContracts.push(
|
||||
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Token,
|
||||
erc721Artifacts.DummyERC721Token,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
|
@@ -1,2 +1,3 @@
|
||||
export * from './erc20_wrapper';
|
||||
export * from './erc721_wrapper';
|
||||
export * from './erc1155_proxy_wrapper';
|
||||
|
@@ -3,11 +3,7 @@
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DummyERC20Token.json",
|
||||
"generated-artifacts/DummyERC721Receiver.json",
|
||||
"generated-artifacts/DummyERC721Token.json",
|
||||
"generated-artifacts/DummyMultipleReturnERC20Token.json",
|
||||
"generated-artifacts/DummyNoReturnERC20Token.json",
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20Proxy.json",
|
||||
"generated-artifacts/ERC721Proxy.json",
|
||||
"generated-artifacts/IAssetData.json",
|
||||
|
23
contracts/coordinator/CHANGELOG.json
Normal file
23
contracts/coordinator/CHANGELOG.json
Normal file
@@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Created Coordinator package"
|
||||
},
|
||||
{
|
||||
"note": "Use separate EIP712 domains for transactions and approvals",
|
||||
"pr": 1705
|
||||
},
|
||||
{
|
||||
"note": "Add `SignatureType.Invalid`",
|
||||
"pr": 1705
|
||||
},
|
||||
{
|
||||
"note": "Set `evmVersion` to `constantinople`",
|
||||
"pr": 1707
|
||||
}
|
||||
],
|
||||
"timestamp": 1553091633
|
||||
}
|
||||
]
|
13
contracts/coordinator/CHANGELOG.md
Normal file
13
contracts/coordinator/CHANGELOG.md
Normal file
@@ -0,0 +1,13 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.0 - _March 20, 2019_
|
||||
|
||||
* Created Coordinator package
|
||||
* Use separate EIP712 domains for transactions and approvals (#1705)
|
||||
* Add `SignatureType.Invalid` (#1705)
|
||||
* Set `evmVersion` to `constantinople` (#1707)
|
@@ -1,13 +1,13 @@
|
||||
## Trade Execution Coordinator (TEC)
|
||||
## Coordinator
|
||||
|
||||
This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more TECs (Trade Execution Coordinators). Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more Coordinators. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-tec --save
|
||||
npm install @0x/contracts-coordinator --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
@@ -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:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-tec yarn build
|
||||
PKG=@0x/contracts-coordinator yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-tec yarn watch
|
||||
PKG=@0x/contracts-coordinator yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
@@ -3,7 +3,12 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"compilerSettings": {
|
||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
@@ -16,5 +21,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": ["src/TEC.sol", "test/TestLibs.sol", "test/TestMixins.sol"]
|
||||
"contracts": [
|
||||
"src/Coordinator.sol",
|
||||
"src/registry/CoordinatorRegistry.sol",
|
||||
"test/TestLibs.sol",
|
||||
"test/TestMixins.sol"
|
||||
]
|
||||
}
|
@@ -16,21 +16,21 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./MixinSignatureValidator.sol";
|
||||
import "./MixinTECApprovalVerifier.sol";
|
||||
import "./MixinTECCore.sol";
|
||||
import "./MixinCoordinatorApprovalVerifier.sol";
|
||||
import "./MixinCoordinatorCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TEC is
|
||||
contract Coordinator is
|
||||
LibConstants,
|
||||
MixinSignatureValidator,
|
||||
MixinTECApprovalVerifier,
|
||||
MixinTECCore
|
||||
MixinCoordinatorApprovalVerifier,
|
||||
MixinCoordinatorCore
|
||||
{
|
||||
constructor (address _exchange)
|
||||
public
|
@@ -16,26 +16,26 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "./libs/LibTECApproval.sol";
|
||||
import "./libs/LibCoordinatorApproval.sol";
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
import "./mixins/MTECApprovalVerifier.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable avoid-tx-origin
|
||||
contract MixinTECApprovalVerifier is
|
||||
contract MixinCoordinatorApprovalVerifier is
|
||||
LibExchangeSelectors,
|
||||
LibTECApproval,
|
||||
LibCoordinatorApproval,
|
||||
LibZeroExTransaction,
|
||||
MSignatureValidator,
|
||||
MTECApprovalVerifier
|
||||
MCoordinatorApprovalVerifier
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibAddressArray for address[];
|
||||
@@ -43,11 +43,13 @@ contract MixinTECApprovalVerifier is
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidTECApprovals(
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
@@ -64,6 +66,7 @@ contract MixinTECApprovalVerifier is
|
||||
assertValidTransactionOrdersApproval(
|
||||
transaction,
|
||||
orders,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
@@ -74,12 +77,14 @@ contract MixinTECApprovalVerifier is
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
@@ -87,6 +92,12 @@ contract MixinTECApprovalVerifier is
|
||||
public
|
||||
view
|
||||
{
|
||||
// Verify that Ethereum tx signer is the same as the approved txOrigin
|
||||
require(
|
||||
tx.origin == txOrigin,
|
||||
"INVALID_ORIGIN"
|
||||
);
|
||||
|
||||
// Hash 0x transaction
|
||||
bytes32 transactionHash = getTransactionHash(transaction);
|
||||
|
||||
@@ -94,10 +105,11 @@ contract MixinTECApprovalVerifier is
|
||||
address[] memory approvalSignerAddresses = new address[](0);
|
||||
|
||||
uint256 signaturesLength = approvalSignatures.length;
|
||||
for (uint256 i = 0; i < signaturesLength; i++) {
|
||||
for (uint256 i = 0; i != signaturesLength; i++) {
|
||||
// Create approval message
|
||||
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
|
||||
TECApproval memory approval = TECApproval({
|
||||
CoordinatorApproval memory approval = CoordinatorApproval({
|
||||
txOrigin: txOrigin,
|
||||
transactionHash: transactionHash,
|
||||
transactionSignature: transactionSignature,
|
||||
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
|
||||
@@ -111,39 +123,30 @@ contract MixinTECApprovalVerifier is
|
||||
);
|
||||
|
||||
// Hash approval message and recover signer address
|
||||
bytes32 approvalHash = getTECApprovalHash(approval);
|
||||
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
|
||||
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
|
||||
|
||||
|
||||
// Add approval signer to list of signers
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
|
||||
}
|
||||
|
||||
|
||||
// Ethereum transaction signer gives implicit signature of approval
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
|
||||
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i < ordersLength; i++) {
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
// Do not check approval if the order's senderAddress is null
|
||||
if (orders[i].senderAddress == address(0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ethereum transaction signer gives implicit signature of approval
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
if (approverAddress == tx.origin) {
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure feeRecipient of order has approved this 0x transaction
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||
require(
|
||||
isOrderApproved,
|
||||
"INVALID_APPROVAL_SIGNATURE"
|
||||
);
|
||||
|
||||
// The Ethereum transaction signer must be the 0x transaction signer or an approver of the 0x transaction
|
||||
require(
|
||||
transaction.signerAddress == tx.origin || approvalSignerAddresses.contains(tx.origin),
|
||||
"INVALID_SENDER"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -16,27 +16,29 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./mixins/MTECApprovalVerifier.sol";
|
||||
import "./interfaces/ITECCore.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
import "./interfaces/ICoordinatorCore.sol";
|
||||
|
||||
|
||||
contract MixinTECCore is
|
||||
contract MixinCoordinatorCore is
|
||||
LibConstants,
|
||||
MTECApprovalVerifier,
|
||||
ITECCore
|
||||
MCoordinatorApprovalVerifier,
|
||||
ICoordinatorCore
|
||||
{
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
@@ -44,8 +46,9 @@ contract MixinTECCore is
|
||||
public
|
||||
{
|
||||
// Validate that the 0x transaction has been approves by each feeRecipient
|
||||
assertValidTECApprovals(
|
||||
assertValidCoordinatorApprovals(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
@@ -59,6 +59,17 @@ contract MixinSignatureValidator is
|
||||
if (signatureType == SignatureType.Illegal) {
|
||||
revert("SIGNATURE_ILLEGAL");
|
||||
|
||||
// Always invalid signature.
|
||||
// Like Illegal, this is always implicitly available and therefore
|
||||
// offered explicitly. It can be implicitly created by providing
|
||||
// a correctly formatted but incorrect signature.
|
||||
} else if (signatureType == SignatureType.Invalid) {
|
||||
require(
|
||||
signature.length == 0,
|
||||
"LENGTH_0_REQUIRED"
|
||||
);
|
||||
revert("SIGNATURE_INVALID");
|
||||
|
||||
// Signature using EIP712
|
||||
} else if (signatureType == SignatureType.EIP712) {
|
||||
require(
|
@@ -16,23 +16,25 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ITECApprovalVerifier {
|
||||
contract ICoordinatorApprovalVerifier {
|
||||
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidTECApprovals(
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
@@ -43,12 +45,14 @@ contract ITECApprovalVerifier {
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
@@ -16,21 +16,23 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ITECCore {
|
||||
contract ICoordinatorCore {
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract ISignatureValidator {
|
@@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract ITransactions {
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/ITransactions.sol";
|
||||
|
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
|
||||
contract LibCoordinatorApproval is
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 Coordinator approval message
|
||||
// keccak256(abi.encodePacked(
|
||||
// "CoordinatorApproval(",
|
||||
// "address txOrigin,",
|
||||
// "bytes32 transactionHash,",
|
||||
// "bytes transactionSignature,",
|
||||
// "uint256 approvalExpirationTimeSeconds",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
|
||||
|
||||
struct CoordinatorApproval {
|
||||
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
||||
bytes32 transactionHash; // EIP712 hash of the transaction, using the domain separator of this contract.
|
||||
bytes transactionSignature; // Signature of the 0x transaction.
|
||||
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the signature expires.
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
|
||||
function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = hashEIP712CoordinatorMessage(hashCoordinatorApproval(approval));
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
function hashCoordinatorApproval(CoordinatorApproval memory approval)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH;
|
||||
bytes memory transactionSignature = approval.transactionSignature;
|
||||
address txOrigin = approval.txOrigin;
|
||||
bytes32 transactionHash = approval.transactionHash;
|
||||
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH,
|
||||
// approval.transactionHash,
|
||||
// keccak256(approval.transactionSignature)
|
||||
// approval.expiration,
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of transaction signature
|
||||
let transactionSignatureHash := keccak256(add(transactionSignature, 32), mload(transactionSignature))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, schemaHash) // hash of schema
|
||||
mstore(add(memPtr, 32), txOrigin) // txOrigin
|
||||
mstore(add(memPtr, 64), transactionHash) // transactionHash
|
||||
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
|
||||
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 160)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
131
contracts/coordinator/contracts/src/libs/LibEIP712Domain.sol
Normal file
131
contracts/coordinator/contracts/src/libs/LibEIP712Domain.sol
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "./LibConstants.sol";
|
||||
|
||||
|
||||
contract LibEIP712Domain is
|
||||
LibConstants
|
||||
{
|
||||
|
||||
// EIP191 header for EIP712 prefix
|
||||
string constant internal EIP191_HEADER = "\x19\x01";
|
||||
|
||||
// EIP712 Domain Name value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
|
||||
|
||||
// EIP712 Domain Version value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_VERSION = "1.0.0";
|
||||
|
||||
// EIP712 Domain Name value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
|
||||
|
||||
// EIP712 Domain Version value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_VERSION = "2";
|
||||
|
||||
// Hash of the EIP712 Domain Separator Schema
|
||||
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
|
||||
"EIP712Domain(",
|
||||
"string name,",
|
||||
"string version,",
|
||||
"address verifyingContract",
|
||||
")"
|
||||
));
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Coordinator
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Exchange
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
|
||||
|
||||
constructor ()
|
||||
public
|
||||
{
|
||||
EIP712_COORDINATOR_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_VERSION)),
|
||||
uint256(address(this))
|
||||
));
|
||||
|
||||
EIP712_EXCHANGE_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_VERSION)),
|
||||
uint256(address(EXCHANGE))
|
||||
));
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of this contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to this EIP712 Domain.
|
||||
function hashEIP712CoordinatorMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of the Exchange contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712ExchangeMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
|
||||
/// @param eip712DomainHash Hash of the domain domain separator data.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
// Assembly for more efficient computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP191_HEADER,
|
||||
// EIP712_DOMAIN_HASH,
|
||||
// hashStruct
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
|
||||
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
|
||||
mstore(add(memPtr, 34), hashStruct) // Hash of struct
|
||||
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 66)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
@@ -25,13 +25,14 @@ contract LibZeroExTransaction is
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 0x transaction schema
|
||||
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked(
|
||||
"ZeroExTransaction(",
|
||||
"uint256 salt,",
|
||||
"address signerAddress,",
|
||||
"bytes data",
|
||||
")"
|
||||
));
|
||||
// keccak256(abi.encodePacked(
|
||||
// "ZeroExTransaction(",
|
||||
// "uint256 salt,",
|
||||
// "address signerAddress,",
|
||||
// "bytes data",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x213c6f636f3ea94e701c0adf9b2624aa45a6c694f9a292c094f9a81c24b5df4c;
|
||||
|
||||
struct ZeroExTransaction {
|
||||
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
|
||||
@@ -47,8 +48,8 @@ contract LibZeroExTransaction is
|
||||
view
|
||||
returns (bytes32 transactionHash)
|
||||
{
|
||||
// Note: this transaction hash will differ from the hash produced by the Exchange contract because it utilizes a different domain hash.
|
||||
transactionHash = hashEIP712Message(hashZeroExTransaction(transaction));
|
||||
// Hash the transaction with the domain separator of the Exchange contract.
|
||||
transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction));
|
||||
return transactionHash;
|
||||
}
|
||||
|
||||
@@ -61,8 +62,7 @@ contract LibZeroExTransaction is
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
|
||||
bytes32 dataHash = keccak256(transaction.data);
|
||||
// TODO(abandeali1): optimize by loading from memory in assembly
|
||||
bytes memory data = transaction.data;
|
||||
uint256 salt = transaction.salt;
|
||||
address signerAddress = transaction.signerAddress;
|
||||
|
||||
@@ -75,6 +75,9 @@ contract LibZeroExTransaction is
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of data
|
||||
let dataHash := keccak256(add(data, 32), mload(data))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
@@ -16,15 +16,15 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../interfaces/ITECApprovalVerifier.sol";
|
||||
import "../interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
contract MTECApprovalVerifier is
|
||||
ITECApprovalVerifier
|
||||
contract MCoordinatorApprovalVerifier is
|
||||
ICoordinatorApprovalVerifier
|
||||
{
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/ISignatureValidator.sol";
|
||||
|
||||
@@ -27,8 +27,9 @@ contract MSignatureValidator is
|
||||
// Allowed signature types.
|
||||
enum SignatureType {
|
||||
Illegal, // 0x00, default value
|
||||
EIP712, // 0x01
|
||||
EthSign, // 0x02
|
||||
NSignatureTypes // 0x03, number of signature types. Always leave at end.
|
||||
Invalid, // 0x01
|
||||
EIP712, // 0x02
|
||||
EthSign, // 0x03
|
||||
NSignatureTypes // 0x04, number of signature types. Always leave at end.
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "./MixinCoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract CoordinatorRegistry is
|
||||
MixinCoordinatorRegistryCore
|
||||
{
|
||||
constructor ()
|
||||
public
|
||||
MixinCoordinatorRegistryCore()
|
||||
{}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "./interfaces/ICoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract MixinCoordinatorRegistryCore is
|
||||
ICoordinatorRegistryCore
|
||||
{
|
||||
// mapping from `coordinatorOperator` -> `coordinatorEndpoint`
|
||||
mapping (address => string) internal coordinatorEndpoints;
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
|
||||
address coordinatorOperator = msg.sender;
|
||||
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
|
||||
emit CoordinatorEndpointSet(coordinatorOperator, coordinatorEndpoint);
|
||||
}
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint)
|
||||
{
|
||||
return coordinatorEndpoints[coordinatorOperator];
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract ICoordinatorRegistryCore
|
||||
{
|
||||
/// @dev Emitted when a Coordinator endpoint is set.
|
||||
event CoordinatorEndpointSet(
|
||||
address coordinatorOperator,
|
||||
string coordinatorEndpoint
|
||||
);
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint);
|
||||
}
|
@@ -16,32 +16,40 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "../src/libs/LibTECApproval.sol";
|
||||
import "../src/libs/LibConstants.sol";
|
||||
import "../src/libs/LibCoordinatorApproval.sol";
|
||||
import "../src/libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestLibs is
|
||||
LibTECApproval,
|
||||
LibConstants,
|
||||
LibCoordinatorApproval,
|
||||
LibZeroExTransaction
|
||||
{
|
||||
/// @dev Calculated the EIP712 hash of the TEC approval mesasage using the domain separator of this contract.
|
||||
/// @param approval TEC approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the TEC approval message with the domain separator of this contract.
|
||||
function publicGetTECApprovalHash(TECApproval memory approval)
|
||||
constructor (address _exchange)
|
||||
public
|
||||
LibConstants(_exchange)
|
||||
{}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
|
||||
function publicGetCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
public
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = getTECApprovalHash(approval);
|
||||
approvalHash = getCoordinatorApprovalHash(approval);
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of this contract.
|
||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @return EIP712 hash of the transaction with the domain separator of this contract.
|
||||
/// @return EIP712 hash of the transaction with the domain separator of the Exchange contract.
|
||||
function publicGetTransactionHash(ZeroExTransaction memory transaction)
|
||||
public
|
||||
view
|
@@ -16,15 +16,22 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "../src/libs/LibConstants.sol";
|
||||
import "../src/MixinSignatureValidator.sol";
|
||||
import "../src/MixinTECApprovalVerifier.sol";
|
||||
import "../src/MixinCoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestMixins is
|
||||
LibConstants,
|
||||
MixinSignatureValidator,
|
||||
MixinTECApprovalVerifier
|
||||
{}
|
||||
MixinCoordinatorApprovalVerifier
|
||||
{
|
||||
constructor (address _exchange)
|
||||
public
|
||||
LibConstants(_exchange)
|
||||
{}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-tec",
|
||||
"version": "0.0.2",
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(IExchange|TEC|TestLibs|TestMixins).json",
|
||||
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry|TestLibs|TestMixins).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -46,11 +46,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^2.0.5",
|
||||
"@0x/contracts-gen": "^1.0.4",
|
||||
"@0x/contracts-test-utils": "^3.0.6",
|
||||
"@0x/dev-utils": "^2.1.2",
|
||||
"@0x/sol-compiler": "^3.1.1",
|
||||
"@0x/abi-gen": "^2.0.7",
|
||||
"@0x/contracts-gen": "^1.0.6",
|
||||
"@0x/contracts-test-utils": "^3.1.0",
|
||||
"@0x/dev-utils": "^2.1.4",
|
||||
"@0x/sol-compiler": "^3.1.4",
|
||||
"@0x/tslint-config": "^3.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
@@ -67,18 +67,18 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.0.1",
|
||||
"@0x/contracts-asset-proxy": "^1.0.7",
|
||||
"@0x/contracts-erc20": "^1.0.7",
|
||||
"@0x/base-contract": "^5.0.3",
|
||||
"@0x/contracts-asset-proxy": "^2.0.0",
|
||||
"@0x/contracts-erc20": "^2.0.0",
|
||||
"@0x/contracts-exchange": "1.0.2",
|
||||
"@0x/contracts-exchange-libs": "^1.1.1",
|
||||
"@0x/contracts-utils": "^2.0.6",
|
||||
"@0x/order-utils": "^7.0.1",
|
||||
"@0x/types": "^2.1.1",
|
||||
"@0x/typescript-typings": "^4.1.0",
|
||||
"@0x/utils": "^4.2.1",
|
||||
"@0x/web3-wrapper": "^6.0.1",
|
||||
"ethereum-types": "^2.1.0",
|
||||
"@0x/contracts-exchange-libs": "^2.0.0",
|
||||
"@0x/contracts-utils": "^3.0.0",
|
||||
"@0x/order-utils": "^7.1.0",
|
||||
"@0x/types": "^2.2.0",
|
||||
"@0x/typescript-typings": "^4.2.0",
|
||||
"@0x/utils": "^4.2.3",
|
||||
"@0x/web3-wrapper": "^6.0.3",
|
||||
"ethereum-types": "^2.1.1",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
@@ -5,11 +5,13 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as TEC from '../generated-artifacts/TEC.json';
|
||||
import * as Coordinator from '../generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
|
||||
import * as TestLibs from '../generated-artifacts/TestLibs.json';
|
||||
import * as TestMixins from '../generated-artifacts/TestMixins.json';
|
||||
export const artifacts = {
|
||||
TEC: TEC as ContractArtifact,
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
TestLibs: TestLibs as ContractArtifact,
|
||||
TestMixins: TestMixins as ContractArtifact,
|
||||
};
|
@@ -3,6 +3,7 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/tec';
|
||||
export * from '../generated-wrappers/coordinator';
|
||||
export * from '../generated-wrappers/coordinator_registry';
|
||||
export * from '../generated-wrappers/test_libs';
|
||||
export * from '../generated-wrappers/test_mixins';
|
@@ -1,4 +1,5 @@
|
||||
import { DummyERC20TokenContract, ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as exchangeArtifacts,
|
||||
ExchangeCancelEventArgs,
|
||||
@@ -24,14 +25,14 @@ import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { ApprovalFactory, artifacts, constants, exchangeDataEncoder, TECContract } from '../src';
|
||||
import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('TEC tests', () => {
|
||||
describe('Coordinator tests', () => {
|
||||
let makerAddress: string;
|
||||
let owner: string;
|
||||
let takerAddress: string;
|
||||
@@ -41,7 +42,7 @@ describe('TEC tests', () => {
|
||||
let erc20TokenA: DummyERC20TokenContract;
|
||||
let erc20TokenB: DummyERC20TokenContract;
|
||||
let zrxToken: DummyERC20TokenContract;
|
||||
let tecContract: TECContract;
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
let exchange: ExchangeContract;
|
||||
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
@@ -86,8 +87,8 @@ describe('TEC tests', () => {
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
tecContract = await TECContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TEC,
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchange.address,
|
||||
@@ -97,7 +98,7 @@ describe('TEC tests', () => {
|
||||
const defaultOrderParams = {
|
||||
...devConstants.STATIC_ORDER_PARAMS,
|
||||
exchangeAddress: exchange.address,
|
||||
senderAddress: tecContract.address,
|
||||
senderAddress: coordinatorContract.address,
|
||||
makerAddress,
|
||||
feeRecipientAddress,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
|
||||
@@ -109,7 +110,7 @@ describe('TEC tests', () => {
|
||||
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
|
||||
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address);
|
||||
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address);
|
||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, tecContract.address);
|
||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -126,10 +127,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -144,7 +150,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
@@ -159,8 +165,9 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
@@ -175,7 +182,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
@@ -190,10 +197,17 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: takerAddress,
|
||||
gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS,
|
||||
}),
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: takerAddress,
|
||||
gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS,
|
||||
},
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
@@ -203,11 +217,16 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -222,10 +241,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -240,16 +264,21 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -262,10 +291,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -281,7 +315,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
@@ -297,8 +331,9 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
@@ -314,7 +349,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
@@ -331,11 +366,16 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -350,10 +390,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -368,16 +413,21 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -388,9 +438,16 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: makerAddress,
|
||||
}),
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
@@ -398,7 +455,7 @@ describe('TEC tests', () => {
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
@@ -409,9 +466,16 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: makerAddress,
|
||||
}),
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
@@ -420,7 +484,7 @@ describe('TEC tests', () => {
|
||||
orders.forEach((order, index) => {
|
||||
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
@@ -432,9 +496,16 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: makerAddress,
|
||||
}),
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
|
||||
@@ -442,8 +513,9 @@ describe('TEC tests', () => {
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(new BigNumber(1));
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
81
contracts/coordinator/test/coordinator_registry.ts
Normal file
81
contracts/coordinator/test/coordinator_registry.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||
|
||||
import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Coordinator Registry tests', () => {
|
||||
let coordinatorOperator: string;
|
||||
const coordinatorEndpoint = 'http://sometec.0x.org';
|
||||
const nilCoordinatorEndpoint = '';
|
||||
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
// setup accounts (skip owner)
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[, coordinatorOperator] = accounts;
|
||||
// deploy coordinator registry
|
||||
coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider);
|
||||
await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('core', () => {
|
||||
it('Should successfully set a Coordinator endpoint', async () => {
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
it('Should successfully unset a Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// unset Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, nilCoordinatorEndpoint);
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
|
||||
});
|
||||
it('Should emit an event when setting Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
const txReceipt = await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
coordinatorEndpoint,
|
||||
);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// validate event
|
||||
expect(txReceipt.logs.length).to.be.equal(1);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<CoordinatorRegistryCoordinatorEndpointSetEventArgs>;
|
||||
expect(log.args.coordinatorOperator).to.be.equal(coordinatorOperator);
|
||||
expect(log.args.coordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,5 +1,6 @@
|
||||
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
@@ -11,6 +12,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('Libs tests', () => {
|
||||
let testLibs: TestLibsContract;
|
||||
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -19,7 +21,12 @@ describe('Libs tests', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
testLibs = await TestLibsContract.deployFrom0xArtifactAsync(artifacts.TestLibs, provider, txDefaults);
|
||||
testLibs = await TestLibsContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestLibs,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeAddress,
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -31,12 +38,12 @@ describe('Libs tests', () => {
|
||||
describe('getTransactionHash', () => {
|
||||
it('should return the correct transaction hash', async () => {
|
||||
const tx = {
|
||||
verifyingContractAddress: testLibs.address,
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
};
|
||||
const expectedTxHash = hashUtils.getTransactionHashHex(tx);
|
||||
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
|
||||
const txHash = await testLibs.publicGetTransactionHash.callAsync(tx);
|
||||
expect(expectedTxHash).to.eq(txHash);
|
||||
});
|
||||
@@ -45,20 +52,27 @@ describe('Libs tests', () => {
|
||||
describe('getApprovalHash', () => {
|
||||
it('should return the correct approval hash', async () => {
|
||||
const signedTx = {
|
||||
verifyingContractAddress: testLibs.address,
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
signature: '0x5678',
|
||||
};
|
||||
const approvalExpirationTimeSeconds = new BigNumber(0);
|
||||
const txOrigin = constants.NULL_ADDRESS;
|
||||
const approval = {
|
||||
transactionHash: hashUtils.getTransactionHashHex(signedTx),
|
||||
txOrigin,
|
||||
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
|
||||
transactionSignature: signedTx.signature,
|
||||
approvalExpirationTimeSeconds,
|
||||
};
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(signedTx, approvalExpirationTimeSeconds);
|
||||
const approvalHash = await testLibs.publicGetTECApprovalHash.callAsync(approval);
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(
|
||||
signedTx,
|
||||
testLibs.address,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalHash = await testLibs.publicGetCoordinatorApprovalHash.callAsync(approval);
|
||||
expect(expectedApprovalHash).to.eq(approvalHash);
|
||||
});
|
||||
});
|
@@ -1,28 +1,22 @@
|
||||
import {
|
||||
addressUtils,
|
||||
chaiSetup,
|
||||
constants as devConstants,
|
||||
expectContractCallFailedAsync,
|
||||
getLatestBlockTimestampAsync,
|
||||
provider,
|
||||
TransactionFactory,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason, SignedOrder } from '@0x/types';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import {
|
||||
ApprovalFactory,
|
||||
artifacts,
|
||||
constants,
|
||||
exchangeDataEncoder,
|
||||
hashUtils,
|
||||
TECSignatureType,
|
||||
TECTransactionFactory,
|
||||
TestMixinsContract,
|
||||
} from '../src';
|
||||
import { ApprovalFactory, artifacts, constants, exchangeDataEncoder, TestMixinsContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@@ -33,10 +27,12 @@ describe('Mixins tests', () => {
|
||||
let approvalSignerAddress1: string;
|
||||
let approvalSignerAddress2: string;
|
||||
let mixins: TestMixinsContract;
|
||||
let transactionFactory: TECTransactionFactory;
|
||||
let transactionFactory: TransactionFactory;
|
||||
let approvalFactory1: ApprovalFactory;
|
||||
let approvalFactory2: ApprovalFactory;
|
||||
let defaultOrder: SignedOrder;
|
||||
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
@@ -44,7 +40,12 @@ describe('Mixins tests', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
mixins = await TestMixinsContract.deployFrom0xArtifactAsync(artifacts.TestMixins, provider, txDefaults);
|
||||
mixins = await TestMixinsContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestMixins,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeAddress,
|
||||
);
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3);
|
||||
defaultOrder = {
|
||||
@@ -67,7 +68,7 @@ describe('Mixins tests', () => {
|
||||
devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(transactionSignerAddress)];
|
||||
const approvalSignerPrivateKey1 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress1)];
|
||||
const approvalSignerPrivateKey2 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress2)];
|
||||
transactionFactory = new TECTransactionFactory(transactionSignerPrivateKey, mixins.address);
|
||||
transactionFactory = new TransactionFactory(transactionSignerPrivateKey, exchangeAddress);
|
||||
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
|
||||
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
|
||||
});
|
||||
@@ -81,41 +82,52 @@ describe('Mixins tests', () => {
|
||||
describe('getSignerAddress', () => {
|
||||
it('should return the correct address using the EthSign signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data, TECSignatureType.EthSign);
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EthSign);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should return the correct address using the EIP712 signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data, TECSignatureType.EIP712);
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EIP712);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should revert with with the Illegal signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const illegalSignatureByte = ethUtil.toBuffer(TECSignatureType.Illegal).toString('hex');
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex');
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${illegalSignatureByte}`;
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureIllegal,
|
||||
);
|
||||
});
|
||||
it('should revert with with the Invalid signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
|
||||
transaction.signature = `0x${invalidSignatureByte}`;
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureInvalid,
|
||||
);
|
||||
});
|
||||
it("should revert with with a signature type that doesn't exist", async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const invalidSignatureByte = '03';
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const invalidSignatureByte = '04';
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${invalidSignatureByte}`;
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureUnsupported,
|
||||
@@ -128,20 +140,26 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -155,20 +173,26 @@ describe('Mixins tests', () => {
|
||||
};
|
||||
const orders = [order];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -178,36 +202,50 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: approvalSignerAddress1,
|
||||
});
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -217,31 +255,44 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: approvalSignerAddress1,
|
||||
});
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -250,8 +301,9 @@ describe('Mixins tests', () => {
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -263,14 +315,19 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -279,8 +336,9 @@ describe('Mixins tests', () => {
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -292,30 +350,36 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -329,20 +393,26 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -355,20 +425,26 @@ describe('Mixins tests', () => {
|
||||
senderAddress: devConstants.NULL_ADDRESS,
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -381,36 +457,48 @@ describe('Mixins tests', () => {
|
||||
senderAddress: devConstants.NULL_ADDRESS,
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, senderAddress: devConstants.NULL_ADDRESS }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -420,21 +508,31 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approval2.signature],
|
||||
@@ -444,50 +542,69 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: approvalSignerAddress1,
|
||||
});
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
transaction,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
@@ -496,24 +613,34 @@ describe('Mixins tests', () => {
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
}),
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -522,8 +649,9 @@ describe('Mixins tests', () => {
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -535,16 +663,25 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approvalSignature2],
|
||||
@@ -553,8 +690,9 @@ describe('Mixins tests', () => {
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approvalSignature2],
|
||||
@@ -566,15 +704,20 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approvalSignature2],
|
||||
@@ -583,8 +726,9 @@ describe('Mixins tests', () => {
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approvalSignature2],
|
||||
@@ -596,16 +740,25 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds1);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds2);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds1,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds2,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||
[approval1.signature, approval2.signature],
|
||||
@@ -614,8 +767,9 @@ describe('Mixins tests', () => {
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||
[approval1.signature, approval2.signature],
|
||||
@@ -627,14 +781,19 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
@@ -643,8 +802,9 @@ describe('Mixins tests', () => {
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
@@ -656,30 +816,36 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -688,26 +854,41 @@ describe('Mixins tests', () => {
|
||||
it('should allow the tx signer to call `cancelOrders` without approval', async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it('should allow the tx signer to call `batchCancelOrders` without approval', async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it('should allow the tx signer to call `cancelOrdersUpTo` without approval', async () => {
|
||||
const orders: SignedOrder[] = [];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,29 +1,35 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { hashUtils, SignedTECApproval, signingUtils, TECSignatureType } from './index';
|
||||
import { hashUtils, SignedCoordinatorApproval } from './index';
|
||||
|
||||
export class ApprovalFactory {
|
||||
private readonly _privateKey: Buffer;
|
||||
private readonly _verifyingContractAddress: string;
|
||||
|
||||
constructor(privateKey: Buffer, verifyingContractAddress: string) {
|
||||
this._privateKey = privateKey;
|
||||
this._verifyingContractAddress = verifyingContractAddress;
|
||||
}
|
||||
|
||||
public newSignedApproval(
|
||||
transaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
signatureType: TECSignatureType = TECSignatureType.EthSign,
|
||||
): SignedTECApproval {
|
||||
const tecTransaction = {
|
||||
...transaction,
|
||||
verifyingContractAddress: this._verifyingContractAddress,
|
||||
};
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(tecTransaction, approvalExpirationTimeSeconds);
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): SignedCoordinatorApproval {
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
|
||||
transaction,
|
||||
this._verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||
const signedApproval = {
|
||||
transaction: tecTransaction,
|
||||
txOrigin,
|
||||
transaction,
|
||||
approvalExpirationTimeSeconds,
|
||||
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
|
||||
};
|
@@ -1,16 +1,6 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export const constants = {
|
||||
TEC_DOMAIN_NAME: '0x Protocol Trade Execution Coordinator',
|
||||
TEC_DOMAIN_VERSION: '1.0.0',
|
||||
TEC_APPROVAL_SCHEMA: {
|
||||
name: 'TECApproval',
|
||||
parameters: [
|
||||
{ name: 'transactionHash', type: 'bytes32' },
|
||||
{ name: 'transactionSignature', type: 'bytes' },
|
||||
{ name: 'approvalExpirationTimeSeconds', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
SINGLE_FILL_FN_NAMES: ['fillOrder', 'fillOrKillOrder', 'fillOrderNoThrow'],
|
||||
BATCH_FILL_FN_NAMES: ['batchFillOrders', 'batchFillOrKillOrders', 'batchFillOrdersNoThrow'],
|
||||
MARKET_FILL_FN_NAMES: ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow'],
|
@@ -0,0 +1,65 @@
|
||||
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, CoordinatorRegistryContract } from '../../src';
|
||||
|
||||
export class CoordinatorRegistryWrapper {
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private _coordinatorRegistryContract?: CoordinatorRegistryContract;
|
||||
/**
|
||||
* Instanitates an CoordinatorRegistryWrapper
|
||||
* @param provider Web3 provider to use for all JSON RPC requests
|
||||
* Instance of CoordinatorRegistryWrapper
|
||||
*/
|
||||
constructor(provider: ZeroExProvider) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
||||
}
|
||||
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
|
||||
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.CoordinatorRegistry,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
if (_.isUndefined(this._coordinatorRegistryContract)) {
|
||||
throw new Error(`Failed to deploy Coordinator Registry contract.`);
|
||||
}
|
||||
return this._coordinatorRegistryContract;
|
||||
}
|
||||
public async setCoordinatorEndpointAsync(
|
||||
coordinatorOperator: string,
|
||||
coordinatorEndpoint: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).setCoordinatorEndpoint.sendTransactionAsync(
|
||||
coordinatorEndpoint,
|
||||
{
|
||||
from: coordinatorOperator,
|
||||
},
|
||||
),
|
||||
);
|
||||
return txReceipt;
|
||||
}
|
||||
public async getCoordinatorEndpointAsync(coordinatorOperator: string): Promise<string> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const coordinatorEndpoint = await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
return coordinatorEndpoint;
|
||||
}
|
||||
private _assertCoordinatorRegistryDeployed(): void {
|
||||
if (_.isUndefined(this._coordinatorRegistryContract)) {
|
||||
throw new Error(
|
||||
'The Coordinator Registry contract was not deployed through the CoordinatorRegistryWrapper. Call `deployCoordinatorRegistryAsync` to deploy.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
48
contracts/coordinator/test/utils/hash_utils.ts
Normal file
48
contracts/coordinator/test/utils/hash_utils.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { eip712Utils, transactionHashUtils } from '@0x/order-utils';
|
||||
import { constants } from '@0x/order-utils/lib/src/constants';
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const hashUtils = {
|
||||
getApprovalHashBuffer(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): Buffer {
|
||||
const domain = {
|
||||
name: constants.COORDINATOR_DOMAIN_NAME,
|
||||
version: constants.COORDINATOR_DOMAIN_VERSION,
|
||||
verifyingContractAddress,
|
||||
};
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const approval = {
|
||||
txOrigin,
|
||||
transactionHash,
|
||||
transactionSignature: transaction.signature,
|
||||
approvalExpirationTimeSeconds: approvalExpirationTimeSeconds.toString(),
|
||||
};
|
||||
const typedData = eip712Utils.createTypedData(
|
||||
constants.COORDINATOR_APPROVAL_SCHEMA.name,
|
||||
{
|
||||
CoordinatorApproval: constants.COORDINATOR_APPROVAL_SCHEMA.parameters,
|
||||
},
|
||||
approval,
|
||||
domain,
|
||||
);
|
||||
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return hashBuffer;
|
||||
},
|
||||
getApprovalHashHex(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): string {
|
||||
const hashHex = `0x${hashUtils
|
||||
.getApprovalHashBuffer(transaction, verifyingContractAddress, txOrigin, approvalExpirationTimeSeconds)
|
||||
.toString('hex')}`;
|
||||
return hashHex;
|
||||
},
|
||||
};
|
@@ -1,6 +1,4 @@
|
||||
export { hashUtils } from './hash_utils';
|
||||
export { signingUtils } from './signing_utils';
|
||||
export { TECTransactionFactory } from './tec_transaction_factory';
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export { constants } from './constants';
|
||||
export { exchangeDataEncoder } from './exchange_data_encoder';
|
@@ -1,18 +1,12 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface TECApproval {
|
||||
export interface CoordinatorApproval {
|
||||
transaction: SignedZeroExTransaction;
|
||||
txOrigin: string;
|
||||
approvalExpirationTimeSeconds: BigNumber;
|
||||
}
|
||||
|
||||
export interface SignedTECApproval extends TECApproval {
|
||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export enum TECSignatureType {
|
||||
Illegal,
|
||||
EIP712,
|
||||
EthSign,
|
||||
NSignatureTypes,
|
||||
}
|
@@ -3,7 +3,8 @@
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/TEC.json",
|
||||
"generated-artifacts/Coordinator.json",
|
||||
"generated-artifacts/CoordinatorRegistry.json",
|
||||
"generated-artifacts/TestLibs.json",
|
||||
"generated-artifacts/TestMixins.json"
|
||||
],
|
20
contracts/erc1155/CHANGELOG.json
Normal file
20
contracts/erc1155/CHANGELOG.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1553091633,
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Created ERC1155 contracts package",
|
||||
"pr": 1657
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.0.2 - _February 26, 2019_
|
||||
## v1.0.1 - _March 20, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.0 - _Invalid date_
|
||||
|
||||
* Created ERC1155 contracts package (#1657)
|
1
contracts/erc1155/DEPLOYS.json
Normal file
1
contracts/erc1155/DEPLOYS.json
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
73
contracts/erc1155/README.md
Normal file
73
contracts/erc1155/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
## ERC1155 Tokens
|
||||
|
||||
This package contains implementations of various [ERC1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md) tokens. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-erc1155 --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
|
||||
|
||||
## 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-erc1155 yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-erc1155 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).
|
30
contracts/erc1155/compiler.json
Normal file
30
contracts/erc1155/compiler.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"artifactsDir": "generated-artifacts",
|
||||
"contractsDir": "contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/ERC1155.sol",
|
||||
"src/ERC1155Mintable.sol",
|
||||
"src/MixinNonFungibleToken.sol",
|
||||
"src/interfaces/IERC1155.sol",
|
||||
"src/interfaces/IERC1155Mintable.sol",
|
||||
"src/interfaces/IERC1155Receiver.sol",
|
||||
"src/mixins/MNonFungibleToken.sol",
|
||||
"test/DummyERC1155Receiver.sol"
|
||||
]
|
||||
}
|
247
contracts/erc1155/contracts/src/ERC1155.sol
Normal file
247
contracts/erc1155/contracts/src/ERC1155.sol
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Address.sol";
|
||||
import "./interfaces/IERC1155.sol";
|
||||
import "./interfaces/IERC1155Receiver.sol";
|
||||
import "./MixinNonFungibleToken.sol";
|
||||
|
||||
|
||||
contract ERC1155 is
|
||||
SafeMath,
|
||||
IERC1155,
|
||||
MixinNonFungibleToken
|
||||
{
|
||||
using Address for address;
|
||||
|
||||
// selectors for receiver callbacks
|
||||
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
|
||||
|
||||
// id => (owner => balance)
|
||||
mapping (uint256 => mapping(address => uint256)) internal balances;
|
||||
|
||||
// owner => (operator => approved)
|
||||
mapping (address => mapping(address => bool)) internal operatorApproval;
|
||||
|
||||
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
|
||||
/// @dev MUST emit TransferSingle event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
|
||||
/// @param from Source address
|
||||
/// @param to Target address
|
||||
/// @param id ID of the token type
|
||||
/// @param value Transfer amount
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
to != address(0x0),
|
||||
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
|
||||
);
|
||||
require(
|
||||
from == msg.sender || operatorApproval[from][msg.sender] == true,
|
||||
"INSUFFICIENT_ALLOWANCE"
|
||||
);
|
||||
|
||||
// perform transfer
|
||||
if (isNonFungible(id)) {
|
||||
require(
|
||||
value == 1,
|
||||
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
|
||||
);
|
||||
require(
|
||||
nfOwners[id] == from,
|
||||
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
|
||||
);
|
||||
nfOwners[id] = to;
|
||||
// You could keep balance of NF type in base type id like so:
|
||||
// uint256 baseType = getNonFungibleBaseType(_id);
|
||||
// balances[baseType][_from] = balances[baseType][_from].safeSub(_value);
|
||||
// balances[baseType][_to] = balances[baseType][_to].safeAdd(_value);
|
||||
} else {
|
||||
balances[id][from] = safeSub(balances[id][from], value);
|
||||
balances[id][to] = safeAdd(balances[id][to], value);
|
||||
}
|
||||
emit TransferSingle(msg.sender, from, to, id, value);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (to.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155Received(
|
||||
msg.sender,
|
||||
from,
|
||||
id,
|
||||
value,
|
||||
data
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
|
||||
/// @dev MUST emit TransferBatch event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if length of `_ids` is not the same as length of `_values`.
|
||||
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
|
||||
/// @param from Source addresses
|
||||
/// @param to Target addresses
|
||||
/// @param ids IDs of each token type
|
||||
/// @param values Transfer amounts per token type
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
to != address(0x0),
|
||||
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
|
||||
);
|
||||
require(
|
||||
ids.length == values.length,
|
||||
"TOKEN_AND_VALUES_LENGTH_MISMATCH"
|
||||
);
|
||||
|
||||
// Only supporting a global operator approval allows us to do
|
||||
// only 1 check and not to touch storage to handle allowances.
|
||||
require(
|
||||
from == msg.sender || operatorApproval[from][msg.sender] == true,
|
||||
"INSUFFICIENT_ALLOWANCE"
|
||||
);
|
||||
|
||||
// perform transfers
|
||||
for (uint256 i = 0; i < ids.length; ++i) {
|
||||
// Cache value to local variable to reduce read costs.
|
||||
uint256 id = ids[i];
|
||||
uint256 value = values[i];
|
||||
|
||||
if (isNonFungible(id)) {
|
||||
require(
|
||||
value == 1,
|
||||
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
|
||||
);
|
||||
require(
|
||||
nfOwners[id] == from,
|
||||
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
|
||||
);
|
||||
nfOwners[id] = to;
|
||||
} else {
|
||||
balances[id][from] = safeSub(balances[id][from], value);
|
||||
balances[id][to] = safeAdd(balances[id][to], value);
|
||||
}
|
||||
}
|
||||
emit TransferBatch(msg.sender, from, to, ids, values);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (to.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155BatchReceived(
|
||||
msg.sender,
|
||||
from,
|
||||
ids,
|
||||
values,
|
||||
data
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_BATCH_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
|
||||
/// @dev MUST emit the ApprovalForAll event on success.
|
||||
/// @param operator Address to add to the set of authorized operators
|
||||
/// @param approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address operator, bool approved) external {
|
||||
operatorApproval[msg.sender][operator] = approved;
|
||||
emit ApprovalForAll(msg.sender, operator, approved);
|
||||
}
|
||||
|
||||
/// @notice Queries the approval status of an operator for a given owner.
|
||||
/// @param owner The owner of the Tokens
|
||||
/// @param operator Address of authorized operator
|
||||
/// @return True if the operator is approved, false if not
|
||||
function isApprovedForAll(address owner, address operator) external view returns (bool) {
|
||||
return operatorApproval[owner][operator];
|
||||
}
|
||||
|
||||
/// @notice Get the balance of an account's Tokens.
|
||||
/// @param owner The address of the token holder
|
||||
/// @param id ID of the Token
|
||||
/// @return The _owner's balance of the Token type requested
|
||||
function balanceOf(address owner, uint256 id) external view returns (uint256) {
|
||||
if (isNonFungibleItem(id)) {
|
||||
return nfOwners[id] == owner ? 1 : 0;
|
||||
}
|
||||
return balances[id][owner];
|
||||
}
|
||||
|
||||
/// @notice Get the balance of multiple account/token pairs
|
||||
/// @param owners The addresses of the token holders
|
||||
/// @param ids ID of the Tokens
|
||||
/// @return The _owner's balance of the Token types requested
|
||||
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory balances_) {
|
||||
// sanity check
|
||||
require(
|
||||
owners.length == ids.length,
|
||||
"OWNERS_AND_IDS_MUST_HAVE_SAME_LENGTH"
|
||||
);
|
||||
|
||||
// get balances
|
||||
balances_ = new uint256[](owners.length);
|
||||
for (uint256 i = 0; i < owners.length; ++i) {
|
||||
uint256 id = ids[i];
|
||||
if (isNonFungibleItem(id)) {
|
||||
balances_[i] = nfOwners[id] == owners[i] ? 1 : 0;
|
||||
} else {
|
||||
balances_[i] = balances[id][owners[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return balances_;
|
||||
}
|
||||
}
|
173
contracts/erc1155/contracts/src/ERC1155Mintable.sol
Normal file
173
contracts/erc1155/contracts/src/ERC1155Mintable.sol
Normal file
@@ -0,0 +1,173 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "./ERC1155.sol";
|
||||
import "./interfaces/IERC1155Mintable.sol";
|
||||
|
||||
|
||||
/// @dev Mintable form of ERC1155
|
||||
/// Shows how easy it is to mint new items
|
||||
contract ERC1155Mintable is
|
||||
IERC1155Mintable,
|
||||
ERC1155
|
||||
{
|
||||
|
||||
/// token nonce
|
||||
uint256 internal nonce;
|
||||
|
||||
/// mapping from token to creator
|
||||
mapping (uint256 => address) public creators;
|
||||
|
||||
/// mapping from token to max index
|
||||
mapping (uint256 => uint256) public maxIndex;
|
||||
|
||||
/// asserts token is owned by msg.sender
|
||||
modifier creatorOnly(uint256 _id) {
|
||||
require(creators[_id] == msg.sender);
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev creates a new token
|
||||
/// @param uri URI of token
|
||||
/// @param isNF is non-fungible token
|
||||
/// @return type_ of token (a unique identifier)
|
||||
function create(
|
||||
string calldata uri,
|
||||
bool isNF
|
||||
)
|
||||
external
|
||||
returns (uint256 type_)
|
||||
{
|
||||
// Store the type in the upper 128 bits
|
||||
type_ = (++nonce << 128);
|
||||
|
||||
// Set a flag if this is an NFI.
|
||||
if (isNF) {
|
||||
type_ = type_ | TYPE_NF_BIT;
|
||||
}
|
||||
|
||||
// This will allow restricted access to creators.
|
||||
creators[type_] = msg.sender;
|
||||
|
||||
// emit a Transfer event with Create semantic to help with discovery.
|
||||
emit TransferSingle(
|
||||
msg.sender,
|
||||
address(0x0),
|
||||
address(0x0),
|
||||
type_,
|
||||
0
|
||||
);
|
||||
|
||||
if (bytes(uri).length > 0) {
|
||||
emit URI(uri, type_);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev mints fungible tokens
|
||||
/// @param id token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
/// @param quantities amounts of minted tokens
|
||||
function mintFungible(
|
||||
uint256 id,
|
||||
address[] calldata to,
|
||||
uint256[] calldata quantities
|
||||
)
|
||||
external
|
||||
creatorOnly(id)
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
isFungible(id),
|
||||
"TRIED_TO_MINT_FUNGIBLE_FOR_NON_FUNGIBLE_TOKEN"
|
||||
);
|
||||
|
||||
// mint tokens
|
||||
for (uint256 i = 0; i < to.length; ++i) {
|
||||
// cache to reduce number of loads
|
||||
address dst = to[i];
|
||||
uint256 quantity = quantities[i];
|
||||
|
||||
// Grant the items to the caller
|
||||
balances[id][dst] = safeAdd(quantity, balances[id][dst]);
|
||||
|
||||
// Emit the Transfer/Mint event.
|
||||
// the 0x0 source address implies a mint
|
||||
// It will also provide the circulating supply info.
|
||||
emit TransferSingle(
|
||||
msg.sender,
|
||||
address(0x0),
|
||||
dst,
|
||||
id,
|
||||
quantity
|
||||
);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (dst.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
id,
|
||||
quantity,
|
||||
""
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev mints a non-fungible token
|
||||
/// @param type_ token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
function mintNonFungible(
|
||||
uint256 type_,
|
||||
address[] calldata to
|
||||
)
|
||||
external
|
||||
creatorOnly(type_)
|
||||
{
|
||||
// No need to check this is a nf type rather than an id since
|
||||
// creatorOnly() will only let a type pass through.
|
||||
require(
|
||||
isNonFungible(type_),
|
||||
"TRIED_TO_MINT_NON_FUNGIBLE_FOR_FUNGIBLE_TOKEN"
|
||||
);
|
||||
|
||||
// Index are 1-based.
|
||||
uint256 index = maxIndex[type_] + 1;
|
||||
|
||||
for (uint256 i = 0; i < to.length; ++i) {
|
||||
// cache to reduce number of loads
|
||||
address dst = to[i];
|
||||
uint256 id = type_ | index + i;
|
||||
|
||||
nfOwners[id] = dst;
|
||||
|
||||
// You could use base-type id to store NF type balances if you wish.
|
||||
// balances[_type][dst] = quantity.safeAdd(balances[_type][dst]);
|
||||
|
||||
emit TransferSingle(msg.sender, address(0x0), dst, id, 1);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (dst.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
id,
|
||||
1,
|
||||
""
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// record the `maxIndex` of this nft type
|
||||
// this allows us to mint more nft's of this type in a subsequent call.
|
||||
maxIndex[type_] = safeAdd(to.length, maxIndex[type_]);
|
||||
}
|
||||
}
|
76
contracts/erc1155/contracts/src/MixinNonFungibleToken.sol
Normal file
76
contracts/erc1155/contracts/src/MixinNonFungibleToken.sol
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "./mixins/MNonFungibleToken.sol";
|
||||
|
||||
|
||||
contract MixinNonFungibleToken is
|
||||
MNonFungibleToken
|
||||
{
|
||||
/// Use a split bit implementation.
|
||||
/// Store the type in the upper 128 bits..
|
||||
uint256 constant internal TYPE_MASK = uint256(uint128(~0)) << 128;
|
||||
|
||||
/// ..and the non-fungible index in the lower 128
|
||||
uint256 constant internal NF_INDEX_MASK = uint128(~0);
|
||||
|
||||
/// The top bit is a flag to tell if this is a NFI.
|
||||
uint256 constant internal TYPE_NF_BIT = 1 << 255;
|
||||
|
||||
/// mapping of nft to owner
|
||||
mapping (uint256 => address) internal nfOwners;
|
||||
|
||||
/// @dev Returns true if token is non-fungible
|
||||
function isNonFungible(uint256 id) public pure returns(bool) {
|
||||
return id & TYPE_NF_BIT == TYPE_NF_BIT;
|
||||
}
|
||||
|
||||
/// @dev Returns true if token is fungible
|
||||
function isFungible(uint256 id) public pure returns(bool) {
|
||||
return id & TYPE_NF_BIT == 0;
|
||||
}
|
||||
|
||||
/// @dev Returns index of non-fungible token
|
||||
function getNonFungibleIndex(uint256 id) public pure returns(uint256) {
|
||||
return id & NF_INDEX_MASK;
|
||||
}
|
||||
|
||||
/// @dev Returns base type of non-fungible token
|
||||
function getNonFungibleBaseType(uint256 id) public pure returns(uint256) {
|
||||
return id & TYPE_MASK;
|
||||
}
|
||||
|
||||
/// @dev Returns true if input is base-type of a non-fungible token
|
||||
function isNonFungibleBaseType(uint256 id) public pure returns(bool) {
|
||||
// A base type has the NF bit but does not have an index.
|
||||
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK == 0);
|
||||
}
|
||||
|
||||
/// @dev Returns true if input is a non-fungible token
|
||||
function isNonFungibleItem(uint256 id) public pure returns(bool) {
|
||||
// A base type has the NF bit but does has an index.
|
||||
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0);
|
||||
}
|
||||
|
||||
/// @dev returns owner of a non-fungible token
|
||||
function ownerOf(uint256 id) public view returns (address) {
|
||||
return nfOwners[id];
|
||||
}
|
||||
}
|
152
contracts/erc1155/contracts/src/interfaces/IERC1155.sol
Normal file
152
contracts/erc1155/contracts/src/interfaces/IERC1155.sol
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
|
||||
/// @title ERC-1155 Multi Token Standard
|
||||
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
|
||||
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
|
||||
interface IERC1155 {
|
||||
|
||||
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
||||
/// including zero value transfers as well as minting or burning.
|
||||
/// Operator will always be msg.sender.
|
||||
/// Either event from address `0x0` signifies a minting operation.
|
||||
/// An event to address `0x0` signifies a burning or melting operation.
|
||||
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
|
||||
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
|
||||
/// To define a token ID with no initial balance, the contract SHOULD emit the TransferSingle event
|
||||
/// from `0x0` to `0x0`, with the token creator as `_operator`.
|
||||
event TransferSingle(
|
||||
address indexed operator,
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 id,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
||||
/// including zero value transfers as well as minting or burning.
|
||||
///Operator will always be msg.sender.
|
||||
/// Either event from address `0x0` signifies a minting operation.
|
||||
/// An event to address `0x0` signifies a burning or melting operation.
|
||||
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
|
||||
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
|
||||
/// To define multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event
|
||||
/// from `0x0` to `0x0`, with the token creator as `_operator`.
|
||||
event TransferBatch(
|
||||
address indexed operator,
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256[] ids,
|
||||
uint256[] values
|
||||
);
|
||||
|
||||
/// @dev MUST emit when an approval is updated.
|
||||
event ApprovalForAll(
|
||||
address indexed owner,
|
||||
address indexed operator,
|
||||
bool approved
|
||||
);
|
||||
|
||||
/// @dev MUST emit when the URI is updated for a token ID.
|
||||
/// URIs are defined in RFC 3986.
|
||||
/// The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata JSON Schema".
|
||||
event URI(
|
||||
string value,
|
||||
uint256 indexed id
|
||||
);
|
||||
|
||||
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
|
||||
/// @dev MUST emit TransferSingle event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
|
||||
/// @param from Source address
|
||||
/// @param to Target address
|
||||
/// @param id ID of the token type
|
||||
/// @param value Transfer amount
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
|
||||
/// @dev MUST emit TransferBatch event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if length of `_ids` is not the same as length of `_values`.
|
||||
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
|
||||
/// @param from Source addresses
|
||||
/// @param to Target addresses
|
||||
/// @param ids IDs of each token type
|
||||
/// @param values Transfer amounts per token type
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
|
||||
/// @dev MUST emit the ApprovalForAll event on success.
|
||||
/// @param operator Address to add to the set of authorized operators
|
||||
/// @param approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address operator, bool approved) external;
|
||||
|
||||
/// @notice Queries the approval status of an operator for a given owner.
|
||||
/// @param owner The owner of the Tokens
|
||||
/// @param operator Address of authorized operator
|
||||
/// @return True if the operator is approved, false if not
|
||||
function isApprovedForAll(address owner, address operator) external view returns (bool);
|
||||
|
||||
/// @notice Get the balance of an account's Tokens.
|
||||
/// @param owner The address of the token holder
|
||||
/// @param id ID of the Token
|
||||
/// @return The _owner's balance of the Token type requested
|
||||
function balanceOf(address owner, uint256 id) external view returns (uint256);
|
||||
|
||||
/// @notice Get the balance of multiple account/token pairs
|
||||
/// @param owners The addresses of the token holders
|
||||
/// @param ids ID of the Tokens
|
||||
/// @return The _owner's balance of the Token types requested
|
||||
function balanceOfBatch(
|
||||
address[] calldata owners,
|
||||
uint256[] calldata ids
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory balances_);
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./IERC1155.sol";
|
||||
|
||||
|
||||
/// @dev Mintable form of ERC1155
|
||||
/// Shows how easy it is to mint new items
|
||||
contract IERC1155Mintable is
|
||||
IERC1155
|
||||
{
|
||||
|
||||
/// @dev creates a new token
|
||||
/// @param uri URI of token
|
||||
/// @param isNF is non-fungible token
|
||||
/// @return _type of token (a unique identifier)
|
||||
function create(
|
||||
string calldata uri,
|
||||
bool isNF
|
||||
)
|
||||
external
|
||||
returns (uint256 type_);
|
||||
|
||||
/// @dev mints fungible tokens
|
||||
/// @param id token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
/// @param quantities amounts of minted tokens
|
||||
function mintFungible(
|
||||
uint256 id,
|
||||
address[] calldata to,
|
||||
uint256[] calldata quantities
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev mints a non-fungible token
|
||||
/// @param type_ token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
function mintNonFungible(
|
||||
uint256 type_,
|
||||
address[] calldata to
|
||||
)
|
||||
external;
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
|
||||
interface IERC1155Receiver {
|
||||
|
||||
/// @notice Handle the receipt of a single ERC1155 token type
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
///transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param id An array containing the ids of the token being transferred
|
||||
/// @param value An array containing the amount of tokens being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155Received(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4);
|
||||
|
||||
/// @notice Handle the receipt of multiple ERC1155 token types
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
/// transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param ids An array containing ids of each token being transferred
|
||||
/// @param values An array containing amounts of each token being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155BatchReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4);
|
||||
}
|
44
contracts/erc1155/contracts/src/mixins/MNonFungibleToken.sol
Normal file
44
contracts/erc1155/contracts/src/mixins/MNonFungibleToken.sol
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
|
||||
contract MNonFungibleToken {
|
||||
|
||||
/// @dev Returns true if token is non-fungible
|
||||
function isNonFungible(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev Returns true if token is fungible
|
||||
function isFungible(uint256 _d) public pure returns(bool);
|
||||
|
||||
/// @dev Returns index of non-fungible token
|
||||
function getNonFungibleIndex(uint256 id) public pure returns(uint256);
|
||||
|
||||
/// @dev Returns base type of non-fungible token
|
||||
function getNonFungibleBaseType(uint256 id) public pure returns(uint256);
|
||||
|
||||
/// @dev Returns true if input is base-type of a non-fungible token
|
||||
function isNonFungibleBaseType(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev Returns true if input is a non-fungible token
|
||||
function isNonFungibleItem(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev returns owner of a non-fungible token
|
||||
function ownerOf(uint256 id) public view returns (address);
|
||||
}
|
126
contracts/erc1155/contracts/test/DummyERC1155Receiver.sol
Normal file
126
contracts/erc1155/contracts/test/DummyERC1155Receiver.sol
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "../src/interfaces/IERC1155Receiver.sol";
|
||||
|
||||
|
||||
contract DummyERC1155Receiver is
|
||||
IERC1155Receiver
|
||||
{
|
||||
|
||||
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
|
||||
bool internal shouldRejectTransfer;
|
||||
|
||||
event TokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 tokenId,
|
||||
uint256 tokenValue,
|
||||
bytes data
|
||||
);
|
||||
|
||||
event BatchTokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] tokenIds,
|
||||
uint256[] tokenValues,
|
||||
bytes data
|
||||
);
|
||||
|
||||
constructor () public {
|
||||
shouldRejectTransfer = false;
|
||||
}
|
||||
|
||||
/// @notice Handle the receipt of a single ERC1155 token type
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
///transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param id An array containing the ids of the token being transferred
|
||||
/// @param value An array containing the amount of tokens being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155Received(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4)
|
||||
{
|
||||
if (shouldRejectTransfer) {
|
||||
revert("TRANSFER_REJECTED");
|
||||
}
|
||||
emit TokenReceived(
|
||||
operator,
|
||||
from,
|
||||
id,
|
||||
value,
|
||||
data
|
||||
);
|
||||
return ERC1155_RECEIVED;
|
||||
}
|
||||
|
||||
/// @notice Handle the receipt of multiple ERC1155 token types
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
/// transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param ids An array containing ids of each token being transferred
|
||||
/// @param values An array containing amounts of each token being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155BatchReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns (bytes4)
|
||||
{
|
||||
if (shouldRejectTransfer) {
|
||||
revert("TRANSFER_REJECTED");
|
||||
}
|
||||
emit BatchTokenReceived(
|
||||
operator,
|
||||
from,
|
||||
ids,
|
||||
values,
|
||||
data
|
||||
);
|
||||
return ERC1155_BATCH_RECEIVED;
|
||||
}
|
||||
|
||||
// @dev If set to true then all future transfers will be rejected.
|
||||
function setRejectTransferFlag(bool _shouldRejectTransfer) external {
|
||||
shouldRejectTransfer = _shouldRejectTransfer;
|
||||
}
|
||||
}
|
82
contracts/erc1155/package.json
Normal file
82
contracts/erc1155/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc1155",
|
||||
"version": "1.0.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Token contracts used by 0x protocol",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile generate_contract_wrappers",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "generated-artifacts/@(DummyERC1155Receiver|ERC1155|ERC1155Mintable|IERC1155|IERC1155Mintable|IERC1155Receiver|MNonFungibleToken|MixinNonFungibleToken).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"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/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^2.0.7",
|
||||
"@0x/contracts-gen": "^1.0.6",
|
||||
"@0x/contracts-test-utils": "^3.1.0",
|
||||
"@0x/dev-utils": "^2.1.4",
|
||||
"@0x/sol-compiler": "^3.1.4",
|
||||
"@0x/tslint-config": "^3.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^4.1.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.0.3",
|
||||
"@0x/contracts-utils": "^3.0.0",
|
||||
"@0x/types": "^2.2.0",
|
||||
"@0x/typescript-typings": "^4.2.0",
|
||||
"@0x/utils": "^4.2.3",
|
||||
"@0x/web3-wrapper": "^6.0.3",
|
||||
"ethereum-types": "^2.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
25
contracts/erc1155/src/artifacts.ts
Normal file
25
contracts/erc1155/src/artifacts.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyERC1155Receiver from '../generated-artifacts/DummyERC1155Receiver.json';
|
||||
import * as ERC1155 from '../generated-artifacts/ERC1155.json';
|
||||
import * as ERC1155Mintable from '../generated-artifacts/ERC1155Mintable.json';
|
||||
import * as IERC1155 from '../generated-artifacts/IERC1155.json';
|
||||
import * as IERC1155Mintable from '../generated-artifacts/IERC1155Mintable.json';
|
||||
import * as IERC1155Receiver from '../generated-artifacts/IERC1155Receiver.json';
|
||||
import * as MixinNonFungibleToken from '../generated-artifacts/MixinNonFungibleToken.json';
|
||||
import * as MNonFungibleToken from '../generated-artifacts/MNonFungibleToken.json';
|
||||
export const artifacts = {
|
||||
DummyERC1155Receiver: DummyERC1155Receiver as ContractArtifact,
|
||||
ERC1155: ERC1155 as ContractArtifact,
|
||||
MNonFungibleToken: MNonFungibleToken as ContractArtifact,
|
||||
ERC1155Mintable: ERC1155Mintable as ContractArtifact,
|
||||
MixinNonFungibleToken: MixinNonFungibleToken as ContractArtifact,
|
||||
IERC1155Mintable: IERC1155Mintable as ContractArtifact,
|
||||
IERC1155Receiver: IERC1155Receiver as ContractArtifact,
|
||||
IERC1155: IERC1155 as ContractArtifact,
|
||||
};
|
3
contracts/erc1155/src/index.ts
Normal file
3
contracts/erc1155/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './wrappers';
|
||||
export * from './artifacts';
|
||||
export { Erc1155Wrapper } from '../test/utils/erc1155_wrapper';
|
13
contracts/erc1155/src/wrappers.ts
Normal file
13
contracts/erc1155/src/wrappers.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/dummy_erc1155_receiver';
|
||||
export * from '../generated-wrappers/erc1155';
|
||||
export * from '../generated-wrappers/erc1155_mintable';
|
||||
export * from '../generated-wrappers/i_erc1155_mintable';
|
||||
export * from '../generated-wrappers/i_erc1155_receiver';
|
||||
export * from '../generated-wrappers/ierc1155';
|
||||
export * from '../generated-wrappers/m_non_fungible_token';
|
||||
export * from '../generated-wrappers/mixin_non_fungible_token';
|
492
contracts/erc1155/test/erc1155_token.ts
Normal file
492
contracts/erc1155/test/erc1155_token.ts
Normal file
@@ -0,0 +1,492 @@
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||
DummyERC1155ReceiverContract,
|
||||
ERC1155MintableContract,
|
||||
} from '../src';
|
||||
|
||||
import { Erc1155Wrapper } from './utils/erc1155_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('ERC1155Token', () => {
|
||||
// constant values used in transfer tests
|
||||
const nftOwnerBalance = new BigNumber(1);
|
||||
const nftNotOwnerBalance = new BigNumber(0);
|
||||
const spenderInitialFungibleBalance = new BigNumber(500);
|
||||
const receiverInitialFungibleBalance = new BigNumber(0);
|
||||
const fungibleValueToTransfer = spenderInitialFungibleBalance.div(2);
|
||||
const nonFungibleValueToTransfer = nftOwnerBalance;
|
||||
const receiverCallbackData = '0x01020304';
|
||||
// tokens & addresses
|
||||
let owner: string;
|
||||
let spender: string;
|
||||
let delegatedSpender: string;
|
||||
let receiver: string;
|
||||
let erc1155Contract: ERC1155MintableContract;
|
||||
let erc1155Receiver: DummyERC1155ReceiverContract;
|
||||
let nonFungibleToken: BigNumber;
|
||||
let erc1155Wrapper: Erc1155Wrapper;
|
||||
let fungibleToken: BigNumber;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
// deploy erc1155 contract & receiver
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[owner, spender, delegatedSpender] = accounts;
|
||||
erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC1155Mintable,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC1155Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
receiver = erc1155Receiver.address;
|
||||
// create wrapper & mint erc1155 tokens
|
||||
erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, provider, owner);
|
||||
fungibleToken = await erc1155Wrapper.mintFungibleTokensAsync([spender], spenderInitialFungibleBalance);
|
||||
let nonFungibleTokens: BigNumber[];
|
||||
[, nonFungibleTokens] = await erc1155Wrapper.mintNonFungibleTokensAsync([spender]);
|
||||
nonFungibleToken = nonFungibleTokens[0];
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('safeTransferFrom', () => {
|
||||
it('should transfer fungible token if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
fungibleToken,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valueToTransfer),
|
||||
receiverInitialFungibleBalance.plus(valueToTransfer),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should transfer non-fungible token if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = nonFungibleToken;
|
||||
const valueToTransfer = nonFungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should trigger callback if transferring to a contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
spenderInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
const tx = await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
expect(tx.logs.length).to.be.equal(2);
|
||||
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
|
||||
// check callback logs
|
||||
const expectedCallbackLog = {
|
||||
operator: spender,
|
||||
from: spender,
|
||||
tokenId: tokenToTransfer,
|
||||
tokenValue: valueToTransfer,
|
||||
data: receiverCallbackData,
|
||||
};
|
||||
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
|
||||
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
|
||||
expect(receiverLog.args.tokenId).to.be.bignumber.equal(expectedCallbackLog.tokenId);
|
||||
expect(receiverLog.args.tokenValue).to.be.bignumber.equal(expectedCallbackLog.tokenValue);
|
||||
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valueToTransfer),
|
||||
receiverInitialFungibleBalance.plus(valueToTransfer),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should throw if transfer reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = spenderInitialFungibleBalance.plus(1);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.Uint256Underflow,
|
||||
);
|
||||
});
|
||||
it('should throw if callback reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// set receiver to reject balances
|
||||
const shouldRejectTransfer = true;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.TransferRejected,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('batchSafeTransferFrom', () => {
|
||||
it('should transfer fungible tokens if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer non-fungible token if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [nonFungibleToken];
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer mix of fungible / non-fungible tokens if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken, nonFungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should trigger callback if transferring to a contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken, nonFungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const tx = await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
expect(tx.logs.length).to.be.equal(2);
|
||||
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
|
||||
// check callback logs
|
||||
const expectedCallbackLog = {
|
||||
operator: spender,
|
||||
from: spender,
|
||||
tokenIds: tokensToTransfer,
|
||||
tokenValues: valuesToTransfer,
|
||||
data: receiverCallbackData,
|
||||
};
|
||||
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
|
||||
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.equal(2);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[0]);
|
||||
expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[1]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.equal(2);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[0]);
|
||||
expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[1]);
|
||||
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should throw if transfer reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)];
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.Uint256Underflow,
|
||||
);
|
||||
});
|
||||
it('should throw if callback reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// set receiver to reject balances
|
||||
const shouldRejectTransfer = true;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.TransferRejected,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('setApprovalForAll', () => {
|
||||
it('should transfer token via safeTransferFrom if called by approved account', async () => {
|
||||
// set approval
|
||||
const isApprovedForAll = true;
|
||||
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.true();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
delegatedSpender,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valueToTransfer),
|
||||
receiverInitialFungibleBalance.plus(valueToTransfer),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should throw if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => {
|
||||
// check approval not set
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.false();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: delegatedSpender },
|
||||
),
|
||||
RevertReason.InsufficientAllowance,
|
||||
);
|
||||
});
|
||||
it('should transfer token via safeBatchTransferFrom if called by approved account', async () => {
|
||||
// set approval
|
||||
const isApprovedForAll = true;
|
||||
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.true();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
delegatedSpender,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should throw if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => {
|
||||
// check approval not set
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.false();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: delegatedSpender },
|
||||
),
|
||||
RevertReason.InsufficientAllowance,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:enable:no-unnecessary-type-assertion
|
17
contracts/erc1155/test/global_hooks.ts
Normal file
17
contracts/erc1155/test/global_hooks.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { env, EnvVars } from '@0x/dev-utils';
|
||||
|
||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||
before('start web3 provider', () => {
|
||||
provider.start();
|
||||
});
|
||||
after('generate coverage report', async () => {
|
||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
||||
await coverageSubprovider.writeCoverageAsync();
|
||||
}
|
||||
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
|
||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
||||
await profilerSubprovider.writeProfilerOutputAsync();
|
||||
}
|
||||
provider.stop();
|
||||
});
|
159
contracts/erc1155/test/utils/erc1155_wrapper.ts
Normal file
159
contracts/erc1155/test/utils/erc1155_wrapper.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { constants, LogDecoder } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155MintableContract, ERC1155TransferSingleEventArgs } from '../../src';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
export class Erc1155Wrapper {
|
||||
private readonly _erc1155Contract: ERC1155MintableContract;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _contractOwner: string;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
|
||||
constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) {
|
||||
this._erc1155Contract = contractInstance;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._contractOwner = contractOwner;
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
||||
}
|
||||
public getContract(): ERC1155MintableContract {
|
||||
return this._erc1155Contract;
|
||||
}
|
||||
public async getBalancesAsync(owners: string[], tokens: BigNumber[]): Promise<BigNumber[]> {
|
||||
const balances = await this._erc1155Contract.balanceOfBatch.callAsync(owners, tokens);
|
||||
return balances;
|
||||
}
|
||||
public async safeTransferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
token: BigNumber,
|
||||
value: BigNumber,
|
||||
callbackData?: string,
|
||||
delegatedSpender?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const spender = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
|
||||
const callbackDataHex = _.isUndefined(callbackData) ? '0x' : callbackData;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.safeTransferFrom.sendTransactionAsync(from, to, token, value, callbackDataHex, {
|
||||
from: spender,
|
||||
}),
|
||||
);
|
||||
return tx;
|
||||
}
|
||||
public async safeBatchTransferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
tokens: BigNumber[],
|
||||
values: BigNumber[],
|
||||
callbackData?: string,
|
||||
delegatedSpender?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const spender = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
|
||||
const callbackDataHex = _.isUndefined(callbackData) ? '0x' : callbackData;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
from,
|
||||
to,
|
||||
tokens,
|
||||
values,
|
||||
callbackDataHex,
|
||||
{ from: spender },
|
||||
),
|
||||
);
|
||||
return tx;
|
||||
}
|
||||
public async mintFungibleTokensAsync(
|
||||
beneficiaries: string[],
|
||||
tokenAmounts: BigNumber | BigNumber[],
|
||||
): Promise<BigNumber> {
|
||||
const tokenUri = 'dummyFungibleToken';
|
||||
const tokenIsNonFungible = false;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
);
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
||||
const token = createFungibleTokenLog.args.id;
|
||||
const tokenAmountsAsArray = _.isArray(tokenAmounts) ? tokenAmounts : [];
|
||||
if (!_.isArray(tokenAmounts)) {
|
||||
_.each(_.range(0, beneficiaries.length), () => {
|
||||
tokenAmountsAsArray.push(tokenAmounts);
|
||||
});
|
||||
}
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await this._erc1155Contract.mintFungible.sendTransactionAsync(token, beneficiaries, tokenAmountsAsArray, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return token;
|
||||
}
|
||||
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
|
||||
const tokenUri = 'dummyNonFungibleToken';
|
||||
const tokenIsNonFungible = true;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
);
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
||||
const token = createFungibleTokenLog.args.id;
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await this._erc1155Contract.mintNonFungible.sendTransactionAsync(token, beneficiaries, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const encodedNftIds: BigNumber[] = [];
|
||||
const nftIdBegin = 1;
|
||||
const nftIdEnd = beneficiaries.length + 1;
|
||||
const nftIdRange = _.range(nftIdBegin, nftIdEnd);
|
||||
_.each(nftIdRange, (nftId: number) => {
|
||||
const encodedNftId = token.plus(nftId);
|
||||
encodedNftIds.push(encodedNftId);
|
||||
});
|
||||
return [token, encodedNftIds];
|
||||
}
|
||||
public async setApprovalForAllAsync(
|
||||
owner: string,
|
||||
beneficiary: string,
|
||||
isApproved: boolean,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.setApprovalForAll.sendTransactionAsync(beneficiary, isApproved, {
|
||||
from: owner,
|
||||
}),
|
||||
);
|
||||
return tx;
|
||||
}
|
||||
public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise<boolean> {
|
||||
const isApprovedForAll = await this._erc1155Contract.isApprovedForAll.callAsync(owner, beneficiary);
|
||||
return isApprovedForAll;
|
||||
}
|
||||
public async assertBalancesAsync(
|
||||
owners: string[],
|
||||
tokens: BigNumber[],
|
||||
expectedBalances: BigNumber[],
|
||||
): Promise<void> {
|
||||
const ownersExtended: string[] = [];
|
||||
let tokensExtended: BigNumber[] = [];
|
||||
_.each(owners, (owner: string) => {
|
||||
tokensExtended = tokensExtended.concat(tokens);
|
||||
_.each(_.range(0, tokens.length), () => {
|
||||
ownersExtended.push(owner);
|
||||
});
|
||||
});
|
||||
const balances = await this.getBalancesAsync(ownersExtended, tokensExtended);
|
||||
_.each(balances, (balance: BigNumber, i: number) => {
|
||||
expect(balance, `${ownersExtended[i]}${tokensExtended[i]}`).to.be.bignumber.equal(expectedBalances[i]);
|
||||
});
|
||||
}
|
||||
}
|
16
contracts/erc1155/tsconfig.json
Normal file
16
contracts/erc1155/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DummyERC1155Receiver.json",
|
||||
"generated-artifacts/ERC1155.json",
|
||||
"generated-artifacts/ERC1155Mintable.json",
|
||||
"generated-artifacts/IERC1155.json",
|
||||
"generated-artifacts/IERC1155Mintable.json",
|
||||
"generated-artifacts/IERC1155Receiver.json",
|
||||
"generated-artifacts/MNonFungibleToken.json",
|
||||
"generated-artifacts/MixinNonFungibleToken.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
6
contracts/erc1155/tslint.json
Normal file
6
contracts/erc1155/tslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
@@ -1,4 +1,32 @@
|
||||
[
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Upgrade contracts to Solidity 0.5.5",
|
||||
"pr": 1682
|
||||
}
|
||||
],
|
||||
"timestamp": 1553091633
|
||||
},
|
||||
{
|
||||
"timestamp": 1551479279,
|
||||
"version": "1.0.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1551299797,
|
||||
"version": "1.0.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1551220833,
|
||||
"version": "1.0.7",
|
||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.0 - _March 20, 2019_
|
||||
|
||||
* Upgrade contracts to Solidity 0.5.5 (#1682)
|
||||
|
||||
## v1.0.9 - _March 1, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.8 - _February 27, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.7 - _February 26, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -4,7 +4,12 @@
|
||||
"useDockerisedSolc": true,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
@@ -27,7 +32,6 @@
|
||||
"src/interfaces/IEtherToken.sol",
|
||||
"test/DummyERC20Token.sol",
|
||||
"test/DummyMultipleReturnERC20Token.sol",
|
||||
"test/DummyNoReturnERC20Token.sol",
|
||||
"test/ReentrantERC20Token.sol"
|
||||
"test/DummyNoReturnERC20Token.sol"
|
||||
]
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./interfaces/IERC20Token.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "./UnlimitedAllowanceERC20Token.sol";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user