Compare commits
319 Commits
@0x/contra
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
3b9d84fa58 | ||
|
6fd96a6fd7 | ||
|
c93b02d55e | ||
|
568f87d5eb | ||
|
49ad0f0d54 | ||
|
0e642f59e1 | ||
|
7c5730fb03 | ||
|
45f0f755ab | ||
|
1ef2913c5b | ||
|
fecbf220b6 | ||
|
17a5f05cf3 | ||
|
6a852ab0ed | ||
|
ec26cff656 | ||
|
cdd34a1214 | ||
|
857a4042ef | ||
|
f51c80adb2 | ||
|
e61f23d001 | ||
|
cbe4c4fbf9 | ||
|
deffdabc30 | ||
|
8811a5387a | ||
|
9336d4e545 | ||
|
f65d8cc325 | ||
|
68656c4083 | ||
|
44793a9cf9 | ||
|
2d0ad6f181 | ||
|
a7f0717afb | ||
|
a9022352e7 | ||
|
9b2231ed24 | ||
|
c123200f38 | ||
|
3c6c4128a6 | ||
|
47e050cbaf | ||
|
62d15117c5 | ||
|
fb8360edfd | ||
|
e557f2fb48 | ||
|
c957b48281 | ||
|
c15c5e12b0 | ||
|
15c3c8074c | ||
|
cba72c811d | ||
|
28a2e56003 | ||
|
8c4c3d56c6 | ||
|
911fcc0bed | ||
|
55fd16ccf1 | ||
|
4e05e41f7f | ||
|
ebab80cff7 | ||
|
91cb162662 | ||
|
fa26f8de51 | ||
|
26602ac2db | ||
|
0b8af181d8 | ||
|
19d661d324 | ||
|
7d29b36246 | ||
|
8ba7b95e86 | ||
|
30c72daed5 | ||
|
575cb99839 | ||
|
0c064bf85b | ||
|
0f3610c92a | ||
|
c8ef10baaf | ||
|
16dc73bd1e | ||
|
4f56d68689 | ||
|
8e6d92cad5 | ||
|
3a1c464543 | ||
|
a0c2f6b7b4 | ||
|
7bfbf0ad3a | ||
|
82ee6750c7 | ||
|
c37fc30c55 | ||
|
09d13b2bfa | ||
|
af0de72bc3 | ||
|
43e32f6a1a | ||
|
e9e6452890 | ||
|
5f699b0c47 | ||
|
bf18b86f9f | ||
|
56f7dd7538 | ||
|
7aa88307f6 | ||
|
8aa69233e0 | ||
|
e843333918 | ||
|
133a4dc4e1 | ||
|
c7945a542e | ||
|
b4e00baa07 | ||
|
dde570706a | ||
|
0b3e3ab990 | ||
|
205c895d75 | ||
|
6402d29dd4 | ||
|
dc18999931 | ||
|
43f8101d0b | ||
|
3d56c06ff3 | ||
|
db9be73fec | ||
|
a02892cbc8 | ||
|
49c67fbb18 | ||
|
6f2e79208a | ||
|
ceb3ba4116 | ||
|
08d4f1402f | ||
|
77fa97f259 | ||
|
3ac5d9add5 | ||
|
cab89f312a | ||
|
8972475389 | ||
|
330f2d54e2 | ||
|
9c181f09ba | ||
|
011ecb8f4b | ||
|
bc2a9beb14 | ||
|
091f5ed8b8 | ||
|
ea9f535a7c | ||
|
f246314b1d | ||
|
cdfd62a296 | ||
|
dcff7d511b | ||
|
16a5475d24 | ||
|
42468c3fa2 | ||
|
9312d5d9f7 | ||
|
33a0c22021 | ||
|
58e9c70203 | ||
|
0067f10a6a | ||
|
59210f5e5e | ||
|
1c695b2759 | ||
|
c7222c17ae | ||
|
0f237d22f9 | ||
|
b1b1162b60 | ||
|
6ee1605a77 | ||
|
a22b2e7a9f | ||
|
86ed32a007 | ||
|
8e8ea6a3ab | ||
|
cc7452da8f | ||
|
06715201a7 | ||
|
281658ba34 | ||
|
f192648c76 | ||
|
07e1d502e7 | ||
|
703e890918 | ||
|
096950729e | ||
|
8869d79c68 | ||
|
752dd04546 | ||
|
3e5d166ec4 | ||
|
64bc1b0990 | ||
|
548b0db6ea | ||
|
c9607e8b2c | ||
|
c676ecb8cf | ||
|
39804fdc83 | ||
|
1a1dc89454 | ||
|
e427698956 | ||
|
575af6b6e8 | ||
|
3a1fc9ee5f | ||
|
1237c7d479 | ||
|
c44e16a88f | ||
|
06c180475e | ||
|
74a2c3a199 | ||
|
9ac715f99d | ||
|
22e39f782f | ||
|
f5a6b84fa3 | ||
|
718407ba6f | ||
|
e603a81a46 | ||
|
03e35846fb | ||
|
c87364f86b | ||
|
a794a33551 | ||
|
494b437f1a | ||
|
92b80fc436 | ||
|
d66101cd9d | ||
|
89ae04803f | ||
|
be95bce4cd | ||
|
01aee08c02 | ||
|
6cba9fd77f | ||
|
673d45361f | ||
|
d91a7fc663 | ||
|
ce8fd44234 | ||
|
6617ad9531 | ||
|
10f8051835 | ||
|
e7dc7167d0 | ||
|
359b804001 | ||
|
fd9084b345 | ||
|
44dac2cd80 | ||
|
a66ea2bf74 | ||
|
a362e9d2d8 | ||
|
1885957bd3 | ||
|
1a409c3731 | ||
|
7b7c64fc6a | ||
|
102fcd3fb8 | ||
|
566e05aea4 | ||
|
f014370531 | ||
|
dfbbe9daa2 | ||
|
6b653fb00d | ||
|
21cf2319d5 | ||
|
4210477e71 | ||
|
93b02e93b9 | ||
|
f4cb8cfb7e | ||
|
ce9f051d42 | ||
|
083216a0c6 | ||
|
820b40e227 | ||
|
59a38a8db0 | ||
|
d0884dcb4d | ||
|
c7ca625408 | ||
|
e46f51339a | ||
|
b45ec47eee | ||
|
c50cbd7a75 | ||
|
5ddc35fdf2 | ||
|
d6c064b9c3 | ||
|
caf6329bb3 | ||
|
008938cf5b | ||
|
3fd29656cb | ||
|
ffac52f42e | ||
|
9114510c00 | ||
|
2dbda6fc42 | ||
|
eae4001622 | ||
|
727d0498b6 | ||
|
e43f2d39bf | ||
|
cde0169733 | ||
|
0e90b0e7d0 | ||
|
b793a31cdd | ||
|
23198174f3 | ||
|
523bc3f951 | ||
|
41d99e77c7 | ||
|
90193c8197 | ||
|
6f5c62914e | ||
|
17faeae47d | ||
|
7283a16710 | ||
|
52c3dc4ad8 | ||
|
1cf8ae5909 | ||
|
51282953bd | ||
|
a6603d6bd6 | ||
|
54a03eacd6 | ||
|
43fa753a13 | ||
|
9d9fe882b6 | ||
|
4f6958b7b5 | ||
|
9a5752fff9 | ||
|
c21932d149 | ||
|
ce6c05637f | ||
|
b0699fc238 | ||
|
8bf7c4cf48 | ||
|
9f6d113fe8 | ||
|
646507c41a | ||
|
65f2626544 | ||
|
7155d878b3 | ||
|
361576814c | ||
|
aa541d0cad | ||
|
7e58385a78 | ||
|
b5545255d0 | ||
|
a22ba8647c | ||
|
22fc0b4337 | ||
|
063d6ff24e | ||
|
09c0b83fe3 | ||
|
a42f3d189c | ||
|
7815da7257 | ||
|
8e2b971f5a | ||
|
3fd7132a0d | ||
|
93edb083fa | ||
|
9e41c648dc | ||
|
a7ef54dbff | ||
|
414084a7ad | ||
|
681e6eab7a | ||
|
701b203c58 | ||
|
cbd0ca4b60 | ||
|
1626566f93 | ||
|
ac75053f69 | ||
|
13afc65b54 | ||
|
aa0a1bb54d | ||
|
2e36c7ef83 | ||
|
43399a9ad9 | ||
|
2ef546210d | ||
|
7b379f3933 | ||
|
f8ac986a0f | ||
|
dc0a78434d | ||
|
d1b0384aef | ||
|
7ac7f45c4a | ||
|
b3c7ccec57 | ||
|
93725ecec0 | ||
|
3c31ef188a | ||
|
53df2130ea | ||
|
8b695f9b98 | ||
|
d914f6fce9 | ||
|
e2e5152648 | ||
|
d3dcf7fb0c | ||
|
a0f5a8b64b | ||
|
ee508f70bc | ||
|
200b3d450f | ||
|
52fc7517f9 | ||
|
cf517b1459 | ||
|
c17984b74f | ||
|
589d2212ee | ||
|
9b922f746b | ||
|
0e7387550c | ||
|
dbf22583b5 | ||
|
6825eb442b | ||
|
45f284973a | ||
|
ef6e691646 | ||
|
e67888d65f | ||
|
584f8b13fe | ||
|
f993b6d1ed | ||
|
035dc607db | ||
|
cf2053ec77 | ||
|
3840ebf538 | ||
|
80cb6b654b | ||
|
ab70c4df74 | ||
|
95e461072f | ||
|
2593f1ff30 | ||
|
c2261a6bbe | ||
|
b383781870 | ||
|
7d121bafd0 | ||
|
6a2911d10f | ||
|
17362bcf44 | ||
|
87906a3af1 | ||
|
c0c27ed637 | ||
|
6be5552944 | ||
|
b4ae42cc9a | ||
|
3c6957095d | ||
|
2020d87824 | ||
|
ac1063dd68 | ||
|
b8e01d7be5 | ||
|
24e4567b25 | ||
|
ccf40fd65e | ||
|
d4729e2669 | ||
|
52d38c63de | ||
|
086c30831d | ||
|
4be83de7e5 | ||
|
650efb95e2 | ||
|
3948f8b66b | ||
|
ffcd297e5b | ||
|
cfb099c65c | ||
|
ba5c702a9e | ||
|
9d4010299a | ||
|
b5492bb023 | ||
|
76724a6c73 | ||
|
57ec0858fe | ||
|
b281b9aac8 | ||
|
f4453c0966 | ||
|
ebd328db06 |
@@ -47,7 +47,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils @0x/contracts-staking
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-tests @0x/contracts-staking
|
||||
test-exchange-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -58,6 +58,16 @@ jobs:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||
test-integrations-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-integrations
|
||||
test-contracts-rest-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -67,11 +77,11 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-dev-utils @0x/contracts-staking
|
||||
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator
|
||||
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
||||
# 3.0. At that time, also remove exclusion from monorepo
|
||||
# package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||
# TODO(abandeali): Re-enable after this package is complete.
|
||||
# - run: yarn wsrun test:circleci @0x/contracts-coordinator
|
||||
test-publish:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -82,7 +92,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
- run:
|
||||
command: yarn test:publish:circleci
|
||||
no_output_timeout: 1800
|
||||
test-doc-generation:
|
||||
@@ -108,6 +118,9 @@ jobs:
|
||||
- run: yarn wsrun test:circleci @0x/abi-gen
|
||||
# TODO (xianny): Needs to be updated for 3.0
|
||||
# - run: yarn wsrun test:circleci @0x/asset-buyer
|
||||
# TODO: Needs to be updated for 3.0. At that time, also remove
|
||||
# exclusion from monorepo package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/asset-swapper
|
||||
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
||||
- run: yarn wsrun test:circleci @0x/assert
|
||||
- run: yarn wsrun test:circleci @0x/base-contract
|
||||
@@ -117,6 +130,9 @@ jobs:
|
||||
- run: yarn wsrun test:circleci @0x/dev-utils
|
||||
- run: yarn wsrun test:circleci @0x/json-schemas
|
||||
- run: yarn wsrun test:circleci @0x/order-utils
|
||||
# TODO: Needs to be updated for 3.0. At that time, also remove
|
||||
# exclusion from monorepo package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/orderbook
|
||||
- run: yarn wsrun test:circleci @0x/sol-compiler
|
||||
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
||||
- run: yarn wsrun test:circleci @0x/sol-doc
|
||||
@@ -184,14 +200,33 @@ jobs:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/ganache-cli:2.2.2
|
||||
- image: 0xorg/launch-kit-backend:74bcc39
|
||||
- image: 0xorg/ganache-cli:4.4.0-beta.1
|
||||
environment:
|
||||
RPC_URL: http://localhost:8545
|
||||
VERSION: 4.4.0-beta.1
|
||||
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
|
||||
- image: 0xorg/mesh:6.0.0-beta-0xv3
|
||||
environment:
|
||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||
ETHEREUM_NETWORK_ID: '50'
|
||||
ETHEREUM_CHAIN_ID: '1337'
|
||||
USE_BOOTSTRAP_LIST: 'true'
|
||||
VERBOSITY: 3
|
||||
PRIVATE_KEY_PATH: ''
|
||||
BLOCK_POLLING_INTERVAL: '5s'
|
||||
P2P_LISTEN_PORT: '60557'
|
||||
command: |
|
||||
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
||||
- image: 0xorg/launch-kit-backend:v3
|
||||
environment:
|
||||
RPC_URL: 'http://localhost:8545'
|
||||
NETWORK_ID: 50
|
||||
WHITELIST_ALL_TOKENS: True
|
||||
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
|
||||
MAKER_FEE_UNIT_AMOUNT: 0
|
||||
TAKER_FEE_UNIT_AMOUNT: 0
|
||||
MESH_ENDPOINT: 'ws://localhost:60557'
|
||||
command: |
|
||||
sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js"
|
||||
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@@ -213,8 +248,14 @@ jobs:
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
./parallel_without_sra_client coverage run setup.py test
|
||||
./parallel coverage run setup.py test
|
||||
./build_docs
|
||||
- run:
|
||||
command: |
|
||||
# copy generated wrappers into contract_wrappers/build,
|
||||
# JUST so CircleCI will persist them as build artifacts.
|
||||
cd python-packages/contract_wrappers/src/zero_ex
|
||||
for i in contract_wrappers/[^__]*/; do mkdir -p ../../build/$i; cp $i/__init__.py ../../build/$i; done
|
||||
- save_cache:
|
||||
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
@@ -239,8 +280,6 @@ jobs:
|
||||
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/sra_client/.coverage
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_addresses/build
|
||||
- store_artifacts:
|
||||
@@ -394,6 +433,9 @@ workflows:
|
||||
- test-exchange-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-integrations-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-rest-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
@@ -415,12 +457,11 @@ workflows:
|
||||
- test-exchange-ganache-3.0
|
||||
- test-rest
|
||||
- static-tests
|
||||
# - test-python:
|
||||
# requires:
|
||||
# - build
|
||||
# - test-rest
|
||||
# - static-tests-python:
|
||||
# requires:
|
||||
# - test-python
|
||||
- test-python:
|
||||
requires:
|
||||
- build
|
||||
- static-tests-python:
|
||||
requires:
|
||||
- build
|
||||
# skip python tox run for now, as we don't yet have multiple test environments to support.
|
||||
# - test-rest-python
|
||||
|
12
.gitignore
vendored
12
.gitignore
vendored
@@ -82,6 +82,7 @@ TODO.md
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# generated contract artifacts/
|
||||
contracts/integrations/generated-artifacts/
|
||||
contracts/staking/generated-artifacts/
|
||||
contracts/coordinator/generated-artifacts/
|
||||
contracts/exchange/generated-artifacts/
|
||||
@@ -115,6 +116,7 @@ contracts/dev-utils/build/
|
||||
|
||||
# generated contract wrappers
|
||||
packages/python-contract-wrappers/generated/
|
||||
contracts/integrations/generated-wrappers/
|
||||
contracts/staking/generated-wrappers/
|
||||
contracts/coordinator/generated-wrappers/
|
||||
contracts/exchange/generated-wrappers/
|
||||
@@ -128,6 +130,7 @@ contracts/erc1155/generated-wrappers/
|
||||
contracts/extensions/generated-wrappers/
|
||||
contracts/exchange-forwarder/generated-wrappers/
|
||||
contracts/dev-utils/generated-wrappers/
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
|
||||
@@ -136,6 +139,8 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_regi
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
|
||||
@@ -146,6 +151,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__in
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
|
||||
|
||||
@@ -162,10 +168,14 @@ __pycache__
|
||||
python-packages/*/src/*.egg-info
|
||||
python-packages/*/.coverage
|
||||
|
||||
# python keeps package-local copies of json schemas
|
||||
# python keeps package-local copies of json schemas and contract addresses
|
||||
python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||
python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json
|
||||
|
||||
# Doc README copy
|
||||
packages/*/docs/README.md
|
||||
|
||||
.DS_Store
|
||||
|
||||
# the snapshot that gets built for migrations sure does have a ton of files
|
||||
packages/migrations/0x_ganache_snapshot*
|
||||
|
@@ -1,5 +1,7 @@
|
||||
lib
|
||||
.nyc_output
|
||||
/contracts/integrations/generated-wrappers
|
||||
/contracts/integrations/generated-artifacts
|
||||
/contracts/staking/generated-wrappers
|
||||
/contracts/staking/generated-artifacts
|
||||
/contracts/coordinator/generated-wrappers
|
||||
|
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "2.3.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
|
||||
"pr": 2034
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "2.3.0-beta.0",
|
||||
"changes": [
|
||||
@@ -25,6 +35,14 @@
|
||||
{
|
||||
"note": "Add `Eth2DaiBridge`",
|
||||
"pr": 2221
|
||||
},
|
||||
{
|
||||
"note": "Add `UniswapBridge`",
|
||||
"pr": 2233
|
||||
},
|
||||
{
|
||||
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
||||
"pr": 2254
|
||||
}
|
||||
],
|
||||
"timestamp": 1570135330
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.3.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
|
||||
|
||||
## v2.3.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
|
||||
@@ -13,6 +17,8 @@ CHANGELOG
|
||||
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
|
||||
* Add `ERC20BridgeProxy` (#2220)
|
||||
* Add `Eth2DaiBridge` (#2221)
|
||||
* Add `UniswapBridge` (#2233)
|
||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||
|
||||
## v2.2.8 - _September 17, 2019_
|
||||
|
||||
|
@@ -19,7 +19,7 @@
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
import "./interfaces/IAssetProxy.sol";
|
||||
@@ -27,10 +27,10 @@ import "./interfaces/IAssetProxy.sol";
|
||||
|
||||
contract ERC1155Proxy is
|
||||
MixinAuthorizable,
|
||||
SafeMath,
|
||||
IAssetProxy
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
||||
@@ -71,7 +71,7 @@ contract ERC1155Proxy is
|
||||
// to avoid copying over `ids` or `data`. This is possible if they are
|
||||
// identical to `values` and the offsets for each are pointing to the
|
||||
// same location in the ABI encoded calldata.
|
||||
scaledValues[i] = _safeMul(values[i], amount);
|
||||
scaledValues[i] = values[i].safeMul(amount);
|
||||
}
|
||||
|
||||
// Execute `safeBatchTransferFrom` call
|
||||
|
@@ -73,7 +73,7 @@ contract ERC20BridgeProxy is
|
||||
uint256 balanceBefore = balanceOf(tokenAddress, to);
|
||||
// Call the bridge, who should transfer `amount` of `tokenAddress` to
|
||||
// `to`.
|
||||
bytes4 success = IERC20Bridge(bridgeAddress).withdrawTo(
|
||||
bytes4 success = IERC20Bridge(bridgeAddress).bridgeTransferFrom(
|
||||
tokenAddress,
|
||||
from,
|
||||
to,
|
||||
|
@@ -20,9 +20,10 @@ pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/IEth2Dai.sol";
|
||||
import "../interfaces/IWallet.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
@@ -42,7 +43,7 @@ contract Eth2DaiBridge is
|
||||
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||
/// @param bridgeData The abi-encoeded "from" token address.
|
||||
/// @return success The magic bytes if successful.
|
||||
function withdrawTo(
|
||||
function bridgeTransferFrom(
|
||||
address toTokenAddress,
|
||||
address /* from */,
|
||||
address to,
|
||||
@@ -57,7 +58,7 @@ contract Eth2DaiBridge is
|
||||
|
||||
IEth2Dai exchange = _getEth2DaiContract();
|
||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||
IERC20Token(fromTokenAddress).approve(address(exchange), uint256(-1));
|
||||
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||
|
||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
|
||||
@@ -67,7 +68,7 @@ contract Eth2DaiBridge is
|
||||
amount
|
||||
);
|
||||
// Transfer the converted `toToken`s to `to`.
|
||||
_transferERC20Token(toTokenAddress, to, boughtAmount);
|
||||
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -94,49 +95,4 @@ contract Eth2DaiBridge is
|
||||
{
|
||||
return IEth2Dai(ETH2DAI_ADDRESS);
|
||||
}
|
||||
|
||||
/// @dev Permissively transfers an ERC20 token that may not adhere to
|
||||
/// specs.
|
||||
/// @param tokenAddress The token contract address.
|
||||
/// @param to The token recipient.
|
||||
/// @param amount The amount of tokens to transfer.
|
||||
function _transferERC20Token(
|
||||
address tokenAddress,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
private
|
||||
{
|
||||
// Transfer tokens.
|
||||
// We do a raw call so we can check the success separate
|
||||
// from the return data.
|
||||
(bool didSucceed, bytes memory returnData) = tokenAddress.call(
|
||||
abi.encodeWithSelector(
|
||||
IERC20Token(0).transfer.selector,
|
||||
to,
|
||||
amount
|
||||
)
|
||||
);
|
||||
if (!didSucceed) {
|
||||
assembly { revert(add(returnData, 0x20), mload(returnData)) }
|
||||
}
|
||||
|
||||
// Check return data.
|
||||
// If there is no return data, we assume the token incorrectly
|
||||
// does not return a bool. In this case we expect it to revert
|
||||
// on failure, which was handled above.
|
||||
// If the token does return data, we require that it is a single
|
||||
// value that evaluates to true.
|
||||
assembly {
|
||||
if returndatasize {
|
||||
didSucceed := 0
|
||||
if eq(returndatasize, 32) {
|
||||
// First 64 bytes of memory are reserved scratch space
|
||||
returndatacopy(0, 0, 32)
|
||||
didSucceed := mload(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
require(didSucceed, "ERC20_TRANSFER_FAILED");
|
||||
}
|
||||
}
|
||||
|
219
contracts/asset-proxy/contracts/src/bridges/UniswapBridge.sol
Normal file
219
contracts/asset-proxy/contracts/src/bridges/UniswapBridge.sol
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "../interfaces/IUniswapExchangeFactory.sol";
|
||||
import "../interfaces/IUniswapExchange.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
// solhint-disable not-rely-on-time
|
||||
contract UniswapBridge is
|
||||
IERC20Bridge,
|
||||
IWallet
|
||||
{
|
||||
/* Mainnet addresses */
|
||||
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
|
||||
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
|
||||
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
||||
// stack overflows.
|
||||
struct WithdrawToState {
|
||||
IUniswapExchange exchange;
|
||||
uint256 fromTokenBalance;
|
||||
IEtherToken weth;
|
||||
}
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
/// @dev Payable fallback to receive ETH from uniswap.
|
||||
function ()
|
||||
external
|
||||
payable
|
||||
{}
|
||||
|
||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
||||
/// token encoded in the bridge data.
|
||||
/// @param toTokenAddress The token to buy and transfer to `to`.
|
||||
/// @param to The recipient of the bought tokens.
|
||||
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||
/// @param bridgeData The abi-encoded "from" token address.
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address toTokenAddress,
|
||||
address /* from */,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
// State memory object to avoid stack overflows.
|
||||
WithdrawToState memory state;
|
||||
// Decode the bridge data to get the `fromTokenAddress`.
|
||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||
|
||||
// Just transfer the tokens if they're the same.
|
||||
if (fromTokenAddress == toTokenAddress) {
|
||||
LibERC20Token.transfer(fromTokenAddress, to, amount);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
// Get the exchange for the token pair.
|
||||
state.exchange = _getUniswapExchangeForTokenPair(
|
||||
fromTokenAddress,
|
||||
toTokenAddress
|
||||
);
|
||||
// Get our balance of `fromTokenAddress` token.
|
||||
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
||||
// Get the weth contract.
|
||||
state.weth = getWethContract();
|
||||
|
||||
// Convert from WETH to a token.
|
||||
if (fromTokenAddress == address(state.weth)) {
|
||||
// Unwrap the WETH.
|
||||
state.weth.withdraw(state.fromTokenBalance);
|
||||
// Buy as much of `toTokenAddress` token with ETH as possible and
|
||||
// transfer it to `to`.
|
||||
state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// Expires after this block.
|
||||
block.timestamp,
|
||||
// Recipient is `to`.
|
||||
to
|
||||
);
|
||||
|
||||
// Convert from a token to WETH.
|
||||
} else if (toTokenAddress == address(state.weth)) {
|
||||
// Grant the exchange an allowance.
|
||||
_grantExchangeAllowance(state.exchange, fromTokenAddress);
|
||||
// Buy as much ETH with `fromTokenAddress` token as possible.
|
||||
uint256 ethBought = state.exchange.tokenToEthSwapInput(
|
||||
// Sell all tokens we hold.
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// Expires after this block.
|
||||
block.timestamp
|
||||
);
|
||||
// Wrap the ETH.
|
||||
state.weth.deposit.value(ethBought)();
|
||||
// Transfer the WETH to `to`.
|
||||
IEtherToken(toTokenAddress).transfer(to, ethBought);
|
||||
|
||||
// Convert from one token to another.
|
||||
} else {
|
||||
// Grant the exchange an allowance.
|
||||
_grantExchangeAllowance(state.exchange, fromTokenAddress);
|
||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
||||
// and transfer it to `to`.
|
||||
state.exchange.tokenToTokenTransferInput(
|
||||
// Sell all tokens we hold.
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// No minimum intermediate ETH buy amount.
|
||||
0,
|
||||
// Expires after this block.
|
||||
block.timestamp,
|
||||
// Recipient is `to`.
|
||||
to,
|
||||
// Convert to `toTokenAddress`.
|
||||
toTokenAddress
|
||||
);
|
||||
}
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
|
||||
/// and sign for itself in orders. Always succeeds.
|
||||
/// @return magicValue Success bytes, always.
|
||||
function isValidSignature(
|
||||
bytes32,
|
||||
bytes calldata
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bytes4 magicValue)
|
||||
{
|
||||
return LEGACY_WALLET_MAGIC_VALUE;
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the weth contract.
|
||||
/// @return token The WETH contract.
|
||||
function getWethContract()
|
||||
public
|
||||
view
|
||||
returns (IEtherToken token)
|
||||
{
|
||||
return IEtherToken(WETH_ADDRESS);
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the uniswap exchange factory contract.
|
||||
/// @return factory The exchange factory contract.
|
||||
function getUniswapExchangeFactoryContract()
|
||||
public
|
||||
view
|
||||
returns (IUniswapExchangeFactory factory)
|
||||
{
|
||||
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
|
||||
}
|
||||
|
||||
/// @dev Grants an unlimited allowance to the exchange for its token
|
||||
/// on behalf of this contract.
|
||||
/// @param exchange The Uniswap token exchange.
|
||||
/// @param tokenAddress The token address for the exchange.
|
||||
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
|
||||
private
|
||||
{
|
||||
LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1));
|
||||
}
|
||||
|
||||
/// @dev Retrieves the uniswap exchange for a given token pair.
|
||||
/// In the case of a WETH-token exchange, this will be the non-WETH token.
|
||||
/// In th ecase of a token-token exchange, this will be the first token.
|
||||
/// @param fromTokenAddress The address of the token we are converting from.
|
||||
/// @param toTokenAddress The address of the token we are converting to.
|
||||
/// @return exchange The uniswap exchange.
|
||||
function _getUniswapExchangeForTokenPair(
|
||||
address fromTokenAddress,
|
||||
address toTokenAddress
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (IUniswapExchange exchange)
|
||||
{
|
||||
address exchangeTokenAddress = fromTokenAddress;
|
||||
// Whichever isn't WETH is the exchange token.
|
||||
if (fromTokenAddress == address(getWethContract())) {
|
||||
exchangeTokenAddress = toTokenAddress;
|
||||
}
|
||||
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
|
||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||
return exchange;
|
||||
}
|
||||
}
|
@@ -31,7 +31,7 @@ contract IERC20Bridge {
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
/// @return success The magic bytes `0x37708e9b` if successful.
|
||||
function withdrawTo(
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
interface IUniswapExchange {
|
||||
|
||||
/// @dev Buys at least `minTokensBought` tokens with ETH and transfer them
|
||||
/// to `recipient`.
|
||||
/// @param minTokensBought The minimum number of tokens to buy.
|
||||
/// @param deadline Time when this order expires.
|
||||
/// @param recipient Who to transfer the tokens to.
|
||||
/// @return tokensBought Amount of tokens bought.
|
||||
function ethToTokenTransferInput(
|
||||
uint256 minTokensBought,
|
||||
uint256 deadline,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 tokensBought);
|
||||
|
||||
/// @dev Buys at least `minEthBought` ETH with tokens.
|
||||
/// @param tokensSold Amount of tokens to sell.
|
||||
/// @param minEthBought The minimum amount of ETH to buy.
|
||||
/// @param deadline Time when this order expires.
|
||||
/// @return ethBought Amount of tokens bought.
|
||||
function tokenToEthSwapInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
returns (uint256 ethBought);
|
||||
|
||||
/// @dev Buys at least `minTokensBought` tokens with the exchange token
|
||||
/// and transfer them to `recipient`.
|
||||
/// @param minTokensBought The minimum number of tokens to buy.
|
||||
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
||||
/// @param deadline Time when this order expires.
|
||||
/// @param recipient Who to transfer the tokens to.
|
||||
/// @param toTokenAddress The token being bought.
|
||||
/// @return tokensBought Amount of tokens bought.
|
||||
function tokenToTokenTransferInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minTokensBought,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline,
|
||||
address recipient,
|
||||
address toTokenAddress
|
||||
)
|
||||
external
|
||||
returns (uint256 tokensBought);
|
||||
|
||||
/// @dev Retrieves the token that is associated with this exchange.
|
||||
/// @return tokenAddress The token address.
|
||||
function toTokenAddress()
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress);
|
||||
}
|
@@ -18,28 +18,15 @@
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "./IUniswapExchange.sol";
|
||||
|
||||
// solhint-disable payable-fallback
|
||||
contract TestLibProxyReceiver {
|
||||
|
||||
function ()
|
||||
interface IUniswapExchangeFactory {
|
||||
|
||||
/// @dev Get the exchange for a token.
|
||||
/// @param tokenAddress The address of the token contract.
|
||||
function getExchange(address tokenAddress)
|
||||
external
|
||||
{
|
||||
// Done in assembly to allow the return.
|
||||
assembly {
|
||||
let calldataSize := calldatasize()
|
||||
|
||||
// Copy all calldata into memory.
|
||||
calldatacopy(0, 0, calldataSize)
|
||||
|
||||
// If the calldatasize is equal to 4, revert.
|
||||
// This allows us to test `proxyCall` with reverts.
|
||||
if eq(calldataSize, 4) {
|
||||
revert(0, 4)
|
||||
}
|
||||
|
||||
// Return. This allows us to test `proxyCall` with returns.
|
||||
return(0, calldataSize)
|
||||
}
|
||||
}
|
||||
view
|
||||
returns (IUniswapExchange);
|
||||
}
|
@@ -72,7 +72,7 @@ contract TestERC20Bridge is
|
||||
testToken.setBalance(owner, balance);
|
||||
}
|
||||
|
||||
function withdrawTo(
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
|
432
contracts/asset-proxy/contracts/test/TestUniswapBridge.sol
Normal file
432
contracts/asset-proxy/contracts/test/TestUniswapBridge.sol
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "../src/bridges/UniswapBridge.sol";
|
||||
import "../src/interfaces/IUniswapExchangeFactory.sol";
|
||||
import "../src/interfaces/IUniswapExchange.sol";
|
||||
|
||||
|
||||
// solhint-disable no-simple-event-func-name
|
||||
contract TestEventsRaiser {
|
||||
|
||||
event TokenTransfer(
|
||||
address token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event TokenApprove(
|
||||
address spender,
|
||||
uint256 allowance
|
||||
);
|
||||
|
||||
event WethDeposit(
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event WethWithdraw(
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event EthToTokenTransferInput(
|
||||
address exchange,
|
||||
uint256 minTokensBought,
|
||||
uint256 deadline,
|
||||
address recipient
|
||||
);
|
||||
|
||||
event TokenToEthSwapInput(
|
||||
address exchange,
|
||||
uint256 tokensSold,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline
|
||||
);
|
||||
|
||||
event TokenToTokenTransferInput(
|
||||
address exchange,
|
||||
uint256 tokensSold,
|
||||
uint256 minTokensBought,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline,
|
||||
address recipient,
|
||||
address toTokenAddress
|
||||
);
|
||||
|
||||
function raiseEthToTokenTransferInput(
|
||||
uint256 minTokensBought,
|
||||
uint256 deadline,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
{
|
||||
emit EthToTokenTransferInput(
|
||||
msg.sender,
|
||||
minTokensBought,
|
||||
deadline,
|
||||
recipient
|
||||
);
|
||||
}
|
||||
|
||||
function raiseTokenToEthSwapInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
{
|
||||
emit TokenToEthSwapInput(
|
||||
msg.sender,
|
||||
tokensSold,
|
||||
minEthBought,
|
||||
deadline
|
||||
);
|
||||
}
|
||||
|
||||
function raiseTokenToTokenTransferInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minTokensBought,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline,
|
||||
address recipient,
|
||||
address toTokenAddress
|
||||
)
|
||||
external
|
||||
{
|
||||
emit TokenToTokenTransferInput(
|
||||
msg.sender,
|
||||
tokensSold,
|
||||
minTokensBought,
|
||||
minEthBought,
|
||||
deadline,
|
||||
recipient,
|
||||
toTokenAddress
|
||||
);
|
||||
}
|
||||
|
||||
function raiseTokenTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
{
|
||||
emit TokenTransfer(
|
||||
msg.sender,
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
function raiseTokenApprove(address spender, uint256 allowance)
|
||||
external
|
||||
{
|
||||
emit TokenApprove(spender, allowance);
|
||||
}
|
||||
|
||||
function raiseWethDeposit(uint256 amount)
|
||||
external
|
||||
{
|
||||
emit WethDeposit(amount);
|
||||
}
|
||||
|
||||
function raiseWethWithdraw(uint256 amount)
|
||||
external
|
||||
{
|
||||
emit WethWithdraw(amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @dev A minimalist ERC20/WETH token.
|
||||
contract TestToken {
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
mapping (address => uint256) public balances;
|
||||
string private _nextRevertReason;
|
||||
|
||||
/// @dev Set the balance for `owner`.
|
||||
function setBalance(address owner)
|
||||
external
|
||||
payable
|
||||
{
|
||||
balances[owner] = msg.value;
|
||||
}
|
||||
|
||||
/// @dev Set the revert reason for `transfer()`,
|
||||
/// `deposit()`, and `withdraw()`.
|
||||
function setRevertReason(string calldata reason)
|
||||
external
|
||||
{
|
||||
_nextRevertReason = reason;
|
||||
}
|
||||
|
||||
/// @dev Just calls `raiseTokenTransfer()` on the caller.
|
||||
function transfer(address to, uint256 amount)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
_revertIfReasonExists();
|
||||
TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Just calls `raiseTokenApprove()` on the caller.
|
||||
function approve(address spender, uint256 allowance)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev `IWETH.deposit()` that increases balances and calls
|
||||
/// `raiseWethDeposit()` on the caller.
|
||||
function deposit()
|
||||
external
|
||||
payable
|
||||
{
|
||||
_revertIfReasonExists();
|
||||
balances[msg.sender] += balances[msg.sender].safeAdd(msg.value);
|
||||
TestEventsRaiser(msg.sender).raiseWethDeposit(msg.value);
|
||||
}
|
||||
|
||||
/// @dev `IWETH.withdraw()` that just reduces balances and calls
|
||||
/// `raiseWethWithdraw()` on the caller.
|
||||
function withdraw(uint256 amount)
|
||||
external
|
||||
{
|
||||
_revertIfReasonExists();
|
||||
balances[msg.sender] = balances[msg.sender].safeSub(amount);
|
||||
msg.sender.transfer(amount);
|
||||
TestEventsRaiser(msg.sender).raiseWethWithdraw(amount);
|
||||
}
|
||||
|
||||
/// @dev Retrieve the balance for `owner`.
|
||||
function balanceOf(address owner)
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return balances[owner];
|
||||
}
|
||||
|
||||
function _revertIfReasonExists()
|
||||
private
|
||||
view
|
||||
{
|
||||
if (bytes(_nextRevertReason).length != 0) {
|
||||
revert(_nextRevertReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestExchange is
|
||||
IUniswapExchange
|
||||
{
|
||||
address public tokenAddress;
|
||||
string private _nextRevertReason;
|
||||
|
||||
constructor(address _tokenAddress) public {
|
||||
tokenAddress = _tokenAddress;
|
||||
}
|
||||
|
||||
function setFillBehavior(
|
||||
string calldata revertReason
|
||||
)
|
||||
external
|
||||
payable
|
||||
{
|
||||
_nextRevertReason = revertReason;
|
||||
}
|
||||
|
||||
function ethToTokenTransferInput(
|
||||
uint256 minTokensBought,
|
||||
uint256 deadline,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 tokensBought)
|
||||
{
|
||||
TestEventsRaiser(msg.sender).raiseEthToTokenTransferInput(
|
||||
minTokensBought,
|
||||
deadline,
|
||||
recipient
|
||||
);
|
||||
_revertIfReasonExists();
|
||||
return address(this).balance;
|
||||
}
|
||||
|
||||
function tokenToEthSwapInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
returns (uint256 ethBought)
|
||||
{
|
||||
TestEventsRaiser(msg.sender).raiseTokenToEthSwapInput(
|
||||
tokensSold,
|
||||
minEthBought,
|
||||
deadline
|
||||
);
|
||||
_revertIfReasonExists();
|
||||
uint256 fillAmount = address(this).balance;
|
||||
msg.sender.transfer(fillAmount);
|
||||
return fillAmount;
|
||||
}
|
||||
|
||||
function tokenToTokenTransferInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minTokensBought,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline,
|
||||
address recipient,
|
||||
address toTokenAddress
|
||||
)
|
||||
external
|
||||
returns (uint256 tokensBought)
|
||||
{
|
||||
TestEventsRaiser(msg.sender).raiseTokenToTokenTransferInput(
|
||||
tokensSold,
|
||||
minTokensBought,
|
||||
minEthBought,
|
||||
deadline,
|
||||
recipient,
|
||||
toTokenAddress
|
||||
);
|
||||
_revertIfReasonExists();
|
||||
return address(this).balance;
|
||||
}
|
||||
|
||||
function toTokenAddress()
|
||||
external
|
||||
view
|
||||
returns (address _tokenAddress)
|
||||
{
|
||||
return tokenAddress;
|
||||
}
|
||||
|
||||
function _revertIfReasonExists()
|
||||
private
|
||||
view
|
||||
{
|
||||
if (bytes(_nextRevertReason).length != 0) {
|
||||
revert(_nextRevertReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @dev UniswapBridge overridden to mock tokens and implement IUniswapExchangeFactory.
|
||||
contract TestUniswapBridge is
|
||||
IUniswapExchangeFactory,
|
||||
TestEventsRaiser,
|
||||
UniswapBridge
|
||||
{
|
||||
TestToken public wethToken;
|
||||
// Token address to TestToken instance.
|
||||
mapping (address => TestToken) private _testTokens;
|
||||
// Token address to TestExchange instance.
|
||||
mapping (address => TestExchange) private _testExchanges;
|
||||
|
||||
constructor() public {
|
||||
wethToken = new TestToken();
|
||||
_testTokens[address(wethToken)] = wethToken;
|
||||
}
|
||||
|
||||
/// @dev Sets the balance of this contract for an existing token.
|
||||
/// The wei attached will be the balance.
|
||||
function setTokenBalance(address tokenAddress)
|
||||
external
|
||||
payable
|
||||
{
|
||||
TestToken token = _testTokens[tokenAddress];
|
||||
token.deposit.value(msg.value)();
|
||||
}
|
||||
|
||||
/// @dev Sets the revert reason for an existing token.
|
||||
function setTokenRevertReason(address tokenAddress, string calldata revertReason)
|
||||
external
|
||||
{
|
||||
TestToken token = _testTokens[tokenAddress];
|
||||
token.setRevertReason(revertReason);
|
||||
}
|
||||
|
||||
/// @dev Create a token and exchange (if they don't exist) for a new token
|
||||
/// and sets the exchange revert and fill behavior. The wei attached
|
||||
/// will be the fill amount for the exchange.
|
||||
/// @param tokenAddress The token address. If zero, one will be created.
|
||||
/// @param revertReason The revert reason for exchange operations.
|
||||
function createTokenAndExchange(
|
||||
address tokenAddress,
|
||||
string calldata revertReason
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (TestToken token, TestExchange exchange)
|
||||
{
|
||||
token = TestToken(tokenAddress);
|
||||
if (tokenAddress == address(0)) {
|
||||
token = new TestToken();
|
||||
}
|
||||
_testTokens[address(token)] = token;
|
||||
exchange = _testExchanges[address(token)];
|
||||
if (address(exchange) == address(0)) {
|
||||
_testExchanges[address(token)] = exchange = new TestExchange(address(token));
|
||||
}
|
||||
exchange.setFillBehavior.value(msg.value)(revertReason);
|
||||
return (token, exchange);
|
||||
}
|
||||
|
||||
/// @dev `IUniswapExchangeFactory.getExchange`
|
||||
function getExchange(address tokenAddress)
|
||||
external
|
||||
view
|
||||
returns (IUniswapExchange)
|
||||
{
|
||||
return IUniswapExchange(_testExchanges[tokenAddress]);
|
||||
}
|
||||
|
||||
// @dev Use `wethToken`.
|
||||
function getWethContract()
|
||||
public
|
||||
view
|
||||
returns (IEtherToken)
|
||||
{
|
||||
return IEtherToken(address(wethToken));
|
||||
}
|
||||
|
||||
// @dev This contract will double as the Uniswap contract.
|
||||
function getUniswapExchangeFactoryContract()
|
||||
public
|
||||
view
|
||||
returns (IUniswapExchangeFactory)
|
||||
{
|
||||
return IUniswapExchangeFactory(address(this));
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "2.3.0-beta.0",
|
||||
"version": "2.3.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
@@ -35,7 +35,7 @@
|
||||
"compile:truffle": "truffle compile"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IWallet|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget).json",
|
||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -48,12 +48,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -71,17 +71,18 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-erc1155": "^1.2.0-beta.0",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.0",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/order-utils": "^8.5.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
|
||||
"@0x/contracts-erc1155": "^1.2.0-beta.1",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.1",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.1",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||
"@0x/order-utils": "^8.5.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
|
@@ -16,7 +16,8 @@ import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispat
|
||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
|
||||
import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json';
|
||||
import * as IWallet from '../generated-artifacts/IWallet.json';
|
||||
import * as IUniswapExchange from '../generated-artifacts/IUniswapExchange.json';
|
||||
import * as IUniswapExchangeFactory from '../generated-artifacts/IUniswapExchangeFactory.json';
|
||||
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
@@ -25,6 +26,8 @@ import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
|
||||
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
|
||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as TestUniswapBridge from '../generated-artifacts/TestUniswapBridge.json';
|
||||
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
|
||||
export const artifacts = {
|
||||
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||
@@ -36,14 +39,17 @@ export const artifacts = {
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||
IWallet: IWallet as ContractArtifact,
|
||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
};
|
||||
|
@@ -14,7 +14,8 @@ export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
||||
export * from '../generated-wrappers/i_authorizable';
|
||||
export * from '../generated-wrappers/i_erc20_bridge';
|
||||
export * from '../generated-wrappers/i_eth2_dai';
|
||||
export * from '../generated-wrappers/i_wallet';
|
||||
export * from '../generated-wrappers/i_uniswap_exchange';
|
||||
export * from '../generated-wrappers/i_uniswap_exchange_factory';
|
||||
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||
export * from '../generated-wrappers/mixin_authorizable';
|
||||
export * from '../generated-wrappers/multi_asset_proxy';
|
||||
@@ -23,3 +24,5 @@ export * from '../generated-wrappers/static_call_proxy';
|
||||
export * from '../generated-wrappers/test_erc20_bridge';
|
||||
export * from '../generated-wrappers/test_eth2_dai_bridge';
|
||||
export * from '../generated-wrappers/test_static_call_target';
|
||||
export * from '../generated-wrappers/test_uniswap_bridge';
|
||||
export * from '../generated-wrappers/uniswap_bridge';
|
||||
|
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { chaiSetup, expectTransactionFailedAsync, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
@@ -60,21 +53,13 @@ describe('Authorizable', () => {
|
||||
});
|
||||
|
||||
it('should allow owner to add an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.true();
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to authorize a duplicate address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
RevertReason.TargetAlreadyAuthorized,
|
||||
@@ -84,11 +69,7 @@ describe('Authorizable', () => {
|
||||
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: notOwner }),
|
||||
RevertReason.OnlyContractOwner,
|
||||
@@ -96,16 +77,8 @@ describe('Authorizable', () => {
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
@@ -122,11 +95,7 @@ describe('Authorizable', () => {
|
||||
|
||||
describe('removeAuthorizedAddressAtIndex', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const index = new BigNumber(0);
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
@@ -137,11 +106,7 @@ describe('Authorizable', () => {
|
||||
});
|
||||
|
||||
it('should revert if index is >= authorities.length', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const index = new BigNumber(1);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
@@ -164,16 +129,8 @@ describe('Authorizable', () => {
|
||||
it('should revert if address at index does not match target', async () => {
|
||||
const address1 = address;
|
||||
const address2 = notOwner;
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address1,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address2,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address1, { from: owner });
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address2, { from: owner });
|
||||
const address1Index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
|
||||
@@ -184,18 +141,11 @@ describe('Authorizable', () => {
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const index = new BigNumber(0);
|
||||
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
index,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(address, index, {
|
||||
from: owner,
|
||||
});
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
@@ -205,19 +155,11 @@ describe('Authorizable', () => {
|
||||
it('should return all authorized addresses', async () => {
|
||||
const initial = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(initial).to.have.length(0);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(afterAdd).to.have.length(1);
|
||||
expect(afterAdd).to.include(address);
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(afterRemove).to.have.length(0);
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import {
|
||||
artifacts as erc1155Artifacts,
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
@@ -23,7 +23,7 @@ import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper } from '../src';
|
||||
import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper, IAssetDataContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@@ -59,6 +59,8 @@ describe('ERC1155Proxy', () => {
|
||||
// tokens
|
||||
let fungibleTokens: BigNumber[];
|
||||
let nonFungibleTokensOwnedBySpender: BigNumber[];
|
||||
// devUtils for encoding and decoding assetData
|
||||
let devUtils: DevUtilsContract;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -72,16 +74,8 @@ describe('ERC1155Proxy', () => {
|
||||
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
erc1155Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(erc1155Proxy.address, { from: owner });
|
||||
// deploy & configure ERC1155 tokens and receiver
|
||||
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||
erc1155Contract = erc1155Wrapper.getContract();
|
||||
@@ -103,6 +97,8 @@ describe('ERC1155Proxy', () => {
|
||||
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||
});
|
||||
// set up devUtils
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, { from: owner });
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -638,7 +634,7 @@ describe('ERC1155Proxy', () => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -696,25 +692,18 @@ describe('ERC1155Proxy', () => {
|
||||
const tokenUri = '';
|
||||
for (const tokenToCreate of tokensToCreate) {
|
||||
// create token
|
||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
tokenUri,
|
||||
{
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
[spender],
|
||||
[spenderInitialBalance],
|
||||
{
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
}
|
||||
///// Step 2/5 /////
|
||||
// Check balances before transfer
|
||||
@@ -747,18 +736,16 @@ describe('ERC1155Proxy', () => {
|
||||
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
|
||||
const valuesToTransfer = tokensToTransfer;
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// remove the function selector and contract address from check, as these change on each test
|
||||
const offsetToTokenIds = 74;
|
||||
const assetDataWithoutContractAddress = assetData.substr(offsetToTokenIds);
|
||||
const expectedAssetDataWithoutContractAddress =
|
||||
|
||||
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/encodeERC1155AssetData) does not use optimized encoding
|
||||
const assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
const selector = assetDataContract.ERC1155Assets.getSelector();
|
||||
const assetDataWithoutContractAddress =
|
||||
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
|
||||
expect(assetDataWithoutContractAddress).to.be.equal(expectedAssetDataWithoutContractAddress);
|
||||
const assetData = `${selector}000000000000000000000000${erc1155ContractAddress.substr(
|
||||
2,
|
||||
)}${assetDataWithoutContractAddress}`;
|
||||
|
||||
///// Step 4/5 /////
|
||||
// Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2;
|
||||
// the expected trade will be token IDs [1, 2] and amounts [2, 4]
|
||||
@@ -805,25 +792,18 @@ describe('ERC1155Proxy', () => {
|
||||
const tokenUri = '';
|
||||
for (const tokenToCreate of tokensToCreate) {
|
||||
// create token
|
||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
tokenUri,
|
||||
{
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
[spender],
|
||||
[spenderInitialBalance],
|
||||
{
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
}
|
||||
///// Step 2/5 /////
|
||||
// Check balances before transfer
|
||||
@@ -867,7 +847,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// create callback data that is the encoded version of `valuesToTransfer`
|
||||
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const generatedAssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -937,25 +917,18 @@ describe('ERC1155Proxy', () => {
|
||||
const tokenUri = '';
|
||||
for (const tokenToCreate of tokensToCreate) {
|
||||
// create token
|
||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
tokenUri,
|
||||
{
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
[spender],
|
||||
[spenderInitialBalance],
|
||||
{
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
}
|
||||
///// Step 2/5 /////
|
||||
// Check balances before transfer
|
||||
@@ -996,7 +969,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// create callback data that is the encoded version of `valuesToTransfer`
|
||||
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const generatedAssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1059,7 +1032,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1106,7 +1079,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1157,7 +1130,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1208,7 +1181,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1259,7 +1232,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1311,7 +1284,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1358,7 +1331,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1409,7 +1382,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1456,7 +1429,7 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1507,13 +1480,13 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
|
||||
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
@@ -1538,13 +1511,13 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
|
||||
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
@@ -1667,13 +1640,9 @@ describe('ERC1155Proxy', () => {
|
||||
it('should propagate revert reason from erc1155 contract failure', async () => {
|
||||
// disable transfers
|
||||
const shouldRejectTransfer = true;
|
||||
await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(
|
||||
shouldRejectTransfer,
|
||||
{
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(shouldRejectTransfer, {
|
||||
from: owner,
|
||||
});
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
|
@@ -11,7 +11,7 @@ import {
|
||||
TransactionHelper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { BigNumber, RawRevertError } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
@@ -45,7 +45,7 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('withdrawTo()', () => {
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
interface WithdrawToOpts {
|
||||
toTokenAddress?: string;
|
||||
fromTokenAddress?: string;
|
||||
@@ -103,9 +103,9 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
_opts.toTokentransferRevertReason,
|
||||
_opts.toTokenTransferReturnData,
|
||||
);
|
||||
// Call withdrawTo().
|
||||
// Call bridgeTransferFrom().
|
||||
const [result, { logs }] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.withdrawTo,
|
||||
testContract.bridgeTransferFrom,
|
||||
// "to" token address
|
||||
_opts.toTokenAddress,
|
||||
// Random from address.
|
||||
@@ -179,14 +179,14 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
return expect(tx).to.revertWith(opts.toTokentransferRevertReason);
|
||||
});
|
||||
|
||||
it('fails if `toTokenAddress.transfer()` returns falsey', async () => {
|
||||
it('fails if `toTokenAddress.transfer()` returns false', async () => {
|
||||
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexLeftPad(0) });
|
||||
const tx = withdrawToAsync(opts);
|
||||
return expect(tx).to.revertWith('ERC20_TRANSFER_FAILED');
|
||||
return expect(tx).to.revertWith(new RawRevertError(hexLeftPad(0)));
|
||||
});
|
||||
|
||||
it('succeeds if `toTokenAddress.transfer()` returns truthy', async () => {
|
||||
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(100) });
|
||||
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
|
||||
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(1) });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
artifacts as erc20Artifacts,
|
||||
@@ -22,7 +23,6 @@ import {
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
@@ -56,6 +56,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
let fromAddress: string;
|
||||
let toAddress: string;
|
||||
|
||||
let devUtils: DevUtilsContract;
|
||||
let erc20TokenA: DummyERC20TokenContract;
|
||||
let erc20TokenB: DummyERC20TokenContract;
|
||||
let erc721TokenA: DummyERC721TokenContract;
|
||||
@@ -91,6 +92,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5));
|
||||
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
||||
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
|
||||
|
||||
@@ -105,64 +107,24 @@ describe('Asset Transfer Proxies', () => {
|
||||
);
|
||||
|
||||
// Configure ERC20Proxy
|
||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
multiAssetProxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
|
||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner });
|
||||
|
||||
// Configure ERC721Proxy
|
||||
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
multiAssetProxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
|
||||
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner });
|
||||
|
||||
// Configure ERC115Proxy
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
multiAssetProxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner });
|
||||
|
||||
// Configure MultiAssetProxy
|
||||
await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||
erc721Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||
erc1155Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, { from: owner });
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address, { from: owner });
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc1155Proxy.address, { from: owner });
|
||||
|
||||
// Deploy and configure ERC20 tokens
|
||||
const numDummyErc20ToDeploy = 2;
|
||||
@@ -192,19 +154,13 @@ describe('Asset Transfer Proxies', () => {
|
||||
);
|
||||
|
||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||
await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
|
||||
fromAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync(fromAddress, constants.INITIAL_ERC20_BALANCE, {
|
||||
from: owner,
|
||||
});
|
||||
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await multipleReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
|
||||
fromAddress,
|
||||
@@ -212,13 +168,11 @@ describe('Asset Transfer Proxies', () => {
|
||||
{
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await multipleReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Deploy and configure ERC721 tokens and receiver
|
||||
@@ -278,7 +232,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
describe('transferFrom', () => {
|
||||
it('should successfully transfer tokens', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
// Perform a transfer from fromAddress to toAddress
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const amount = new BigNumber(10);
|
||||
@@ -308,7 +262,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should successfully transfer tokens that do not return a value', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
|
||||
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(noReturnErc20Token.address);
|
||||
// Perform a transfer from fromAddress to toAddress
|
||||
const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
|
||||
const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
|
||||
@@ -337,7 +291,9 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should successfully transfer tokens and ignore extra assetData', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const extraData = '0102030405060708';
|
||||
const encodedAssetData = `${assetDataUtils.encodeERC20AssetData(erc20TokenA.address)}${extraData}`;
|
||||
const encodedAssetData = `${await devUtils.encodeERC20AssetData.callAsync(
|
||||
erc20TokenA.address,
|
||||
)}${extraData}`;
|
||||
// Perform a transfer from fromAddress to toAddress
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const amount = new BigNumber(10);
|
||||
@@ -367,7 +323,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should do nothing if transferring 0 amount of a token', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
// Perform a transfer from fromAddress to toAddress
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const amount = new BigNumber(0);
|
||||
@@ -397,7 +353,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if allowances are too low', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
// Create allowance less than transfer amount. Set allowance on proxy.
|
||||
const allowance = new BigNumber(0);
|
||||
const amount = new BigNumber(10);
|
||||
@@ -407,12 +363,9 @@ describe('Asset Transfer Proxies', () => {
|
||||
toAddress,
|
||||
amount,
|
||||
);
|
||||
await erc20TokenA.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
allowance,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc20TokenA.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: fromAddress,
|
||||
});
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
// Perform a transfer; expect this to fail.
|
||||
await expectTransactionFailedAsync(
|
||||
@@ -429,7 +382,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if allowances are too low and token does not return a value', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
|
||||
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(noReturnErc20Token.address);
|
||||
// Create allowance less than transfer amount. Set allowance on proxy.
|
||||
const allowance = new BigNumber(0);
|
||||
const amount = new BigNumber(10);
|
||||
@@ -439,12 +392,9 @@ describe('Asset Transfer Proxies', () => {
|
||||
toAddress,
|
||||
amount,
|
||||
);
|
||||
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
allowance,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: fromAddress,
|
||||
});
|
||||
const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
|
||||
const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
|
||||
// Perform a transfer; expect this to fail.
|
||||
@@ -464,7 +414,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if caller is not authorized', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
// Perform a transfer from fromAddress to toAddress
|
||||
const amount = new BigNumber(10);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
@@ -488,7 +438,9 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if token returns more than 32 bytes', async () => {
|
||||
// Construct ERC20 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC20AssetData(multipleReturnErc20Token.address);
|
||||
const encodedAssetData = await devUtils.encodeERC20AssetData.callAsync(
|
||||
multipleReturnErc20Token.address,
|
||||
);
|
||||
const amount = new BigNumber(10);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
encodedAssetData,
|
||||
@@ -535,7 +487,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
describe('transferFrom', () => {
|
||||
it('should successfully transfer tokens', async () => {
|
||||
// Construct ERC721 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
// Verify pre-condition
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
@@ -563,7 +518,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should successfully transfer tokens and ignore extra assetData', async () => {
|
||||
// Construct ERC721 asset data
|
||||
const extraData = '0102030405060708';
|
||||
const encodedAssetData = `${assetDataUtils.encodeERC721AssetData(
|
||||
const encodedAssetData = `${await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
)}${extraData}`;
|
||||
@@ -593,7 +548,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should not call onERC721Received when transferring to a smart contract', async () => {
|
||||
// Construct ERC721 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
// Verify pre-condition
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
@@ -623,7 +581,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if transferring 0 amount of a token', async () => {
|
||||
// Construct ERC721 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
// Verify pre-condition
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
@@ -649,7 +610,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if transferring > 1 amount of a token', async () => {
|
||||
// Construct ERC721 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
// Verify pre-condition
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
@@ -675,24 +639,21 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if allowances are too low', async () => {
|
||||
// Construct ERC721 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
// Verify pre-condition
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
// Remove blanket transfer approval for fromAddress.
|
||||
await erc721TokenA.setApprovalForAll.awaitTransactionSuccessAsync(
|
||||
erc721Proxy.address,
|
||||
false,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc721TokenA.setApprovalForAll.awaitTransactionSuccessAsync(erc721Proxy.address, false, {
|
||||
from: fromAddress,
|
||||
});
|
||||
// Remove token transfer approval for fromAddress.
|
||||
await erc721TokenA.approve.awaitTransactionSuccessAsync(
|
||||
constants.NULL_ADDRESS,
|
||||
erc721AFromTokenId,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc721TokenA.approve.awaitTransactionSuccessAsync(constants.NULL_ADDRESS, erc721AFromTokenId, {
|
||||
from: fromAddress,
|
||||
});
|
||||
// Perform a transfer; expect this to fail.
|
||||
const amount = new BigNumber(1);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
@@ -715,7 +676,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
|
||||
it('should revert if caller is not authorized', async () => {
|
||||
// Construct ERC721 asset data
|
||||
const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const encodedAssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
// Verify pre-condition
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
@@ -762,10 +726,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should transfer a single ERC20 token', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const amounts = [erc20Amount];
|
||||
const nestedAssetData = [erc20AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -793,7 +757,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should dispatch an ERC20 transfer when input amount is 0', async () => {
|
||||
const inputAmount = constants.ZERO_AMOUNT;
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const amounts = [erc20Amount];
|
||||
const nestedAssetData = [erc20AssetData];
|
||||
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
|
||||
@@ -824,11 +788,11 @@ describe('Asset Transfer Proxies', () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount1 = new BigNumber(10);
|
||||
const erc20Amount2 = new BigNumber(20);
|
||||
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const amounts = [erc20Amount1, erc20Amount2];
|
||||
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -857,11 +821,11 @@ describe('Asset Transfer Proxies', () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount1 = new BigNumber(10);
|
||||
const erc20Amount2 = new BigNumber(20);
|
||||
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
|
||||
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenB.address);
|
||||
const amounts = [erc20Amount1, erc20Amount2];
|
||||
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -896,10 +860,13 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should transfer a single ERC721 token', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc721Amount];
|
||||
const nestedAssetData = [erc721AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -922,8 +889,11 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should successfully transfer multiple of the same ERC721 token', async () => {
|
||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
|
||||
const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(
|
||||
const erc721AssetData1 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const erc721AssetData2 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId2,
|
||||
);
|
||||
@@ -931,7 +901,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const amounts = [erc721Amount, erc721Amount];
|
||||
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -957,13 +927,19 @@ describe('Asset Transfer Proxies', () => {
|
||||
expect(newOwnerFromAsset2).to.be.equal(toAddress);
|
||||
});
|
||||
it('should successfully transfer multiple different ERC721 tokens', async () => {
|
||||
const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
|
||||
const erc721AssetData1 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const erc721AssetData2 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenB.address,
|
||||
erc721BFromTokenId,
|
||||
);
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const amounts = [erc721Amount, erc721Amount];
|
||||
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1004,7 +980,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1014,7 +990,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1060,7 +1036,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1070,7 +1046,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1124,7 +1100,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1134,7 +1110,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
const multiAssetAmount = new BigNumber(1);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1181,13 +1157,13 @@ describe('Asset Transfer Proxies', () => {
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData1 = assetDataUtils.encodeERC1155AssetData(
|
||||
const erc1155AssetData1 = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const erc1155AssetData2 = assetDataUtils.encodeERC1155AssetData(
|
||||
const erc1155AssetData2 = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155Contract2.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -1197,7 +1173,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier, valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData1, erc1155AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1228,15 +1204,18 @@ describe('Asset Transfer Proxies', () => {
|
||||
// setup test parameters
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const erc1155TokenHolders = [fromAddress, toAddress];
|
||||
const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const erc1155ValuesToTransfer = [new BigNumber(25)];
|
||||
const erc1155Amount = new BigNumber(23);
|
||||
const erc1155ReceiverCallbackData = '0x0102030405';
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
const erc1155AssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155Contract.address,
|
||||
erc1155TokensToTransfer,
|
||||
erc1155ValuesToTransfer,
|
||||
@@ -1244,7 +1223,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount, erc1155Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1299,12 +1278,15 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1336,13 +1318,19 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should successfully transfer tokens and ignore extra assetData', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
const extraData = '0102030405060708090001020304050607080900010203040506070809000102';
|
||||
const assetData = `${assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`;
|
||||
const assetData = `${await devUtils.encodeMultiAssetData.callAsync(
|
||||
amounts,
|
||||
nestedAssetData,
|
||||
)}${extraData}`;
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1375,11 +1363,11 @@ describe('Asset Transfer Proxies', () => {
|
||||
const inputAmount = new BigNumber(100);
|
||||
const erc20Amount1 = new BigNumber(10);
|
||||
const erc20Amount2 = new BigNumber(20);
|
||||
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
|
||||
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenB.address);
|
||||
const amounts = [erc20Amount1, erc20Amount2];
|
||||
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1415,19 +1403,25 @@ describe('Asset Transfer Proxies', () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount1 = new BigNumber(10);
|
||||
const erc20Amount2 = new BigNumber(20);
|
||||
const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
|
||||
const erc20AssetData1 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc20AssetData2 = await devUtils.encodeERC20AssetData.callAsync(erc20TokenB.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
|
||||
const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1];
|
||||
const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(
|
||||
const erc721AssetData1 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const erc721AssetData2 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId2,
|
||||
);
|
||||
const erc721AssetData3 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
|
||||
const erc721AssetData4 = assetDataUtils.encodeERC721AssetData(
|
||||
const erc721AssetData3 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenB.address,
|
||||
erc721BFromTokenId,
|
||||
);
|
||||
const erc721AssetData4 = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenB.address,
|
||||
erc721BFromTokenId2,
|
||||
);
|
||||
@@ -1440,7 +1434,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
erc721AssetData3,
|
||||
erc721AssetData4,
|
||||
];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1492,13 +1486,16 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if a single transfer fails', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
// 2 is an invalid erc721 amount
|
||||
const erc721Amount = new BigNumber(2);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1517,15 +1514,17 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if an AssetProxy is not registered', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const invalidProxyId = '0x12345678';
|
||||
const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`;
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, invalidErc721AssetData];
|
||||
// HACK: This is used to get around validation built into assetDataUtils
|
||||
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1544,12 +1543,14 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
// HACK: This is used to get around validation built into assetDataUtils
|
||||
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1568,10 +1569,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if amounts multiplication results in an overflow', async () => {
|
||||
const inputAmount = new BigNumber(2).pow(128);
|
||||
const erc20Amount = new BigNumber(2).pow(128);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const amounts = [erc20Amount];
|
||||
const nestedAssetData = [erc20AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1590,13 +1591,12 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = '0x123456';
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
// HACK: This is used to get around validation built into assetDataUtils
|
||||
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1615,12 +1615,15 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if caller is not authorized', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1639,12 +1642,15 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if asset data overflows beyond the bounds of calldata', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1669,12 +1675,15 @@ describe('Asset Transfer Proxies', () => {
|
||||
it('should revert if asset data resolves to a location beyond the bounds of calldata', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
@@ -1700,12 +1709,15 @@ describe('Asset Transfer Proxies', () => {
|
||||
// setup test parameters
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc20AssetData = await devUtils.encodeERC20AssetData.callAsync(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc721AssetData = await devUtils.encodeERC721AssetData.callAsync(
|
||||
erc721TokenA.address,
|
||||
erc721AFromTokenId,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const assetData = await devUtils.encodeMultiAssetData.callAsync(amounts, nestedAssetData);
|
||||
const extraData = '01';
|
||||
const assetDataWithExtraData = `${assetData}${extraData}`;
|
||||
const badData = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
@@ -25,6 +25,7 @@ describe('StaticCallProxy', () => {
|
||||
let fromAddress: string;
|
||||
let toAddress: string;
|
||||
|
||||
let devUtils: DevUtilsContract;
|
||||
let staticCallProxy: IAssetProxyContract;
|
||||
let staticCallTarget: TestStaticCallTargetContract;
|
||||
|
||||
@@ -43,6 +44,7 @@ describe('StaticCallProxy', () => {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
staticCallProxy = new IAssetProxyContract(
|
||||
staticCallProxyWithoutTransferFrom.address,
|
||||
provider,
|
||||
@@ -86,7 +88,7 @@ describe('StaticCallProxy', () => {
|
||||
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -114,9 +116,11 @@ describe('StaticCallProxy', () => {
|
||||
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
||||
const staticCallData = constants.NULL_BYTES;
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.slice(0, -128);
|
||||
const assetData = (await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
)).slice(0, -128);
|
||||
const assetDataByteLen = (assetData.length - 2) / 2;
|
||||
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
@@ -126,7 +130,7 @@ describe('StaticCallProxy', () => {
|
||||
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -147,7 +151,7 @@ describe('StaticCallProxy', () => {
|
||||
it('should revert if the callTarget attempts to write to state', async () => {
|
||||
const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -159,7 +163,7 @@ describe('StaticCallProxy', () => {
|
||||
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
|
||||
const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -173,7 +177,7 @@ describe('StaticCallProxy', () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -186,7 +190,7 @@ describe('StaticCallProxy', () => {
|
||||
it('should be successful if a function call with no inputs and no outputs is successful', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -196,14 +200,18 @@ describe('StaticCallProxy', () => {
|
||||
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
|
||||
const staticCallData = '0x0102030405060708';
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash);
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
toAddress,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
it('should be successful if a function call with one static input returns the correct value', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -214,7 +222,7 @@ describe('StaticCallProxy', () => {
|
||||
const dynamicInput = '0x0102030405060708';
|
||||
const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
@@ -237,7 +245,7 @@ describe('StaticCallProxy', () => {
|
||||
const expectedResultHash = ethUtil.bufferToHex(
|
||||
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
||||
);
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
|
365
contracts/asset-proxy/test/uniswap_bridge.ts
Normal file
365
contracts/asset-proxy/test/uniswap_bridge.ts
Normal file
@@ -0,0 +1,365 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
filterLogs,
|
||||
filterLogsToArguments,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
hexRandom,
|
||||
Numberish,
|
||||
randomAddress,
|
||||
TransactionHelper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
TestUniswapBridgeContract,
|
||||
TestUniswapBridgeEthToTokenTransferInputEventArgs as EthToTokenTransferInputArgs,
|
||||
TestUniswapBridgeEvents as ContractEvents,
|
||||
TestUniswapBridgeTokenApproveEventArgs as TokenApproveArgs,
|
||||
TestUniswapBridgeTokenToEthSwapInputEventArgs as TokenToEthSwapInputArgs,
|
||||
TestUniswapBridgeTokenToTokenTransferInputEventArgs as TokenToTokenTransferInputArgs,
|
||||
TestUniswapBridgeTokenTransferEventArgs as TokenTransferArgs,
|
||||
TestUniswapBridgeWethDepositEventArgs as WethDepositArgs,
|
||||
TestUniswapBridgeWethWithdrawEventArgs as WethWithdrawArgs,
|
||||
} from '../src';
|
||||
|
||||
blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
const txHelper = new TransactionHelper(env.web3Wrapper, artifacts);
|
||||
let testContract: TestUniswapBridgeContract;
|
||||
let wethTokenAddress: string;
|
||||
|
||||
before(async () => {
|
||||
testContract = await TestUniswapBridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestUniswapBridge,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
wethTokenAddress = await testContract.wethToken.callAsync();
|
||||
});
|
||||
|
||||
describe('isValidSignature()', () => {
|
||||
it('returns success bytes', async () => {
|
||||
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||
const result = await testContract.isValidSignature.callAsync(hexRandom(), hexRandom(_.random(0, 32)));
|
||||
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
interface WithdrawToOpts {
|
||||
fromTokenAddress: string;
|
||||
toTokenAddress: string;
|
||||
fromTokenBalance: Numberish;
|
||||
toAddress: string;
|
||||
amount: Numberish;
|
||||
exchangeRevertReason: string;
|
||||
exchangeFillAmount: Numberish;
|
||||
toTokenRevertReason: string;
|
||||
fromTokenRevertReason: string;
|
||||
}
|
||||
|
||||
function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts {
|
||||
return {
|
||||
fromTokenAddress: constants.NULL_ADDRESS,
|
||||
toTokenAddress: constants.NULL_ADDRESS,
|
||||
fromTokenBalance: getRandomInteger(1, 1e18),
|
||||
toAddress: randomAddress(),
|
||||
amount: getRandomInteger(1, 1e18),
|
||||
exchangeRevertReason: '',
|
||||
exchangeFillAmount: getRandomInteger(1, 1e18),
|
||||
toTokenRevertReason: '',
|
||||
fromTokenRevertReason: '',
|
||||
...opts,
|
||||
};
|
||||
}
|
||||
|
||||
interface WithdrawToResult {
|
||||
opts: WithdrawToOpts;
|
||||
result: string;
|
||||
logs: DecodedLogs;
|
||||
blockTime: number;
|
||||
}
|
||||
|
||||
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
|
||||
const _opts = createWithdrawToOpts(opts);
|
||||
// Create the "from" token and exchange.
|
||||
[[_opts.fromTokenAddress]] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createTokenAndExchange,
|
||||
_opts.fromTokenAddress,
|
||||
_opts.exchangeRevertReason,
|
||||
{ value: new BigNumber(_opts.exchangeFillAmount) },
|
||||
);
|
||||
// Create the "to" token and exchange.
|
||||
[[_opts.toTokenAddress]] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createTokenAndExchange,
|
||||
_opts.toTokenAddress,
|
||||
_opts.exchangeRevertReason,
|
||||
{ value: new BigNumber(_opts.exchangeFillAmount) },
|
||||
);
|
||||
await testContract.setTokenRevertReason.awaitTransactionSuccessAsync(
|
||||
_opts.toTokenAddress,
|
||||
_opts.toTokenRevertReason,
|
||||
);
|
||||
await testContract.setTokenRevertReason.awaitTransactionSuccessAsync(
|
||||
_opts.fromTokenAddress,
|
||||
_opts.fromTokenRevertReason,
|
||||
);
|
||||
// Set the token balance for the token we're converting from.
|
||||
await testContract.setTokenBalance.awaitTransactionSuccessAsync(_opts.fromTokenAddress, {
|
||||
value: new BigNumber(_opts.fromTokenBalance),
|
||||
});
|
||||
// Call bridgeTransferFrom().
|
||||
const [result, receipt] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.bridgeTransferFrom,
|
||||
// The "to" token address.
|
||||
_opts.toTokenAddress,
|
||||
// The "from" address.
|
||||
randomAddress(),
|
||||
// The "to" address.
|
||||
_opts.toAddress,
|
||||
// The amount to transfer to "to"
|
||||
new BigNumber(_opts.amount),
|
||||
// ABI-encoded "from" token address.
|
||||
hexLeftPad(_opts.fromTokenAddress),
|
||||
);
|
||||
return {
|
||||
opts: _opts,
|
||||
result,
|
||||
logs: (receipt.logs as any) as DecodedLogs,
|
||||
blockTime: await env.web3Wrapper.getBlockTimestampAsync(receipt.blockNumber),
|
||||
};
|
||||
}
|
||||
|
||||
async function getExchangeForTokenAsync(tokenAddress: string): Promise<string> {
|
||||
return testContract.getExchange.callAsync(tokenAddress);
|
||||
}
|
||||
|
||||
it('returns magic bytes on success', async () => {
|
||||
const { result } = await withdrawToAsync();
|
||||
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
||||
});
|
||||
|
||||
it('just transfers tokens to `to` if the same tokens are in play', async () => {
|
||||
const [[tokenAddress]] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createTokenAndExchange,
|
||||
constants.NULL_ADDRESS,
|
||||
'',
|
||||
);
|
||||
const { opts, result, logs } = await withdrawToAsync({
|
||||
fromTokenAddress: tokenAddress,
|
||||
toTokenAddress: tokenAddress,
|
||||
});
|
||||
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
||||
const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer);
|
||||
expect(transfers.length).to.eq(1);
|
||||
expect(transfers[0].token).to.eq(tokenAddress);
|
||||
expect(transfers[0].from).to.eq(testContract.address);
|
||||
expect(transfers[0].to).to.eq(opts.toAddress);
|
||||
expect(transfers[0].amount).to.bignumber.eq(opts.amount);
|
||||
});
|
||||
|
||||
describe('token -> token', () => {
|
||||
it('calls `IUniswapExchange.tokenToTokenTransferInput()', async () => {
|
||||
const { opts, logs, blockTime } = await withdrawToAsync();
|
||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||
const calls = filterLogsToArguments<TokenToTokenTransferInputArgs>(
|
||||
logs,
|
||||
ContractEvents.TokenToTokenTransferInput,
|
||||
);
|
||||
expect(calls.length).to.eq(1);
|
||||
expect(calls[0].exchange).to.eq(exchangeAddress);
|
||||
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
||||
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
|
||||
expect(calls[0].minEthBought).to.bignumber.eq(0);
|
||||
expect(calls[0].deadline).to.bignumber.eq(blockTime);
|
||||
expect(calls[0].recipient).to.eq(opts.toAddress);
|
||||
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);
|
||||
});
|
||||
|
||||
it('sets allowance for "from" token', async () => {
|
||||
const { opts, logs } = await withdrawToAsync();
|
||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||
expect(approvals.length).to.eq(1);
|
||||
expect(approvals[0].spender).to.eq(exchangeAddress);
|
||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||
});
|
||||
|
||||
it('sets allowance for "from" token on subsequent calls', async () => {
|
||||
const { opts } = await withdrawToAsync();
|
||||
const { logs } = await withdrawToAsync(opts);
|
||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||
expect(approvals.length).to.eq(1);
|
||||
expect(approvals[0].spender).to.eq(exchangeAddress);
|
||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||
});
|
||||
|
||||
it('fails if "from" token does not exist', async () => {
|
||||
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
getRandomInteger(1, 1e18),
|
||||
hexLeftPad(randomAddress()),
|
||||
);
|
||||
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||
});
|
||||
|
||||
it('fails if the exchange fails', async () => {
|
||||
const revertReason = 'FOOBAR';
|
||||
const tx = withdrawToAsync({
|
||||
exchangeRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
});
|
||||
});
|
||||
|
||||
describe('token -> ETH', () => {
|
||||
it('calls `IUniswapExchange.tokenToEthSwapInput()`, `WETH.deposit()`, then `transfer()`', async () => {
|
||||
const { opts, logs, blockTime } = await withdrawToAsync({
|
||||
toTokenAddress: wethTokenAddress,
|
||||
});
|
||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||
let calls: any = filterLogs<TokenToEthSwapInputArgs>(logs, ContractEvents.TokenToEthSwapInput);
|
||||
expect(calls.length).to.eq(1);
|
||||
expect(calls[0].args.exchange).to.eq(exchangeAddress);
|
||||
expect(calls[0].args.tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
||||
expect(calls[0].args.minEthBought).to.bignumber.eq(opts.amount);
|
||||
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
|
||||
calls = filterLogs<WethDepositArgs>(
|
||||
logs.slice(calls[0].logIndex as number),
|
||||
ContractEvents.WethDeposit,
|
||||
);
|
||||
expect(calls.length).to.eq(1);
|
||||
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
|
||||
calls = filterLogs<TokenTransferArgs>(
|
||||
logs.slice(calls[0].logIndex as number),
|
||||
ContractEvents.TokenTransfer,
|
||||
);
|
||||
expect(calls.length).to.eq(1);
|
||||
expect(calls[0].args.token).to.eq(opts.toTokenAddress);
|
||||
expect(calls[0].args.from).to.eq(testContract.address);
|
||||
expect(calls[0].args.to).to.eq(opts.toAddress);
|
||||
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
|
||||
});
|
||||
|
||||
it('sets allowance for "from" token', async () => {
|
||||
const { opts, logs } = await withdrawToAsync({
|
||||
toTokenAddress: wethTokenAddress,
|
||||
});
|
||||
const transfers = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||
expect(transfers.length).to.eq(1);
|
||||
expect(transfers[0].spender).to.eq(exchangeAddress);
|
||||
expect(transfers[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||
});
|
||||
|
||||
it('sets allowance for "from" token on subsequent calls', async () => {
|
||||
const { opts } = await withdrawToAsync({
|
||||
toTokenAddress: wethTokenAddress,
|
||||
});
|
||||
const { logs } = await withdrawToAsync(opts);
|
||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||
expect(approvals.length).to.eq(1);
|
||||
expect(approvals[0].spender).to.eq(exchangeAddress);
|
||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||
});
|
||||
|
||||
it('fails if "from" token does not exist', async () => {
|
||||
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
getRandomInteger(1, 1e18),
|
||||
hexLeftPad(wethTokenAddress),
|
||||
);
|
||||
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||
});
|
||||
|
||||
it('fails if `WETH.deposit()` fails', async () => {
|
||||
const revertReason = 'FOOBAR';
|
||||
const tx = withdrawToAsync({
|
||||
toTokenAddress: wethTokenAddress,
|
||||
toTokenRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
});
|
||||
|
||||
it('fails if the exchange fails', async () => {
|
||||
const revertReason = 'FOOBAR';
|
||||
const tx = withdrawToAsync({
|
||||
toTokenAddress: wethTokenAddress,
|
||||
exchangeRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ETH -> token', () => {
|
||||
it('calls `WETH.withdraw()`, then `IUniswapExchange.ethToTokenTransferInput()`', async () => {
|
||||
const { opts, logs, blockTime } = await withdrawToAsync({
|
||||
fromTokenAddress: wethTokenAddress,
|
||||
});
|
||||
const exchangeAddress = await getExchangeForTokenAsync(opts.toTokenAddress);
|
||||
let calls: any = filterLogs<WethWithdrawArgs>(logs, ContractEvents.WethWithdraw);
|
||||
expect(calls.length).to.eq(1);
|
||||
expect(calls[0].args.amount).to.bignumber.eq(opts.fromTokenBalance);
|
||||
calls = filterLogs<EthToTokenTransferInputArgs>(
|
||||
logs.slice(calls[0].logIndex as number),
|
||||
ContractEvents.EthToTokenTransferInput,
|
||||
);
|
||||
expect(calls.length).to.eq(1);
|
||||
expect(calls[0].args.exchange).to.eq(exchangeAddress);
|
||||
expect(calls[0].args.minTokensBought).to.bignumber.eq(opts.amount);
|
||||
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
|
||||
expect(calls[0].args.recipient).to.eq(opts.toAddress);
|
||||
});
|
||||
|
||||
it('does not set any allowance', async () => {
|
||||
const { logs } = await withdrawToAsync({
|
||||
fromTokenAddress: wethTokenAddress,
|
||||
});
|
||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||
expect(approvals).to.be.empty('');
|
||||
});
|
||||
|
||||
it('fails if "to" token does not exist', async () => {
|
||||
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
|
||||
wethTokenAddress,
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
getRandomInteger(1, 1e18),
|
||||
hexLeftPad(randomAddress()),
|
||||
);
|
||||
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||
});
|
||||
|
||||
it('fails if the `WETH.withdraw()` fails', async () => {
|
||||
const revertReason = 'FOOBAR';
|
||||
const tx = withdrawToAsync({
|
||||
fromTokenAddress: wethTokenAddress,
|
||||
fromTokenRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
});
|
||||
|
||||
it('fails if the exchange fails', async () => {
|
||||
const revertReason = 'FOOBAR';
|
||||
const tx = withdrawToAsync({
|
||||
fromTokenAddress: wethTokenAddress,
|
||||
exchangeRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,3 +1,4 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
constants,
|
||||
@@ -7,7 +8,6 @@ import {
|
||||
LogDecoder,
|
||||
txDefaults,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
@@ -26,6 +26,7 @@ export class ERC1155ProxyWrapper {
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private _proxyContract?: ERC1155ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||
@@ -37,6 +38,7 @@ export class ERC1155ProxyWrapper {
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||
this._dummyTokenWrappers = [];
|
||||
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._fungibleTokenIds = [];
|
||||
@@ -95,7 +97,7 @@ export class ERC1155ProxyWrapper {
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return abi encoded tx data.
|
||||
*/
|
||||
public getTransferFromAbiEncodedTxData(
|
||||
public async getTransferFromAbiEncodedTxDataAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
@@ -105,11 +107,11 @@ export class ERC1155ProxyWrapper {
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
assetData_?: string,
|
||||
): string {
|
||||
): Promise<string> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? assetDataUtils.encodeERC1155AssetData(
|
||||
? await this._devUtils.encodeERC1155AssetData.callAsync(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
@@ -169,7 +171,7 @@ export class ERC1155ProxyWrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? assetDataUtils.encodeERC1155AssetData(
|
||||
? await this._devUtils.encodeERC1155AssetData.callAsync(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
@@ -12,6 +12,7 @@ export class ERC20Wrapper {
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private _proxyContract?: ERC20ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
/**
|
||||
@@ -26,6 +27,7 @@ export class ERC20Wrapper {
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
}
|
||||
public async deployDummyTokensAsync(
|
||||
numberToDeploy: number,
|
||||
@@ -70,46 +72,39 @@ export class ERC20Wrapper {
|
||||
tokenOwnerAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
|
||||
(this._proxyContract as ERC20ProxyContract).address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: tokenOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
|
||||
return balance;
|
||||
}
|
||||
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
await tokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||
userAddress,
|
||||
amount,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
|
||||
);
|
||||
}
|
||||
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
|
||||
return allowance;
|
||||
}
|
||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||
proxyAddress,
|
||||
amount,
|
||||
{ from: userAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(proxyAddress, amount, { from: userAddress });
|
||||
}
|
||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
@@ -151,9 +146,8 @@ export class ERC20Wrapper {
|
||||
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
||||
return tokenAddresses;
|
||||
}
|
||||
private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
|
||||
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
|
||||
const tokenAddress = erc20ProxyData.tokenAddress;
|
||||
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
|
||||
const [proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData.callAsync(assetData); // tslint:disable-line:no-unused-variable
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||
|
@@ -93,22 +93,14 @@ export class ERC721Wrapper {
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
|
||||
proxyAddress,
|
||||
isApproved,
|
||||
{ from: ownerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(proxyAddress, isApproved, {
|
||||
from: ownerAddress,
|
||||
});
|
||||
}
|
||||
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||
to,
|
||||
tokenId,
|
||||
{ from: tokenOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(to, tokenId, { from: tokenOwner });
|
||||
}
|
||||
public async transferFromAsync(
|
||||
tokenAddress: string,
|
||||
@@ -117,31 +109,19 @@ export class ERC721Wrapper {
|
||||
userAddress: string,
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.transferFrom.awaitTransactionSuccessAsync(
|
||||
currentOwner,
|
||||
userAddress,
|
||||
tokenId,
|
||||
{ from: currentOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await tokenContract.transferFrom.awaitTransactionSuccessAsync(currentOwner, userAddress, tokenId, {
|
||||
from: currentOwner,
|
||||
});
|
||||
}
|
||||
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.mint.awaitTransactionSuccessAsync(
|
||||
userAddress,
|
||||
tokenId,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await tokenContract.mint.awaitTransactionSuccessAsync(userAddress, tokenId, {
|
||||
from: this._contractOwnerAddress,
|
||||
});
|
||||
}
|
||||
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.burn.awaitTransactionSuccessAsync(
|
||||
owner,
|
||||
tokenId,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await tokenContract.burn.awaitTransactionSuccessAsync(owner, tokenId, { from: this._contractOwnerAddress });
|
||||
}
|
||||
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
|
@@ -14,7 +14,8 @@
|
||||
"generated-artifacts/IAuthorizable.json",
|
||||
"generated-artifacts/IERC20Bridge.json",
|
||||
"generated-artifacts/IEth2Dai.json",
|
||||
"generated-artifacts/IWallet.json",
|
||||
"generated-artifacts/IUniswapExchange.json",
|
||||
"generated-artifacts/IUniswapExchangeFactory.json",
|
||||
"generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||
"generated-artifacts/MixinAuthorizable.json",
|
||||
"generated-artifacts/MultiAssetProxy.json",
|
||||
@@ -22,7 +23,9 @@
|
||||
"generated-artifacts/StaticCallProxy.json",
|
||||
"generated-artifacts/TestERC20Bridge.json",
|
||||
"generated-artifacts/TestEth2DaiBridge.json",
|
||||
"generated-artifacts/TestStaticCallTarget.json"
|
||||
"generated-artifacts/TestStaticCallTarget.json",
|
||||
"generated-artifacts/TestUniswapBridge.json",
|
||||
"generated-artifacts/UniswapBridge.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.1.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.1.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Add chainId to domain separator (#1742)
|
||||
|
@@ -19,12 +19,15 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "./libs/LibCoordinatorApproval.sol";
|
||||
import "./libs/LibCoordinatorRichErrors.sol";
|
||||
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
@@ -32,7 +35,7 @@ import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
// solhint-disable avoid-tx-origin
|
||||
contract MixinCoordinatorApprovalVerifier is
|
||||
LibCoordinatorApproval,
|
||||
LibZeroExTransaction,
|
||||
LibEIP712ExchangeDomain,
|
||||
ICoordinatorSignatureValidator,
|
||||
ICoordinatorApprovalVerifier
|
||||
{
|
||||
@@ -44,13 +47,12 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
@@ -67,7 +69,6 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
orders,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
);
|
||||
}
|
||||
@@ -75,7 +76,7 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
/// @return orders The orders from the Exchange calldata.
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
pure
|
||||
@@ -84,7 +85,6 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
bytes4 selector = data.readBytes4(0);
|
||||
if (
|
||||
selector == IExchange(address(0)).fillOrder.selector ||
|
||||
selector == IExchange(address(0)).fillOrderNoThrow.selector ||
|
||||
selector == IExchange(address(0)).fillOrKillOrder.selector
|
||||
) {
|
||||
// Decode single order
|
||||
@@ -98,8 +98,10 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
selector == IExchange(address(0)).batchFillOrders.selector ||
|
||||
selector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||
selector == IExchange(address(0)).marketBuyOrders.selector ||
|
||||
selector == IExchange(address(0)).marketSellOrders.selector
|
||||
selector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).marketBuyOrdersFillOrKill.selector ||
|
||||
selector == IExchange(address(0)).marketSellOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).marketSellOrdersFillOrKill.selector
|
||||
) {
|
||||
// Decode all orders
|
||||
// solhint-disable indent
|
||||
@@ -107,7 +109,10 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order[])
|
||||
);
|
||||
} else if (selector == IExchange(address(0)).matchOrders.selector) {
|
||||
} else if (
|
||||
selector == IExchange(address(0)).matchOrders.selector ||
|
||||
selector == IExchange(address(0)).matchOrdersWithMaximalFill.selector
|
||||
) {
|
||||
// Decode left and right orders
|
||||
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
@@ -127,27 +132,24 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function _assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
internal
|
||||
view
|
||||
{
|
||||
// Verify that Ethereum tx signer is the same as the approved txOrigin
|
||||
require(
|
||||
tx.origin == txOrigin,
|
||||
"INVALID_ORIGIN"
|
||||
);
|
||||
if (tx.origin != txOrigin) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidOriginError(txOrigin));
|
||||
}
|
||||
|
||||
// Hash 0x transaction
|
||||
bytes32 transactionHash = getTransactionHash(transaction);
|
||||
bytes32 transactionHash = LibZeroExTransaction.getTypedDataHash(transaction, EIP712_EXCHANGE_DOMAIN_HASH);
|
||||
|
||||
// Create empty list of approval signers
|
||||
address[] memory approvalSignerAddresses = new address[](0);
|
||||
@@ -155,21 +157,12 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
uint256 signaturesLength = approvalSignatures.length;
|
||||
for (uint256 i = 0; i != signaturesLength; i++) {
|
||||
// Create approval message
|
||||
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
|
||||
CoordinatorApproval memory approval = CoordinatorApproval({
|
||||
txOrigin: txOrigin,
|
||||
transactionHash: transactionHash,
|
||||
transactionSignature: transactionSignature,
|
||||
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
|
||||
transactionSignature: transactionSignature
|
||||
});
|
||||
|
||||
// Ensure approval has not expired
|
||||
require(
|
||||
// solhint-disable-next-line not-rely-on-time
|
||||
currentApprovalExpirationTimeSeconds > block.timestamp,
|
||||
"APPROVAL_EXPIRED"
|
||||
);
|
||||
|
||||
// Hash approval message and recover signer address
|
||||
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
|
||||
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
|
||||
@@ -191,10 +184,12 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
// Ensure feeRecipient of order has approved this 0x transaction
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||
require(
|
||||
isOrderApproved,
|
||||
"INVALID_APPROVAL_SIGNATURE"
|
||||
);
|
||||
if (!isOrderApproved) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidApprovalSignatureError(
|
||||
transactionHash,
|
||||
approverAddress
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,41 +20,53 @@ pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Refundable.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./interfaces/ICoordinatorCore.sol";
|
||||
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract MixinCoordinatorCore is
|
||||
Refundable,
|
||||
LibConstants,
|
||||
ICoordinatorApprovalVerifier,
|
||||
ICoordinatorCore
|
||||
{
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
|
||||
/// @dev A payable fallback function that makes this contract "payable". This is necessary to allow
|
||||
/// this contract to gracefully handle refunds from the Exchange.
|
||||
function ()
|
||||
external
|
||||
payable
|
||||
{}
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
|
||||
/// each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
payable
|
||||
refundFinalBalance
|
||||
{
|
||||
// Validate that the 0x transaction has been approves by each feeRecipient
|
||||
assertValidCoordinatorApprovals(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
);
|
||||
|
||||
// Execute the transaction
|
||||
EXCHANGE.executeTransaction(transaction, transactionSignature);
|
||||
EXCHANGE.executeTransaction.value(msg.value)(transaction, transactionSignature);
|
||||
}
|
||||
}
|
||||
|
@@ -19,7 +19,9 @@
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||
import "./libs/LibCoordinatorRichErrors.sol";
|
||||
|
||||
|
||||
contract MixinSignatureValidator is
|
||||
@@ -30,24 +32,32 @@ contract MixinSignatureValidator is
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
/// @return signerAddress Address of the signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
returns (address signerAddress)
|
||||
{
|
||||
require(
|
||||
signature.length > 0,
|
||||
"LENGTH_GREATER_THAN_0_REQUIRED"
|
||||
);
|
||||
uint256 signatureLength = signature.length;
|
||||
if (signatureLength == 0) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
|
||||
// Pop last byte off of signature byte array.
|
||||
uint8 signatureTypeRaw = uint8(signature.popLastByte());
|
||||
uint8 signatureTypeRaw = uint8(signature[signature.length - 1]);
|
||||
|
||||
// Ensure signature is supported
|
||||
require(
|
||||
signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
|
||||
"SIGNATURE_UNSUPPORTED"
|
||||
);
|
||||
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
|
||||
SignatureType signatureType = SignatureType(signatureTypeRaw);
|
||||
|
||||
@@ -57,25 +67,32 @@ contract MixinSignatureValidator is
|
||||
// it an explicit option. This aids testing and analysis. It is
|
||||
// also the initialization value for the enum type.
|
||||
if (signatureType == SignatureType.Illegal) {
|
||||
revert("SIGNATURE_ILLEGAL");
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.ILLEGAL,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
|
||||
// Always invalid signature.
|
||||
// Like Illegal, this is always implicitly available and therefore
|
||||
// offered explicitly. It can be implicitly created by providing
|
||||
// a correctly formatted but incorrect signature.
|
||||
} else if (signatureType == SignatureType.Invalid) {
|
||||
require(
|
||||
signature.length == 0,
|
||||
"LENGTH_0_REQUIRED"
|
||||
);
|
||||
revert("SIGNATURE_INVALID");
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
|
||||
// Signature using EIP712
|
||||
} else if (signatureType == SignatureType.EIP712) {
|
||||
require(
|
||||
signature.length == 65,
|
||||
"LENGTH_65_REQUIRED"
|
||||
);
|
||||
if (signatureLength != 66) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
@@ -89,10 +106,13 @@ contract MixinSignatureValidator is
|
||||
|
||||
// Signed using web3.eth_sign
|
||||
} else if (signatureType == SignatureType.EthSign) {
|
||||
require(
|
||||
signature.length == 65,
|
||||
"LENGTH_65_REQUIRED"
|
||||
);
|
||||
if (signatureLength != 66) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
@@ -113,6 +133,10 @@ contract MixinSignatureValidator is
|
||||
// that we currently support. In this case returning false
|
||||
// may lead the caller to incorrectly believe that the
|
||||
// signature was invalid.)
|
||||
revert("SIGNATURE_UNSUPPORTED");
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@@ -30,13 +30,12 @@ contract ICoordinatorApprovalVerifier {
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
@@ -44,7 +43,7 @@ contract ICoordinatorApprovalVerifier {
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
/// @return orders The orders from the Exchange calldata.
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
pure
|
||||
|
@@ -24,18 +24,19 @@ import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
|
||||
contract ICoordinatorCore {
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
|
||||
/// each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public;
|
||||
public
|
||||
payable;
|
||||
}
|
||||
|
@@ -30,14 +30,14 @@ contract ICoordinatorSignatureValidator {
|
||||
Wallet, // 0x04
|
||||
Validator, // 0x05
|
||||
PreSigned, // 0x06
|
||||
OrderValidator, // 0x07
|
||||
WalletOrderValidator, // 0x08
|
||||
NSignatureTypes // 0x09, number of signature types. Always leave at end.
|
||||
EIP1271Wallet, // 0x07
|
||||
NSignatureTypes // 0x08, number of signature types. Always leave at end.
|
||||
}
|
||||
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
/// @return signerAddress Address of the signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
|
@@ -30,22 +30,24 @@ contract LibCoordinatorApproval is
|
||||
// "CoordinatorApproval(",
|
||||
// "address txOrigin,",
|
||||
// "bytes32 transactionHash,",
|
||||
// "bytes transactionSignature,",
|
||||
// "uint256 approvalExpirationTimeSeconds",
|
||||
// "bytes transactionSignature",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
|
||||
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH =
|
||||
0xa6511c04ca44625d50986f8c36bedc09366207a17b96e347094053a9f8507168;
|
||||
|
||||
struct CoordinatorApproval {
|
||||
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
||||
bytes32 transactionHash; // EIP712 hash of the transaction.
|
||||
bytes transactionSignature; // Signature of the 0x transaction.
|
||||
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the approval expires.
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
|
||||
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage using the domain
|
||||
/// separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, and transaction
|
||||
/// signature.
|
||||
/// @return approvalHash EIP712 hash of the Coordinator approval message with the domain
|
||||
/// separator of this contract.
|
||||
function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
public
|
||||
view
|
||||
@@ -55,9 +57,10 @@ contract LibCoordinatorApproval is
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, and transaction
|
||||
// signature.
|
||||
/// @return result EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
function _hashCoordinatorApproval(CoordinatorApproval memory approval)
|
||||
internal
|
||||
pure
|
||||
@@ -67,7 +70,6 @@ contract LibCoordinatorApproval is
|
||||
bytes memory transactionSignature = approval.transactionSignature;
|
||||
address txOrigin = approval.txOrigin;
|
||||
bytes32 transactionHash = approval.transactionHash;
|
||||
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
@@ -75,7 +77,6 @@ contract LibCoordinatorApproval is
|
||||
// approval.txOrigin,
|
||||
// approval.transactionHash,
|
||||
// keccak256(approval.transactionSignature)
|
||||
// approval.approvalExpirationTimeSeconds,
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
@@ -89,9 +90,8 @@ contract LibCoordinatorApproval is
|
||||
mstore(add(memPtr, 32), txOrigin) // txOrigin
|
||||
mstore(add(memPtr, 64), transactionHash) // transactionHash
|
||||
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
|
||||
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 160)
|
||||
result := keccak256(memPtr, 128)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
library LibCoordinatorRichErrors {
|
||||
enum SignatureErrorCodes {
|
||||
INVALID_LENGTH,
|
||||
UNSUPPORTED,
|
||||
ILLEGAL,
|
||||
INVALID
|
||||
}
|
||||
|
||||
// bytes4(keccak256("SignatureError(uint8,bytes32,bytes)"))
|
||||
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
|
||||
0x779c5223;
|
||||
|
||||
// bytes4(keccak256("InvalidOriginError(address)"))
|
||||
bytes4 internal constant INVALID_ORIGIN_ERROR_SELECTOR =
|
||||
0xa458d7ff;
|
||||
|
||||
// bytes4(keccak256("InvalidApprovalSignatureError(bytes32,address)"))
|
||||
bytes4 internal constant INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR =
|
||||
0xd789b640;
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
function SignatureError(
|
||||
SignatureErrorCodes errorCode,
|
||||
bytes32 hash,
|
||||
bytes memory signature
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
SIGNATURE_ERROR_SELECTOR,
|
||||
errorCode,
|
||||
hash,
|
||||
signature
|
||||
);
|
||||
}
|
||||
|
||||
function InvalidOriginError(
|
||||
address expectedOrigin
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
INVALID_ORIGIN_ERROR_SELECTOR,
|
||||
expectedOrigin
|
||||
);
|
||||
}
|
||||
|
||||
function InvalidApprovalSignatureError(
|
||||
bytes32 transactionHash,
|
||||
address approverAddress
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR,
|
||||
transactionHash,
|
||||
approverAddress
|
||||
);
|
||||
}
|
||||
}
|
@@ -21,15 +21,13 @@ pragma solidity ^0.5.9;
|
||||
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
|
||||
|
||||
|
||||
contract LibEIP712CoordinatorDomain is
|
||||
LibEIP712
|
||||
{
|
||||
contract LibEIP712CoordinatorDomain {
|
||||
|
||||
// EIP712 Domain Name value for the Coordinator
|
||||
string constant public EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
|
||||
|
||||
// EIP712 Domain Version value for the Coordinator
|
||||
string constant public EIP712_COORDINATOR_DOMAIN_VERSION = "2.0.0";
|
||||
string constant public EIP712_COORDINATOR_DOMAIN_VERSION = "3.0.0";
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Coordinator
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
@@ -43,7 +41,9 @@ contract LibEIP712CoordinatorDomain is
|
||||
)
|
||||
public
|
||||
{
|
||||
address verifyingContractAddress = verifyingContractAddressIfExists == address(0) ? address(this) : verifyingContractAddressIfExists;
|
||||
address verifyingContractAddress = verifyingContractAddressIfExists == address(0)
|
||||
? address(this)
|
||||
: verifyingContractAddressIfExists;
|
||||
EIP712_COORDINATOR_DOMAIN_HASH = LibEIP712.hashEIP712Domain(
|
||||
EIP712_COORDINATOR_DOMAIN_NAME,
|
||||
EIP712_COORDINATOR_DOMAIN_VERSION,
|
||||
@@ -55,7 +55,7 @@ contract LibEIP712CoordinatorDomain is
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of this contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to this EIP712 Domain.
|
||||
/// @return result EIP712 hash applied to this EIP712 Domain.
|
||||
function _hashEIP712CoordinatorMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
|
@@ -29,7 +29,7 @@ contract MixinCoordinatorRegistryCore is
|
||||
mapping (address => string) internal coordinatorEndpoints;
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
|
||||
address coordinatorOperator = msg.sender;
|
||||
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
|
||||
@@ -37,7 +37,8 @@ contract MixinCoordinatorRegistryCore is
|
||||
}
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
/// @param coordinatorOperator Operator of the Coordinator endpoint.
|
||||
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
|
@@ -29,11 +29,12 @@ contract ICoordinatorRegistryCore
|
||||
);
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
/// @param coordinatorOperator Operator of the Coordinator endpoint.
|
||||
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "2.1.0-beta.0",
|
||||
"version": "2.1.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
@@ -35,7 +35,7 @@
|
||||
"compile:truffle": "truffle compile"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry).json",
|
||||
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry|ICoordinatorApprovalVerifier|ICoordinatorCore|ICoordinatorRegistryCore|ICoordinatorSignatureValidator|LibConstants|LibCoordinatorApproval|LibCoordinatorRichErrors|LibEIP712CoordinatorDomain|MixinCoordinatorApprovalVerifier|MixinCoordinatorCore|MixinCoordinatorRegistryCore|MixinSignatureValidator).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -48,12 +48,17 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.1",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.1",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/order-utils": "^8.5.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -61,6 +66,7 @@
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"lodash": "^4.17.11",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
@@ -71,20 +77,13 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.0",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.0",
|
||||
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/order-utils": "^8.5.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -7,7 +7,31 @@ import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Coordinator from '../generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
|
||||
import * as ICoordinatorApprovalVerifier from '../generated-artifacts/ICoordinatorApprovalVerifier.json';
|
||||
import * as ICoordinatorCore from '../generated-artifacts/ICoordinatorCore.json';
|
||||
import * as ICoordinatorRegistryCore from '../generated-artifacts/ICoordinatorRegistryCore.json';
|
||||
import * as ICoordinatorSignatureValidator from '../generated-artifacts/ICoordinatorSignatureValidator.json';
|
||||
import * as LibConstants from '../generated-artifacts/LibConstants.json';
|
||||
import * as LibCoordinatorApproval from '../generated-artifacts/LibCoordinatorApproval.json';
|
||||
import * as LibCoordinatorRichErrors from '../generated-artifacts/LibCoordinatorRichErrors.json';
|
||||
import * as LibEIP712CoordinatorDomain from '../generated-artifacts/LibEIP712CoordinatorDomain.json';
|
||||
import * as MixinCoordinatorApprovalVerifier from '../generated-artifacts/MixinCoordinatorApprovalVerifier.json';
|
||||
import * as MixinCoordinatorCore from '../generated-artifacts/MixinCoordinatorCore.json';
|
||||
import * as MixinCoordinatorRegistryCore from '../generated-artifacts/MixinCoordinatorRegistryCore.json';
|
||||
import * as MixinSignatureValidator from '../generated-artifacts/MixinSignatureValidator.json';
|
||||
export const artifacts = {
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
MixinCoordinatorApprovalVerifier: MixinCoordinatorApprovalVerifier as ContractArtifact,
|
||||
MixinCoordinatorCore: MixinCoordinatorCore as ContractArtifact,
|
||||
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
|
||||
ICoordinatorApprovalVerifier: ICoordinatorApprovalVerifier as ContractArtifact,
|
||||
ICoordinatorCore: ICoordinatorCore as ContractArtifact,
|
||||
ICoordinatorSignatureValidator: ICoordinatorSignatureValidator as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
|
||||
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
|
||||
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
MixinCoordinatorRegistryCore: MixinCoordinatorRegistryCore as ContractArtifact,
|
||||
ICoordinatorRegistryCore: ICoordinatorRegistryCore as ContractArtifact,
|
||||
};
|
||||
|
@@ -5,3 +5,15 @@
|
||||
*/
|
||||
export * from '../generated-wrappers/coordinator';
|
||||
export * from '../generated-wrappers/coordinator_registry';
|
||||
export * from '../generated-wrappers/i_coordinator_approval_verifier';
|
||||
export * from '../generated-wrappers/i_coordinator_core';
|
||||
export * from '../generated-wrappers/i_coordinator_registry_core';
|
||||
export * from '../generated-wrappers/i_coordinator_signature_validator';
|
||||
export * from '../generated-wrappers/lib_constants';
|
||||
export * from '../generated-wrappers/lib_coordinator_approval';
|
||||
export * from '../generated-wrappers/lib_coordinator_rich_errors';
|
||||
export * from '../generated-wrappers/lib_e_i_p712_coordinator_domain';
|
||||
export * from '../generated-wrappers/mixin_coordinator_approval_verifier';
|
||||
export * from '../generated-wrappers/mixin_coordinator_core';
|
||||
export * from '../generated-wrappers/mixin_coordinator_registry_core';
|
||||
export * from '../generated-wrappers/mixin_signature_validator';
|
||||
|
@@ -1,531 +0,0 @@
|
||||
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as exchangeArtifacts,
|
||||
constants as exchangeConstants,
|
||||
ExchangeCancelEventArgs,
|
||||
ExchangeCancelUpToEventArgs,
|
||||
ExchangeContract,
|
||||
exchangeDataEncoder,
|
||||
ExchangeFillEventArgs,
|
||||
ExchangeFunctionName,
|
||||
} from '@0x/contracts-exchange';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
getLatestBlockTimestampAsync,
|
||||
OrderFactory,
|
||||
provider,
|
||||
TransactionFactory,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber, providerUtils } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Coordinator tests', () => {
|
||||
let chainId: number;
|
||||
let makerAddress: string;
|
||||
let owner: string;
|
||||
let takerAddress: string;
|
||||
let feeRecipientAddress: string;
|
||||
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc20TokenA: DummyERC20TokenContract;
|
||||
let erc20TokenB: DummyERC20TokenContract;
|
||||
let makerFeeToken: DummyERC20TokenContract;
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
let exchange: ExchangeContract;
|
||||
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
let orderFactory: OrderFactory;
|
||||
let takerTransactionFactory: TransactionFactory;
|
||||
let makerTransactionFactory: TransactionFactory;
|
||||
let approvalFactory: ApprovalFactory;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
chainId = await providerUtils.getChainIdAsync(provider);
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4));
|
||||
|
||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||
const numDummyErc20ToDeploy = 3;
|
||||
[erc20TokenA, erc20TokenB, makerFeeToken] = await erc20Wrapper.deployDummyTokensAsync(
|
||||
numDummyErc20ToDeploy,
|
||||
constants.DUMMY_TOKEN_DECIMALS,
|
||||
);
|
||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
exchange.address,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
|
||||
// Configure order defaults
|
||||
const defaultOrderParams = {
|
||||
...constants.STATIC_ORDER_PARAMS,
|
||||
senderAddress: coordinatorContract.address,
|
||||
makerAddress,
|
||||
feeRecipientAddress,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
|
||||
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
|
||||
exchangeAddress: exchange.address,
|
||||
chainId,
|
||||
};
|
||||
const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||
const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
|
||||
const feeRecipientPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
|
||||
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
|
||||
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId);
|
||||
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
|
||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('single order fills', () => {
|
||||
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`${fnName} should fill the order with a signed approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(1);
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it(`${fnName} should fill the order if called by approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: feeRecipientAddress },
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(1);
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it(`${fnName} should revert with no approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: takerAddress,
|
||||
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
|
||||
},
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an invalid approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an expired approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('batch order fills', () => {
|
||||
for (const fnName of [...exchangeConstants.MARKET_FILL_FN_NAMES, ...exchangeConstants.BATCH_FILL_FN_NAMES]) {
|
||||
it(`${fnName} should fill the orders with a signed approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it(`${fnName} should fill the orders if called by approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: feeRecipientAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it(`${fnName} should revert with an invalid approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an expired approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('cancels', () => {
|
||||
it('cancelOrder call should be successful without an approval', async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
|
||||
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it('batchCancelOrders call should be successful without an approval', async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
|
||||
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it('cancelOrdersUpTo call should be successful without an approval', async () => {
|
||||
const targetEpoch = constants.ZERO_AMOUNT;
|
||||
const data = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetEpoch);
|
||||
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.orderSenderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(targetEpoch.plus(1));
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
@@ -1,81 +1,72 @@
|
||||
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import { blockchainTests, expect, verifyEvents } from '@0x/contracts-test-utils';
|
||||
|
||||
import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||
import { artifacts, CoordinatorRegistryContract, CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||
|
||||
import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Coordinator Registry tests', () => {
|
||||
blockchainTests.resets('Coordinator Registry tests', env => {
|
||||
let coordinatorRegistry: CoordinatorRegistryContract;
|
||||
let coordinatorOperator: string;
|
||||
const coordinatorEndpoint = 'http://sometec.0x.org';
|
||||
const nilCoordinatorEndpoint = '';
|
||||
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
// setup accounts (skip owner)
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const accounts = await env.getAccountAddressesAsync();
|
||||
[, coordinatorOperator] = accounts;
|
||||
// deploy coordinator registry
|
||||
coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider);
|
||||
await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.CoordinatorRegistry,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
describe('core', () => {
|
||||
it('Should successfully set a Coordinator endpoint', async () => {
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(coordinatorEndpoint, {
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
it('Should successfully unset a Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(coordinatorEndpoint, {
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// unset Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, nilCoordinatorEndpoint);
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(nilCoordinatorEndpoint, {
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
|
||||
});
|
||||
it('Should emit an event when setting Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
const txReceipt = await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
const txReceipt = await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(
|
||||
coordinatorEndpoint,
|
||||
{
|
||||
from: coordinatorOperator,
|
||||
},
|
||||
);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// validate event
|
||||
expect(txReceipt.logs.length).to.be.equal(1);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<CoordinatorRegistryCoordinatorEndpointSetEventArgs>;
|
||||
expect(log.args.coordinatorOperator).to.be.equal(coordinatorOperator);
|
||||
expect(log.args.coordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
const expectedEvent: CoordinatorRegistryCoordinatorEndpointSetEventArgs = {
|
||||
coordinatorOperator,
|
||||
coordinatorEndpoint,
|
||||
};
|
||||
verifyEvents(txReceipt, [expectedEvent], 'CoordinatorEndpointSet');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,67 +1,32 @@
|
||||
import { chaiSetup, constants, provider, randomAddress, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { BigNumber, providerUtils } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { artifacts, CoordinatorContract, hashUtils } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('Libs tests', () => {
|
||||
blockchainTests.resets('Libs tests', env => {
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
let chainId: number;
|
||||
const exchangeAddress = randomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
chainId = await providerUtils.getChainIdAsync(provider);
|
||||
chainId = await env.getChainIdAsync();
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
exchangeAddress,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('getTransactionHash', () => {
|
||||
it('should return the correct transaction hash', async () => {
|
||||
const tx = {
|
||||
salt: new BigNumber(0),
|
||||
expirationTimeSeconds: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
domain: {
|
||||
verifyingContract: exchangeAddress,
|
||||
chainId,
|
||||
},
|
||||
};
|
||||
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
|
||||
const txHash = await coordinatorContract.getTransactionHash.callAsync(tx);
|
||||
expect(expectedTxHash).to.eq(txHash);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApprovalHash', () => {
|
||||
it('should return the correct approval hash', async () => {
|
||||
const signedTx = {
|
||||
salt: new BigNumber(0),
|
||||
expirationTimeSeconds: new BigNumber(0),
|
||||
salt: constants.ZERO_AMOUNT,
|
||||
gasPrice: constants.ZERO_AMOUNT,
|
||||
expirationTimeSeconds: constants.ZERO_AMOUNT,
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
signature: '0x5678',
|
||||
@@ -70,20 +35,13 @@ describe('Libs tests', () => {
|
||||
chainId,
|
||||
},
|
||||
};
|
||||
const approvalExpirationTimeSeconds = new BigNumber(0);
|
||||
const txOrigin = constants.NULL_ADDRESS;
|
||||
const approval = {
|
||||
txOrigin,
|
||||
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
|
||||
transactionSignature: signedTx.signature,
|
||||
approvalExpirationTimeSeconds,
|
||||
};
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(
|
||||
signedTx,
|
||||
coordinatorContract.address,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(signedTx, coordinatorContract.address, txOrigin);
|
||||
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
|
||||
expect(expectedApprovalHash).to.eq(approvalHash);
|
||||
});
|
||||
|
@@ -1,29 +1,20 @@
|
||||
import { constants as exchangeConstants, exchangeDataEncoder, ExchangeFunctionName } from '@0x/contracts-exchange';
|
||||
import {
|
||||
chaiSetup,
|
||||
blockchainTests,
|
||||
constants,
|
||||
expectContractCallFailedAsync,
|
||||
getLatestBlockTimestampAsync,
|
||||
provider,
|
||||
expect,
|
||||
hexConcat,
|
||||
hexSlice,
|
||||
randomAddress,
|
||||
TransactionFactory,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, LibBytesRevertErrors, providerUtils } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import { CoordinatorRevertErrors, transactionHashUtils } from '@0x/order-utils';
|
||||
import { SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, LibBytesRevertErrors } from '@0x/utils';
|
||||
|
||||
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('Mixins tests', () => {
|
||||
blockchainTests.resets('Mixins tests', env => {
|
||||
let chainId: number;
|
||||
let transactionSignerAddress: string;
|
||||
let approvalSignerAddress1: string;
|
||||
@@ -36,23 +27,17 @@ describe('Mixins tests', () => {
|
||||
const exchangeAddress = randomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
chainId = await providerUtils.getChainIdAsync(provider);
|
||||
chainId = await env.getChainIdAsync();
|
||||
mixins = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
exchangeAddress,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3);
|
||||
const accounts = await env.getAccountAddressesAsync();
|
||||
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts;
|
||||
defaultOrder = {
|
||||
makerAddress: constants.NULL_ADDRESS,
|
||||
takerAddress: constants.NULL_ADDRESS,
|
||||
@@ -79,12 +64,6 @@ describe('Mixins tests', () => {
|
||||
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
|
||||
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('getSignerAddress', () => {
|
||||
it('should return the correct address using the EthSign signature type', async () => {
|
||||
@@ -104,37 +83,62 @@ describe('Mixins tests', () => {
|
||||
it('should revert with with the Illegal signature type', async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex');
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${illegalSignatureByte}`;
|
||||
transaction.signature = hexConcat(
|
||||
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
SignatureType.Illegal,
|
||||
);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
|
||||
RevertReason.SignatureIllegal,
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Illegal,
|
||||
transactionHash,
|
||||
transaction.signature,
|
||||
),
|
||||
);
|
||||
});
|
||||
it('should revert with with the Invalid signature type', async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
|
||||
transaction.signature = `0x${invalidSignatureByte}`;
|
||||
transaction.signature = hexConcat(SignatureType.Invalid);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
|
||||
RevertReason.SignatureInvalid,
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Invalid,
|
||||
transactionHash,
|
||||
transaction.signature,
|
||||
),
|
||||
);
|
||||
});
|
||||
it("should revert with with a signature type that doesn't exist", async () => {
|
||||
it('should revert with with a signature type that equals `NSignatureTypes`', async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const invalidSignatureByte = '04';
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${invalidSignatureByte}`;
|
||||
transaction.signature = hexConcat(
|
||||
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
SignatureType.NSignatureTypes,
|
||||
);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
|
||||
RevertReason.SignatureUnsupported,
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Unsupported,
|
||||
transactionHash,
|
||||
transaction.signature,
|
||||
),
|
||||
);
|
||||
});
|
||||
it("should revert with with a signature type that isn't supported", async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
transaction.signature = hexConcat(
|
||||
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
SignatureType.Wallet,
|
||||
);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Unsupported,
|
||||
transactionHash,
|
||||
transaction.signature,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -182,11 +186,21 @@ describe('Mixins tests', () => {
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
for (const fnName of [
|
||||
ExchangeFunctionName.CancelOrder,
|
||||
ExchangeFunctionName.BatchCancelOrders,
|
||||
ExchangeFunctionName.CancelOrdersUpTo,
|
||||
]) {
|
||||
for (const fnName of exchangeConstants.MATCH_ORDER_FN_NAMES) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
signature: constants.NULL_BYTES,
|
||||
exchangeAddress: constants.NULL_ADDRESS,
|
||||
chainId,
|
||||
}));
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
for (const fnName of exchangeConstants.CANCEL_ORDER_FN_NAMES) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
@@ -214,27 +228,20 @@ describe('Mixins tests', () => {
|
||||
|
||||
describe('Single order approvals', () => {
|
||||
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1]`, async () => {
|
||||
const order = {
|
||||
...defaultOrder,
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
@@ -242,23 +249,16 @@ describe('Mixins tests', () => {
|
||||
const orders = [order];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
@@ -267,33 +267,25 @@ describe('Mixins tests', () => {
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
@@ -302,81 +294,48 @@ describe('Mixins tests', () => {
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const signature = hexConcat(
|
||||
hexSlice(approval.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval.signature, 6),
|
||||
);
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
transaction.signature,
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
);
|
||||
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -384,52 +343,38 @@ describe('Mixins tests', () => {
|
||||
for (const fnName of [
|
||||
...exchangeConstants.BATCH_FILL_FN_NAMES,
|
||||
...exchangeConstants.MARKET_FILL_FN_NAMES,
|
||||
ExchangeFunctionName.MatchOrders,
|
||||
...exchangeConstants.MATCH_ORDER_FN_NAMES,
|
||||
]) {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||
...order,
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||
...order,
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
@@ -441,56 +386,37 @@ describe('Mixins tests', () => {
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, senderAddress: constants.NULL_ADDRESS }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
@@ -499,200 +425,125 @@ describe('Mixins tests', () => {
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
transaction.signature,
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const signature = hexConcat(
|
||||
hexSlice(approval.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval.signature, 6),
|
||||
);
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
transaction.signature,
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approvalSignature2 = hexConcat(
|
||||
hexSlice(approval2.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval2.signature, 6),
|
||||
);
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
transaction.signature,
|
||||
[approval1.signature, approvalSignature2],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approvalSignature2],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress2),
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approvalSignature2 = hexConcat(
|
||||
hexSlice(approval2.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval2.signature, 6),
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approvalSignature2],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalSignature2],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress2),
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds1,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds2,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
transaction.signature,
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
);
|
||||
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -706,7 +557,6 @@ describe('Mixins tests', () => {
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
@@ -719,7 +569,6 @@ describe('Mixins tests', () => {
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
@@ -731,7 +580,6 @@ describe('Mixins tests', () => {
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import { signingUtils } from '@0x/contracts-test-utils';
|
||||
import { hexConcat, signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { hashUtils, SignedCoordinatorApproval } from './index';
|
||||
|
||||
@@ -17,21 +15,14 @@ export class ApprovalFactory {
|
||||
public newSignedApproval(
|
||||
transaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): SignedCoordinatorApproval {
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
|
||||
transaction,
|
||||
this._verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(transaction, this._verifyingContractAddress, txOrigin);
|
||||
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||
const signedApproval = {
|
||||
txOrigin,
|
||||
transaction,
|
||||
approvalExpirationTimeSeconds,
|
||||
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
|
||||
signature: hexConcat(signatureBuff),
|
||||
};
|
||||
return signedApproval;
|
||||
}
|
||||
|
@@ -1,65 +0,0 @@
|
||||
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
|
||||
|
||||
import { artifacts, CoordinatorRegistryContract } from '../../src';
|
||||
|
||||
export class CoordinatorRegistryWrapper {
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private _coordinatorRegistryContract?: CoordinatorRegistryContract;
|
||||
/**
|
||||
* Instanitates an CoordinatorRegistryWrapper
|
||||
* @param provider Web3 provider to use for all JSON RPC requests
|
||||
* Instance of CoordinatorRegistryWrapper
|
||||
*/
|
||||
constructor(provider: ZeroExProvider) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
||||
}
|
||||
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
|
||||
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.CoordinatorRegistry,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
if (this._coordinatorRegistryContract === undefined) {
|
||||
throw new Error(`Failed to deploy Coordinator Registry contract.`);
|
||||
}
|
||||
return this._coordinatorRegistryContract;
|
||||
}
|
||||
public async setCoordinatorEndpointAsync(
|
||||
coordinatorOperator: string,
|
||||
coordinatorEndpoint: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).setCoordinatorEndpoint.sendTransactionAsync(
|
||||
coordinatorEndpoint,
|
||||
{
|
||||
from: coordinatorOperator,
|
||||
},
|
||||
),
|
||||
);
|
||||
return txReceipt;
|
||||
}
|
||||
public async getCoordinatorEndpointAsync(coordinatorOperator: string): Promise<string> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const coordinatorEndpoint = await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
return coordinatorEndpoint;
|
||||
}
|
||||
private _assertCoordinatorRegistryDeployed(): void {
|
||||
if (this._coordinatorRegistryContract === undefined) {
|
||||
throw new Error(
|
||||
'The Coordinator Registry contract was not deployed through the CoordinatorRegistryWrapper. Call `deployCoordinatorRegistryAsync` to deploy.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,33 +1,16 @@
|
||||
import { hexConcat } from '@0x/contracts-test-utils';
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import { signTypedDataUtils } from '@0x/utils';
|
||||
|
||||
export const hashUtils = {
|
||||
getApprovalHashBuffer(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContract: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): Buffer {
|
||||
const typedData = eip712Utils.createCoordinatorApprovalTypedData(
|
||||
transaction,
|
||||
verifyingContract,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
getApprovalHashBuffer(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): Buffer {
|
||||
const typedData = eip712Utils.createCoordinatorApprovalTypedData(transaction, verifyingContract, txOrigin);
|
||||
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return hashBuffer;
|
||||
},
|
||||
getApprovalHashHex(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContract: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): string {
|
||||
const hashHex = `0x${hashUtils
|
||||
.getApprovalHashBuffer(transaction, verifyingContract, txOrigin, approvalExpirationTimeSeconds)
|
||||
.toString('hex')}`;
|
||||
getApprovalHashHex(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): string {
|
||||
const hashHex = hexConcat(hashUtils.getApprovalHashBuffer(transaction, verifyingContract, txOrigin));
|
||||
return hashHex;
|
||||
},
|
||||
};
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorApproval {
|
||||
transaction: SignedZeroExTransaction;
|
||||
txOrigin: string;
|
||||
approvalExpirationTimeSeconds: BigNumber;
|
||||
}
|
||||
|
||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||
|
@@ -2,6 +2,21 @@
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": ["generated-artifacts/Coordinator.json", "generated-artifacts/CoordinatorRegistry.json"],
|
||||
"files": [
|
||||
"generated-artifacts/Coordinator.json",
|
||||
"generated-artifacts/CoordinatorRegistry.json",
|
||||
"generated-artifacts/ICoordinatorApprovalVerifier.json",
|
||||
"generated-artifacts/ICoordinatorCore.json",
|
||||
"generated-artifacts/ICoordinatorRegistryCore.json",
|
||||
"generated-artifacts/ICoordinatorSignatureValidator.json",
|
||||
"generated-artifacts/LibConstants.json",
|
||||
"generated-artifacts/LibCoordinatorApproval.json",
|
||||
"generated-artifacts/LibCoordinatorRichErrors.json",
|
||||
"generated-artifacts/LibEIP712CoordinatorDomain.json",
|
||||
"generated-artifacts/MixinCoordinatorApprovalVerifier.json",
|
||||
"generated-artifacts/MixinCoordinatorCore.json",
|
||||
"generated-artifacts/MixinCoordinatorRegistryCore.json",
|
||||
"generated-artifacts/MixinSignatureValidator.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
@@ -1,4 +1,18 @@
|
||||
[
|
||||
{
|
||||
"version": "0.1.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `encodeStaticCallAssetData` and `decodeStaticCallAssetData` in LibAssetData",
|
||||
"pr": 2034
|
||||
},
|
||||
{
|
||||
"note": "Add `revertIfInvalidAssetData` in LibAssetData",
|
||||
"pr": 2034
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "0.1.0-beta.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.1.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Add `encodeStaticCallAssetData` and `decodeStaticCallAssetData` in LibAssetData (#2034)
|
||||
* Add `revertIfInvalidAssetData` in LibAssetData (#2034)
|
||||
|
||||
## v0.1.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Use built in selectors instead of hard coded constants (#2055)
|
||||
|
@@ -316,6 +316,29 @@ contract LibAssetData {
|
||||
return (balances, allowances);
|
||||
}
|
||||
|
||||
/// @dev Decode AssetProxy identifier
|
||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-20, ERC-721, ERC1155, or MultiAsset asset.
|
||||
/// @return The AssetProxy identifier
|
||||
function decodeAssetProxyId(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
bytes4 assetProxyId
|
||||
)
|
||||
{
|
||||
assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
require(
|
||||
assetProxyId == IAssetData(address(0)).ERC20Token.selector ||
|
||||
assetProxyId == IAssetData(address(0)).ERC721Token.selector ||
|
||||
assetProxyId == IAssetData(address(0)).ERC1155Assets.selector ||
|
||||
assetProxyId == IAssetData(address(0)).MultiAsset.selector ||
|
||||
assetProxyId == IAssetData(address(0)).StaticCall.selector,
|
||||
"WRONG_PROXY_ID"
|
||||
);
|
||||
return assetProxyId;
|
||||
}
|
||||
|
||||
/// @dev Encode ERC-20 asset data into the format described in the AssetProxy contract specification.
|
||||
/// @param tokenAddress The address of the ERC-20 contract hosting the asset to be traded.
|
||||
/// @return AssetProxy-compliant data describing the asset.
|
||||
@@ -330,7 +353,7 @@ contract LibAssetData {
|
||||
|
||||
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
|
||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
|
||||
/// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20
|
||||
/// @return The AssetProxy identifier, and the address of the ERC-20
|
||||
/// contract hosting this asset.
|
||||
function decodeERC20AssetData(bytes memory assetData)
|
||||
public
|
||||
@@ -515,4 +538,75 @@ contract LibAssetData {
|
||||
);
|
||||
// solhint-enable indent
|
||||
}
|
||||
|
||||
/// @dev Encode StaticCall asset data into the format described in the AssetProxy contract specification.
|
||||
/// @param staticCallTargetAddress Target address of StaticCall.
|
||||
/// @param staticCallData Data that will be passed to staticCallTargetAddress in the StaticCall.
|
||||
/// @param expectedReturnDataHash Expected Keccak-256 hash of the StaticCall return data.
|
||||
/// @return AssetProxy-compliant asset data describing the set of assets.
|
||||
function encodeStaticCallAssetData(
|
||||
address staticCallTargetAddress,
|
||||
bytes memory staticCallData,
|
||||
bytes32 expectedReturnDataHash
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (bytes memory assetData)
|
||||
{
|
||||
assetData = abi.encodeWithSelector(
|
||||
IAssetData(address(0)).StaticCall.selector,
|
||||
staticCallTargetAddress,
|
||||
staticCallData,
|
||||
expectedReturnDataHash
|
||||
);
|
||||
return assetData;
|
||||
}
|
||||
|
||||
/// @dev Decode StaticCall asset data from the format described in the AssetProxy contract specification.
|
||||
/// @param assetData AssetProxy-compliant asset data describing a StaticCall asset
|
||||
/// @return The StaticCall AssetProxy identifier, the target address of the StaticCAll, the data to be
|
||||
/// passed to the target address, and the expected Keccak-256 hash of the static call return data.
|
||||
function decodeStaticCallAssetData(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
bytes4 assetProxyId,
|
||||
address staticCallTargetAddress,
|
||||
bytes memory staticCallData,
|
||||
bytes32 expectedReturnDataHash
|
||||
)
|
||||
{
|
||||
assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
require(
|
||||
assetProxyId == IAssetData(address(0)).StaticCall.selector,
|
||||
"WRONG_PROXY_ID"
|
||||
);
|
||||
|
||||
(staticCallTargetAddress, staticCallData, expectedReturnDataHash) = abi.decode(
|
||||
assetData.slice(4, assetData.length),
|
||||
(address, bytes, bytes32)
|
||||
);
|
||||
}
|
||||
|
||||
function revertIfInvalidAssetData(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
{
|
||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
||||
decodeERC20AssetData(assetData);
|
||||
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||
decodeERC721AssetData(assetData);
|
||||
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||
decodeERC1155AssetData(assetData);
|
||||
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||
decodeMultiAssetData(assetData);
|
||||
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||
decodeStaticCallAssetData(assetData);
|
||||
} else {
|
||||
revert("WRONG_PROXY_ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,35 +1,22 @@
|
||||
{
|
||||
"name": "@0x/contracts-dev-utils",
|
||||
"version": "0.1.0-beta.0",
|
||||
"version": "0.1.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "0x protocol specific utility contracts",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"test": "echo !!! Run tests in @0x/contracts-tests instead !!!",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "UNLIMITED_CONTRACT_SIZE=true mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||
"quantify_bytecode": "echo EVM bytecode object lengths:;for i in ./generated-artifacts/*.json; do node -e \"console.log('$i\t' + (require('$i').compilerOutput.evm.bytecode.object.length - 2) / 2)\"; done",
|
||||
@@ -49,21 +36,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
@@ -72,21 +49,15 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
|
||||
"@0x/contracts-erc1155": "^1.2.0-beta.0",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.0",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.0",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.0",
|
||||
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/order-utils": "^8.5.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
"@0x/assert": "^2.2.0-beta.1",
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contract-addresses": "^3.3.0-beta.2",
|
||||
"@0x/json-schemas": "^4.1.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"ethers": "~4.0.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,10 +1,23 @@
|
||||
[
|
||||
{
|
||||
"version": "1.2.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "1.2.0-beta.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`.",
|
||||
"pr": 1819
|
||||
},
|
||||
{
|
||||
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
||||
"pr": 2254
|
||||
}
|
||||
],
|
||||
"timestamp": 1570135330
|
||||
|
@@ -5,9 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.2.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.2.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`. (#1819)
|
||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||
|
||||
## v1.1.15 - _September 17, 2019_
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddress.sol";
|
||||
import "./interfaces/IERC1155.sol";
|
||||
import "./interfaces/IERC1155Receiver.sol";
|
||||
@@ -26,11 +26,11 @@ import "./MixinNonFungibleToken.sol";
|
||||
|
||||
|
||||
contract ERC1155 is
|
||||
SafeMath,
|
||||
IERC1155,
|
||||
MixinNonFungibleToken
|
||||
{
|
||||
using LibAddress for address;
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
// selectors for receiver callbacks
|
||||
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||
@@ -88,11 +88,11 @@ contract ERC1155 is
|
||||
nfOwners[id] = to;
|
||||
// You could keep balance of NF type in base type id like so:
|
||||
// uint256 baseType = getNonFungibleBaseType(_id);
|
||||
// balances[baseType][_from] = balances[baseType][_from]._safeSub(_value);
|
||||
// balances[baseType][_to] = balances[baseType][_to]._safeAdd(_value);
|
||||
// balances[baseType][_from] = balances[baseType][_from].safeSub(_value);
|
||||
// balances[baseType][_to] = balances[baseType][_to].safeAdd(_value);
|
||||
} else {
|
||||
balances[id][from] = _safeSub(balances[id][from], value);
|
||||
balances[id][to] = _safeAdd(balances[id][to], value);
|
||||
balances[id][from] = balances[id][from].safeSub(value);
|
||||
balances[id][to] = balances[id][to].safeAdd(value);
|
||||
}
|
||||
emit TransferSingle(msg.sender, from, to, id, value);
|
||||
|
||||
@@ -170,8 +170,8 @@ contract ERC1155 is
|
||||
);
|
||||
nfOwners[id] = to;
|
||||
} else {
|
||||
balances[id][from] = _safeSub(balances[id][from], value);
|
||||
balances[id][to] = _safeAdd(balances[id][to], value);
|
||||
balances[id][from] = balances[id][from].safeSub(value);
|
||||
balances[id][to] = balances[id][to].safeAdd(value);
|
||||
}
|
||||
}
|
||||
emit TransferBatch(msg.sender, from, to, ids, values);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "./ERC1155.sol";
|
||||
import "./interfaces/IERC1155Mintable.sol";
|
||||
|
||||
@@ -11,6 +11,7 @@ contract ERC1155Mintable is
|
||||
IERC1155Mintable,
|
||||
ERC1155
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// token nonce
|
||||
uint256 internal nonce;
|
||||
@@ -37,7 +38,7 @@ contract ERC1155Mintable is
|
||||
)
|
||||
external
|
||||
returns (uint256 type_)
|
||||
{
|
||||
{
|
||||
// Store the type in the upper 128 bits
|
||||
type_ = (++nonce << 128);
|
||||
|
||||
@@ -114,7 +115,7 @@ contract ERC1155Mintable is
|
||||
uint256 quantity = quantities[i];
|
||||
|
||||
// Grant the items to the caller
|
||||
balances[id][dst] = _safeAdd(quantity, balances[id][dst]);
|
||||
balances[id][dst] = quantity.safeAdd(balances[id][dst]);
|
||||
|
||||
// Emit the Transfer/Mint event.
|
||||
// the 0x0 source address implies a mint
|
||||
@@ -172,7 +173,7 @@ contract ERC1155Mintable is
|
||||
nfOwners[id] = dst;
|
||||
|
||||
// You could use base-type id to store NF type balances if you wish.
|
||||
// balances[_type][dst] = quantity._safeAdd(balances[_type][dst]);
|
||||
// balances[_type][dst] = quantity.safeAdd(balances[_type][dst]);
|
||||
|
||||
emit TransferSingle(msg.sender, address(0x0), dst, id, 1);
|
||||
|
||||
@@ -194,6 +195,6 @@ contract ERC1155Mintable is
|
||||
|
||||
// record the `maxIndex` of this nft type
|
||||
// this allows us to mint more nft's of this type in a subsequent call.
|
||||
maxIndex[type_] = _safeAdd(to.length, maxIndex[type_]);
|
||||
maxIndex[type_] = to.length.safeAdd(maxIndex[type_]);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc1155",
|
||||
"version": "1.2.0-beta.0",
|
||||
"version": "1.2.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
@@ -48,11 +48,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -70,14 +70,14 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { constants, LogDecoder } from '@0x/contracts-test-utils';
|
||||
import { LogDecoder } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
@@ -100,7 +100,6 @@ export class Erc1155Wrapper {
|
||||
beneficiaries,
|
||||
tokenAmountsAsArray,
|
||||
{ from: this._contractOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
|
||||
@@ -114,12 +113,9 @@ export class Erc1155Wrapper {
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
||||
const token = createFungibleTokenLog.args.id;
|
||||
await this._erc1155Contract.mintNonFungible.awaitTransactionSuccessAsync(
|
||||
token,
|
||||
beneficiaries,
|
||||
{ from: this._contractOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await this._erc1155Contract.mintNonFungible.awaitTransactionSuccessAsync(token, beneficiaries, {
|
||||
from: this._contractOwner,
|
||||
});
|
||||
const encodedNftIds: BigNumber[] = [];
|
||||
const nftIdBegin = 1;
|
||||
const nftIdEnd = beneficiaries.length + 1;
|
||||
|
@@ -1,9 +1,23 @@
|
||||
[
|
||||
{
|
||||
"version": "2.3.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Create `LibERC20Token`",
|
||||
"pr": 2309
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "2.3.0-beta.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
},
|
||||
{
|
||||
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
||||
"pr": 2254
|
||||
}
|
||||
],
|
||||
"timestamp": 1570135330
|
||||
|
@@ -5,9 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.3.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Create `LibERC20Token` (#2309)
|
||||
|
||||
## v2.3.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||
|
||||
## v2.2.14 - _September 17, 2019_
|
||||
|
||||
|
@@ -87,13 +87,13 @@ contract ERC20Token is
|
||||
balances[_to] += _value;
|
||||
balances[_from] -= _value;
|
||||
allowed[_from][msg.sender] -= _value;
|
||||
|
||||
|
||||
emit Transfer(
|
||||
_from,
|
||||
_to,
|
||||
_value
|
||||
);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
119
contracts/erc20/contracts/src/LibERC20Token.sol
Normal file
119
contracts/erc20/contracts/src/LibERC20Token.sol
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "../src/interfaces/IERC20Token.sol";
|
||||
|
||||
|
||||
library LibERC20Token {
|
||||
|
||||
/// @dev Calls `IERC20Token(token).approve()`.
|
||||
/// Reverts if `false` is returned or if the return
|
||||
/// data length is nonzero and not 32 bytes.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param allowance The allowance to set.
|
||||
function approve(
|
||||
address token,
|
||||
address spender,
|
||||
uint256 allowance
|
||||
)
|
||||
internal
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
IERC20Token(0).approve.selector,
|
||||
spender,
|
||||
allowance
|
||||
);
|
||||
_callWithOptionalBooleanResult(token, callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).transfer()`.
|
||||
/// Reverts if `false` is returned or if the return
|
||||
/// data length is nonzero and not 32 bytes.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function transfer(
|
||||
address token,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
IERC20Token(0).transfer.selector,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
_callWithOptionalBooleanResult(token, callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).transferFrom()`.
|
||||
/// Reverts if `false` is returned or if the return
|
||||
/// data length is nonzero and not 32 bytes.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param from The owner of the tokens.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function transferFrom(
|
||||
address token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
IERC20Token(0).transferFrom.selector,
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
_callWithOptionalBooleanResult(token, callData);
|
||||
}
|
||||
|
||||
/// @dev Executes a call on address `target` with calldata `callData`
|
||||
/// and asserts that either nothing was returned or a single boolean
|
||||
/// was returned equal to `true`.
|
||||
/// @param target The call target.
|
||||
/// @param callData The abi-encoded call data.
|
||||
function _callWithOptionalBooleanResult(
|
||||
address target,
|
||||
bytes memory callData
|
||||
)
|
||||
private
|
||||
{
|
||||
(bool didSucceed, bytes memory resultData) = target.call(callData);
|
||||
if (didSucceed) {
|
||||
if (resultData.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (resultData.length == 32) {
|
||||
uint256 result = LibBytes.readUint256(resultData, 0);
|
||||
if (result == 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
LibRichErrors.rrevert(resultData);
|
||||
}
|
||||
}
|
@@ -18,22 +18,23 @@
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "./UnlimitedAllowanceERC20Token.sol";
|
||||
|
||||
|
||||
contract MintableERC20Token is
|
||||
SafeMath,
|
||||
contract MintableERC20Token is
|
||||
UnlimitedAllowanceERC20Token
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev Mints new tokens
|
||||
/// @param _to Address of the beneficiary that will own the minted token
|
||||
/// @param _value Amount of tokens to mint
|
||||
function _mint(address _to, uint256 _value)
|
||||
internal
|
||||
{
|
||||
balances[_to] = _safeAdd(_value, balances[_to]);
|
||||
_totalSupply = _safeAdd(_totalSupply, _value);
|
||||
balances[_to] = _value.safeAdd(balances[_to]);
|
||||
_totalSupply = _totalSupply.safeAdd(_value);
|
||||
|
||||
emit Transfer(
|
||||
address(0),
|
||||
@@ -48,8 +49,8 @@ contract MintableERC20Token is
|
||||
function _burn(address _owner, uint256 _value)
|
||||
internal
|
||||
{
|
||||
balances[_owner] = _safeSub(balances[_owner], _value);
|
||||
_totalSupply = _safeSub(_totalSupply, _value);
|
||||
balances[_owner] = balances[_owner].safeSub(_value);
|
||||
_totalSupply = _totalSupply.safeSub(_value);
|
||||
|
||||
emit Transfer(
|
||||
_owner,
|
||||
|
@@ -18,14 +18,17 @@
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "../src/MintableERC20Token.sol";
|
||||
|
||||
|
||||
contract DummyERC20Token is
|
||||
contract DummyERC20Token is
|
||||
Ownable,
|
||||
MintableERC20Token
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
string public name;
|
||||
string public symbol;
|
||||
uint256 public decimals;
|
||||
@@ -55,9 +58,9 @@ contract DummyERC20Token is
|
||||
{
|
||||
uint256 currBalance = balances[_target];
|
||||
if (_value < currBalance) {
|
||||
_totalSupply = _safeSub(_totalSupply, _safeSub(currBalance, _value));
|
||||
_totalSupply = _totalSupply.safeSub(currBalance.safeSub(_value));
|
||||
} else {
|
||||
_totalSupply = _safeAdd(_totalSupply, _safeSub(_value, currBalance));
|
||||
_totalSupply = _totalSupply.safeAdd(_value.safeSub(currBalance));
|
||||
}
|
||||
balances[_target] = _value;
|
||||
}
|
||||
|
72
contracts/erc20/contracts/test/TestLibERC20Token.sol
Normal file
72
contracts/erc20/contracts/test/TestLibERC20Token.sol
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "../src/LibERC20Token.sol";
|
||||
import "./TestLibERC20TokenTarget.sol";
|
||||
|
||||
|
||||
contract TestLibERC20Token {
|
||||
|
||||
TestLibERC20TokenTarget public target;
|
||||
|
||||
constructor() public {
|
||||
target = new TestLibERC20TokenTarget();
|
||||
}
|
||||
|
||||
function testApprove(
|
||||
bool shouldRevert,
|
||||
bytes calldata revertData,
|
||||
bytes calldata returnData,
|
||||
address spender,
|
||||
uint256 allowance
|
||||
)
|
||||
external
|
||||
{
|
||||
target.setBehavior(shouldRevert, revertData, returnData);
|
||||
LibERC20Token.approve(address(target), spender, allowance);
|
||||
}
|
||||
|
||||
function testTransfer(
|
||||
bool shouldRevert,
|
||||
bytes calldata revertData,
|
||||
bytes calldata returnData,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
{
|
||||
target.setBehavior(shouldRevert, revertData, returnData);
|
||||
LibERC20Token.transfer(address(target), to, amount);
|
||||
}
|
||||
|
||||
function testTransferFrom(
|
||||
bool shouldRevert,
|
||||
bytes calldata revertData,
|
||||
bytes calldata returnData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
{
|
||||
target.setBehavior(shouldRevert, revertData, returnData);
|
||||
LibERC20Token.transferFrom(address(target), from, to, amount);
|
||||
}
|
||||
}
|
98
contracts/erc20/contracts/test/TestLibERC20TokenTarget.sol
Normal file
98
contracts/erc20/contracts/test/TestLibERC20TokenTarget.sol
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
contract TestLibERC20TokenTarget {
|
||||
|
||||
event ApproveCalled(
|
||||
address spender,
|
||||
uint256 allowance
|
||||
);
|
||||
|
||||
event TransferCalled(
|
||||
address to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event TransferFromCalled(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
bool private _shouldRevert;
|
||||
bytes private _revertData;
|
||||
bytes private _returnData;
|
||||
|
||||
function setBehavior(
|
||||
bool shouldRevert,
|
||||
bytes calldata revertData,
|
||||
bytes calldata returnData
|
||||
)
|
||||
external
|
||||
{
|
||||
_shouldRevert = shouldRevert;
|
||||
_revertData = revertData;
|
||||
_returnData = returnData;
|
||||
}
|
||||
|
||||
function approve(
|
||||
address spender,
|
||||
uint256 allowance
|
||||
)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
emit ApproveCalled(spender, allowance);
|
||||
_execute();
|
||||
}
|
||||
|
||||
function transfer(
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
emit TransferCalled(to, amount);
|
||||
_execute();
|
||||
}
|
||||
|
||||
function transferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
emit TransferFromCalled(from, to, amount);
|
||||
_execute();
|
||||
}
|
||||
|
||||
function _execute() private view {
|
||||
if (_shouldRevert) {
|
||||
bytes memory revertData = _revertData;
|
||||
assembly { revert(add(revertData, 0x20), mload(revertData)) }
|
||||
}
|
||||
bytes memory returnData = _returnData;
|
||||
assembly { return(add(returnData, 0x20), mload(returnData)) }
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "2.3.0-beta.0",
|
||||
"version": "2.3.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
@@ -34,7 +34,7 @@
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IEtherToken|MintableERC20Token|UnlimitedAllowanceERC20Token|UntransferrableDummyERC20Token|WETH9|ZRXToken).json",
|
||||
"abis": "./generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IEtherToken|LibERC20Token|MintableERC20Token|TestLibERC20Token|TestLibERC20TokenTarget|UnlimitedAllowanceERC20Token|UntransferrableDummyERC20Token|WETH9|ZRXToken).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -47,12 +47,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -69,13 +69,13 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -11,13 +11,17 @@ import * as DummyNoReturnERC20Token from '../generated-artifacts/DummyNoReturnER
|
||||
import * as ERC20Token from '../generated-artifacts/ERC20Token.json';
|
||||
import * as IERC20Token from '../generated-artifacts/IERC20Token.json';
|
||||
import * as IEtherToken from '../generated-artifacts/IEtherToken.json';
|
||||
import * as LibERC20Token from '../generated-artifacts/LibERC20Token.json';
|
||||
import * as MintableERC20Token from '../generated-artifacts/MintableERC20Token.json';
|
||||
import * as TestLibERC20Token from '../generated-artifacts/TestLibERC20Token.json';
|
||||
import * as TestLibERC20TokenTarget from '../generated-artifacts/TestLibERC20TokenTarget.json';
|
||||
import * as UnlimitedAllowanceERC20Token from '../generated-artifacts/UnlimitedAllowanceERC20Token.json';
|
||||
import * as UntransferrableDummyERC20Token from '../generated-artifacts/UntransferrableDummyERC20Token.json';
|
||||
import * as WETH9 from '../generated-artifacts/WETH9.json';
|
||||
import * as ZRXToken from '../generated-artifacts/ZRXToken.json';
|
||||
export const artifacts = {
|
||||
ERC20Token: ERC20Token as ContractArtifact,
|
||||
LibERC20Token: LibERC20Token as ContractArtifact,
|
||||
MintableERC20Token: MintableERC20Token as ContractArtifact,
|
||||
UnlimitedAllowanceERC20Token: UnlimitedAllowanceERC20Token as ContractArtifact,
|
||||
WETH9: WETH9 as ContractArtifact,
|
||||
@@ -27,5 +31,7 @@ export const artifacts = {
|
||||
DummyERC20Token: DummyERC20Token as ContractArtifact,
|
||||
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
|
||||
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
|
||||
TestLibERC20Token: TestLibERC20Token as ContractArtifact,
|
||||
TestLibERC20TokenTarget: TestLibERC20TokenTarget as ContractArtifact,
|
||||
UntransferrableDummyERC20Token: UntransferrableDummyERC20Token as ContractArtifact,
|
||||
};
|
||||
|
@@ -9,7 +9,10 @@ export * from '../generated-wrappers/dummy_no_return_erc20_token';
|
||||
export * from '../generated-wrappers/erc20_token';
|
||||
export * from '../generated-wrappers/i_erc20_token';
|
||||
export * from '../generated-wrappers/i_ether_token';
|
||||
export * from '../generated-wrappers/lib_erc20_token';
|
||||
export * from '../generated-wrappers/mintable_erc20_token';
|
||||
export * from '../generated-wrappers/test_lib_erc20_token';
|
||||
export * from '../generated-wrappers/test_lib_erc20_token_target';
|
||||
export * from '../generated-wrappers/unlimited_allowance_erc20_token';
|
||||
export * from '../generated-wrappers/untransferrable_dummy_erc20_token';
|
||||
export * from '../generated-wrappers/weth9';
|
||||
|
419
contracts/erc20/test/lib_erc20_token.ts
Normal file
419
contracts/erc20/test/lib_erc20_token.ts
Normal file
@@ -0,0 +1,419 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
randomAddress,
|
||||
verifyEventsFromLogs,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { RawRevertError, StringRevertError } from '@0x/utils';
|
||||
|
||||
import { artifacts, TestLibERC20TokenContract, TestLibERC20TokenTargetEvents } from '../src';
|
||||
|
||||
blockchainTests('LibERC20Token', env => {
|
||||
let testContract: TestLibERC20TokenContract;
|
||||
const REVERT_STRING = 'WHOOPSIE';
|
||||
const ENCODED_TRUE = hexLeftPad(1);
|
||||
const ENCODED_FALSE = hexLeftPad(0);
|
||||
const ENCODED_TWO = hexLeftPad(2);
|
||||
const ENCODED_SHORT_TRUE = hexLeftPad(2, 31);
|
||||
const ENCODED_LONG_TRUE = hexLeftPad(2, 33);
|
||||
|
||||
before(async () => {
|
||||
testContract = await TestLibERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestLibERC20Token,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
function encodeRevert(message: string): string {
|
||||
return new StringRevertError(message).encode();
|
||||
}
|
||||
|
||||
describe('approve()', () => {
|
||||
it('calls the target with the correct arguments', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
const { logs } = await testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
expect(logs).to.be.length(1);
|
||||
verifyEventsFromLogs(logs, [{ spender, allowance }], TestLibERC20TokenTargetEvents.ApproveCalled);
|
||||
});
|
||||
|
||||
it('succeeds if the target returns true', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
await testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
});
|
||||
|
||||
it('succeeds if the target returns nothing', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
await testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
constants.NULL_BYTES,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
});
|
||||
|
||||
it('fails if the target returns false', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_FALSE,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns nonzero and not true', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TWO,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns less than 32 bytes', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_SHORT_TRUE,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns greater than 32 bytes', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_LONG_TRUE,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target reverts', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
true,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
return expect(tx).to.revertWith(REVERT_STRING);
|
||||
});
|
||||
|
||||
it('fails if the target reverts with no data', async () => {
|
||||
const spender = randomAddress();
|
||||
const allowance = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testApprove.awaitTransactionSuccessAsync(
|
||||
true,
|
||||
constants.NULL_BYTES,
|
||||
ENCODED_TRUE,
|
||||
spender,
|
||||
allowance,
|
||||
);
|
||||
return expect(tx).to.be.rejectedWith('revert');
|
||||
});
|
||||
});
|
||||
|
||||
describe('transfer()', () => {
|
||||
it('calls the target with the correct arguments', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const { logs } = await testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
expect(logs).to.be.length(1);
|
||||
verifyEventsFromLogs(logs, [{ to, amount }], TestLibERC20TokenTargetEvents.TransferCalled);
|
||||
});
|
||||
|
||||
it('succeeds if the target returns true', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
await testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
});
|
||||
|
||||
it('succeeds if the target returns nothing', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
await testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
constants.NULL_BYTES,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
});
|
||||
|
||||
it('fails if the target returns false', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_FALSE,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns nonzero and not true', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TWO,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns less than 32 bytes', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_SHORT_TRUE,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns greater than 32 bytes', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_LONG_TRUE,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target reverts', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
true,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
return expect(tx).to.revertWith(REVERT_STRING);
|
||||
});
|
||||
|
||||
it('fails if the target reverts with no data', async () => {
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransfer.awaitTransactionSuccessAsync(
|
||||
true,
|
||||
constants.NULL_BYTES,
|
||||
ENCODED_TRUE,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
return expect(tx).to.be.rejectedWith('revert');
|
||||
});
|
||||
});
|
||||
|
||||
describe('transferFrom()', () => {
|
||||
it('calls the target with the correct arguments', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const { logs } = await testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
expect(logs).to.be.length(1);
|
||||
verifyEventsFromLogs(logs, [{ from: owner, to, amount }], TestLibERC20TokenTargetEvents.TransferFromCalled);
|
||||
});
|
||||
|
||||
it('succeeds if the target returns true', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
await testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
});
|
||||
|
||||
it('succeeds if the target returns nothing', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
await testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
constants.NULL_BYTES,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
});
|
||||
|
||||
it('fails if the target returns false', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_FALSE,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns nonzero and not true', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TWO,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns less than 32 bytes', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_SHORT_TRUE,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target returns greater than 32 bytes', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
false,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_LONG_TRUE,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('fails if the target reverts', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
true,
|
||||
encodeRevert(REVERT_STRING),
|
||||
ENCODED_TRUE,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
return expect(tx).to.revertWith(REVERT_STRING);
|
||||
});
|
||||
|
||||
it('fails if the target reverts with no data', async () => {
|
||||
const owner = randomAddress();
|
||||
const to = randomAddress();
|
||||
const amount = getRandomInteger(0, 100e18);
|
||||
const tx = testContract.testTransferFrom.awaitTransactionSuccessAsync(
|
||||
true,
|
||||
constants.NULL_BYTES,
|
||||
ENCODED_TRUE,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
);
|
||||
return expect(tx).to.be.rejectedWith('revert');
|
||||
});
|
||||
});
|
||||
});
|
@@ -9,7 +9,10 @@
|
||||
"generated-artifacts/ERC20Token.json",
|
||||
"generated-artifacts/IERC20Token.json",
|
||||
"generated-artifacts/IEtherToken.json",
|
||||
"generated-artifacts/LibERC20Token.json",
|
||||
"generated-artifacts/MintableERC20Token.json",
|
||||
"generated-artifacts/TestLibERC20Token.json",
|
||||
"generated-artifacts/TestLibERC20TokenTarget.json",
|
||||
"generated-artifacts/UnlimitedAllowanceERC20Token.json",
|
||||
"generated-artifacts/UntransferrableDummyERC20Token.json",
|
||||
"generated-artifacts/WETH9.json",
|
||||
|
@@ -1,9 +1,22 @@
|
||||
[
|
||||
{
|
||||
"version": "2.2.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "2.2.0-beta.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
},
|
||||
{
|
||||
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
||||
"pr": 2254
|
||||
}
|
||||
],
|
||||
"timestamp": 1570135330
|
||||
|
@@ -5,9 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.2.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||
|
||||
## v2.1.15 - _September 17, 2019_
|
||||
|
||||
|
@@ -20,13 +20,14 @@ pragma solidity ^0.5.9;
|
||||
|
||||
import "./interfaces/IERC721Token.sol";
|
||||
import "./interfaces/IERC721Receiver.sol";
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
|
||||
|
||||
contract ERC721Token is
|
||||
IERC721Token,
|
||||
SafeMath
|
||||
IERC721Token
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
// Function selector for ERC721Receiver.onERC721Received
|
||||
// 0x150b7a02
|
||||
bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
|
||||
@@ -163,7 +164,7 @@ contract ERC721Token is
|
||||
_approved
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// @notice Count all NFTs assigned to an owner
|
||||
/// @dev NFTs assigned to the zero address are considered invalid, and this
|
||||
/// function throws for queries about the zero address.
|
||||
@@ -223,9 +224,9 @@ contract ERC721Token is
|
||||
}
|
||||
|
||||
owners[_tokenId] = _to;
|
||||
balances[_from] = _safeSub(balances[_from], 1);
|
||||
balances[_to] = _safeAdd(balances[_to], 1);
|
||||
|
||||
balances[_from] = balances[_from].safeSub(1);
|
||||
balances[_to] = balances[_to].safeAdd(1);
|
||||
|
||||
emit Transfer(
|
||||
_from,
|
||||
_to,
|
||||
|
@@ -18,16 +18,19 @@
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "./ERC721Token.sol";
|
||||
|
||||
|
||||
contract MintableERC721Token is
|
||||
ERC721Token
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev Function to mint a new token
|
||||
/// Reverts if the given token ID already exists
|
||||
/// @param _to Address of the beneficiary that will own the minted token
|
||||
/// @param _tokenId ID of the token to be minted by the msg.sender
|
||||
/// @param _tokenId ID of the token to be minted by the msg.sender
|
||||
function _mint(address _to, uint256 _tokenId)
|
||||
internal
|
||||
{
|
||||
@@ -43,7 +46,7 @@ contract MintableERC721Token is
|
||||
);
|
||||
|
||||
owners[_tokenId] = _to;
|
||||
balances[_to] = _safeAdd(balances[_to], 1);
|
||||
balances[_to] = balances[_to].safeAdd(1);
|
||||
|
||||
emit Transfer(
|
||||
address(0),
|
||||
@@ -71,7 +74,7 @@ contract MintableERC721Token is
|
||||
);
|
||||
|
||||
owners[_tokenId] = address(0);
|
||||
balances[_owner] = _safeSub(balances[_owner], 1);
|
||||
balances[_owner] = balances[_owner].safeSub(1);
|
||||
|
||||
emit Transfer(
|
||||
_owner,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc721",
|
||||
"version": "2.2.0-beta.0",
|
||||
"version": "2.2.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
@@ -48,12 +48,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -71,13 +71,13 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "3.1.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Use `LibERC20Token` in `MixinAssets`",
|
||||
"pr": 2309
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "3.1.0-beta.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Use `LibERC20Token` in `MixinAssets` (#2309)
|
||||
|
||||
## v3.1.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -22,6 +22,7 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./libs/LibForwarderRichErrors.sol";
|
||||
@@ -105,38 +106,8 @@ contract MixinAssets is
|
||||
internal
|
||||
{
|
||||
address token = assetData.readAddress(16);
|
||||
|
||||
// Transfer tokens.
|
||||
// We do a raw call so we can check the success separate
|
||||
// from the return data.
|
||||
(bool success, bytes memory returnData) = token.call(abi.encodeWithSelector(
|
||||
ERC20_TRANSFER_SELECTOR,
|
||||
msg.sender,
|
||||
amount
|
||||
));
|
||||
if (!success) {
|
||||
LibRichErrors.rrevert(LibForwarderRichErrors.TransferFailedError(returnData));
|
||||
}
|
||||
|
||||
// Check return data.
|
||||
// If there is no return data, we assume the token incorrectly
|
||||
// does not return a bool. In this case we expect it to revert
|
||||
// on failure, which was handled above.
|
||||
// If the token does return data, we require that it is a single
|
||||
// value that evaluates to true.
|
||||
assembly {
|
||||
if returndatasize {
|
||||
success := 0
|
||||
if eq(returndatasize, 32) {
|
||||
// First 64 bytes of memory are reserved scratch space
|
||||
returndatacopy(0, 0, 32)
|
||||
success := mload(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
LibRichErrors.rrevert(LibForwarderRichErrors.TransferFailedError(returnData));
|
||||
}
|
||||
LibERC20Token.transfer(token, msg.sender, amount);
|
||||
}
|
||||
|
||||
/// @dev Decodes ERC721 assetData and transfers given amount to sender.
|
||||
|
@@ -51,10 +51,6 @@ library LibForwarderRichErrors {
|
||||
bytes4 internal constant OVERSPENT_WETH_ERROR_SELECTOR =
|
||||
0xcdcbed5d;
|
||||
|
||||
// bytes4(keccak256("TransferFailedError(bytes)"))
|
||||
bytes4 internal constant TRANSFER_FAILED_ERROR_SELECTOR =
|
||||
0x5e7eb60f;
|
||||
|
||||
// bytes4(keccak256("DefaultFunctionWethContractOnlyError(address)"))
|
||||
bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
|
||||
0x08b18698;
|
||||
@@ -160,19 +156,6 @@ library LibForwarderRichErrors {
|
||||
);
|
||||
}
|
||||
|
||||
function TransferFailedError(
|
||||
bytes memory errorData
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
TRANSFER_FAILED_ERROR_SELECTOR,
|
||||
errorData
|
||||
);
|
||||
}
|
||||
|
||||
function DefaultFunctionWethContractOnlyError(
|
||||
address senderAddress
|
||||
)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-forwarder",
|
||||
"version": "3.1.0-beta.0",
|
||||
"version": "3.1.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -14,16 +14,15 @@
|
||||
"build:ts": "tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||
"test": "yarn run_mocha",
|
||||
"test": "echo !!! Tests have been relocated to @0x/contracts-integrations !!!",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
@@ -35,7 +34,7 @@
|
||||
"compile:truffle": "truffle compile"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth|TestProtocolFeeCollector).json",
|
||||
"abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -48,12 +47,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -71,19 +70,20 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.0",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.0",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.0",
|
||||
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/order-utils": "^8.5.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.1",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.1",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.1",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.1",
|
||||
"@0x/contracts-exchange-libs": "^3.1.0-beta.1",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||
"@0x/order-utils": "^8.5.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -15,7 +15,6 @@ import * as MixinAssets from '../generated-artifacts/MixinAssets.json';
|
||||
import * as MixinExchangeWrapper from '../generated-artifacts/MixinExchangeWrapper.json';
|
||||
import * as MixinForwarderCore from '../generated-artifacts/MixinForwarderCore.json';
|
||||
import * as MixinWeth from '../generated-artifacts/MixinWeth.json';
|
||||
import * as TestProtocolFeeCollector from '../generated-artifacts/TestProtocolFeeCollector.json';
|
||||
export const artifacts = {
|
||||
Forwarder: Forwarder as ContractArtifact,
|
||||
MixinAssets: MixinAssets as ContractArtifact,
|
||||
@@ -27,5 +26,4 @@ export const artifacts = {
|
||||
IForwarderCore: IForwarderCore as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
|
||||
TestProtocolFeeCollector: TestProtocolFeeCollector as ContractArtifact,
|
||||
};
|
||||
|
@@ -1,3 +1,2 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
export * from '../test/utils';
|
||||
|
@@ -13,4 +13,3 @@ export * from '../generated-wrappers/mixin_assets';
|
||||
export * from '../generated-wrappers/mixin_exchange_wrapper';
|
||||
export * from '../generated-wrappers/mixin_forwarder_core';
|
||||
export * from '../generated-wrappers/mixin_weth';
|
||||
export * from '../generated-wrappers/test_protocol_fee_collector';
|
||||
|
@@ -1,716 +0,0 @@
|
||||
import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { artifacts as exchangeArtifacts, ExchangeContract, ExchangeWrapper } from '@0x/contracts-exchange';
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
ContractName,
|
||||
expect,
|
||||
getLatestBlockTimestampAsync,
|
||||
OrderFactory,
|
||||
sendTransactionResult,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils, ForwarderRevertErrors } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
ForwarderContract,
|
||||
ForwarderTestFactory,
|
||||
ForwarderWrapper,
|
||||
TestProtocolFeeCollectorContract,
|
||||
} from '../src';
|
||||
|
||||
const DECIMALS_DEFAULT = 18;
|
||||
|
||||
blockchainTests(ContractName.Forwarder, env => {
|
||||
let owner: string;
|
||||
let makerAddress: string;
|
||||
let takerAddress: string;
|
||||
let orderFeeRecipientAddress: string;
|
||||
let forwarderFeeRecipientAddress: string;
|
||||
let defaultMakerAssetAddress: string;
|
||||
|
||||
let weth: DummyERC20TokenContract;
|
||||
let erc20Token: DummyERC20TokenContract;
|
||||
let secondErc20Token: DummyERC20TokenContract;
|
||||
let erc721Token: DummyERC721TokenContract;
|
||||
let forwarderContract: ForwarderContract;
|
||||
let wethContract: WETH9Contract;
|
||||
let exchangeContract: ExchangeContract;
|
||||
let protocolFeeCollector: TestProtocolFeeCollectorContract;
|
||||
|
||||
let forwarderWrapper: ForwarderWrapper;
|
||||
let exchangeWrapper: ExchangeWrapper;
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
|
||||
let orderFactory: OrderFactory;
|
||||
let forwarderTestFactory: ForwarderTestFactory;
|
||||
|
||||
let chainId: number;
|
||||
let wethAssetData: string;
|
||||
let erc721MakerAssetIds: BigNumber[];
|
||||
|
||||
const GAS_PRICE = new BigNumber(env.txDefaults.gasPrice || constants.DEFAULT_GAS_PRICE);
|
||||
const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150);
|
||||
const PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
|
||||
|
||||
before(async () => {
|
||||
// Set up addresses
|
||||
const accounts = await env.getAccountAddressesAsync();
|
||||
const usedAddresses = ([
|
||||
owner,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
orderFeeRecipientAddress,
|
||||
forwarderFeeRecipientAddress,
|
||||
] = accounts);
|
||||
|
||||
// Set up Exchange
|
||||
chainId = await env.getChainIdAsync();
|
||||
exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
exchangeWrapper = new ExchangeWrapper(exchangeContract);
|
||||
|
||||
// Set up ERC20
|
||||
erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
|
||||
[erc20Token, secondErc20Token] = await erc20Wrapper.deployDummyTokensAsync(2, constants.DUMMY_TOKEN_DECIMALS);
|
||||
const erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
|
||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
|
||||
from: owner,
|
||||
});
|
||||
|
||||
// Set up WETH
|
||||
wethContract = await WETH9Contract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.WETH9,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
);
|
||||
weth = new DummyERC20TokenContract(wethContract.address, env.provider);
|
||||
wethAssetData = assetDataUtils.encodeERC20AssetData(wethContract.address);
|
||||
erc20Wrapper.addDummyTokenContract(weth);
|
||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||
|
||||
// Set up ERC721
|
||||
const erc721Wrapper = new ERC721Wrapper(env.provider, usedAddresses, owner);
|
||||
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
||||
const erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
||||
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||
erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
|
||||
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
|
||||
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
|
||||
from: owner,
|
||||
});
|
||||
|
||||
// Set up Protocol Fee Collector
|
||||
protocolFeeCollector = await TestProtocolFeeCollectorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestProtocolFeeCollector,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
wethContract.address,
|
||||
);
|
||||
await exchangeContract.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(PROTOCOL_FEE_MULTIPLIER);
|
||||
await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(
|
||||
protocolFeeCollector.address,
|
||||
);
|
||||
erc20Wrapper.addTokenOwnerAddress(protocolFeeCollector.address);
|
||||
|
||||
// Set defaults
|
||||
defaultMakerAssetAddress = erc20Token.address;
|
||||
const defaultTakerAssetAddress = wethContract.address;
|
||||
const defaultOrderParams = {
|
||||
makerAddress,
|
||||
feeRecipientAddress: orderFeeRecipientAddress,
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(200, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, DECIMALS_DEFAULT),
|
||||
makerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
|
||||
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
exchangeAddress: exchangeContract.address,
|
||||
chainId,
|
||||
};
|
||||
|
||||
// Set up Forwarder
|
||||
forwarderContract = await ForwarderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Forwarder,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
exchangeContract.address,
|
||||
wethAssetData,
|
||||
);
|
||||
forwarderWrapper = new ForwarderWrapper(forwarderContract, env.provider);
|
||||
await forwarderWrapper.approveMakerAssetProxyAsync(defaultOrderParams.makerAssetData, {
|
||||
from: takerAddress,
|
||||
});
|
||||
erc20Wrapper.addTokenOwnerAddress(forwarderContract.address);
|
||||
|
||||
// Set up factories
|
||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||
forwarderTestFactory = new ForwarderTestFactory(
|
||||
exchangeWrapper,
|
||||
forwarderWrapper,
|
||||
erc20Wrapper,
|
||||
forwarderContract.address,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
protocolFeeCollector.address,
|
||||
orderFeeRecipientAddress,
|
||||
forwarderFeeRecipientAddress,
|
||||
weth.address,
|
||||
GAS_PRICE,
|
||||
PROTOCOL_FEE_MULTIPLIER,
|
||||
);
|
||||
});
|
||||
|
||||
blockchainTests.resets('constructor', () => {
|
||||
it('should revert if assetProxy is unregistered', async () => {
|
||||
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
|
||||
const deployForwarder = (ForwarderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Forwarder,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
exchange.address,
|
||||
wethAssetData,
|
||||
) as any) as sendTransactionResult;
|
||||
|
||||
await expect(deployForwarder).to.revertWith(new ForwarderRevertErrors.UnregisteredAssetProxyError());
|
||||
});
|
||||
});
|
||||
blockchainTests.resets('marketSellOrdersWithEth without extra fees', () => {
|
||||
it('should fill a single order without a taker fee', async () => {
|
||||
const orderWithoutFee = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithoutFee], 0.78, [erc20Token]);
|
||||
});
|
||||
it('should fill multiple orders without taker fees', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync();
|
||||
const secondOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(285, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(21, DECIMALS_DEFAULT),
|
||||
});
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.51, [erc20Token]);
|
||||
});
|
||||
it('should fill a single order with a percentage fee', async () => {
|
||||
const orderWithPercentageFee = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithPercentageFee], 0.58, [erc20Token]);
|
||||
});
|
||||
it('should fill multiple orders with percentage fees', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
const secondOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(190, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(31, DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.34, [erc20Token]);
|
||||
});
|
||||
it('should fail to fill an order with a percentage fee if the asset proxy is not yet approved', async () => {
|
||||
const unapprovedAsset = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: unapprovedAsset,
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: unapprovedAsset,
|
||||
});
|
||||
|
||||
const ethValue = order.takerAssetAmount;
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
|
||||
// Execute test case
|
||||
const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
|
||||
value: ethValue,
|
||||
from: takerAddress,
|
||||
});
|
||||
|
||||
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
const totalEthSpent = GAS_PRICE.times(tx.gasUsed);
|
||||
|
||||
// Validate test case
|
||||
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
|
||||
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
erc20Balances[makerAddress][defaultMakerAssetAddress],
|
||||
);
|
||||
expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
erc20Balances[takerAddress][defaultMakerAssetAddress],
|
||||
);
|
||||
expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
|
||||
erc20Balances[makerAddress][weth.address],
|
||||
);
|
||||
expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should fill a single order with a WETH fee', async () => {
|
||||
const orderWithWethFee = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithWethFee], 0.13, [erc20Token]);
|
||||
});
|
||||
it('should fill multiple orders with WETH fees', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
const secondOrderWithWethFee = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(33, DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
const orders = [firstOrder, secondOrderWithWethFee];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.25, [erc20Token]);
|
||||
});
|
||||
it('should refund remaining ETH if amount is greater than takerAssetAmount', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
const ethValue = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(2);
|
||||
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
|
||||
const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
|
||||
value: ethValue,
|
||||
from: takerAddress,
|
||||
});
|
||||
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
const totalEthSpent = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(GAS_PRICE.times(tx.gasUsed));
|
||||
|
||||
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
|
||||
});
|
||||
it('should fill orders with different makerAssetData', async () => {
|
||||
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: firstOrderMakerAssetData,
|
||||
});
|
||||
|
||||
const secondOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
const secondOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: secondOrderMakerAssetData,
|
||||
});
|
||||
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
|
||||
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
|
||||
});
|
||||
it('should fail to fill an order with a fee denominated in an asset other than makerAsset or WETH', async () => {
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const takerFeeAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData,
|
||||
takerFeeAssetData,
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
});
|
||||
|
||||
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should fill a partially-filled order without a taker fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.3, [erc20Token]);
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.8, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid maker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid taker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
takerAssetAmount: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an expired order', async () => {
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const expiredOrder = await orderFactory.newSignedOrderAsync({
|
||||
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a fully filled order', async () => {
|
||||
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder], 1, [erc20Token]);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a cancelled order', async () => {
|
||||
const cancelledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
});
|
||||
blockchainTests.resets('marketSellOrdersWithEth with extra fees', () => {
|
||||
it('should fill the order and send fee to feeRecipient', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(157, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(36, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.67, [erc20Token], {
|
||||
forwarderFeePercentage: new BigNumber(2),
|
||||
});
|
||||
});
|
||||
it('should fail if the fee is set too high', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
const forwarderFeePercentage = new BigNumber(6);
|
||||
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
|
||||
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage),
|
||||
);
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
|
||||
forwarderFeePercentage,
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
});
|
||||
blockchainTests.resets('marketBuyOrdersWithEth without extra fees', () => {
|
||||
it('should buy the exact amount of makerAsset in a single order', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(131, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(20, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.62, [erc20Token]);
|
||||
});
|
||||
it('should buy the exact amount of makerAsset in multiple orders', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync();
|
||||
const secondOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(77, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
|
||||
});
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketBuyTestAsync(orders, 1.96, [erc20Token]);
|
||||
});
|
||||
it('should buy exactly makerAssetBuyAmount in orders with different makerAssetData', async () => {
|
||||
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: firstOrderMakerAssetData,
|
||||
});
|
||||
|
||||
const secondOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
const secondOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: secondOrderMakerAssetData,
|
||||
});
|
||||
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
|
||||
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketBuyTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
|
||||
});
|
||||
it('should buy the exact amount of makerAsset and return excess ETH', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(80, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.57, [erc20Token], {
|
||||
ethValueAdjustment: 2,
|
||||
});
|
||||
});
|
||||
it('should buy the exact amount of makerAsset from a single order with a WETH fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(79, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.38, [erc20Token]);
|
||||
});
|
||||
it('should buy the exact amount of makerAsset from a single order with a percentage fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(80, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.52, [erc20Token]);
|
||||
});
|
||||
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
const revertError = new ForwarderRevertErrors.CompleteBuyFailedError(
|
||||
order.makerAssetAmount.times(0.5),
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
ethValueAdjustment: -2,
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should buy an ERC721 asset from a single order', async () => {
|
||||
const makerAssetId = erc721MakerAssetIds[0];
|
||||
const erc721Order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([erc721Order], 1, [erc721Token], {
|
||||
makerAssetId,
|
||||
});
|
||||
});
|
||||
it('should buy an ERC721 asset and pay a WETH fee', async () => {
|
||||
const makerAssetId = erc721MakerAssetIds[0];
|
||||
const erc721orderWithWethFee = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([erc721orderWithWethFee], 1, [erc721Token], {
|
||||
makerAssetId,
|
||||
});
|
||||
});
|
||||
it('should fail to fill an order with a fee denominated in an asset other than makerAsset or WETH', async () => {
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const takerFeeAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData,
|
||||
takerFeeAssetData,
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
});
|
||||
|
||||
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should fill a partially-filled order without a taker fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.3, [erc20Token]);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.8, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid maker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid taker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
takerAssetAmount: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an expired order', async () => {
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const expiredOrder = await orderFactory.newSignedOrderAsync({
|
||||
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a fully filled order', async () => {
|
||||
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder], 1, [erc20Token]);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a cancelled order', async () => {
|
||||
const cancelledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('Should buy slightly greater makerAsset when exchange rate is rounded', async () => {
|
||||
// The 0x Protocol contracts round the exchange rate in favor of the Maker.
|
||||
// In this case, the taker must round up how much they're going to spend, which
|
||||
// in turn increases the amount of MakerAsset being purchased.
|
||||
// Example:
|
||||
// The taker wants to buy 5 units of the MakerAsset at a rate of 3M/2T.
|
||||
// For every 2 units of TakerAsset, the taker will receive 3 units of MakerAsset.
|
||||
// To purchase 5 units, the taker must spend 10/3 = 3.33 units of TakerAssset.
|
||||
// However, the Taker can only spend whole units.
|
||||
// Spending floor(10/3) = 3 units will yield a profit of Floor(3*3/2) = Floor(4.5) = 4 units of MakerAsset.
|
||||
// Spending ceil(10/3) = 4 units will yield a profit of Floor(4*3/2) = 6 units of MakerAsset.
|
||||
//
|
||||
// The forwarding contract will opt for the second option, which overbuys, to ensure the taker
|
||||
// receives at least the amount of MakerAsset they requested.
|
||||
//
|
||||
// Construct test case using values from example above
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber('30'),
|
||||
takerAssetAmount: new BigNumber('20'),
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20Token.address),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
|
||||
makerFee: new BigNumber(0),
|
||||
takerFee: new BigNumber(0),
|
||||
});
|
||||
const desiredMakerAssetFillAmount = new BigNumber('5');
|
||||
const makerAssetFillAmount = new BigNumber('6');
|
||||
const primaryTakerAssetFillAmount = new BigNumber('4');
|
||||
const ethValue = primaryTakerAssetFillAmount.plus(PROTOCOL_FEE);
|
||||
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
|
||||
// Execute test case
|
||||
const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
|
||||
value: ethValue,
|
||||
from: takerAddress,
|
||||
});
|
||||
// Fetch end balances and construct expected outputs
|
||||
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
const totalEthSpent = ethValue.plus(GAS_PRICE.times(tx.gasUsed));
|
||||
// Validate test case
|
||||
expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
|
||||
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
|
||||
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
|
||||
);
|
||||
expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
|
||||
);
|
||||
expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
|
||||
erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
|
||||
);
|
||||
expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('Should buy slightly greater MakerAsset when exchange rate is rounded (Regression Test)', async () => {
|
||||
// Disable protocol fees for regression test
|
||||
await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(constants.NULL_ADDRESS);
|
||||
// Order taken from a transaction on mainnet that failed due to a rounding error.
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber('268166666666666666666'),
|
||||
takerAssetAmount: new BigNumber('219090625878836371'),
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20Token.address),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
|
||||
makerFee: new BigNumber(0),
|
||||
takerFee: new BigNumber(0),
|
||||
});
|
||||
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
|
||||
// The taker will receive more than the desired amount of makerAsset due to rounding
|
||||
const desiredMakerAssetFillAmount = new BigNumber('5000000000000000000');
|
||||
const ethValue = new BigNumber('4084971271824171');
|
||||
const makerAssetFillAmount = ethValue
|
||||
.times(order.makerAssetAmount)
|
||||
.dividedToIntegerBy(order.takerAssetAmount);
|
||||
// Execute test case
|
||||
const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
|
||||
value: ethValue,
|
||||
from: takerAddress,
|
||||
});
|
||||
// Fetch end balances and construct expected outputs
|
||||
const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
|
||||
const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
const primaryTakerAssetFillAmount = ethValue;
|
||||
const totalEthSpent = primaryTakerAssetFillAmount.plus(GAS_PRICE.times(tx.gasUsed));
|
||||
// Validate test case
|
||||
expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
|
||||
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
|
||||
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
|
||||
);
|
||||
expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
|
||||
);
|
||||
expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
|
||||
erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
|
||||
);
|
||||
expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
});
|
||||
blockchainTests.resets('marketBuyOrdersWithEth with extra fees', () => {
|
||||
it('should buy the asset and send fee to feeRecipient', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(125, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.33, [erc20Token], {
|
||||
forwarderFeePercentage: new BigNumber(2),
|
||||
});
|
||||
});
|
||||
it('should fail if the fee is set too high', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
|
||||
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, new BigNumber(6)),
|
||||
);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
forwarderFeePercentage: new BigNumber(6),
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should fail if there is not enough ETH remaining to pay the fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
const forwarderFeePercentage = new BigNumber(2);
|
||||
const ethFee = ForwarderTestFactory.getPercentageOfValue(
|
||||
order.takerAssetAmount.times(0.5).plus(PROTOCOL_FEE),
|
||||
forwarderFeePercentage,
|
||||
);
|
||||
|
||||
const revertError = new ForwarderRevertErrors.InsufficientEthForFeeError(ethFee, ethFee.minus(1));
|
||||
|
||||
// -2 to compensate for the extra 1 wei added in ForwarderTestFactory to account for rounding
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
ethValueAdjustment: -2,
|
||||
forwarderFeePercentage,
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
||||
// tslint:enable:no-unnecessary-type-assertion
|
@@ -1,19 +0,0 @@
|
||||
import { env, EnvVars } from '@0x/dev-utils';
|
||||
|
||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
|
||||
before('start web3 provider', () => {
|
||||
providerUtils.startProviderEngine(provider);
|
||||
});
|
||||
after('generate coverage report', async () => {
|
||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
||||
await coverageSubprovider.writeCoverageAsync();
|
||||
}
|
||||
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
|
||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
||||
await profilerSubprovider.writeProfilerOutputAsync();
|
||||
}
|
||||
provider.stop();
|
||||
});
|
@@ -1,415 +0,0 @@
|
||||
import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { ExchangeWrapper } from '@0x/contracts-exchange';
|
||||
import { constants, ERC20BalancesByOwner, expect, OrderStatus, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { OrderInfo, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ForwarderWrapper } from './forwarder_wrapper';
|
||||
|
||||
// Necessary bookkeeping to validate Forwarder results
|
||||
interface ForwarderFillState {
|
||||
takerAssetFillAmount: BigNumber;
|
||||
makerAssetFillAmount: {
|
||||
[makerAssetData: string]: BigNumber;
|
||||
};
|
||||
protocolFees: BigNumber;
|
||||
wethFees: BigNumber;
|
||||
percentageFees: {
|
||||
[makerAssetData: string]: BigNumber;
|
||||
};
|
||||
maxOversoldWeth: BigNumber;
|
||||
maxOverboughtMakerAsset: BigNumber;
|
||||
}
|
||||
|
||||
// Since bignumber is not compatible with chai's within
|
||||
function expectBalanceWithin(balance: BigNumber, low: BigNumber, high: BigNumber, message?: string): void {
|
||||
expect(balance, message).to.be.bignumber.gte(low);
|
||||
expect(balance, message).to.be.bignumber.lte(high);
|
||||
}
|
||||
|
||||
export class ForwarderTestFactory {
|
||||
public static getPercentageOfValue(value: BigNumber, percentage: BigNumber): BigNumber {
|
||||
const numerator = constants.PERCENTAGE_DENOMINATOR.times(percentage).dividedToIntegerBy(100);
|
||||
const newValue = value.times(numerator).dividedToIntegerBy(constants.PERCENTAGE_DENOMINATOR);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _exchangeWrapper: ExchangeWrapper,
|
||||
private readonly _forwarderWrapper: ForwarderWrapper,
|
||||
private readonly _erc20Wrapper: ERC20Wrapper,
|
||||
private readonly _forwarderAddress: string,
|
||||
private readonly _makerAddress: string,
|
||||
private readonly _takerAddress: string,
|
||||
private readonly _protocolFeeCollectorAddress: string,
|
||||
private readonly _orderFeeRecipientAddress: string,
|
||||
private readonly _forwarderFeeRecipientAddress: string,
|
||||
private readonly _wethAddress: string,
|
||||
private readonly _gasPrice: BigNumber,
|
||||
private readonly _protocolFeeMultiplier: BigNumber,
|
||||
) {}
|
||||
|
||||
public async marketBuyTestAsync(
|
||||
orders: SignedOrder[],
|
||||
fractionalNumberOfOrdersToFill: number,
|
||||
makerAssetContracts: Array<DummyERC20TokenContract | DummyERC721TokenContract>,
|
||||
options: {
|
||||
ethValueAdjustment?: number; // Used to provided insufficient/excess ETH
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
makerAssetId?: BigNumber;
|
||||
revertError?: RevertError;
|
||||
} = {},
|
||||
): Promise<void> {
|
||||
const ethValueAdjustment = options.ethValueAdjustment || 0;
|
||||
const forwarderFeePercentage = options.forwarderFeePercentage || constants.ZERO_AMOUNT;
|
||||
|
||||
const erc20Balances = await this._erc20Wrapper.getBalancesAsync();
|
||||
const takerEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(this._takerAddress);
|
||||
const forwarderFeeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(
|
||||
this._forwarderFeeRecipientAddress,
|
||||
);
|
||||
|
||||
const ordersInfoBefore = await Promise.all(orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)));
|
||||
const orderStatusesBefore = ordersInfoBefore.map(orderInfo => orderInfo.orderStatus);
|
||||
|
||||
const expectedResults = this._computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
|
||||
const wethSpent = expectedResults.takerAssetFillAmount
|
||||
.plus(expectedResults.protocolFees)
|
||||
.plus(expectedResults.wethFees)
|
||||
.plus(expectedResults.maxOversoldWeth);
|
||||
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(wethSpent, forwarderFeePercentage);
|
||||
const ethValue = wethSpent.plus(ethSpentOnForwarderFee).plus(ethValueAdjustment);
|
||||
|
||||
const feePercentage = ForwarderTestFactory.getPercentageOfValue(
|
||||
constants.PERCENTAGE_DENOMINATOR,
|
||||
forwarderFeePercentage,
|
||||
);
|
||||
|
||||
const totalMakerAssetFillAmount = Object.values(expectedResults.makerAssetFillAmount).reduce((prev, current) =>
|
||||
prev.plus(current),
|
||||
);
|
||||
const totalPercentageFees = Object.values(expectedResults.percentageFees).reduce((prev, current) =>
|
||||
prev.plus(current),
|
||||
);
|
||||
const tx = this._forwarderWrapper.marketBuyOrdersWithEthAsync(
|
||||
orders,
|
||||
totalMakerAssetFillAmount.minus(totalPercentageFees),
|
||||
{
|
||||
value: ethValue,
|
||||
from: this._takerAddress,
|
||||
},
|
||||
{ feePercentage, feeRecipient: this._forwarderFeeRecipientAddress },
|
||||
);
|
||||
|
||||
if (options.revertError !== undefined) {
|
||||
await expect(tx).to.revertWith(options.revertError);
|
||||
} else {
|
||||
const gasUsed = (await tx).gasUsed;
|
||||
const ordersInfoAfter = await Promise.all(
|
||||
orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)),
|
||||
);
|
||||
const orderStatusesAfter = ordersInfoAfter.map(orderInfo => orderInfo.orderStatus);
|
||||
|
||||
await this._checkResultsAsync(
|
||||
fractionalNumberOfOrdersToFill,
|
||||
orderStatusesBefore,
|
||||
orderStatusesAfter,
|
||||
gasUsed,
|
||||
expectedResults,
|
||||
takerEthBalanceBefore,
|
||||
erc20Balances,
|
||||
makerAssetContracts,
|
||||
{
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
makerAssetId: options.makerAssetId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async marketSellTestAsync(
|
||||
orders: SignedOrder[],
|
||||
fractionalNumberOfOrdersToFill: number,
|
||||
makerAssetContracts: DummyERC20TokenContract[],
|
||||
options: {
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
revertError?: RevertError;
|
||||
} = {},
|
||||
): Promise<void> {
|
||||
const forwarderFeePercentage = options.forwarderFeePercentage || constants.ZERO_AMOUNT;
|
||||
|
||||
const erc20Balances = await this._erc20Wrapper.getBalancesAsync();
|
||||
const takerEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(this._takerAddress);
|
||||
const forwarderFeeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(
|
||||
this._forwarderFeeRecipientAddress,
|
||||
);
|
||||
|
||||
const ordersInfoBefore = await Promise.all(orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)));
|
||||
const orderStatusesBefore = ordersInfoBefore.map(orderInfo => orderInfo.orderStatus);
|
||||
|
||||
const expectedResults = this._computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
|
||||
const wethSpent = expectedResults.takerAssetFillAmount
|
||||
.plus(expectedResults.protocolFees)
|
||||
.plus(expectedResults.wethFees)
|
||||
.plus(expectedResults.maxOversoldWeth);
|
||||
|
||||
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(wethSpent, forwarderFeePercentage);
|
||||
const ethValue = wethSpent.plus(ethSpentOnForwarderFee);
|
||||
|
||||
const feePercentage = ForwarderTestFactory.getPercentageOfValue(
|
||||
constants.PERCENTAGE_DENOMINATOR,
|
||||
forwarderFeePercentage,
|
||||
);
|
||||
|
||||
const tx = this._forwarderWrapper.marketSellOrdersWithEthAsync(
|
||||
orders,
|
||||
{
|
||||
value: ethValue,
|
||||
from: this._takerAddress,
|
||||
},
|
||||
{ feePercentage, feeRecipient: this._forwarderFeeRecipientAddress },
|
||||
);
|
||||
|
||||
if (options.revertError !== undefined) {
|
||||
await expect(tx).to.revertWith(options.revertError);
|
||||
} else {
|
||||
const gasUsed = (await tx).gasUsed;
|
||||
const orderStatusesAfter = await Promise.all(
|
||||
orders.map(async order => (await this._exchangeWrapper.getOrderInfoAsync(order)).orderStatus),
|
||||
);
|
||||
|
||||
await this._checkResultsAsync(
|
||||
fractionalNumberOfOrdersToFill,
|
||||
orderStatusesBefore,
|
||||
orderStatusesAfter,
|
||||
gasUsed,
|
||||
expectedResults,
|
||||
takerEthBalanceBefore,
|
||||
erc20Balances,
|
||||
makerAssetContracts,
|
||||
{
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _checkErc20Balances(
|
||||
oldBalances: ERC20BalancesByOwner,
|
||||
newBalances: ERC20BalancesByOwner,
|
||||
expectedResults: ForwarderFillState,
|
||||
makerAssetContract: DummyERC20TokenContract,
|
||||
): void {
|
||||
const makerAssetAddress = makerAssetContract.address;
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerAssetAddress);
|
||||
|
||||
const {
|
||||
maxOverboughtMakerAsset,
|
||||
makerAssetFillAmount: { [makerAssetData]: makerAssetFillAmount },
|
||||
percentageFees: { [makerAssetData]: percentageFees },
|
||||
} = expectedResults;
|
||||
|
||||
expectBalanceWithin(
|
||||
newBalances[this._makerAddress][makerAssetAddress],
|
||||
oldBalances[this._makerAddress][makerAssetAddress]
|
||||
.minus(makerAssetFillAmount)
|
||||
.minus(maxOverboughtMakerAsset),
|
||||
oldBalances[this._makerAddress][makerAssetAddress].minus(makerAssetFillAmount),
|
||||
'Maker makerAsset balance',
|
||||
);
|
||||
expectBalanceWithin(
|
||||
newBalances[this._takerAddress][makerAssetAddress],
|
||||
oldBalances[this._takerAddress][makerAssetAddress].plus(makerAssetFillAmount).minus(percentageFees),
|
||||
oldBalances[this._takerAddress][makerAssetAddress]
|
||||
.plus(makerAssetFillAmount)
|
||||
.minus(percentageFees)
|
||||
.plus(maxOverboughtMakerAsset),
|
||||
'Taker makerAsset balance',
|
||||
);
|
||||
expect(
|
||||
newBalances[this._orderFeeRecipientAddress][makerAssetAddress],
|
||||
'Order fee recipient makerAsset balance',
|
||||
).to.be.bignumber.equal(oldBalances[this._orderFeeRecipientAddress][makerAssetAddress].plus(percentageFees));
|
||||
expect(
|
||||
newBalances[this._forwarderAddress][makerAssetAddress],
|
||||
'Forwarder contract makerAsset balance',
|
||||
).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
}
|
||||
|
||||
private async _checkResultsAsync(
|
||||
fractionalNumberOfOrdersToFill: number,
|
||||
orderStatusesBefore: OrderStatus[],
|
||||
orderStatusesAfter: OrderStatus[],
|
||||
gasUsed: number,
|
||||
expectedResults: ForwarderFillState,
|
||||
takerEthBalanceBefore: BigNumber,
|
||||
erc20Balances: ERC20BalancesByOwner,
|
||||
makerAssetContracts: Array<DummyERC20TokenContract | DummyERC721TokenContract>,
|
||||
options: {
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
forwarderFeeRecipientEthBalanceBefore?: BigNumber;
|
||||
makerAssetId?: BigNumber;
|
||||
} = {},
|
||||
): Promise<void> {
|
||||
for (const [i, orderStatus] of orderStatusesAfter.entries()) {
|
||||
let expectedOrderStatus = orderStatusesBefore[i];
|
||||
if (fractionalNumberOfOrdersToFill >= i + 1 && orderStatusesBefore[i] === OrderStatus.Fillable) {
|
||||
expectedOrderStatus = OrderStatus.FullyFilled;
|
||||
}
|
||||
expect(orderStatus, ` Order ${i} status`).to.equal(expectedOrderStatus);
|
||||
}
|
||||
|
||||
const wethSpent = expectedResults.takerAssetFillAmount
|
||||
.plus(expectedResults.protocolFees)
|
||||
.plus(expectedResults.wethFees);
|
||||
const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(
|
||||
wethSpent,
|
||||
options.forwarderFeePercentage || constants.ZERO_AMOUNT,
|
||||
);
|
||||
const totalEthSpent = wethSpent.plus(ethSpentOnForwarderFee).plus(this._gasPrice.times(gasUsed));
|
||||
|
||||
const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(this._takerAddress);
|
||||
const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(this._forwarderAddress);
|
||||
const newBalances = await this._erc20Wrapper.getBalancesAsync();
|
||||
|
||||
expectBalanceWithin(
|
||||
takerEthBalanceAfter,
|
||||
takerEthBalanceBefore.minus(totalEthSpent).minus(expectedResults.maxOversoldWeth),
|
||||
takerEthBalanceBefore.minus(totalEthSpent),
|
||||
'Taker ETH balance',
|
||||
);
|
||||
if (options.forwarderFeeRecipientEthBalanceBefore !== undefined) {
|
||||
const fowarderFeeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(
|
||||
this._forwarderFeeRecipientAddress,
|
||||
);
|
||||
expect(fowarderFeeRecipientEthBalanceAfter, 'Forwarder fee recipient ETH balance').to.be.bignumber.equal(
|
||||
options.forwarderFeeRecipientEthBalanceBefore.plus(ethSpentOnForwarderFee),
|
||||
);
|
||||
}
|
||||
|
||||
for (const makerAssetContract of makerAssetContracts) {
|
||||
if (makerAssetContract instanceof DummyERC20TokenContract) {
|
||||
this._checkErc20Balances(erc20Balances, newBalances, expectedResults, makerAssetContract);
|
||||
} else if (options.makerAssetId !== undefined) {
|
||||
const newOwner = await makerAssetContract.ownerOf.callAsync(options.makerAssetId);
|
||||
expect(newOwner, 'New ERC721 owner').to.be.bignumber.equal(this._takerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
expectBalanceWithin(
|
||||
newBalances[this._makerAddress][this._wethAddress],
|
||||
erc20Balances[this._makerAddress][this._wethAddress].plus(expectedResults.takerAssetFillAmount),
|
||||
erc20Balances[this._makerAddress][this._wethAddress]
|
||||
.plus(expectedResults.takerAssetFillAmount)
|
||||
.plus(expectedResults.maxOversoldWeth),
|
||||
'Maker WETH balance',
|
||||
);
|
||||
expect(
|
||||
newBalances[this._orderFeeRecipientAddress][this._wethAddress],
|
||||
'Order fee recipient WETH balance',
|
||||
).to.be.bignumber.equal(
|
||||
erc20Balances[this._orderFeeRecipientAddress][this._wethAddress].plus(expectedResults.wethFees),
|
||||
);
|
||||
expect(
|
||||
newBalances[this._forwarderAddress][this._wethAddress],
|
||||
'Forwarder contract WETH balance',
|
||||
).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
}
|
||||
|
||||
// Simulates filling some orders via the Forwarder contract. For example, if
|
||||
// orders = [A, B, C, D] and fractionalNumberOfOrdersToFill = 2.3, then
|
||||
// we simulate A and B being completely filled, and 0.3 * C being filled.
|
||||
private _computeExpectedResults(
|
||||
orders: SignedOrder[],
|
||||
ordersInfoBefore: OrderInfo[],
|
||||
fractionalNumberOfOrdersToFill: number,
|
||||
): ForwarderFillState {
|
||||
const currentState: ForwarderFillState = {
|
||||
takerAssetFillAmount: constants.ZERO_AMOUNT,
|
||||
makerAssetFillAmount: {},
|
||||
protocolFees: constants.ZERO_AMOUNT,
|
||||
wethFees: constants.ZERO_AMOUNT,
|
||||
percentageFees: {},
|
||||
maxOversoldWeth: constants.ZERO_AMOUNT,
|
||||
maxOverboughtMakerAsset: constants.ZERO_AMOUNT,
|
||||
};
|
||||
let remainingOrdersToFill = fractionalNumberOfOrdersToFill;
|
||||
|
||||
for (const [i, order] of orders.entries()) {
|
||||
if (currentState.makerAssetFillAmount[order.makerAssetData] === undefined) {
|
||||
currentState.makerAssetFillAmount[order.makerAssetData] = new BigNumber(0);
|
||||
}
|
||||
if (currentState.percentageFees[order.makerAssetData] === undefined) {
|
||||
currentState.percentageFees[order.makerAssetData] = new BigNumber(0);
|
||||
}
|
||||
|
||||
if (remainingOrdersToFill === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ordersInfoBefore[i].orderStatus !== OrderStatus.Fillable) {
|
||||
// If the order is not fillable, skip over it but still count it towards fractionalNumberOfOrdersToFill
|
||||
remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
let makerAssetAmount;
|
||||
let takerAssetAmount;
|
||||
let takerFee;
|
||||
if (remainingOrdersToFill < 1) {
|
||||
makerAssetAmount = order.makerAssetAmount.times(remainingOrdersToFill).integerValue();
|
||||
takerAssetAmount = order.takerAssetAmount.times(remainingOrdersToFill).integerValue();
|
||||
takerFee = order.takerFee.times(remainingOrdersToFill).integerValue();
|
||||
|
||||
// Up to 1 wei worth of WETH will be oversold on the last order due to rounding
|
||||
currentState.maxOversoldWeth = new BigNumber(1);
|
||||
// Equivalently, up to 1 wei worth of maker asset will be overbought
|
||||
currentState.maxOverboughtMakerAsset = currentState.maxOversoldWeth
|
||||
.times(order.makerAssetAmount)
|
||||
.dividedToIntegerBy(order.takerAssetAmount);
|
||||
} else {
|
||||
makerAssetAmount = order.makerAssetAmount;
|
||||
takerAssetAmount = order.takerAssetAmount;
|
||||
takerFee = order.takerFee;
|
||||
}
|
||||
|
||||
// Accounting for partially filled orders
|
||||
// As with unfillable orders, these still count as 1 towards fractionalNumberOfOrdersToFill
|
||||
const takerAssetFilled = ordersInfoBefore[i].orderTakerAssetFilledAmount;
|
||||
const makerAssetFilled = takerAssetFilled
|
||||
.times(order.makerAssetAmount)
|
||||
.dividedToIntegerBy(order.takerAssetAmount);
|
||||
takerAssetAmount = BigNumber.max(takerAssetAmount.minus(takerAssetFilled), constants.ZERO_AMOUNT);
|
||||
makerAssetAmount = BigNumber.max(makerAssetAmount.minus(makerAssetFilled), constants.ZERO_AMOUNT);
|
||||
|
||||
currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
|
||||
currentState.makerAssetFillAmount[order.makerAssetData] = currentState.makerAssetFillAmount[
|
||||
order.makerAssetData
|
||||
].plus(makerAssetAmount);
|
||||
|
||||
if (this._protocolFeeCollectorAddress !== constants.NULL_ADDRESS) {
|
||||
currentState.protocolFees = currentState.protocolFees.plus(
|
||||
this._gasPrice.times(this._protocolFeeMultiplier),
|
||||
);
|
||||
}
|
||||
if (order.takerFeeAssetData === order.makerAssetData) {
|
||||
currentState.percentageFees[order.makerAssetData] = currentState.percentageFees[
|
||||
order.makerAssetData
|
||||
].plus(takerFee);
|
||||
} else if (order.takerFeeAssetData === order.takerAssetData) {
|
||||
currentState.wethFees = currentState.wethFees.plus(takerFee);
|
||||
}
|
||||
|
||||
remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
|
||||
}
|
||||
|
||||
return currentState;
|
||||
}
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
import { artifacts as erc20Artifacts } from '@0x/contracts-erc20';
|
||||
import { artifacts as erc721Artifacts } from '@0x/contracts-erc721';
|
||||
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
||||
import { constants, LogDecoder, Web3ProviderEngine } from '@0x/contracts-test-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { TransactionReceiptWithDecodedLogs, TxDataPayable } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ForwarderContract } from '../../generated-wrappers/forwarder';
|
||||
import { artifacts } from '../../src/artifacts';
|
||||
|
||||
export class ForwarderWrapper {
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _forwarderContract: ForwarderContract;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
constructor(contractInstance: ForwarderContract, provider: Web3ProviderEngine) {
|
||||
this._forwarderContract = contractInstance;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, {
|
||||
...artifacts,
|
||||
...exchangeArtifacts,
|
||||
...erc20Artifacts,
|
||||
...erc721Artifacts,
|
||||
});
|
||||
}
|
||||
public async marketSellOrdersWithEthAsync(
|
||||
orders: SignedOrder[],
|
||||
txData: TxDataPayable,
|
||||
opts: { feePercentage?: BigNumber; feeRecipient?: string } = {},
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const feePercentage = opts.feePercentage === undefined ? constants.ZERO_AMOUNT : opts.feePercentage;
|
||||
const feeRecipient = opts.feeRecipient === undefined ? constants.NULL_ADDRESS : opts.feeRecipient;
|
||||
const txHash = await this._forwarderContract.marketSellOrdersWithEth.sendTransactionAsync(
|
||||
orders,
|
||||
orders.map(signedOrder => signedOrder.signature),
|
||||
feePercentage,
|
||||
feeRecipient,
|
||||
txData,
|
||||
);
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async marketBuyOrdersWithEthAsync(
|
||||
orders: SignedOrder[],
|
||||
makerAssetFillAmount: BigNumber,
|
||||
txData: TxDataPayable,
|
||||
opts: { feePercentage?: BigNumber; feeRecipient?: string } = {},
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const feePercentage = opts.feePercentage === undefined ? constants.ZERO_AMOUNT : opts.feePercentage;
|
||||
const feeRecipient = opts.feeRecipient === undefined ? constants.NULL_ADDRESS : opts.feeRecipient;
|
||||
const txHash = await this._forwarderContract.marketBuyOrdersWithEth.sendTransactionAsync(
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
orders.map(signedOrder => signedOrder.signature),
|
||||
feePercentage,
|
||||
feeRecipient,
|
||||
txData,
|
||||
);
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async withdrawAssetAsync(
|
||||
assetData: string,
|
||||
amount: BigNumber,
|
||||
txData: TxDataPayable,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._forwarderContract.withdrawAsset.sendTransactionAsync(assetData, amount, txData);
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async approveMakerAssetProxyAsync(
|
||||
assetData: string,
|
||||
txData: TxDataPayable,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._forwarderContract.approveMakerAssetProxy.sendTransactionAsync(assetData, txData);
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
}
|
@@ -1,2 +0,0 @@
|
||||
export * from './forwarder_wrapper';
|
||||
export * from './forwarder_test_factory';
|
@@ -12,8 +12,7 @@
|
||||
"generated-artifacts/MixinAssets.json",
|
||||
"generated-artifacts/MixinExchangeWrapper.json",
|
||||
"generated-artifacts/MixinForwarderCore.json",
|
||||
"generated-artifacts/MixinWeth.json",
|
||||
"generated-artifacts/TestProtocolFeeCollector.json"
|
||||
"generated-artifacts/MixinWeth.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "3.1.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "3.1.0-beta.0",
|
||||
"changes": [
|
||||
@@ -105,6 +114,10 @@
|
||||
{
|
||||
"note": "Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields.",
|
||||
"pr": 2075
|
||||
},
|
||||
{
|
||||
"note": "Move `IWallet.sol` from `asset-proxy` and `exchange` packages to here.",
|
||||
"pr": 2233
|
||||
}
|
||||
],
|
||||
"timestamp": 1570135330
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Break up `LibEIP712` into reusable components (#1742)
|
||||
@@ -33,6 +37,7 @@ CHANGELOG
|
||||
* Remove `_hashEIP712ExchangeMessage` from `LibEIP712ExchangeDomain` (#2055)
|
||||
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
||||
* Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields. (#2075)
|
||||
* Move `IWallet.sol` from `asset-proxy` and `exchange` packages to here. (#2233)
|
||||
|
||||
## v3.0.8 - _September 17, 2019_
|
||||
|
||||
|
@@ -85,9 +85,9 @@ library LibMath {
|
||||
));
|
||||
}
|
||||
|
||||
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
||||
// safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
||||
// ceil(a / b) = floor((a + b - 1) / b)
|
||||
// To implement `ceil(a / b)` using _safeDiv.
|
||||
// To implement `ceil(a / b)` using safeDiv.
|
||||
partialAmount = numerator.safeMul(target)
|
||||
.safeAdd(denominator.safeSub(1))
|
||||
.safeDiv(denominator);
|
||||
@@ -127,9 +127,9 @@ library LibMath {
|
||||
pure
|
||||
returns (uint256 partialAmount)
|
||||
{
|
||||
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
||||
// safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
||||
// ceil(a / b) = floor((a + b - 1) / b)
|
||||
// To implement `ceil(a / b)` using _safeDiv.
|
||||
// To implement `ceil(a / b)` using safeDiv.
|
||||
partialAmount = numerator.safeMul(target)
|
||||
.safeAdd(denominator.safeSub(1))
|
||||
.safeDiv(denominator);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-libs",
|
||||
"version": "3.1.0-beta.0",
|
||||
"version": "3.1.0-beta.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
@@ -35,7 +35,7 @@
|
||||
"compile:truffle": "truffle compile"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json",
|
||||
"abis": "./generated-artifacts/@(IWallet|LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -48,13 +48,13 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||
"@0x/subproviders": "^5.1.0-beta.0",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/subproviders": "^5.1.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -73,14 +73,14 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.0",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||
"@0x/order-utils": "^8.5.0-beta.0",
|
||||
"@0x/types": "^2.5.0-beta.0",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||
"@0x/utils": "^4.6.0-beta.0",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||
"ethereum-types": "^2.2.0-beta.0",
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||
"@0x/order-utils": "^8.5.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as IWallet from '../generated-artifacts/IWallet.json';
|
||||
import * as LibEIP712ExchangeDomain from '../generated-artifacts/LibEIP712ExchangeDomain.json';
|
||||
import * as LibExchangeRichErrors from '../generated-artifacts/LibExchangeRichErrors.json';
|
||||
import * as LibFillResults from '../generated-artifacts/LibFillResults.json';
|
||||
@@ -18,6 +19,7 @@ import * as TestLibMath from '../generated-artifacts/TestLibMath.json';
|
||||
import * as TestLibOrder from '../generated-artifacts/TestLibOrder.json';
|
||||
import * as TestLibZeroExTransaction from '../generated-artifacts/TestLibZeroExTransaction.json';
|
||||
export const artifacts = {
|
||||
IWallet: IWallet as ContractArtifact,
|
||||
LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact,
|
||||
LibExchangeRichErrors: LibExchangeRichErrors as ContractArtifact,
|
||||
LibFillResults: LibFillResults as ContractArtifact,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user