Merge branch 'development' into addCoordinatorSupport

* development: (93 commits)
  Linting
  Copied MixinAssetProxyDispatcher from exchange
  Added deployed contract packages to readme (and link to top-level readme)
  Bumped version of ERC1155Proxy to 0.5.5 + merged ERC20Proxy/ERC721Proxy/MultiAssetProxy dependencies into base contract files
  Make credits program participants 1 row
  Correct comment in `hashEIP712Message()` in `LibEIP712Domai.sol`. Merge janky changelog notes in `types` package. Correct changelog note in `coordinator` changelog.
  Add PR numbers to changelogs.
  additional zero outcome tests
  renamed perUnitValue to valueMultiplier
  added test for amount=0
  Adjusted changelog version since nothing has been published during this PR
  Added calldatacopy comment
  Do not revert if value or amount are zero. Only if amount is non-zero and there is an overflow.
  updated comment for calldatacopy
  yarn.lock for erc1155 proxy
  added exports for 1155 proxy
  Renamed tokenIds -> ids, tokenValues -> values, callbackData -> data to be consistent with the ERC1155 reference implementation.
  Rebased against development
  Ran prettier
  Updated changelogs and documentation for erc1155 proxy
  ...
This commit is contained in:
Fabio Berger
2019-03-18 11:20:22 +01:00
278 changed files with 6176 additions and 770 deletions

View File

@@ -47,6 +47,7 @@ jobs:
- run: yarn wsrun test:circleci @0x/contracts-exchange-libs
- run: yarn wsrun test:circleci @0x/contracts-erc20
- run: yarn wsrun test:circleci @0x/contracts-erc721
- run: yarn wsrun test:circleci @0x/contracts-erc1155
- run: yarn wsrun test:circleci @0x/contracts-extensions
- run: yarn wsrun test:circleci @0x/contracts-asset-proxy
- run: yarn wsrun test:circleci @0x/contracts-exchange
@@ -68,6 +69,7 @@ jobs:
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-libs
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc20
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc721
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc1155
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-extensions
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-asset-proxy
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange

2
.gitignore vendored
View File

@@ -91,6 +91,7 @@ contracts/utils/generated-artifacts/
contracts/exchange-libs/generated-artifacts/
contracts/erc20/generated-artifacts/
contracts/erc721/generated-artifacts/
contracts/erc1155/generated-artifacts/
contracts/extensions/generated-artifacts/
contracts/exchange-forwarder/generated-artifacts/
packages/sol-tracing-utils/test/fixtures/artifacts/
@@ -106,6 +107,7 @@ contracts/utils/generated-wrappers/
contracts/exchange-libs/generated-wrappers/
contracts/erc20/generated-wrappers/
contracts/erc721/generated-wrappers/
contracts/erc1155/generated-wrappers/
contracts/extensions/generated-wrappers/
contracts/exchange-forwarder/generated-wrappers/
packages/metacoin/src/contract_wrappers

View File

@@ -16,6 +16,8 @@ lib
/contracts/erc20/generated-artifacts
/contracts/erc721/generated-wrappers
/contracts/erc721/generated-artifacts
/contracts/erc1155/generated-wrappers
/contracts/erc1155/generated-artifacts
/contracts/extensions/generated-wrappers
/contracts/extensions/generated-artifacts
/contracts/exchange-forwarder/generated-wrappers

View File

@@ -34,11 +34,14 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
### Solidity Packages
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
| Package | Version | Description |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [![npm](https://img.shields.io/npm/v/@0x/contracts-asset-proxy.svg)](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
| [`@0x/contracts-erc20`](/contracts/erc20) | [![npm](https://img.shields.io/npm/v/@0x/contracts-erc20.svg)](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
| [`@0x/contracts-erc721`](/contracts/erc721) | [![npm](https://img.shields.io/npm/v/@0x/contracts-erc721.svg)](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [![npm](https://img.shields.io/npm/v/@0x/contracts-erc1155.svg)](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
| [`@0x/contracts-exchange`](/contracts/exchange) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange.svg)](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange-forwarder.svg)](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange-libs.svg)](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |

12
contracts/README.md Normal file
View File

@@ -0,0 +1,12 @@
#### Deployed Contract Packages
| Contract | Package | Version | Git Tag |
| --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2) | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2) |
| ERC20Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
| ERC721Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
| Exchange | [`@0x/contracts-exchange`](/contracts/exchange) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1) | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1) |
| DutchAuction | [`@0x/contracts-extensions`](/contracts/extensions) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2) | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2) |
| Forwarder | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) |
| MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
| ZRXToken | [`@0x/contracts-erc20`](/contracts/erc20) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1) | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1) |

View File

@@ -1,10 +1,22 @@
[
{
"version": "1.0.10",
"version": "2.0.0",
"changes": [
{
"note": "Set evmVersion to byzantium",
"pr": 1678
},
{
"note": "Do not reexport external dependencies",
"pr": 1682
},
{
"note": "Add ERC1155Proxy",
"pr": 1661
},
{
"note": "Bumped solidity version to ^0.5.5",
"pr": 1701
}
]
},

View File

@@ -5,7 +5,11 @@
"isOfflineMode": false,
"compilerSettings": {
"evmVersion": "byzantium",
"optimizer": { "enabled": true, "runs": 1000000 },
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
@@ -19,11 +23,7 @@
}
},
"contracts": [
"@0x/contracts-erc20/contracts/test/DummyERC20Token.sol",
"@0x/contracts-erc20/contracts/test/DummyMultipleReturnERC20Token.sol",
"@0x/contracts-erc20/contracts/test/DummyNoReturnERC20Token.sol",
"@0x/contracts-erc721/contracts/test/DummyERC721Receiver.sol",
"@0x/contracts-erc721/contracts/test/DummyERC721Token.sol",
"src/ERC1155Proxy.sol",
"src/ERC20Proxy.sol",
"src/ERC721Proxy.sol",
"src/MixinAuthorizable.sol",

View File

@@ -0,0 +1,267 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "./MixinAuthorizable.sol";
contract ERC1155Proxy is
MixinAuthorizable
{
// Id of this proxy.
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Token(address,uint256[],uint256[],bytes)"));
function ()
external
{
// Input calldata to this function is encoded as follows:
// -- TABLE #1 --
// | Area | Offset (**) | Length | Contents |
// |----------|-------------|-------------|---------------------------------|
// | Header | 0 | 4 | function selector |
// | Params | | 4 * 32 | function parameters: |
// | | 4 | | 1. offset to assetData (*) |
// | | 36 | | 2. from |
// | | 68 | | 3. to |
// | | 100 | | 4. amount |
// | Data | | | assetData: |
// | | 132 | 32 | assetData Length |
// | | 164 | (see below) | assetData Contents |
//
//
// Asset data is encoded as follows:
// -- TABLE #2 --
// | Area | Offset | Length | Contents |
// |----------|-------------|---------|-------------------------------------|
// | Header | 0 | 4 | assetProxyId |
// | Params | | 4 * 32 | function parameters: |
// | | 4 | | 1. address of ERC1155 contract |
// | | 36 | | 2. offset to ids (*) |
// | | 68 | | 3. offset to values (*) |
// | | 100 | | 4. offset to data (*) |
// | Data | | | ids: |
// | | 132 | 32 | 1. ids Length |
// | | 164 | a | 2. ids Contents |
// | | | | values: |
// | | 164 + a | 32 | 1. values Length |
// | | 196 + a | b | 2. values Contents |
// | | | | data |
// | | 196 + a + b | 32 | 1. data Length |
// | | 228 + a + b | c | 2. data Contents |
//
//
// Calldata for target ERC155 asset is encoded for safeBatchTransferFrom:
// -- TABLE #3 --
// | Area | Offset (**) | Length | Contents |
// |----------|-------------|---------|-------------------------------------|
// | Header | 0 | 4 | safeBatchTransferFrom selector |
// | Params | | 5 * 32 | function parameters: |
// | | 4 | | 1. from address |
// | | 36 | | 2. to address |
// | | 68 | | 3. offset to ids (*) |
// | | 100 | | 4. offset to values (*) |
// | | 132 | | 5. offset to data (*) |
// | Data | | | ids: |
// | | 164 | 32 | 1. ids Length |
// | | 196 | a | 2. ids Contents |
// | | | | values: |
// | | 196 + a | 32 | 1. values Length |
// | | 228 + a | b | 2. values Contents |
// | | | | data |
// | | 228 + a + b | 32 | 1. data Length |
// | | 260 + a + b | c | 2. data Contents |
//
//
// (*): offset is computed from start of function parameters, so offset
// by an additional 4 bytes in the calldata.
//
// (**): the `Offset` column is computed assuming no calldata compression;
// offsets in the Data Area are dynamic and should be evaluated in
// real-time.
//
// WARNING: The ABIv2 specification allows additional padding between
// the Params and Data section. This will result in a larger
// offset to assetData.
//
// Note: Table #1 and Table #2 exists in Calldata. We construct Table #3 in memory.
//
//
assembly {
// The first 4 bytes of calldata holds the function selector
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
// `transferFrom` will be called with the following parameters:
// assetData Encoded byte array.
// from Address to transfer asset from.
// to Address to transfer asset to.
// amount Amount of asset to transfer.
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
// where k is the key left padded to 32 bytes and p is the storage slot
mstore(0, caller)
mstore(32, authorized_slot)
// Revert if authorized[msg.sender] == false
if iszero(sload(keccak256(0, 64))) {
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
mstore(96, 0)
revert(0, 100)
}
// Construct Table #3 in memory, starting at memory offset 0.
// The algorithm below maps asset data from Table #1 and Table #2 to Table #3, while
// scaling the `values` (Table #2) by `amount` (Table #1). Once Table #3 has
// been constructed in memory, the destination erc1155 contract is called using this
// as its calldata. This process is divided into four steps, below.
////////// STEP 1/4 //////////
// Map relevant fields from assetData (Table #2) into memory (Table #3)
// The Contents column of Table #2 is the same as Table #3,
// beginning from parameter 3 - `offset to ids (*)`
// The offsets in these rows are offset by 32 bytes in Table #3.
// Strategy:
// 1. Copy the assetData into memory at offset 32
// 2. Increment by 32 the offsets to `ids`, `values`, and `data`
// Load offset to `assetData`
let assetDataOffset := calldataload(4)
// Load length in bytes of `assetData`, computed by:
// 4 (function selector)
// + assetDataOffset
let assetDataLength := calldataload(add(4, assetDataOffset))
// This corresponds to the beginning of the Data Area for Table #3.
// Computed by:
// 4 (function selector)
// + assetDataOffset
// + 32 (length of assetData)
calldatacopy(
32, // aligned such that "offset to ids" is at the correct location for Table #3
add(36, assetDataOffset), // beginning of asset data contents
assetDataLength // length of asset data
)
// Increment by 32 the offsets to `ids`, `values`, and `data`
mstore(68, add(mload(68), 32))
mstore(100, add(mload(100), 32))
mstore(132, add(mload(132), 32))
// Record the address of the destination erc1155 asset for later.
let assetAddress := and(
mload(36),
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
)
////////// STEP 2/4 //////////
let amount := calldataload(100)
let valuesOffset := add(mload(100), 4) // add 4 for calldata offset
let valuesLengthInBytes := mul(
mload(valuesOffset),
32
)
let valuesBegin := add(valuesOffset, 32)
let valuesEnd := add(valuesBegin, valuesLengthInBytes)
for { let tokenValueOffset := valuesBegin }
lt(tokenValueOffset, valuesEnd)
{ tokenValueOffset := add(tokenValueOffset, 32) }
{
// Load token value and generate scaled value
let tokenValue := mload(tokenValueOffset)
let scaledTokenValue := mul(tokenValue, amount)
// Revert if `amount` != 0 and multiplication resulted in an overflow
if iszero(or(
iszero(amount),
eq(div(scaledTokenValue, amount), tokenValue)
)) {
// Revert with `Error("UINT256_OVERFLOW")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
mstore(96, 0)
revert(0, 100)
}
// There was no overflow, update `tokenValue` with its scaled counterpart
mstore(tokenValueOffset, scaledTokenValue)
}
////////// STEP 3/4 //////////
// Store the safeBatchTransferFrom function selector,
// and copy `from`/`to` fields from Table #1 to Table #3.
// The function selector is computed using:
// bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"))
mstore(0, 0x2eb2c2d600000000000000000000000000000000000000000000000000000000)
// Copy `from` and `to` fields from Table #1 to Table #3
calldatacopy(
4, // aligned such that `from` and `to` are at the correct location for Table #3
36, // beginning of `from` field from Table #1
64 // 32 bytes for `from` + 32 bytes for `to` field
)
////////// STEP 4/4 //////////
// Call into the destination erc1155 contract using as calldata Table #3 (constructed in-memory above)
let success := call(
gas, // forward all gas
assetAddress, // call address of erc1155 asset
0, // don't send any ETH
0, // pointer to start of input
add(assetDataLength, 32), // length of input (Table #3) is 32 bytes longer than `assetData` (Table #2)
0, // write output over memory that won't be reused
0 // don't copy output to memory
)
// Revert with reason given by AssetProxy if `transferFrom` call failed
if iszero(success) {
returndatacopy(
0, // copy to memory at 0
0, // copy from return data at 0
returndatasize() // copy all return data
)
revert(0, returndatasize())
}
// Return if call was successful
return(0, 0)
}
// Revert if undefined function is called
revert(0, 0)
}
}
/// @dev Gets the proxy id associated with the proxy address.
/// @return Proxy id.
function getProxyId()
external
pure
returns (bytes4)
{
return PROXY_ID;
}
}

View File

@@ -16,7 +16,7 @@
*/
pragma solidity 0.4.24;
pragma solidity ^0.5.5;
import "./MixinAuthorizable.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity 0.4.24;
pragma solidity ^0.5.5;
import "./MixinAuthorizable.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "./mixins/MAssetProxyDispatcher.sol";
@@ -28,7 +28,7 @@ contract MixinAssetProxyDispatcher is
MAssetProxyDispatcher
{
// Mapping from Asset Proxy Id's to their respective Asset Proxy
mapping (bytes4 => IAssetProxy) public assetProxies;
mapping (bytes4 => address) public assetProxies;
/// @dev Registers an asset proxy to its asset proxy id.
/// Once an asset proxy is registered, it cannot be unregistered.
@@ -37,10 +37,8 @@ contract MixinAssetProxyDispatcher is
external
onlyOwner
{
IAssetProxy assetProxyContract = IAssetProxy(assetProxy);
// Ensure that no asset proxy exists with current id.
bytes4 assetProxyId = assetProxyContract.getProxyId();
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
address currentAssetProxy = assetProxies[assetProxyId];
require(
currentAssetProxy == address(0),
@@ -48,7 +46,7 @@ contract MixinAssetProxyDispatcher is
);
// Add asset proxy and log registration.
assetProxies[assetProxyId] = assetProxyContract;
assetProxies[assetProxyId] = assetProxy;
emit AssetProxyRegistered(
assetProxyId,
assetProxy

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "./mixins/MAuthorizable.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity 0.4.24;
pragma solidity ^0.5.5;
import "./MixinAssetProxyDispatcher.sol";
import "./MixinAuthorizable.sol";

View File

@@ -17,7 +17,7 @@
*/
// solhint-disable
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
@@ -36,8 +36,8 @@ interface IAssetData {
external;
function MultiAsset(
uint256[] amounts,
bytes[] nestedAssetData
uint256[] calldata amounts,
bytes[] calldata nestedAssetData
)
external;

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "./IAuthorizable.sol";
@@ -30,7 +30,7 @@ contract IAssetProxy is
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFrom(
bytes assetData,
bytes calldata assetData,
address from,
address to,
uint256 amount

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
contract IAssetProxyDispatcher {

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "../interfaces/IAssetProxyDispatcher.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "../interfaces/IAuthorizable.sol";

View File

@@ -33,7 +33,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -70,7 +70,8 @@
"@0x/base-contract": "^5.0.2",
"@0x/contracts-erc20": "^1.0.9",
"@0x/contracts-erc721": "^1.0.9",
"@0x/contracts-utils": "2.0.1",
"@0x/contracts-erc1155": "^1.0.0",
"@0x/contracts-utils": "^2.0.8",
"@0x/order-utils": "^7.0.2",
"@0x/types": "^2.1.1",
"@0x/typescript-typings": "^4.1.0",

View File

@@ -5,11 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as DummyERC20Token from '../generated-artifacts/DummyERC20Token.json';
import * as DummyERC721Receiver from '../generated-artifacts/DummyERC721Receiver.json';
import * as DummyERC721Token from '../generated-artifacts/DummyERC721Token.json';
import * as DummyMultipleReturnERC20Token from '../generated-artifacts/DummyMultipleReturnERC20Token.json';
import * as DummyNoReturnERC20Token from '../generated-artifacts/DummyNoReturnERC20Token.json';
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
import * as IAssetData from '../generated-artifacts/IAssetData.json';
@@ -18,13 +14,9 @@ import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
export const artifacts = {
DummyERC20Token: DummyERC20Token as ContractArtifact,
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
DummyERC721Receiver: DummyERC721Receiver as ContractArtifact,
DummyERC721Token: DummyERC721Token as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,

View File

@@ -3,11 +3,7 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/dummy_erc20_token';
export * from '../generated-wrappers/dummy_erc721_receiver';
export * from '../generated-wrappers/dummy_erc721_token';
export * from '../generated-wrappers/dummy_multiple_return_erc20_token';
export * from '../generated-wrappers/dummy_no_return_erc20_token';
export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_proxy';
export * from '../generated-wrappers/erc721_proxy';
export * from '../generated-wrappers/i_asset_data';

View File

@@ -0,0 +1,790 @@
import {
artifacts as erc1155Artifacts,
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
DummyERC1155ReceiverContract,
ERC1155MintableContract,
Erc1155Wrapper,
} from '@0x/contracts-erc1155';
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
expectTransactionFailedWithoutReasonAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import { ERC1155ProxyWrapper, ERC721ProxyContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('ERC1155Proxy', () => {
// constant values used in transfer tests
const nftOwnerBalance = new BigNumber(1);
const nftNotOwnerBalance = new BigNumber(0);
const spenderInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
const receiverInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
const receiverContractInitialFungibleBalance = new BigNumber(0);
const fungibleValueToTransferSmall = spenderInitialFungibleBalance.div(100);
const fungibleValueToTransferLarge = spenderInitialFungibleBalance.div(4);
const valueMultiplierSmall = new BigNumber(2);
const valueMultiplierNft = new BigNumber(1);
const nonFungibleValueToTransfer = nftOwnerBalance;
const receiverCallbackData = '0x01020304';
// addresses
let owner: string;
let notAuthorized: string;
let authorized: string;
let spender: string;
let receiver: string;
let receiverContract: string;
// contracts & wrappers
let erc1155Proxy: ERC721ProxyContract;
let erc1155Receiver: DummyERC1155ReceiverContract;
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
let erc1155Contract: ERC1155MintableContract;
let erc1155Wrapper: Erc1155Wrapper;
// tokens
let fungibleTokens: BigNumber[];
let nonFungibleTokensOwnedBySpender: BigNumber[];
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
/// deploy & configure ERC1155Proxy
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
from: owner,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(erc1155Proxy.address, {
from: owner,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// deploy & configure ERC1155 tokens and receiver
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
erc1155Contract = erc1155Wrapper.getContract();
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
erc1155Artifacts.DummyERC1155Receiver,
provider,
txDefaults,
);
receiverContract = erc1155Receiver.address;
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
fungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
nonFungibleTokensOwnedBySpender = [];
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
const nonFungibleTokenAsString = nonFungibleToken.toString();
const nonFungibleTokenHeldBySpender =
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
});
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('general', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: erc1155Proxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
it('should have an id of 0x9645780d', async () => {
const proxyId = await erc1155Proxy.getProxyId.callAsync();
// proxy computed using -- bytes4(keccak256("erc1155Token(address,uint256[],uint256[],bytes)"));
const expectedProxyId = '0x9645780d';
expect(proxyId).to.equal(expectedProxyId);
});
});
describe('transferFrom', () => {
it('should successfully transfer value for a single, fungible token', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier);
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(totalValueTransferred),
receiverInitialFungibleBalance.plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value for the same fungible token several times', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = fungibleTokens[0];
const tokensToTransfer = [tokenToTransfer, tokenToTransfer, tokenToTransfer];
const valuesToTransfer = [
fungibleValueToTransferSmall.plus(10),
fungibleValueToTransferSmall.plus(20),
fungibleValueToTransferSmall.plus(30),
];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
// receiver
receiverInitialFungibleBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
let totalValueTransferred = _.reduce(valuesToTransfer, (sum: BigNumber, value: BigNumber) => {
return sum.plus(value);
}) as BigNumber;
totalValueTransferred = totalValueTransferred.times(valueMultiplier);
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(totalValueTransferred),
// receiver
receiverInitialFungibleBalance.plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should successfully transfer value for several fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 3);
const valuesToTransfer = [
fungibleValueToTransferSmall.plus(10),
fungibleValueToTransferSmall.plus(20),
fungibleValueToTransferSmall.plus(30),
];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
// receiver
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(totalValuesTransferred[0]),
spenderInitialFungibleBalance.minus(totalValuesTransferred[1]),
spenderInitialFungibleBalance.minus(totalValuesTransferred[2]),
// receiver
receiverInitialFungibleBalance.plus(totalValuesTransferred[0]),
receiverInitialFungibleBalance.plus(totalValuesTransferred[1]),
receiverInitialFungibleBalance.plus(totalValuesTransferred[2]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer a non-fungible token', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [nonFungibleValueToTransfer];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [
// spender
nftNotOwnerBalance,
// receiver
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer multiple non-fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
const valuesToTransfer = [
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [
// spender
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
// receiver
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value for a combination of several fungible/non-fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const fungibleTokensToTransfer = fungibleTokens.slice(0, 3);
const nonFungibleTokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 2);
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
const valuesToTransfer = [
fungibleValueToTransferLarge,
fungibleValueToTransferSmall,
fungibleValueToTransferSmall,
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
// spender
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
expectedInitialBalances[3].minus(totalValuesTransferred[3]),
expectedInitialBalances[4].minus(totalValuesTransferred[4]),
// receiver
expectedInitialBalances[5].plus(totalValuesTransferred[0]),
expectedInitialBalances[6].plus(totalValuesTransferred[1]),
expectedInitialBalances[7].plus(totalValuesTransferred[2]),
expectedInitialBalances[8].plus(totalValuesTransferred[3]),
expectedInitialBalances[9].plus(totalValuesTransferred[4]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value to a smart contract and trigger its callback', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value and ignore extra assetData', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const extraData = '0102030405060708';
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
extraData,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if value is zero', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [new BigNumber(0)];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if value multiplier is zero', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = new BigNumber(0);
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if there are no tokens in asset data', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer: BigNumber[] = [];
const valuesToTransfer: BigNumber[] = [];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should propagate revert reason from erc1155 contract failure', async () => {
// disable transfers
const shouldRejectTransfer = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.TransferRejected,
);
});
it('should revert if transferring the same non-fungible token more than once', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const nftToTransfer = nonFungibleTokensOwnedBySpender[0];
const tokensToTransfer = [nftToTransfer, nftToTransfer];
const valuesToTransfer = [nonFungibleValueToTransfer, nonFungibleValueToTransfer];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.NFTNotOwnedByFromAddress,
);
});
it('should revert if there is a multiplication overflow', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
const maxUintValue = new BigNumber(2).pow(256).minus(1);
const valuesToTransfer = [nonFungibleValueToTransfer, maxUintValue, nonFungibleValueToTransfer];
const valueMultiplier = new BigNumber(2);
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.Uint256Overflow,
);
});
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [nonFungibleValueToTransfer];
const valueMultiplier = new BigNumber(2);
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.AmountEqualToOneRequired,
);
});
it('should revert if transferring > 1 instances of a non-fungible token (`valuesToTransfer` field >1)', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [new BigNumber(2)];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.AmountEqualToOneRequired,
);
});
it('should revert if sender balance is insufficient', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valueGreaterThanSpenderBalance = spenderInitialFungibleBalance.plus(1);
const valuesToTransfer = [valueGreaterThanSpenderBalance];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.Uint256Underflow,
);
});
it('should revert if sender allowance is insufficient', async () => {
// dremove allowance for ERC1155 proxy
const wrapper = erc1155ProxyWrapper.getContractWrapper(erc1155Contract.address);
const isApproved = false;
await wrapper.setApprovalForAllAsync(spender, erc1155Proxy.address, isApproved);
const isApprovedActualValue = await wrapper.isApprovedForAllAsync(spender, erc1155Proxy.address);
expect(isApprovedActualValue).to.be.equal(isApproved);
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.InsufficientAllowance,
);
});
it('should revert if caller is not authorized', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
notAuthorized,
),
RevertReason.SenderNotAuthorized,
);
});
});
});
// tslint:enable:no-unnecessary-type-assertion
// tslint:disable:max-file-line-count

View File

@@ -1,3 +1,15 @@
import {
artifacts as erc20Artifacts,
DummyERC20TokenContract,
DummyERC20TokenTransferEventArgs,
DummyMultipleReturnERC20TokenContract,
DummyNoReturnERC20TokenContract,
} from '@0x/contracts-erc20';
import {
artifacts as erc721Artifacts,
DummyERC721ReceiverContract,
DummyERC721TokenContract,
} from '@0x/contracts-erc721';
import {
chaiSetup,
constants,
@@ -18,12 +30,6 @@ import * as _ from 'lodash';
import {
artifacts,
DummyERC20TokenContract,
DummyERC20TokenTransferEventArgs,
DummyERC721ReceiverContract,
DummyERC721TokenContract,
DummyMultipleReturnERC20TokenContract,
DummyNoReturnERC20TokenContract,
ERC20ProxyContract,
ERC20Wrapper,
ERC721ProxyContract,
@@ -148,7 +154,7 @@ describe('Asset Transfer Proxies', () => {
constants.DUMMY_TOKEN_DECIMALS,
);
noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyNoReturnERC20Token,
erc20Artifacts.DummyNoReturnERC20Token,
provider,
txDefaults,
constants.DUMMY_TOKEN_NAME,
@@ -157,7 +163,7 @@ describe('Asset Transfer Proxies', () => {
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyMultipleReturnERC20Token,
erc20Artifacts.DummyMultipleReturnERC20Token,
provider,
txDefaults,
constants.DUMMY_TOKEN_NAME,
@@ -198,7 +204,7 @@ describe('Asset Transfer Proxies', () => {
// Deploy and configure ERC721 tokens and receiver
[erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
artifacts.DummyERC721Receiver,
erc721Artifacts.DummyERC721Receiver,
provider,
txDefaults,
);
@@ -562,7 +568,7 @@ describe('Asset Transfer Proxies', () => {
erc721Receiver.address,
amount,
);
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc721Artifacts });
const tx = await logDecoder.getTxWithDecodedLogsAsync(
await web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
@@ -754,7 +760,7 @@ describe('Asset Transfer Proxies', () => {
inputAmount,
);
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc20Artifacts });
const tx = await logDecoder.getTxWithDecodedLogsAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,

View File

@@ -0,0 +1,383 @@
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import {
constants,
ERC1155FungibleHoldingsByOwner,
ERC1155HoldingsByOwner,
ERC1155NonFungibleHoldingsByOwner,
LogDecoder,
txDefaults,
} from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
export class ERC1155ProxyWrapper {
private readonly _tokenOwnerAddresses: string[];
private readonly _fungibleTokenIds: string[];
private readonly _nonFungibleTokenIds: string[];
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
private readonly _contractOwnerAddress: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _provider: Provider;
private readonly _logDecoder: LogDecoder;
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
private readonly _assetProxyInterface: IAssetProxyContract;
private _proxyContract?: ERC1155ProxyContract;
private _proxyIdIfExists?: string;
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
this._web3Wrapper = new Web3Wrapper(provider);
this._provider = provider;
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
this._dummyTokenWrappers = [];
this._assetProxyInterface = new IAssetProxyContract(
artifacts.IAssetProxy.compilerOutput.abi,
constants.NULL_ADDRESS,
provider,
);
this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress;
this._fungibleTokenIds = [];
this._nonFungibleTokenIds = [];
this._nfts = [];
}
/**
* @dev Deploys dummy ERC1155 contracts
* @return An array of ERC1155 wrappers; one for each deployed contract.
*/
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
// tslint:disable-next-line:no-unused-variable
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
erc1155Artifacts.ERC1155Mintable,
this._provider,
txDefaults,
);
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
this._dummyTokenWrappers.push(erc1155Wrapper);
}
return this._dummyTokenWrappers;
}
/**
* @dev Deploys the ERC1155 proxy
* @return Deployed ERC1155 proxy contract instance
*/
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
artifacts.ERC1155Proxy,
this._provider,
txDefaults,
);
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
return this._proxyContract;
}
/**
* @dev Gets the ERC1155 proxy id
*/
public getProxyId(): string {
this._validateProxyContractExistsOrThrow();
return this._proxyIdIfExists as string;
}
/**
* @dev transfers erc1155 fungible/non-fungible tokens.
* @param from source address
* @param to destination address
* @param contractAddress address of erc155 contract
* @param tokensToTransfer array of erc1155 tokens to transfer
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
* @param receiverCallbackData callback data if `to` is a contract
* @param authorizedSender sender of `transferFrom` transaction
* @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return tranasction hash.
*/
public async transferFromAsync(
from: string,
to: string,
contractAddress: string,
tokensToTransfer: BigNumber[],
valuesToTransfer: BigNumber[],
valueMultiplier: BigNumber,
receiverCallbackData: string,
authorizedSender: string,
extraData?: string,
): Promise<string> {
this._validateProxyContractExistsOrThrow();
let encodedAssetData = assetDataUtils.encodeERC1155AssetData(
contractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
if (!_.isUndefined(extraData)) {
encodedAssetData = `${encodedAssetData}${extraData}`;
}
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
encodedAssetData,
from,
to,
valueMultiplier,
);
const txHash = await this._web3Wrapper.sendTransactionAsync({
to: (this._proxyContract as ERC1155ProxyContract).address,
data,
from: authorizedSender,
});
return txHash;
}
/**
* @dev transfers erc1155 fungible/non-fungible tokens.
* @param from source address
* @param to destination address
* @param contractAddress address of erc155 contract
* @param tokensToTransfer array of erc1155 tokens to transfer
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
* @param receiverCallbackData callback data if `to` is a contract
* @param authorizedSender sender of `transferFrom` transaction
* @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return tranasction receipt with decoded logs.
*/
public async transferFromWithLogsAsync(
from: string,
to: string,
contractAddress: string,
tokensToTransfer: BigNumber[],
valuesToTransfer: BigNumber[],
valueMultiplier: BigNumber,
receiverCallbackData: string,
authorizedSender: string,
extraData?: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
await this.transferFromAsync(
from,
to,
contractAddress,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorizedSender,
extraData,
),
);
return txReceipt;
}
/**
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
* tokens for each token owner address (`_tokenOwnerAddresses`).
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
*/
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
this._validateDummyTokenContractsExistOrThrow();
this._validateProxyContractExistsOrThrow();
this._initialTokenIdsByOwner = {
fungible: {},
nonFungible: {},
};
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
// Set balances accordingly
for (const dummyWrapper of this._dummyTokenWrappers) {
const dummyAddress = dummyWrapper.getContract().address;
// tslint:disable-next-line:no-unused-variable
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
// Create a fungible token
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
this._tokenOwnerAddresses,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
);
const tokenIdAsString = tokenId.toString();
this._fungibleTokenIds.push(tokenIdAsString);
// Mint tokens for each owner for this token
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
// tslint:disable-next-line:no-unused-variable
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress])) {
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
}
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
await dummyWrapper.setApprovalForAllAsync(
tokenOwnerAddress,
(this._proxyContract as ERC1155ProxyContract).address,
true,
);
}
}
// Non-fungible tokens
// tslint:disable-next-line:no-unused-variable
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
const tokenIdAsString = tokenId.toString();
this._nonFungibleTokenIds.push(tokenIdAsString);
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
}
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
}
this._nfts.push({ id: nftIds[i], tokenId });
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
await dummyWrapper.setApprovalForAllAsync(
tokenOwnerAddress,
(this._proxyContract as ERC1155ProxyContract).address,
true,
);
});
}
}
this._initialTokenIdsByOwner = {
fungible: fungibleHoldingsByOwner,
nonFungible: nonFungibleHoldingsByOwner,
};
return this._initialTokenIdsByOwner;
}
/**
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
* tokens for each token owner address (`_tokenOwnerAddresses`).
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
*/
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
this._validateDummyTokenContractsExistOrThrow();
this._validateBalancesAndAllowancesSetOrThrow();
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
const tokenContract = dummyTokenWrapper.getContract();
const tokenAddress = tokenContract.address;
// Construct batch balance call
const tokenOwners: string[] = [];
const tokenIds: BigNumber[] = [];
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
for (const tokenId of this._fungibleTokenIds) {
tokenOwners.push(tokenOwnerAddress);
tokenIds.push(new BigNumber(tokenId));
}
for (const nft of this._nfts) {
tokenOwners.push(tokenOwnerAddress);
tokenIds.push(nft.id);
}
}
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
// Parse out balances into fungible / non-fungible token holdings
let i = 0;
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
// Fungible tokens
for (const tokenId of this._fungibleTokenIds) {
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress])) {
tokenHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
}
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
}
// Non-fungible tokens
for (const nft of this._nfts) {
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
}
if (
_.isUndefined(
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()],
)
) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
}
const isOwner = balances[i++];
if (isOwner.isEqualTo(1)) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
nft.id,
);
}
}
}
}
const holdingsByOwner = {
fungible: tokenHoldingsByOwner,
nonFungible: nonFungibleHoldingsByOwner,
};
return holdingsByOwner;
}
/**
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
* @param userAddress owner of ERC1155 tokens.
* @param contractAddress address of ERC1155 contract.
* @return True iff the proxy is approved for all. False otherwise.
*/
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
this._validateProxyContractExistsOrThrow();
const tokenContract = this._getContractFromAddress(contractAddress);
const operator = (this._proxyContract as ERC1155ProxyContract).address;
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
return didApproveAll;
}
public getFungibleTokenIds(): BigNumber[] {
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
return new BigNumber(tokenIdAsString);
});
return fungibleTokenIds;
}
public getNonFungibleTokenIds(): BigNumber[] {
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
return new BigNumber(tokenIdAsString);
});
return nonFungibleTokenIds;
}
public getTokenOwnerAddresses(): string[] {
return this._tokenOwnerAddresses;
}
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
return wrapper.getContract().address === contractAddress;
});
if (_.isUndefined(tokenWrapper)) {
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
}
return tokenWrapper;
}
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
if (_.isUndefined(tokenContractIfExists)) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
}
return tokenContractIfExists.getContract();
}
private _validateDummyTokenContractsExistOrThrow(): void {
if (_.isUndefined(this._dummyTokenWrappers)) {
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
private _validateProxyContractExistsOrThrow(): void {
if (_.isUndefined(this._proxyContract)) {
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
}
}
private _validateBalancesAndAllowancesSetOrThrow(): void {
if (
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
) {
throw new Error(
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
);
}
}
}

View File

@@ -1,3 +1,4 @@
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
@@ -5,7 +6,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
import { ZeroExProvider } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts, DummyERC20TokenContract, ERC20ProxyContract } from '../../src';
import { artifacts, ERC20ProxyContract } from '../../src';
export class ERC20Wrapper {
private readonly _tokenOwnerAddresses: string[];
@@ -36,7 +37,7 @@ export class ERC20Wrapper {
for (let i = 0; i < numberToDeploy; i++) {
this._dummyTokenContracts.push(
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC20Token,
erc20Artifacts.DummyERC20Token,
this._provider,
txDefaults,
constants.DUMMY_TOKEN_NAME,

View File

@@ -1,3 +1,4 @@
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
@@ -5,7 +6,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
import { ZeroExProvider } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts, DummyERC721TokenContract, ERC721ProxyContract } from '../../src';
import { artifacts, ERC721ProxyContract } from '../../src';
export class ERC721Wrapper {
private readonly _tokenOwnerAddresses: string[];
@@ -28,7 +29,7 @@ export class ERC721Wrapper {
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
this._dummyTokenContracts.push(
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC721Token,
erc721Artifacts.DummyERC721Token,
this._provider,
txDefaults,
constants.DUMMY_TOKEN_NAME,

View File

@@ -1,2 +1,3 @@
export * from './erc20_wrapper';
export * from './erc721_wrapper';
export * from './erc1155_proxy_wrapper';

View File

@@ -3,11 +3,7 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/DummyERC20Token.json",
"generated-artifacts/DummyERC721Receiver.json",
"generated-artifacts/DummyERC721Token.json",
"generated-artifacts/DummyMultipleReturnERC20Token.json",
"generated-artifacts/DummyNoReturnERC20Token.json",
"generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20Proxy.json",
"generated-artifacts/ERC721Proxy.json",
"generated-artifacts/IAssetData.json",

View File

@@ -4,6 +4,14 @@
"changes": [
{
"note": "Created Coordinator package"
},
{
"note": "Use separate EIP712 domains for transactions and approvals",
"pr": 1705
},
{
"note": "Add `SignatureType.Invalid`",
"pr": 1705
}
]
}

View File

@@ -4,7 +4,11 @@
"useDockerisedSolc": true,
"compilerSettings": {
"evmVersion": "byzantium",
"optimizer": { "enabled": true, "runs": 1000000 },
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [

View File

@@ -16,7 +16,7 @@
*/
pragma solidity 0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "./libs/LibConstants.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
@@ -125,14 +125,14 @@ contract MixinCoordinatorApprovalVerifier is
// Hash approval message and recover signer address
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
// Add approval signer to list of signers
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
}
// Ethereum transaction signer gives implicit signature of approval
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
// Do not check approval if the order's senderAddress is null

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "./libs/LibZeroExTransaction.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "./mixins/MSignatureValidator.sol";
@@ -59,6 +59,17 @@ contract MixinSignatureValidator is
if (signatureType == SignatureType.Illegal) {
revert("SIGNATURE_ILLEGAL");
// Always invalid signature.
// Like Illegal, this is always implicitly available and therefore
// offered explicitly. It can be implicitly created by providing
// a correctly formatted but incorrect signature.
} else if (signatureType == SignatureType.Invalid) {
require(
signature.length == 0,
"LENGTH_0_REQUIRED"
);
revert("SIGNATURE_INVALID");
// Signature using EIP712
} else if (signatureType == SignatureType.EIP712) {
require(

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "../libs/LibZeroExTransaction.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
contract ISignatureValidator {

View File

@@ -15,7 +15,7 @@
limitations under the License.
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
contract ITransactions {

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
import "../interfaces/ITransactions.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
import "./LibEIP712Domain.sol";
@@ -50,7 +50,7 @@ contract LibCoordinatorApproval is
view
returns (bytes32 approvalHash)
{
approvalHash = hashEIP712Message(hashCoordinatorApproval(approval));
approvalHash = hashEIP712CoordinatorMessage(hashCoordinatorApproval(approval));
return approvalHash;
}
@@ -79,7 +79,7 @@ contract LibCoordinatorApproval is
assembly {
// Compute hash of transaction signature
let transactionSignatureHash := keccak256(add(transactionSignature, 32), mload(transactionSignature))
// Load free memory pointer
let memPtr := mload(64)

View File

@@ -16,19 +16,29 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
import "./LibConstants.sol";
contract LibEIP712Domain {
contract LibEIP712Domain is
LibConstants
{
// EIP191 header for EIP712 prefix
string constant internal EIP191_HEADER = "\x19\x01";
// EIP712 Domain Name value
string constant internal EIP712_DOMAIN_NAME = "0x Protocol Coordinator";
// EIP712 Domain Name value for the Coordinator
string constant internal EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
// EIP712 Domain Version value
string constant internal EIP712_DOMAIN_VERSION = "1.0.0";
// EIP712 Domain Version value for the Coordinator
string constant internal EIP712_COORDINATOR_DOMAIN_VERSION = "1.0.0";
// EIP712 Domain Name value for the Exchange
string constant internal EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
// EIP712 Domain Version value for the Exchange
string constant internal EIP712_EXCHANGE_DOMAIN_VERSION = "2";
// Hash of the EIP712 Domain Separator Schema
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
@@ -39,36 +49,70 @@ contract LibEIP712Domain {
")"
));
// Hash of the EIP712 Domain Separator data
// Hash of the EIP712 Domain Separator data for the Coordinator
// solhint-disable-next-line var-name-mixedcase
bytes32 public EIP712_DOMAIN_HASH;
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
// Hash of the EIP712 Domain Separator data for the Exchange
// solhint-disable-next-line var-name-mixedcase
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
constructor ()
public
{
EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(
EIP712_COORDINATOR_DOMAIN_HASH = keccak256(abi.encodePacked(
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
keccak256(bytes(EIP712_DOMAIN_NAME)),
keccak256(bytes(EIP712_DOMAIN_VERSION)),
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_NAME)),
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_VERSION)),
uint256(address(this))
));
EIP712_EXCHANGE_DOMAIN_HASH = keccak256(abi.encodePacked(
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_NAME)),
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_VERSION)),
uint256(address(EXCHANGE))
));
}
/// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain.
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
/// of this contract.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to this EIP712 Domain.
function hashEIP712Message(bytes32 hashStruct)
function hashEIP712CoordinatorMessage(bytes32 hashStruct)
internal
view
returns (bytes32 result)
{
bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;
return hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
}
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
/// of the Exchange contract.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
function hashEIP712ExchangeMessage(bytes32 hashStruct)
internal
view
returns (bytes32 result)
{
return hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
}
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
/// @param eip712DomainHash Hash of the domain domain separator data.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
internal
pure
returns (bytes32 result)
{
// Assembly for more efficient computing:
// keccak256(abi.encodePacked(
// EIP191_HEADER,
// EIP712_DOMAIN_HASH,
// hashStruct
// hashStruct
// ));
assembly {

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
import "./LibEIP712Domain.sol";
@@ -48,8 +48,8 @@ contract LibZeroExTransaction is
view
returns (bytes32 transactionHash)
{
// Note: this transaction hash will differ from the hash produced by the Exchange contract because it utilizes a different domain hash.
transactionHash = hashEIP712Message(hashZeroExTransaction(transaction));
// Hash the transaction with the domain separator of the Exchange contract.
transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction));
return transactionHash;
}
@@ -77,7 +77,7 @@ contract LibZeroExTransaction is
assembly {
// Compute hash of data
let dataHash := keccak256(add(data, 32), mload(data))
// Load free memory pointer
let memPtr := mload(64)

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
import "../interfaces/ISignatureValidator.sol";
@@ -27,8 +27,9 @@ contract MSignatureValidator is
// Allowed signature types.
enum SignatureType {
Illegal, // 0x00, default value
EIP712, // 0x01
EthSign, // 0x02
NSignatureTypes // 0x03, number of signature types. Always leave at end.
Invalid, // 0x01
EIP712, // 0x02
EthSign, // 0x03
NSignatureTypes // 0x04, number of signature types. Always leave at end.
}
}

View File

@@ -16,8 +16,7 @@
*/
pragma solidity 0.5.3;
pragma experimental ABIEncoderV2;
pragma solidity ^0.5.5;
import "./MixinCoordinatorRegistryCore.sol";

View File

@@ -16,8 +16,7 @@
*/
pragma solidity 0.5.3;
pragma experimental ABIEncoderV2;
pragma solidity ^0.5.5;
import "./interfaces/ICoordinatorRegistryCore.sol";

View File

@@ -16,8 +16,7 @@
*/
pragma solidity 0.5.3;
pragma experimental ABIEncoderV2;
pragma solidity ^0.5.5;
// solhint-disable no-empty-blocks

View File

@@ -16,17 +16,25 @@
*/
pragma solidity ^0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "../src/libs/LibConstants.sol";
import "../src/libs/LibCoordinatorApproval.sol";
import "../src/libs/LibZeroExTransaction.sol";
// solhint-disable no-empty-blocks
contract TestLibs is
LibConstants,
LibCoordinatorApproval,
LibZeroExTransaction
{
constructor (address _exchange)
public
LibConstants(_exchange)
{}
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
@@ -39,9 +47,9 @@ contract TestLibs is
return approvalHash;
}
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of this contract.
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @return EIP712 hash of the transaction with the domain separator of this contract.
/// @return EIP712 hash of the transaction with the domain separator of the Exchange contract.
function publicGetTransactionHash(ZeroExTransaction memory transaction)
public
view

View File

@@ -16,15 +16,22 @@
*/
pragma solidity 0.5.3;
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "../src/libs/LibConstants.sol";
import "../src/MixinSignatureValidator.sol";
import "../src/MixinCoordinatorApprovalVerifier.sol";
// solhint-disable no-empty-blocks
contract TestMixins is
LibConstants,
MixinSignatureValidator,
MixinCoordinatorApprovalVerifier
{}
{
constructor (address _exchange)
public
LibConstants(_exchange)
{}
}

View File

@@ -1,4 +1,5 @@
import { DummyERC20TokenContract, ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import {
artifacts as exchangeArtifacts,
ExchangeCancelEventArgs,

View File

@@ -1,5 +1,6 @@
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { transactionHashUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
@@ -11,6 +12,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Libs tests', () => {
let testLibs: TestLibsContract;
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
before(async () => {
await blockchainLifecycle.startAsync();
@@ -19,7 +21,12 @@ describe('Libs tests', () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
testLibs = await TestLibsContract.deployFrom0xArtifactAsync(artifacts.TestLibs, provider, txDefaults);
testLibs = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
provider,
txDefaults,
exchangeAddress,
);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -31,12 +38,12 @@ describe('Libs tests', () => {
describe('getTransactionHash', () => {
it('should return the correct transaction hash', async () => {
const tx = {
verifyingContractAddress: testLibs.address,
verifyingContractAddress: exchangeAddress,
salt: new BigNumber(0),
signerAddress: constants.NULL_ADDRESS,
data: '0x1234',
};
const expectedTxHash = hashUtils.getTransactionHashHex(tx);
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
const txHash = await testLibs.publicGetTransactionHash.callAsync(tx);
expect(expectedTxHash).to.eq(txHash);
});
@@ -45,7 +52,7 @@ describe('Libs tests', () => {
describe('getApprovalHash', () => {
it('should return the correct approval hash', async () => {
const signedTx = {
verifyingContractAddress: testLibs.address,
verifyingContractAddress: exchangeAddress,
salt: new BigNumber(0),
signerAddress: constants.NULL_ADDRESS,
data: '0x1234',
@@ -55,12 +62,13 @@ describe('Libs tests', () => {
const txOrigin = constants.NULL_ADDRESS;
const approval = {
txOrigin,
transactionHash: hashUtils.getTransactionHashHex(signedTx),
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
transactionSignature: signedTx.signature,
approvalExpirationTimeSeconds,
};
const expectedApprovalHash = hashUtils.getApprovalHashHex(
signedTx,
testLibs.address,
txOrigin,
approvalExpirationTimeSeconds,
);

View File

@@ -1,28 +1,22 @@
import {
addressUtils,
chaiSetup,
constants as devConstants,
expectContractCallFailedAsync,
getLatestBlockTimestampAsync,
provider,
TransactionFactory,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason, SignedOrder } from '@0x/types';
import { transactionHashUtils } from '@0x/order-utils';
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import {
ApprovalFactory,
artifacts,
constants,
CoordinatorSignatureType,
CoordinatorTransactionFactory,
exchangeDataEncoder,
hashUtils,
TestMixinsContract,
} from '../src';
import { ApprovalFactory, artifacts, constants, exchangeDataEncoder, TestMixinsContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
@@ -33,10 +27,12 @@ describe('Mixins tests', () => {
let approvalSignerAddress1: string;
let approvalSignerAddress2: string;
let mixins: TestMixinsContract;
let transactionFactory: CoordinatorTransactionFactory;
let transactionFactory: TransactionFactory;
let approvalFactory1: ApprovalFactory;
let approvalFactory2: ApprovalFactory;
let defaultOrder: SignedOrder;
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
before(async () => {
await blockchainLifecycle.startAsync();
});
@@ -44,7 +40,12 @@ describe('Mixins tests', () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
mixins = await TestMixinsContract.deployFrom0xArtifactAsync(artifacts.TestMixins, provider, txDefaults);
mixins = await TestMixinsContract.deployFrom0xArtifactAsync(
artifacts.TestMixins,
provider,
txDefaults,
exchangeAddress,
);
const accounts = await web3Wrapper.getAvailableAddressesAsync();
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3);
defaultOrder = {
@@ -67,7 +68,7 @@ describe('Mixins tests', () => {
devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(transactionSignerAddress)];
const approvalSignerPrivateKey1 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress1)];
const approvalSignerPrivateKey2 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress2)];
transactionFactory = new CoordinatorTransactionFactory(transactionSignerPrivateKey, mixins.address);
transactionFactory = new TransactionFactory(transactionSignerPrivateKey, exchangeAddress);
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
});
@@ -81,47 +82,52 @@ describe('Mixins tests', () => {
describe('getSignerAddress', () => {
it('should return the correct address using the EthSign signature type', async () => {
const data = devConstants.NULL_BYTES;
const transaction = transactionFactory.newSignedCoordinatorTransaction(
data,
CoordinatorSignatureType.EthSign,
);
const transactionHash = hashUtils.getTransactionHashHex(transaction);
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EthSign);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
expect(transaction.signerAddress).to.eq(signerAddress);
});
it('should return the correct address using the EIP712 signature type', async () => {
const data = devConstants.NULL_BYTES;
const transaction = transactionFactory.newSignedCoordinatorTransaction(
data,
CoordinatorSignatureType.EIP712,
);
const transactionHash = hashUtils.getTransactionHashHex(transaction);
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EIP712);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
expect(transaction.signerAddress).to.eq(signerAddress);
});
it('should revert with with the Illegal signature type', async () => {
const data = devConstants.NULL_BYTES;
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const illegalSignatureByte = ethUtil.toBuffer(CoordinatorSignatureType.Illegal).toString('hex');
const transaction = transactionFactory.newSignedTransaction(data);
const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex');
transaction.signature = `${transaction.signature.slice(
0,
transaction.signature.length - 2,
)}${illegalSignatureByte}`;
const transactionHash = hashUtils.getTransactionHashHex(transaction);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expectContractCallFailedAsync(
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
RevertReason.SignatureIllegal,
);
});
it('should revert with with the Invalid signature type', async () => {
const data = devConstants.NULL_BYTES;
const transaction = transactionFactory.newSignedTransaction(data);
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
transaction.signature = `0x${invalidSignatureByte}`;
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expectContractCallFailedAsync(
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
RevertReason.SignatureInvalid,
);
});
it("should revert with with a signature type that doesn't exist", async () => {
const data = devConstants.NULL_BYTES;
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const invalidSignatureByte = '03';
const transaction = transactionFactory.newSignedTransaction(data);
const invalidSignatureByte = '04';
transaction.signature = `${transaction.signature.slice(
0,
transaction.signature.length - 2,
)}${invalidSignatureByte}`;
const transactionHash = hashUtils.getTransactionHashHex(transaction);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expectContractCallFailedAsync(
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
RevertReason.SignatureUnsupported,
@@ -134,7 +140,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -167,7 +173,7 @@ describe('Mixins tests', () => {
};
const orders = [order];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -196,7 +202,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
@@ -220,7 +226,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -249,7 +255,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
@@ -273,7 +279,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -309,7 +315,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -344,7 +350,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -387,7 +393,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -419,7 +425,7 @@ describe('Mixins tests', () => {
senderAddress: devConstants.NULL_ADDRESS,
}));
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -451,7 +457,7 @@ describe('Mixins tests', () => {
senderAddress: devConstants.NULL_ADDRESS,
}));
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
@@ -473,7 +479,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, senderAddress: devConstants.NULL_ADDRESS }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -502,7 +508,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(
@@ -536,7 +542,7 @@ describe('Mixins tests', () => {
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
await mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
orders,
@@ -558,7 +564,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(
@@ -593,7 +599,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
expectContractCallFailedAsync(
mixins.assertValidTransactionOrdersApproval.callAsync(
transaction,
@@ -621,7 +627,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory1.newSignedApproval(
@@ -657,7 +663,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(
@@ -698,7 +704,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(
@@ -734,7 +740,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
@@ -775,7 +781,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval2 = approvalFactory2.newSignedApproval(
@@ -810,7 +816,7 @@ describe('Mixins tests', () => {
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval1 = approvalFactory1.newSignedApproval(
@@ -848,7 +854,7 @@ describe('Mixins tests', () => {
it('should allow the tx signer to call `cancelOrders` without approval', async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
@@ -861,7 +867,7 @@ describe('Mixins tests', () => {
it('should allow the tx signer to call `batchCancelOrders` without approval', async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
@@ -874,7 +880,7 @@ describe('Mixins tests', () => {
it('should allow the tx signer to call `cancelOrdersUpTo` without approval', async () => {
const orders: SignedOrder[] = [];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
const transaction = transactionFactory.newSignedCoordinatorTransaction(data);
const transaction = transactionFactory.newSignedTransaction(data);
await mixins.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,

View File

@@ -1,35 +1,35 @@
import { SignedZeroExTransaction } from '@0x/types';
import { signingUtils } from '@0x/contracts-test-utils';
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import { CoordinatorSignatureType, hashUtils, SignedCoordinatorApproval, signingUtils } from './index';
import { hashUtils, SignedCoordinatorApproval } from './index';
export class ApprovalFactory {
private readonly _privateKey: Buffer;
private readonly _verifyingContractAddress: string;
constructor(privateKey: Buffer, verifyingContractAddress: string) {
this._privateKey = privateKey;
this._verifyingContractAddress = verifyingContractAddress;
}
public newSignedApproval(
transaction: SignedZeroExTransaction,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
signatureType: CoordinatorSignatureType = CoordinatorSignatureType.EthSign,
signatureType: SignatureType = SignatureType.EthSign,
): SignedCoordinatorApproval {
const coordinatorTransaction = {
...transaction,
verifyingContractAddress: this._verifyingContractAddress,
};
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
coordinatorTransaction,
transaction,
this._verifyingContractAddress,
txOrigin,
approvalExpirationTimeSeconds,
);
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
const signedApproval = {
txOrigin,
transaction: coordinatorTransaction,
transaction,
approvalExpirationTimeSeconds,
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
};

View File

@@ -1,17 +1,6 @@
import { BigNumber } from '@0x/utils';
export const constants = {
COORDINATOR_DOMAIN_NAME: '0x Protocol Coordinator',
COORDINATOR_DOMAIN_VERSION: '1.0.0',
COORDINATOR_APPROVAL_SCHEMA: {
name: 'CoordinatorApproval',
parameters: [
{ name: 'txOrigin', type: 'address' },
{ name: 'transactionHash', type: 'bytes32' },
{ name: 'transactionSignature', type: 'bytes' },
{ name: 'approvalExpirationTimeSeconds', type: 'uint256' },
],
},
SINGLE_FILL_FN_NAMES: ['fillOrder', 'fillOrKillOrder', 'fillOrderNoThrow'],
BATCH_FILL_FN_NAMES: ['batchFillOrders', 'batchFillOrKillOrders', 'batchFillOrdersNoThrow'],
MARKET_FILL_FN_NAMES: ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow'],

View File

@@ -1,34 +0,0 @@
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { SignedZeroExTransaction } from '@0x/types';
import * as ethUtil from 'ethereumjs-util';
import { CoordinatorSignatureType, hashUtils, signingUtils } from './index';
export class CoordinatorTransactionFactory {
private readonly _signerBuff: Buffer;
private readonly _verifyingContractAddress: string;
private readonly _privateKey: Buffer;
constructor(privateKey: Buffer, verifyingContractAddress: string) {
this._privateKey = privateKey;
this._verifyingContractAddress = verifyingContractAddress;
this._signerBuff = ethUtil.privateToAddress(this._privateKey);
}
public newSignedCoordinatorTransaction(
data: string,
signatureType: CoordinatorSignatureType = CoordinatorSignatureType.EthSign,
): SignedZeroExTransaction {
const transaction = {
verifyingContractAddress: this._verifyingContractAddress,
signerAddress: ethUtil.addHexPrefix(this._signerBuff.toString('hex')),
salt: generatePseudoRandomSalt(),
data,
};
const transactionHashBuff = hashUtils.getTransactionHashBuffer(transaction);
const signatureBuff = signingUtils.signMessage(transactionHashBuff, this._privateKey, signatureType);
const signedTransaction = {
...transaction,
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
};
return signedTransaction;
}
}

View File

@@ -1,23 +1,22 @@
import { eip712Utils } from '@0x/order-utils';
import { constants as orderUtilsConstants } from '@0x/order-utils/lib/src/constants';
import { SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
import { eip712Utils, transactionHashUtils } from '@0x/order-utils';
import { constants } from '@0x/order-utils/lib/src/constants';
import { SignedZeroExTransaction } from '@0x/types';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as _ from 'lodash';
import { constants } from './index';
export const hashUtils = {
getApprovalHashBuffer(
transaction: SignedZeroExTransaction,
verifyingContractAddress: string,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): Buffer {
const domain = {
name: constants.COORDINATOR_DOMAIN_NAME,
version: constants.COORDINATOR_DOMAIN_VERSION,
verifyingContractAddress: transaction.verifyingContractAddress,
verifyingContractAddress,
};
const transactionHash = hashUtils.getTransactionHashHex(transaction);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
const approval = {
txOrigin,
transactionHash,
@@ -37,34 +36,13 @@ export const hashUtils = {
},
getApprovalHashHex(
transaction: SignedZeroExTransaction,
verifyingContractAddress: string,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): string {
const hashHex = `0x${hashUtils
.getApprovalHashBuffer(transaction, txOrigin, approvalExpirationTimeSeconds)
.getApprovalHashBuffer(transaction, verifyingContractAddress, txOrigin, approvalExpirationTimeSeconds)
.toString('hex')}`;
return hashHex;
},
getTransactionHashBuffer(transaction: ZeroExTransaction | SignedZeroExTransaction): Buffer {
const domain = {
name: constants.COORDINATOR_DOMAIN_NAME,
version: constants.COORDINATOR_DOMAIN_VERSION,
verifyingContractAddress: transaction.verifyingContractAddress,
};
const normalizedTransaction = _.mapValues(transaction, value => {
return !_.isString(value) ? value.toString() : value;
});
const typedData = eip712Utils.createTypedData(
orderUtilsConstants.EXCHANGE_ZEROEX_TRANSACTION_SCHEMA.name,
{ ZeroExTransaction: orderUtilsConstants.EXCHANGE_ZEROEX_TRANSACTION_SCHEMA.parameters },
normalizedTransaction,
domain,
);
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
return hashBuffer;
},
getTransactionHashHex(transaction: ZeroExTransaction | SignedZeroExTransaction): string {
const hashHex = `0x${hashUtils.getTransactionHashBuffer(transaction).toString('hex')}`;
return hashHex;
},
};

View File

@@ -1,6 +1,4 @@
export { hashUtils } from './hash_utils';
export { signingUtils } from './signing_utils';
export { CoordinatorTransactionFactory } from './coordinator_transaction_factory';
export { ApprovalFactory } from './approval_factory';
export { constants } from './constants';
export { exchangeDataEncoder } from './exchange_data_encoder';

View File

@@ -1,30 +0,0 @@
import * as ethUtil from 'ethereumjs-util';
import { CoordinatorSignatureType } from './types';
export const signingUtils = {
signMessage(message: Buffer, privateKey: Buffer, signatureType: CoordinatorSignatureType): Buffer {
if (signatureType === CoordinatorSignatureType.EthSign) {
const prefixedMessage = ethUtil.hashPersonalMessage(message);
const ecSignature = ethUtil.ecsign(prefixedMessage, privateKey);
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(signatureType),
]);
return signature;
} else if (signatureType === CoordinatorSignatureType.EIP712) {
const ecSignature = ethUtil.ecsign(message, privateKey);
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(signatureType),
]);
return signature;
} else {
throw new Error(`${signatureType} is not a valid signature type`);
}
},
};

View File

@@ -10,10 +10,3 @@ export interface CoordinatorApproval {
export interface SignedCoordinatorApproval extends CoordinatorApproval {
signature: string;
}
export enum CoordinatorSignatureType {
Illegal,
EIP712,
EthSign,
NSignatureTypes,
}

View File

@@ -0,0 +1,11 @@
[
{
"version": "1.0.0",
"changes": [
{
"note": "Created ERC1155 contracts package",
"pr": 1657
}
]
}
]

View File

@@ -0,0 +1,6 @@
<!--
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
Edit the package's CHANGELOG.json file only.
-->
CHANGELOG

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
{
"artifactsDir": "generated-artifacts",
"contractsDir": "contracts",
"useDockerisedSolc": true,
"compilerSettings": {
"evmVersion": "byzantium",
"optimizer": { "enabled": true, "runs": 1000000 },
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
},
"contracts": [
"src/ERC1155.sol",
"src/ERC1155Mintable.sol",
"src/MixinNonFungibleToken.sol",
"src/interfaces/IERC1155.sol",
"src/interfaces/IERC1155Mintable.sol",
"src/interfaces/IERC1155Receiver.sol",
"src/mixins/MNonFungibleToken.sol",
"test/DummyERC1155Receiver.sol"
]
}

View File

@@ -0,0 +1,247 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/Address.sol";
import "./interfaces/IERC1155.sol";
import "./interfaces/IERC1155Receiver.sol";
import "./MixinNonFungibleToken.sol";
contract ERC1155 is
SafeMath,
IERC1155,
MixinNonFungibleToken
{
using Address for address;
// selectors for receiver callbacks
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
// id => (owner => balance)
mapping (uint256 => mapping(address => uint256)) internal balances;
// owner => (operator => approved)
mapping (address => mapping(address => bool)) internal operatorApproval;
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
/// @dev MUST emit TransferSingle event on success.
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
/// MUST throw if `_to` is the zero address.
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
/// MUST throw on any other error.
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
/// @param from Source address
/// @param to Target address
/// @param id ID of the token type
/// @param value Transfer amount
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 value,
bytes calldata data
)
external
{
// sanity checks
require(
to != address(0x0),
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
);
require(
from == msg.sender || operatorApproval[from][msg.sender] == true,
"INSUFFICIENT_ALLOWANCE"
);
// perform transfer
if (isNonFungible(id)) {
require(
value == 1,
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
);
require(
nfOwners[id] == from,
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
);
nfOwners[id] = to;
// You could keep balance of NF type in base type id like so:
// uint256 baseType = getNonFungibleBaseType(_id);
// balances[baseType][_from] = balances[baseType][_from].safeSub(_value);
// balances[baseType][_to] = balances[baseType][_to].safeAdd(_value);
} else {
balances[id][from] = safeSub(balances[id][from], value);
balances[id][to] = safeAdd(balances[id][to], value);
}
emit TransferSingle(msg.sender, from, to, id, value);
// if `to` is a contract then trigger its callback
if (to.isContract()) {
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155Received(
msg.sender,
from,
id,
value,
data
);
require(
callbackReturnValue == ERC1155_RECEIVED,
"BAD_RECEIVER_RETURN_VALUE"
);
}
}
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
/// @dev MUST emit TransferBatch event on success.
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
/// MUST throw if `_to` is the zero address.
/// MUST throw if length of `_ids` is not the same as length of `_values`.
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
/// MUST throw on any other error.
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
/// @param from Source addresses
/// @param to Target addresses
/// @param ids IDs of each token type
/// @param values Transfer amounts per token type
/// @param data Additional data with no specified format, sent in call to `_to`
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
{
// sanity checks
require(
to != address(0x0),
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
);
require(
ids.length == values.length,
"TOKEN_AND_VALUES_LENGTH_MISMATCH"
);
// Only supporting a global operator approval allows us to do
// only 1 check and not to touch storage to handle allowances.
require(
from == msg.sender || operatorApproval[from][msg.sender] == true,
"INSUFFICIENT_ALLOWANCE"
);
// perform transfers
for (uint256 i = 0; i < ids.length; ++i) {
// Cache value to local variable to reduce read costs.
uint256 id = ids[i];
uint256 value = values[i];
if (isNonFungible(id)) {
require(
value == 1,
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
);
require(
nfOwners[id] == from,
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
);
nfOwners[id] = to;
} else {
balances[id][from] = safeSub(balances[id][from], value);
balances[id][to] = safeAdd(balances[id][to], value);
}
}
emit TransferBatch(msg.sender, from, to, ids, values);
// if `to` is a contract then trigger its callback
if (to.isContract()) {
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155BatchReceived(
msg.sender,
from,
ids,
values,
data
);
require(
callbackReturnValue == ERC1155_BATCH_RECEIVED,
"BAD_RECEIVER_RETURN_VALUE"
);
}
}
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
/// @dev MUST emit the ApprovalForAll event on success.
/// @param operator Address to add to the set of authorized operators
/// @param approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address operator, bool approved) external {
operatorApproval[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
/// @notice Queries the approval status of an operator for a given owner.
/// @param owner The owner of the Tokens
/// @param operator Address of authorized operator
/// @return True if the operator is approved, false if not
function isApprovedForAll(address owner, address operator) external view returns (bool) {
return operatorApproval[owner][operator];
}
/// @notice Get the balance of an account's Tokens.
/// @param owner The address of the token holder
/// @param id ID of the Token
/// @return The _owner's balance of the Token type requested
function balanceOf(address owner, uint256 id) external view returns (uint256) {
if (isNonFungibleItem(id)) {
return nfOwners[id] == owner ? 1 : 0;
}
return balances[id][owner];
}
/// @notice Get the balance of multiple account/token pairs
/// @param owners The addresses of the token holders
/// @param ids ID of the Tokens
/// @return The _owner's balance of the Token types requested
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory balances_) {
// sanity check
require(
owners.length == ids.length,
"OWNERS_AND_IDS_MUST_HAVE_SAME_LENGTH"
);
// get balances
balances_ = new uint256[](owners.length);
for (uint256 i = 0; i < owners.length; ++i) {
uint256 id = ids[i];
if (isNonFungibleItem(id)) {
balances_[i] = nfOwners[id] == owners[i] ? 1 : 0;
} else {
balances_[i] = balances[id][owners[i]];
}
}
return balances_;
}
}

View File

@@ -0,0 +1,173 @@
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "./ERC1155.sol";
import "./interfaces/IERC1155Mintable.sol";
/// @dev Mintable form of ERC1155
/// Shows how easy it is to mint new items
contract ERC1155Mintable is
IERC1155Mintable,
ERC1155
{
/// token nonce
uint256 internal nonce;
/// mapping from token to creator
mapping (uint256 => address) public creators;
/// mapping from token to max index
mapping (uint256 => uint256) public maxIndex;
/// asserts token is owned by msg.sender
modifier creatorOnly(uint256 _id) {
require(creators[_id] == msg.sender);
_;
}
/// @dev creates a new token
/// @param uri URI of token
/// @param isNF is non-fungible token
/// @return type_ of token (a unique identifier)
function create(
string calldata uri,
bool isNF
)
external
returns (uint256 type_)
{
// Store the type in the upper 128 bits
type_ = (++nonce << 128);
// Set a flag if this is an NFI.
if (isNF) {
type_ = type_ | TYPE_NF_BIT;
}
// This will allow restricted access to creators.
creators[type_] = msg.sender;
// emit a Transfer event with Create semantic to help with discovery.
emit TransferSingle(
msg.sender,
address(0x0),
address(0x0),
type_,
0
);
if (bytes(uri).length > 0) {
emit URI(uri, type_);
}
}
/// @dev mints fungible tokens
/// @param id token type
/// @param to beneficiaries of minted tokens
/// @param quantities amounts of minted tokens
function mintFungible(
uint256 id,
address[] calldata to,
uint256[] calldata quantities
)
external
creatorOnly(id)
{
// sanity checks
require(
isFungible(id),
"TRIED_TO_MINT_FUNGIBLE_FOR_NON_FUNGIBLE_TOKEN"
);
// mint tokens
for (uint256 i = 0; i < to.length; ++i) {
// cache to reduce number of loads
address dst = to[i];
uint256 quantity = quantities[i];
// Grant the items to the caller
balances[id][dst] = safeAdd(quantity, balances[id][dst]);
// Emit the Transfer/Mint event.
// the 0x0 source address implies a mint
// It will also provide the circulating supply info.
emit TransferSingle(
msg.sender,
address(0x0),
dst,
id,
quantity
);
// if `to` is a contract then trigger its callback
if (dst.isContract()) {
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
msg.sender,
msg.sender,
id,
quantity,
""
);
require(
callbackReturnValue == ERC1155_RECEIVED,
"BAD_RECEIVER_RETURN_VALUE"
);
}
}
}
/// @dev mints a non-fungible token
/// @param type_ token type
/// @param to beneficiaries of minted tokens
function mintNonFungible(
uint256 type_,
address[] calldata to
)
external
creatorOnly(type_)
{
// No need to check this is a nf type rather than an id since
// creatorOnly() will only let a type pass through.
require(
isNonFungible(type_),
"TRIED_TO_MINT_NON_FUNGIBLE_FOR_FUNGIBLE_TOKEN"
);
// Index are 1-based.
uint256 index = maxIndex[type_] + 1;
for (uint256 i = 0; i < to.length; ++i) {
// cache to reduce number of loads
address dst = to[i];
uint256 id = type_ | index + i;
nfOwners[id] = dst;
// You could use base-type id to store NF type balances if you wish.
// balances[_type][dst] = quantity.safeAdd(balances[_type][dst]);
emit TransferSingle(msg.sender, address(0x0), dst, id, 1);
// if `to` is a contract then trigger its callback
if (dst.isContract()) {
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
msg.sender,
msg.sender,
id,
1,
""
);
require(
callbackReturnValue == ERC1155_RECEIVED,
"BAD_RECEIVER_RETURN_VALUE"
);
}
}
// record the `maxIndex` of this nft type
// this allows us to mint more nft's of this type in a subsequent call.
maxIndex[type_] = safeAdd(to.length, maxIndex[type_]);
}
}

View File

@@ -0,0 +1,76 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "./mixins/MNonFungibleToken.sol";
contract MixinNonFungibleToken is
MNonFungibleToken
{
/// Use a split bit implementation.
/// Store the type in the upper 128 bits..
uint256 constant internal TYPE_MASK = uint256(uint128(~0)) << 128;
/// ..and the non-fungible index in the lower 128
uint256 constant internal NF_INDEX_MASK = uint128(~0);
/// The top bit is a flag to tell if this is a NFI.
uint256 constant internal TYPE_NF_BIT = 1 << 255;
/// mapping of nft to owner
mapping (uint256 => address) internal nfOwners;
/// @dev Returns true if token is non-fungible
function isNonFungible(uint256 id) public pure returns(bool) {
return id & TYPE_NF_BIT == TYPE_NF_BIT;
}
/// @dev Returns true if token is fungible
function isFungible(uint256 id) public pure returns(bool) {
return id & TYPE_NF_BIT == 0;
}
/// @dev Returns index of non-fungible token
function getNonFungibleIndex(uint256 id) public pure returns(uint256) {
return id & NF_INDEX_MASK;
}
/// @dev Returns base type of non-fungible token
function getNonFungibleBaseType(uint256 id) public pure returns(uint256) {
return id & TYPE_MASK;
}
/// @dev Returns true if input is base-type of a non-fungible token
function isNonFungibleBaseType(uint256 id) public pure returns(bool) {
// A base type has the NF bit but does not have an index.
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK == 0);
}
/// @dev Returns true if input is a non-fungible token
function isNonFungibleItem(uint256 id) public pure returns(bool) {
// A base type has the NF bit but does has an index.
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0);
}
/// @dev returns owner of a non-fungible token
function ownerOf(uint256 id) public view returns (address) {
return nfOwners[id];
}
}

View File

@@ -0,0 +1,152 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
/// @title ERC-1155 Multi Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
interface IERC1155 {
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
/// including zero value transfers as well as minting or burning.
/// Operator will always be msg.sender.
/// Either event from address `0x0` signifies a minting operation.
/// An event to address `0x0` signifies a burning or melting operation.
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
/// To define a token ID with no initial balance, the contract SHOULD emit the TransferSingle event
/// from `0x0` to `0x0`, with the token creator as `_operator`.
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 value
);
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
/// including zero value transfers as well as minting or burning.
///Operator will always be msg.sender.
/// Either event from address `0x0` signifies a minting operation.
/// An event to address `0x0` signifies a burning or melting operation.
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
/// To define multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event
/// from `0x0` to `0x0`, with the token creator as `_operator`.
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/// @dev MUST emit when an approval is updated.
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
/// @dev MUST emit when the URI is updated for a token ID.
/// URIs are defined in RFC 3986.
/// The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata JSON Schema".
event URI(
string value,
uint256 indexed id
);
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
/// @dev MUST emit TransferSingle event on success.
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
/// MUST throw if `_to` is the zero address.
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
/// MUST throw on any other error.
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
/// @param from Source address
/// @param to Target address
/// @param id ID of the token type
/// @param value Transfer amount
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 value,
bytes calldata data
)
external;
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
/// @dev MUST emit TransferBatch event on success.
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
/// MUST throw if `_to` is the zero address.
/// MUST throw if length of `_ids` is not the same as length of `_values`.
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
/// MUST throw on any other error.
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
/// @param from Source addresses
/// @param to Target addresses
/// @param ids IDs of each token type
/// @param values Transfer amounts per token type
/// @param data Additional data with no specified format, sent in call to `_to`
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external;
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
/// @dev MUST emit the ApprovalForAll event on success.
/// @param operator Address to add to the set of authorized operators
/// @param approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address operator, bool approved) external;
/// @notice Queries the approval status of an operator for a given owner.
/// @param owner The owner of the Tokens
/// @param operator Address of authorized operator
/// @return True if the operator is approved, false if not
function isApprovedForAll(address owner, address operator) external view returns (bool);
/// @notice Get the balance of an account's Tokens.
/// @param owner The address of the token holder
/// @param id ID of the Token
/// @return The _owner's balance of the Token type requested
function balanceOf(address owner, uint256 id) external view returns (uint256);
/// @notice Get the balance of multiple account/token pairs
/// @param owners The addresses of the token holders
/// @param ids ID of the Tokens
/// @return The _owner's balance of the Token types requested
function balanceOfBatch(
address[] calldata owners,
uint256[] calldata ids
)
external
view
returns (uint256[] memory balances_);
}

View File

@@ -0,0 +1,42 @@
pragma solidity ^0.5.5;
import "./IERC1155.sol";
/// @dev Mintable form of ERC1155
/// Shows how easy it is to mint new items
contract IERC1155Mintable is
IERC1155
{
/// @dev creates a new token
/// @param uri URI of token
/// @param isNF is non-fungible token
/// @return _type of token (a unique identifier)
function create(
string calldata uri,
bool isNF
)
external
returns (uint256 type_);
/// @dev mints fungible tokens
/// @param id token type
/// @param to beneficiaries of minted tokens
/// @param quantities amounts of minted tokens
function mintFungible(
uint256 id,
address[] calldata to,
uint256[] calldata quantities
)
external;
/// @dev mints a non-fungible token
/// @param type_ token type
/// @param to beneficiaries of minted tokens
function mintNonFungible(
uint256 type_,
address[] calldata to
)
external;
}

View File

@@ -0,0 +1,67 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
interface IERC1155Receiver {
/// @notice Handle the receipt of a single ERC1155 token type
/// @dev The smart contract calls this function on the recipient
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
///transaction being reverted
/// Note: the contract address is always the message sender
/// @param operator The address which called `safeTransferFrom` function
/// @param from The address which previously owned the token
/// @param id An array containing the ids of the token being transferred
/// @param value An array containing the amount of tokens being transferred
/// @param data Additional data with no specified format
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
)
external
returns(bytes4);
/// @notice Handle the receipt of multiple ERC1155 token types
/// @dev The smart contract calls this function on the recipient
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted
/// Note: the contract address is always the message sender
/// @param operator The address which called `safeTransferFrom` function
/// @param from The address which previously owned the token
/// @param ids An array containing ids of each token being transferred
/// @param values An array containing amounts of each token being transferred
/// @param data Additional data with no specified format
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
returns(bytes4);
}

View File

@@ -0,0 +1,44 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
contract MNonFungibleToken {
/// @dev Returns true if token is non-fungible
function isNonFungible(uint256 id) public pure returns(bool);
/// @dev Returns true if token is fungible
function isFungible(uint256 _d) public pure returns(bool);
/// @dev Returns index of non-fungible token
function getNonFungibleIndex(uint256 id) public pure returns(uint256);
/// @dev Returns base type of non-fungible token
function getNonFungibleBaseType(uint256 id) public pure returns(uint256);
/// @dev Returns true if input is base-type of a non-fungible token
function isNonFungibleBaseType(uint256 id) public pure returns(bool);
/// @dev Returns true if input is a non-fungible token
function isNonFungibleItem(uint256 id) public pure returns(bool);
/// @dev returns owner of a non-fungible token
function ownerOf(uint256 id) public view returns (address);
}

View File

@@ -0,0 +1,126 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "../src/interfaces/IERC1155Receiver.sol";
contract DummyERC1155Receiver is
IERC1155Receiver
{
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
bool internal shouldRejectTransfer;
event TokenReceived(
address operator,
address from,
uint256 tokenId,
uint256 tokenValue,
bytes data
);
event BatchTokenReceived(
address operator,
address from,
uint256[] tokenIds,
uint256[] tokenValues,
bytes data
);
constructor () public {
shouldRejectTransfer = false;
}
/// @notice Handle the receipt of a single ERC1155 token type
/// @dev The smart contract calls this function on the recipient
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
///transaction being reverted
/// Note: the contract address is always the message sender
/// @param operator The address which called `safeTransferFrom` function
/// @param from The address which previously owned the token
/// @param id An array containing the ids of the token being transferred
/// @param value An array containing the amount of tokens being transferred
/// @param data Additional data with no specified format
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
)
external
returns(bytes4)
{
if (shouldRejectTransfer) {
revert("TRANSFER_REJECTED");
}
emit TokenReceived(
operator,
from,
id,
value,
data
);
return ERC1155_RECEIVED;
}
/// @notice Handle the receipt of multiple ERC1155 token types
/// @dev The smart contract calls this function on the recipient
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted
/// Note: the contract address is always the message sender
/// @param operator The address which called `safeTransferFrom` function
/// @param from The address which previously owned the token
/// @param ids An array containing ids of each token being transferred
/// @param values An array containing amounts of each token being transferred
/// @param data Additional data with no specified format
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
returns (bytes4)
{
if (shouldRejectTransfer) {
revert("TRANSFER_REJECTED");
}
emit BatchTokenReceived(
operator,
from,
ids,
values,
data
);
return ERC1155_BATCH_RECEIVED;
}
// @dev If set to true then all future transfers will be rejected.
function setRejectTransferFlag(bool _shouldRejectTransfer) external {
shouldRejectTransfer = _shouldRejectTransfer;
}
}

View File

@@ -0,0 +1,82 @@
{
"name": "@0x/contracts-erc1155",
"version": "1.0.0",
"engines": {
"node": ">=6.12"
},
"description": "Token contracts used by 0x protocol",
"main": "lib/src/index.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler:report:html": "istanbul report html && open coverage/index.html",
"coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test",
"contracts:gen": "contracts-gen",
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "generated-artifacts/@(DummyERC1155Receiver|ERC1155|ERC1155Mintable|IERC1155|IERC1155Mintable|IERC1155Receiver|MNonFungibleToken|MixinNonFungibleToken).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/0xProject/0x-monorepo/issues"
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^2.0.6",
"@0x/contracts-gen": "^1.0.5",
"@0x/contracts-test-utils": "^3.0.8",
"@0x/dev-utils": "^2.1.3",
"@0x/sol-compiler": "^3.1.3",
"@0x/tslint-config": "^3.0.0",
"@types/lodash": "4.14.104",
"@types/node": "*",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^4.1.0",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
"solhint": "^1.4.1",
"tslint": "5.11.0",
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.0.2",
"@0x/contracts-utils": "^2.0.8",
"@0x/types": "^2.1.1",
"@0x/typescript-typings": "^4.1.0",
"@0x/utils": "^4.2.2",
"@0x/web3-wrapper": "^6.0.2",
"ethereum-types": "^2.1.0",
"lodash": "^4.17.11"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,25 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as DummyERC1155Receiver from '../generated-artifacts/DummyERC1155Receiver.json';
import * as ERC1155 from '../generated-artifacts/ERC1155.json';
import * as ERC1155Mintable from '../generated-artifacts/ERC1155Mintable.json';
import * as IERC1155 from '../generated-artifacts/IERC1155.json';
import * as IERC1155Mintable from '../generated-artifacts/IERC1155Mintable.json';
import * as IERC1155Receiver from '../generated-artifacts/IERC1155Receiver.json';
import * as MixinNonFungibleToken from '../generated-artifacts/MixinNonFungibleToken.json';
import * as MNonFungibleToken from '../generated-artifacts/MNonFungibleToken.json';
export const artifacts = {
DummyERC1155Receiver: DummyERC1155Receiver as ContractArtifact,
ERC1155: ERC1155 as ContractArtifact,
MNonFungibleToken: MNonFungibleToken as ContractArtifact,
ERC1155Mintable: ERC1155Mintable as ContractArtifact,
MixinNonFungibleToken: MixinNonFungibleToken as ContractArtifact,
IERC1155Mintable: IERC1155Mintable as ContractArtifact,
IERC1155Receiver: IERC1155Receiver as ContractArtifact,
IERC1155: IERC1155 as ContractArtifact,
};

View File

@@ -0,0 +1,3 @@
export * from './wrappers';
export * from './artifacts';
export { Erc1155Wrapper } from '../test/utils/erc1155_wrapper';

View File

@@ -0,0 +1,13 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/dummy_erc1155_receiver';
export * from '../generated-wrappers/erc1155';
export * from '../generated-wrappers/erc1155_mintable';
export * from '../generated-wrappers/i_erc1155_mintable';
export * from '../generated-wrappers/i_erc1155_receiver';
export * from '../generated-wrappers/ierc1155';
export * from '../generated-wrappers/m_non_fungible_token';
export * from '../generated-wrappers/mixin_non_fungible_token';

View File

@@ -0,0 +1,492 @@
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import {
artifacts,
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
DummyERC1155ReceiverContract,
ERC1155MintableContract,
} from '../src';
import { Erc1155Wrapper } from './utils/erc1155_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('ERC1155Token', () => {
// constant values used in transfer tests
const nftOwnerBalance = new BigNumber(1);
const nftNotOwnerBalance = new BigNumber(0);
const spenderInitialFungibleBalance = new BigNumber(500);
const receiverInitialFungibleBalance = new BigNumber(0);
const fungibleValueToTransfer = spenderInitialFungibleBalance.div(2);
const nonFungibleValueToTransfer = nftOwnerBalance;
const receiverCallbackData = '0x01020304';
// tokens & addresses
let owner: string;
let spender: string;
let delegatedSpender: string;
let receiver: string;
let erc1155Contract: ERC1155MintableContract;
let erc1155Receiver: DummyERC1155ReceiverContract;
let nonFungibleToken: BigNumber;
let erc1155Wrapper: Erc1155Wrapper;
let fungibleToken: BigNumber;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// deploy erc1155 contract & receiver
const accounts = await web3Wrapper.getAvailableAddressesAsync();
[owner, spender, delegatedSpender] = accounts;
erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
artifacts.ERC1155Mintable,
provider,
txDefaults,
);
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
artifacts.DummyERC1155Receiver,
provider,
txDefaults,
);
receiver = erc1155Receiver.address;
// create wrapper & mint erc1155 tokens
erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, provider, owner);
fungibleToken = await erc1155Wrapper.mintFungibleTokensAsync([spender], spenderInitialFungibleBalance);
let nonFungibleTokens: BigNumber[];
[, nonFungibleTokens] = await erc1155Wrapper.mintNonFungibleTokensAsync([spender]);
nonFungibleToken = nonFungibleTokens[0];
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('safeTransferFrom', () => {
it('should transfer fungible token if called by token owner', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = fungibleToken;
const valueToTransfer = fungibleValueToTransfer;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
await erc1155Wrapper.safeTransferFromAsync(
spender,
receiver,
fungibleToken,
valueToTransfer,
receiverCallbackData,
);
// check balances after transfer
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(valueToTransfer),
receiverInitialFungibleBalance.plus(valueToTransfer),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should transfer non-fungible token if called by token owner', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = nonFungibleToken;
const valueToTransfer = nonFungibleValueToTransfer;
// check balances before transfer
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
await erc1155Wrapper.safeTransferFromAsync(
spender,
receiver,
tokenToTransfer,
valueToTransfer,
receiverCallbackData,
);
// check balances after transfer
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should trigger callback if transferring to a contract', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = fungibleToken;
const valueToTransfer = fungibleValueToTransfer;
// check balances before transfer
const expectedInitialBalances = [
spenderInitialFungibleBalance,
receiverInitialFungibleBalance,
nftOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
const tx = await erc1155Wrapper.safeTransferFromAsync(
spender,
receiver,
tokenToTransfer,
valueToTransfer,
receiverCallbackData,
);
expect(tx.logs.length).to.be.equal(2);
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
// check callback logs
const expectedCallbackLog = {
operator: spender,
from: spender,
tokenId: tokenToTransfer,
tokenValue: valueToTransfer,
data: receiverCallbackData,
};
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
expect(receiverLog.args.tokenId).to.be.bignumber.equal(expectedCallbackLog.tokenId);
expect(receiverLog.args.tokenValue).to.be.bignumber.equal(expectedCallbackLog.tokenValue);
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
// check balances after transfer
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(valueToTransfer),
receiverInitialFungibleBalance.plus(valueToTransfer),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should throw if transfer reverts', async () => {
// setup test parameters
const tokenToTransfer = fungibleToken;
const valueToTransfer = spenderInitialFungibleBalance.plus(1);
// execute transfer
await expectTransactionFailedAsync(
erc1155Contract.safeTransferFrom.sendTransactionAsync(
spender,
receiver,
tokenToTransfer,
valueToTransfer,
receiverCallbackData,
{ from: spender },
),
RevertReason.Uint256Underflow,
);
});
it('should throw if callback reverts', async () => {
// setup test parameters
const tokenToTransfer = fungibleToken;
const valueToTransfer = fungibleValueToTransfer;
// set receiver to reject balances
const shouldRejectTransfer = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// execute transfer
await expectTransactionFailedAsync(
erc1155Contract.safeTransferFrom.sendTransactionAsync(
spender,
receiver,
tokenToTransfer,
valueToTransfer,
receiverCallbackData,
{ from: spender },
),
RevertReason.TransferRejected,
);
});
});
describe('batchSafeTransferFrom', () => {
it('should transfer fungible tokens if called by token owner', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [fungibleValueToTransfer];
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155Wrapper.safeBatchTransferFromAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
// check balances after transfer
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer non-fungible token if called by token owner', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = [nonFungibleToken];
const valuesToTransfer = [nonFungibleValueToTransfer];
// check balances before transfer
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155Wrapper.safeBatchTransferFromAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
// check balances after transfer
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer mix of fungible / non-fungible tokens if called by token owner', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = [fungibleToken, nonFungibleToken];
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
nftOwnerBalance,
// receiver
receiverInitialFungibleBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155Wrapper.safeBatchTransferFromAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
// check balances after transfer
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
nftNotOwnerBalance,
// receiver
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should trigger callback if transferring to a contract', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = [fungibleToken, nonFungibleToken];
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
nftOwnerBalance,
// receiver
receiverInitialFungibleBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const tx = await erc1155Wrapper.safeBatchTransferFromAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
expect(tx.logs.length).to.be.equal(2);
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
// check callback logs
const expectedCallbackLog = {
operator: spender,
from: spender,
tokenIds: tokensToTransfer,
tokenValues: valuesToTransfer,
data: receiverCallbackData,
};
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
expect(receiverLog.args.tokenIds.length).to.be.equal(2);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[0]);
expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[1]);
expect(receiverLog.args.tokenValues.length).to.be.equal(2);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[0]);
expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[1]);
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
// check balances after transfer
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
nftNotOwnerBalance,
// receiver
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should throw if transfer reverts', async () => {
// setup test parameters
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)];
// execute transfer
await expectTransactionFailedAsync(
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
{ from: spender },
),
RevertReason.Uint256Underflow,
);
});
it('should throw if callback reverts', async () => {
// setup test parameters
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [fungibleValueToTransfer];
// set receiver to reject balances
const shouldRejectTransfer = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// execute transfer
await expectTransactionFailedAsync(
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
{ from: spender },
),
RevertReason.TransferRejected,
);
});
});
describe('setApprovalForAll', () => {
it('should transfer token via safeTransferFrom if called by approved account', async () => {
// set approval
const isApprovedForAll = true;
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
expect(isApprovedForAllCheck).to.be.true();
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = fungibleToken;
const valueToTransfer = fungibleValueToTransfer;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
await erc1155Wrapper.safeTransferFromAsync(
spender,
receiver,
tokenToTransfer,
valueToTransfer,
receiverCallbackData,
delegatedSpender,
);
// check balances after transfer
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(valueToTransfer),
receiverInitialFungibleBalance.plus(valueToTransfer),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should throw if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => {
// check approval not set
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
expect(isApprovedForAllCheck).to.be.false();
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = fungibleToken;
const valueToTransfer = fungibleValueToTransfer;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155Contract.safeTransferFrom.sendTransactionAsync(
spender,
receiver,
tokenToTransfer,
valueToTransfer,
receiverCallbackData,
{ from: delegatedSpender },
),
RevertReason.InsufficientAllowance,
);
});
it('should transfer token via safeBatchTransferFrom if called by approved account', async () => {
// set approval
const isApprovedForAll = true;
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
expect(isApprovedForAllCheck).to.be.true();
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [fungibleValueToTransfer];
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155Wrapper.safeBatchTransferFromAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
delegatedSpender,
);
// check balances after transfer
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should throw if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => {
// check approval not set
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
expect(isApprovedForAllCheck).to.be.false();
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [fungibleValueToTransfer];
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
spender,
receiver,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
{ from: delegatedSpender },
),
RevertReason.InsufficientAllowance,
);
});
});
});
// tslint:enable:no-unnecessary-type-assertion

View File

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

View File

@@ -0,0 +1,159 @@
import { constants, LogDecoder } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts, ERC1155MintableContract, ERC1155TransferSingleEventArgs } from '../../src';
const expect = chai.expect;
export class Erc1155Wrapper {
private readonly _erc1155Contract: ERC1155MintableContract;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _contractOwner: string;
private readonly _logDecoder: LogDecoder;
constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) {
this._erc1155Contract = contractInstance;
this._web3Wrapper = new Web3Wrapper(provider);
this._contractOwner = contractOwner;
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
}
public getContract(): ERC1155MintableContract {
return this._erc1155Contract;
}
public async getBalancesAsync(owners: string[], tokens: BigNumber[]): Promise<BigNumber[]> {
const balances = await this._erc1155Contract.balanceOfBatch.callAsync(owners, tokens);
return balances;
}
public async safeTransferFromAsync(
from: string,
to: string,
token: BigNumber,
value: BigNumber,
callbackData?: string,
delegatedSpender?: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const spender = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
const callbackDataHex = _.isUndefined(callbackData) ? '0x' : callbackData;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.safeTransferFrom.sendTransactionAsync(from, to, token, value, callbackDataHex, {
from: spender,
}),
);
return tx;
}
public async safeBatchTransferFromAsync(
from: string,
to: string,
tokens: BigNumber[],
values: BigNumber[],
callbackData?: string,
delegatedSpender?: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const spender = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
const callbackDataHex = _.isUndefined(callbackData) ? '0x' : callbackData;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
from,
to,
tokens,
values,
callbackDataHex,
{ from: spender },
),
);
return tx;
}
public async mintFungibleTokensAsync(
beneficiaries: string[],
tokenAmounts: BigNumber | BigNumber[],
): Promise<BigNumber> {
const tokenUri = 'dummyFungibleToken';
const tokenIsNonFungible = false;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, {
from: this._contractOwner,
}),
);
// tslint:disable-next-line no-unnecessary-type-assertion
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
const token = createFungibleTokenLog.args.id;
const tokenAmountsAsArray = _.isArray(tokenAmounts) ? tokenAmounts : [];
if (!_.isArray(tokenAmounts)) {
_.each(_.range(0, beneficiaries.length), () => {
tokenAmountsAsArray.push(tokenAmounts);
});
}
await this._web3Wrapper.awaitTransactionSuccessAsync(
await this._erc1155Contract.mintFungible.sendTransactionAsync(token, beneficiaries, tokenAmountsAsArray, {
from: this._contractOwner,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
return token;
}
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
const tokenUri = 'dummyNonFungibleToken';
const tokenIsNonFungible = true;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, {
from: this._contractOwner,
}),
);
// tslint:disable-next-line no-unnecessary-type-assertion
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
const token = createFungibleTokenLog.args.id;
await this._web3Wrapper.awaitTransactionSuccessAsync(
await this._erc1155Contract.mintNonFungible.sendTransactionAsync(token, beneficiaries, {
from: this._contractOwner,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const encodedNftIds: BigNumber[] = [];
const nftIdBegin = 1;
const nftIdEnd = beneficiaries.length + 1;
const nftIdRange = _.range(nftIdBegin, nftIdEnd);
_.each(nftIdRange, (nftId: number) => {
const encodedNftId = token.plus(nftId);
encodedNftIds.push(encodedNftId);
});
return [token, encodedNftIds];
}
public async setApprovalForAllAsync(
owner: string,
beneficiary: string,
isApproved: boolean,
): Promise<TransactionReceiptWithDecodedLogs> {
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.setApprovalForAll.sendTransactionAsync(beneficiary, isApproved, {
from: owner,
}),
);
return tx;
}
public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise<boolean> {
const isApprovedForAll = await this._erc1155Contract.isApprovedForAll.callAsync(owner, beneficiary);
return isApprovedForAll;
}
public async assertBalancesAsync(
owners: string[],
tokens: BigNumber[],
expectedBalances: BigNumber[],
): Promise<void> {
const ownersExtended: string[] = [];
let tokensExtended: BigNumber[] = [];
_.each(owners, (owner: string) => {
tokensExtended = tokensExtended.concat(tokens);
_.each(_.range(0, tokens.length), () => {
ownersExtended.push(owner);
});
});
const balances = await this.getBalancesAsync(ownersExtended, tokensExtended);
_.each(balances, (balance: BigNumber, i: number) => {
expect(balance, `${ownersExtended[i]}${tokensExtended[i]}`).to.be.bignumber.equal(expectedBalances[i]);
});
}
}

View File

@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/DummyERC1155Receiver.json",
"generated-artifacts/ERC1155.json",
"generated-artifacts/ERC1155Mintable.json",
"generated-artifacts/IERC1155.json",
"generated-artifacts/IERC1155Mintable.json",
"generated-artifacts/IERC1155Receiver.json",
"generated-artifacts/MNonFungibleToken.json",
"generated-artifacts/MixinNonFungibleToken.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

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

View File

@@ -1,10 +1,14 @@
[
{
"version": "1.0.10",
"version": "2.0.0",
"changes": [
{
"note": "Set evmVersion to byzantium",
"pr": 1678
},
{
"note": "Upgrade contracts to Solidity 0.5.5",
"pr": 1682
}
]
},

View File

@@ -5,7 +5,11 @@
"isOfflineMode": false,
"compilerSettings": {
"evmVersion": "byzantium",
"optimizer": { "enabled": true, "runs": 1000000 },
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
@@ -28,7 +32,6 @@
"src/interfaces/IEtherToken.sol",
"test/DummyERC20Token.sol",
"test/DummyMultipleReturnERC20Token.sol",
"test/DummyNoReturnERC20Token.sol",
"test/ReentrantERC20Token.sol"
"test/DummyNoReturnERC20Token.sol"
]
}

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "./interfaces/IERC20Token.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "./UnlimitedAllowanceERC20Token.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "./ERC20Token.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
contract IERC20Token {

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "./IERC20Token.sol";

View File

@@ -16,7 +16,7 @@
*/
pragma solidity 0.4.24;
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "../src/MintableERC20Token.sol";
@@ -32,8 +32,8 @@ contract DummyERC20Token is
uint256 public constant MAX_MINT_AMOUNT = 10000000000000000000000;
constructor (
string _name,
string _symbol,
string memory _name,
string memory _symbol,
uint256 _decimals,
uint256 _totalSupply
)

View File

@@ -16,7 +16,7 @@
*/
pragma solidity 0.4.24;
pragma solidity ^0.5.5;
import "./DummyERC20Token.sol";
@@ -26,8 +26,8 @@ contract DummyMultipleReturnERC20Token is
DummyERC20Token
{
constructor (
string _name,
string _symbol,
string memory _name,
string memory _symbol,
uint256 _decimals,
uint256 _totalSupply
)

View File

@@ -16,7 +16,7 @@
*/
pragma solidity 0.4.24;
pragma solidity ^0.5.5;
import "./DummyERC20Token.sol";
@@ -26,8 +26,8 @@ contract DummyNoReturnERC20Token is
DummyERC20Token
{
constructor (
string _name,
string _symbol,
string memory _name,
string memory _symbol,
uint256 _decimals,
uint256 _totalSupply
)

View File

@@ -33,7 +33,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IEtherToken|MintableERC20Token|ReentrantERC20Token|UnlimitedAllowanceERC20Token|WETH9|ZRXToken).json",
"abis": "./generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IEtherToken|MintableERC20Token|UnlimitedAllowanceERC20Token|WETH9|ZRXToken).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -68,8 +68,8 @@
},
"dependencies": {
"@0x/base-contract": "^5.0.2",
"@0x/contracts-exchange-libs": "1.0.2",
"@0x/contracts-utils": "2.0.1",
"@0x/contracts-exchange-libs": "^1.1.3",
"@0x/contracts-utils": "^2.0.8",
"@0x/types": "^2.1.1",
"@0x/typescript-typings": "^4.1.0",
"@0x/utils": "^4.2.2",

View File

@@ -12,7 +12,6 @@ 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 MintableERC20Token from '../generated-artifacts/MintableERC20Token.json';
import * as ReentrantERC20Token from '../generated-artifacts/ReentrantERC20Token.json';
import * as UnlimitedAllowanceERC20Token from '../generated-artifacts/UnlimitedAllowanceERC20Token.json';
import * as WETH9 from '../generated-artifacts/WETH9.json';
import * as ZRXToken from '../generated-artifacts/ZRXToken.json';
@@ -27,5 +26,4 @@ export const artifacts = {
DummyERC20Token: DummyERC20Token as ContractArtifact,
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
ReentrantERC20Token: ReentrantERC20Token as ContractArtifact,
};

View File

@@ -10,7 +10,6 @@ export * from '../generated-wrappers/erc20_token';
export * from '../generated-wrappers/i_erc20_token';
export * from '../generated-wrappers/i_ether_token';
export * from '../generated-wrappers/mintable_erc20_token';
export * from '../generated-wrappers/reentrant_erc20_token';
export * from '../generated-wrappers/unlimited_allowance_erc20_token';
export * from '../generated-wrappers/weth9';
export * from '../generated-wrappers/zrx_token';

View File

@@ -10,7 +10,6 @@
"generated-artifacts/IERC20Token.json",
"generated-artifacts/IEtherToken.json",
"generated-artifacts/MintableERC20Token.json",
"generated-artifacts/ReentrantERC20Token.json",
"generated-artifacts/UnlimitedAllowanceERC20Token.json",
"generated-artifacts/WETH9.json",
"generated-artifacts/ZRXToken.json"

View File

@@ -1,10 +1,14 @@
[
{
"version": "1.0.10",
"version": "2.0.0",
"changes": [
{
"note": "Set evmVersion to byzantium",
"pr": 1678
},
{
"note": "Upgrade contracts to Solidity 0.5.5",
"pr": 1682
}
]
},

View File

@@ -5,7 +5,11 @@
"isOfflineMode": false,
"compilerSettings": {
"evmVersion": "byzantium",
"optimizer": { "enabled": true, "runs": 1000000 },
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "./interfaces/IERC721Token.sol";
import "./interfaces/IERC721Receiver.sol";
@@ -59,7 +59,7 @@ contract ERC721Token is
address _from,
address _to,
uint256 _tokenId,
bytes _data
bytes calldata _data
)
external
{

View File

@@ -16,7 +16,7 @@
*/
pragma solidity ^0.4.24;
pragma solidity ^0.5.5;
import "./ERC721Token.sol";

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