Basic ERC1155 Implementation. Derived from reference implementation, with bug fixes.

This commit is contained in:
Greg Hysen 2019-02-27 09:55:43 -08:00
parent 22af796302
commit 7c850cc082
22 changed files with 1249 additions and 0 deletions

2
.gitignore vendored
View File

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

View File

@ -0,0 +1,3 @@
contracts/tokens/ZRXToken/ERC20Token_v1.sol
contracts/tokens/ZRXToken/Token_v1.sol
contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol

View File

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

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,28 @@
{
"artifactsDir": "generated-artifacts",
"contractsDir": "contracts",
"useDockerisedSolc": true,
"compilerSettings": {
"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/interfaces/IERC1155.sol",
"src/interfaces/IERC1155Receiver.sol",
"test/DummyERC1155Receiver.sol",
"test/DummyERC1155Token.sol",
"test/InvalidERC1155Receiver.sol"
]
}

View File

@ -0,0 +1,161 @@
pragma solidity ^0.5.3;
import "./lib/SafeMath.sol";
import "./lib/Address.sol";
import "./interfaces/IERC1155.sol";
import "./interfaces/IERC1155Receiver.sol";
// A sample implementation of core ERC1155 function.
contract ERC1155 is
IERC1155,
SafeMath
{
using Address for address;
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;
// Use a split bit implementation.
// Store the type in the upper 128 bits..
uint256 constant TYPE_MASK = uint256(uint128(~0)) << 128;
// ..and the non-fungible index in the lower 128
uint256 constant NF_INDEX_MASK = uint128(~0);
// The top bit is a flag to tell if this is a NFI.
uint256 constant TYPE_NF_BIT = 1 << 255;
mapping (uint256 => address) nfOwners;
// Only to make code clearer. Should not be functions
function isNonFungible(uint256 _id) public pure returns(bool) {
return _id & TYPE_NF_BIT == TYPE_NF_BIT;
}
function isFungible(uint256 _id) public pure returns(bool) {
return _id & TYPE_NF_BIT == 0;
}
function getNonFungibleIndex(uint256 _id) public pure returns(uint256) {
return _id & NF_INDEX_MASK;
}
function getNonFungibleBaseType(uint256 _id) public pure returns(uint256) {
return _id & TYPE_MASK;
}
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);
}
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);
}
function ownerOf(uint256 _id) public view returns (address) {
return nfOwners[_id];
}
// overide
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external {
require(_to != address(0x0), "cannot send to zero address");
require(_from == msg.sender || operatorApproval[_from][msg.sender] == true, "Need operator approval for 3rd party transfers.");
if (isNonFungible(_id)) {
require(nfOwners[_id] == _from);
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.isContract()) {
require(IERC1155Receiver(_to).onERC1155Received(msg.sender, _from, _id, _value, _data) == ERC1155_RECEIVED);
}
}
// overide
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external {
require(_to != address(0x0), "cannot send to zero address");
require(_ids.length == _values.length, "Array length must match");
// 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, "Need operator approval for 3rd party 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(nfOwners[id] == _from);
nfOwners[id] = _to;
} else {
balances[id][_from] = safeSub(balances[id][_from], value);
balances[id][_to] = safeAdd(value, balances[id][_to]);
}
}
emit TransferBatch(msg.sender, _from, _to, _ids, _values);
if (_to.isContract()) {
require(IERC1155Receiver(_to).onERC1155BatchReceived(msg.sender, _from, _ids, _values, _data) == ERC1155_BATCH_RECEIVED);
}
}
function balanceOf(address _owner, uint256 _id) external view returns (uint256) {
if (isNonFungibleItem(_id))
return nfOwners[_id] == _owner ? 1 : 0;
return balances[_id][_owner];
}
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory) {
require(_owners.length == _ids.length);
uint256[] memory 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_;
}
/**
@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 safeAdd 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];
}
}

View File

@ -0,0 +1,98 @@
pragma solidity ^0.5.3;
import "./lib/SafeMath.sol";
import "./ERC1155.sol";
/**
@dev Mintable form of ERC1155
Shows how easy it is to mint new items
*/
contract ERC1155Mintable is
ERC1155
{
uint256 nonce;
mapping (uint256 => address) public creators;
mapping (uint256 => uint256) public maxIndex;
modifier creatorOnly(uint256 _id) {
require(creators[_id] == msg.sender);
_;
}
// This function only creates the type.
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);
}
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));
// Index are 1-based.
uint256 index = maxIndex[_type] + 1;
for (uint256 i = 0; i < _to.length; ++i) {
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 (dst.isContract()) {
require(IERC1155Receiver(dst).onERC1155Received(msg.sender, msg.sender, id, 1, '') == ERC1155_RECEIVED);
}
}
maxIndex[_type] = safeAdd(_to.length, maxIndex[_type]);
}
function mintFungible(uint256 _id, address[] calldata _to, uint256[] calldata _quantities) external creatorOnly(_id) {
require(isFungible(_id));
for (uint256 i = 0; i < _to.length; ++i) {
address to = _to[i];
uint256 quantity = _quantities[i];
// Grant the items to the caller
balances[_id][to] = safeAdd(quantity, balances[_id][to]);
// 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), to, _id, quantity);
if (to.isContract()) {
require(IERC1155Receiver(to).onERC1155Received(msg.sender, msg.sender, _id, quantity, '') == ERC1155_RECEIVED);
}
}
}
}

View File

@ -0,0 +1,124 @@
/*
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.3;
/**
@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 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);
/**
@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);
}

View File

@ -0,0 +1,55 @@
/*
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.3;
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,75 @@
/*
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.3;
import "../src/interfaces/IERC1155Receiver.sol";
contract DummyERC1155Receiver is
IERC1155Receiver
{
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
constructor () public {}
event TokenReceived(
address operator,
address from,
uint256 tokenId,
uint256 tokenValue,
bytes data
);
event BatchTokenReceived(
address operator,
address from,
uint256[] tokenIds,
uint256[] tokenValues,
bytes data
);
function onERC1155Received(
address _operator,
address _from,
uint256 _id,
uint256 _value,
bytes calldata _data
)
external
returns (bytes4)
{
emit TokenReceived(_operator, _from, _id, _value, _data);
return ERC1155_RECEIVED;
}
function onERC1155BatchReceived(
address _operator,
address _from,
uint256[] calldata _ids,
uint256[] calldata _values,
bytes calldata _data
)
external
returns (bytes4)
{
emit BatchTokenReceived(_operator, _from, _ids, _values, _data);
return ERC1155_BATCH_RECEIVED;
}
}

View File

@ -0,0 +1,40 @@
/*
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.3;
import "../src/ERC1155Mintable.sol";
// solhint-disable no-empty-blocks
contract DummyERC1155Token is
ERC1155Mintable
{
string public name;
string public symbol;
constructor (
string memory _name,
string memory _symbol
)
public
{
name = _name;
symbol = _symbol;
}
}

View File

@ -0,0 +1,68 @@
/*
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.3;
contract InvalidERC1155Receiver //is
//IERC1155Receiver
{
/*
// Actual function signature is `onERC1155Received(address,address,uint256,bytes)`
bytes4 constant internal INVALID_ERC1155_RECEIVED = bytes4(keccak256("onERC1155Received(address,uint256,bytes)"));
event TokenReceived(
address operator,
address from,
uint256 tokenId,
bytes data
);
/// @notice Handle the receipt of an NFT
/// @dev The ERC1155 smart contract calls this function on the recipient
/// after a `transfer`. 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 _tokenId The NFT identifier which is being transferred
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,bytes)"))`
/// unless throwing
function onERC1155Received(
address _operator,
address _from,
uint256 _tokenId,
bytes _data
)
external
returns (bytes4)
{
emit TokenReceived(
_operator,
_from,
_tokenId,
_data
);
return INVALID_ERC1155_RECEIVED;
}
*/
}

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|DummyERC1155Token|ERC1155|ERC1155Mintable|IERC1155|IERC1155Receiver|InvalidERC1155Receiver).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.4",
"@0x/contracts-gen": "^1.0.3",
"@0x/contracts-test-utils": "^3.0.5",
"@0x/dev-utils": "^2.1.1",
"@0x/sol-compiler": "^3.1.0",
"@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.0",
"@0x/contracts-utils": "2.0.1",
"@0x/types": "^2.1.0",
"@0x/typescript-typings": "^4.1.0",
"@0x/utils": "^4.2.0",
"@0x/web3-wrapper": "^6.0.0",
"ethereum-types": "^2.1.0",
"lodash": "^4.17.11"
},
"publishConfig": {
"access": "public"
}
}

View File

@ -0,0 +1,23 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as DummyERC1155Receiver from '../generated-artifacts/DummyERC1155Receiver.json';
import * as DummyERC1155Token from '../generated-artifacts/DummyERC1155Token.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 IERC1155Receiver from '../generated-artifacts/IERC1155Receiver.json';
import * as InvalidERC1155Receiver from '../generated-artifacts/InvalidERC1155Receiver.json';
export const artifacts = {
ERC1155: ERC1155 as ContractArtifact,
ERC1155Mintable: ERC1155Mintable as ContractArtifact,
IERC1155: IERC1155 as ContractArtifact,
IERC1155Receiver: IERC1155Receiver as ContractArtifact,
DummyERC1155Receiver: DummyERC1155Receiver as ContractArtifact,
DummyERC1155Token: DummyERC1155Token as ContractArtifact,
InvalidERC1155Receiver: InvalidERC1155Receiver as ContractArtifact,
};

View File

@ -0,0 +1,2 @@
export * from './wrappers';
export * from './artifacts';

View File

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

View File

@ -0,0 +1,347 @@
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
expectTransactionFailedWithoutReasonAsync,
LogDecoder,
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,
DummyERC1155ReceiverContract,
DummyERC1155ReceiverTokenReceivedEventArgs,
DummyERC1155TokenContract,
//DummyERC1155TokenTransferEventArgs,
InvalidERC1155ReceiverContract,
ERC1155TransferSingleEventArgs,
DummyERC1155ReceiverBatchTokenReceivedEventArgs
} from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('ERC1155Token', () => {
const DUMMY_FUNGIBLE_TOKEN_URI = 'FUN';
const DUMMY_FUNGIBLE_TOKEN_IS_FUNGIBLE = false;
const DUMMY_NONFUNGIBLE_TOKEN_URI = 'NOFUN';
const DUMMY_NONFUNGIBLE_TOKEN_IS_FUNGIBLE = true;
let owner: string;
let spender: string;
const spenderInitialBalance = new BigNumber(500);
const receiverInitialBalance = new BigNumber(0);
let token: DummyERC1155TokenContract;
let erc1155Receiver: DummyERC1155ReceiverContract;
let logDecoder: LogDecoder;
const tokenId = new BigNumber(1);
let dummyNft: BigNumber;
const nftOwnerBalance = new BigNumber(1);
const nftNotOwnerBalance = new BigNumber(0);
let dummyFungibleTokenType: BigNumber;
let dummyNonFungibleTokenType: BigNumber;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
spender = accounts[1];
token = await DummyERC1155TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC1155Token,
provider,
txDefaults,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
);
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
artifacts.DummyERC1155Receiver,
provider,
txDefaults,
);
logDecoder = new LogDecoder(web3Wrapper, artifacts);
// Create & mint fungible token
let txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
await token.create.sendTransactionAsync(
DUMMY_FUNGIBLE_TOKEN_URI,
DUMMY_FUNGIBLE_TOKEN_IS_FUNGIBLE
)
);
const createFungibleTokenLog = txReceipt.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
dummyFungibleTokenType = createFungibleTokenLog.args._id;
await web3Wrapper.awaitTransactionSuccessAsync(
await token.mintFungible.sendTransactionAsync(
dummyFungibleTokenType,
[spender],
[spenderInitialBalance],
{ from: owner }
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Create & mint non-fungible token
txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
await token.create.sendTransactionAsync(
DUMMY_NONFUNGIBLE_TOKEN_URI,
DUMMY_NONFUNGIBLE_TOKEN_IS_FUNGIBLE
)
);
const createNonFungibleTokenLog = txReceipt.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
dummyNonFungibleTokenType = createNonFungibleTokenLog.args._id;
await web3Wrapper.awaitTransactionSuccessAsync(
await token.mintNonFungible.sendTransactionAsync(
dummyNonFungibleTokenType,
[spender],
{ from: owner }
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
dummyNft = dummyNonFungibleTokenType.plus(1);
});
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 from = spender;
const to = erc1155Receiver.address;
const fromIdx = 0;
const toIdx = 1;
const participatingOwners = [from, to];
const participatingTokens = [dummyFungibleTokenType, dummyFungibleTokenType];
const tokenTypesToTransfer = [dummyFungibleTokenType];
const valueToTransfer = new BigNumber(200);
const valuesToTransfer = [valueToTransfer];
const callbackData = constants.NULL_BYTES;
// check balances before transfer
const balancesBeforeTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesBeforeTransfer[fromIdx]).to.be.bignumber.equal(spenderInitialBalance);
expect(balancesBeforeTransfer[toIdx]).to.be.bignumber.equal(receiverInitialBalance);
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await token.safeTransferFrom.sendTransactionAsync(from, to, tokenTypesToTransfer[0], valuesToTransfer[0], callbackData, {from}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances after transfer
const balancesAfterTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesAfterTransfer[fromIdx]).to.be.bignumber.equal(balancesBeforeTransfer[fromIdx].minus(valueToTransfer));
expect(balancesAfterTransfer[toIdx]).to.be.bignumber.equal(balancesBeforeTransfer[toIdx].plus(valueToTransfer));
});
it('should transfer non-fungible token if called by token owner', async () => {
// setup test parameters
const from = spender;
const to = erc1155Receiver.address;
const fromIdx = 0;
const toIdx = 1;
const participatingOwners = [from, to];
const participatingTokens = [dummyNft, dummyNft];
const tokenTypesToTransfer = [dummyNft];
const valueToTransfer = new BigNumber(1);
const valuesToTransfer = [valueToTransfer];
const callbackData = constants.NULL_BYTES;
// check balances before transfer
const balancesBeforeTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesBeforeTransfer[fromIdx]).to.be.bignumber.equal(nftOwnerBalance);
expect(balancesBeforeTransfer[toIdx]).to.be.bignumber.equal(nftNotOwnerBalance);
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await token.safeTransferFrom.sendTransactionAsync(from, to, tokenTypesToTransfer[0], valuesToTransfer[0], callbackData, {from}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances after transfer
const balancesAfterTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesAfterTransfer[fromIdx]).to.be.bignumber.equal(nftNotOwnerBalance);
expect(balancesAfterTransfer[toIdx]).to.be.bignumber.equal(nftOwnerBalance);
});
it('should trigger callback if transferring to a contract', async () => {
// setup test parameters
const from = spender;
const to = erc1155Receiver.address;
const fromIdxFungible = 0;
const toIdxFungible = 1;
const fromIdxNonFungible = 2;
const toIdxNonFungible = 3;
const participatingOwners = [from, to, from, to];
const participatingTokens = [dummyFungibleTokenType, dummyFungibleTokenType, dummyNft, dummyNft];
const tokenTypesToTransfer = [dummyFungibleTokenType, dummyNft];
const fungibleValueToTransfer = new BigNumber(200);
const nonFungibleValueToTransfer = new BigNumber(1);
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
const callbackData = '0x01020304';
// check balances before transfer
const balancesBeforeTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesBeforeTransfer[fromIdxFungible]).to.be.bignumber.equal(spenderInitialBalance);
expect(balancesBeforeTransfer[toIdxFungible]).to.be.bignumber.equal(receiverInitialBalance);
expect(balancesBeforeTransfer[fromIdxNonFungible]).to.be.bignumber.equal(nftOwnerBalance);
expect(balancesBeforeTransfer[toIdxNonFungible]).to.be.bignumber.equal(nftNotOwnerBalance);
// execute transfer
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
await token.safeTransferFrom.sendTransactionAsync(from, to, tokenTypesToTransfer[0], valuesToTransfer[0], callbackData, {from})
);
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverTokenReceivedEventArgs>;
// check callback logs
expect(receiverLog.args.operator).to.be.equal(from);
expect(receiverLog.args.from).to.be.equal(from);
expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenTypesToTransfer[0]);
expect(receiverLog.args.tokenValue).to.be.bignumber.equal(valuesToTransfer[0]);
expect(receiverLog.args.data).to.be.deep.equal(callbackData);
// check balances after transfer
const balancesAfterTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesAfterTransfer[fromIdxFungible]).to.be.bignumber.equal(balancesBeforeTransfer[fromIdxFungible].minus(fungibleValueToTransfer));
expect(balancesAfterTransfer[toIdxFungible]).to.be.bignumber.equal(balancesBeforeTransfer[toIdxFungible].plus(fungibleValueToTransfer));
});
});
describe('batchSafeTransferFrom', () => {
it('should transfer fungible tokens if called by token owner', async () => {
// setup test parameters
const from = spender;
const to = erc1155Receiver.address;
const fromIdx = 0;
const toIdx = 1;
const participatingOwners = [from, to];
const participatingTokens = [dummyFungibleTokenType, dummyFungibleTokenType];
const tokenTypesToTransfer = [dummyFungibleTokenType];
const valueToTransfer = new BigNumber(200);
const valuesToTransfer = [valueToTransfer];
const callbackData = constants.NULL_BYTES;
// check balances before transfer
const balancesBeforeTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesBeforeTransfer[fromIdx]).to.be.bignumber.equal(spenderInitialBalance);
expect(balancesBeforeTransfer[toIdx]).to.be.bignumber.equal(receiverInitialBalance);
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await token.safeBatchTransferFrom.sendTransactionAsync(from, to, tokenTypesToTransfer, valuesToTransfer, callbackData, {from}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances after transfer
const balancesAfterTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesAfterTransfer[fromIdx]).to.be.bignumber.equal(balancesBeforeTransfer[fromIdx].minus(valueToTransfer));
expect(balancesAfterTransfer[toIdx]).to.be.bignumber.equal(balancesBeforeTransfer[toIdx].plus(valueToTransfer));
});
it('should transfer non-fungible token if called by token owner', async () => {
// setup test parameters
const from = spender;
const to = erc1155Receiver.address;
const fromIdx = 0;
const toIdx = 1;
const participatingOwners = [from, to];
const participatingTokens = [dummyNft, dummyNft];
const tokenTypesToTransfer = [dummyNft];
const valueToTransfer = new BigNumber(1);
const valuesToTransfer = [valueToTransfer];
const callbackData = constants.NULL_BYTES;
// check balances before transfer
const balancesBeforeTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesBeforeTransfer[fromIdx]).to.be.bignumber.equal(nftOwnerBalance);
expect(balancesBeforeTransfer[toIdx]).to.be.bignumber.equal(nftNotOwnerBalance);
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await token.safeBatchTransferFrom.sendTransactionAsync(from, to, tokenTypesToTransfer, valuesToTransfer, callbackData, {from}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances after transfer
const balancesAfterTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesAfterTransfer[fromIdx]).to.be.bignumber.equal(nftNotOwnerBalance);
expect(balancesAfterTransfer[toIdx]).to.be.bignumber.equal(nftOwnerBalance);
});
it('should transfer mix of fungible / non-fungible tokens if called by token owner', async () => {
// setup test parameters
const from = spender;
const to = erc1155Receiver.address;
const fromIdxFungible = 0;
const toIdxFungible = 1;
const fromIdxNonFungible = 2;
const toIdxNonFungible = 3;
const participatingOwners = [from, to, from, to];
const participatingTokens = [dummyFungibleTokenType, dummyFungibleTokenType, dummyNft, dummyNft];
const tokenTypesToTransfer = [dummyFungibleTokenType, dummyNft];
const fungibleValueToTransfer = new BigNumber(200);
const nonFungibleValueToTransfer = new BigNumber(1);
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
const callbackData = constants.NULL_BYTES;
// check balances before transfer
const balancesBeforeTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesBeforeTransfer[fromIdxFungible]).to.be.bignumber.equal(spenderInitialBalance);
expect(balancesBeforeTransfer[toIdxFungible]).to.be.bignumber.equal(receiverInitialBalance);
expect(balancesBeforeTransfer[fromIdxNonFungible]).to.be.bignumber.equal(nftOwnerBalance);
expect(balancesBeforeTransfer[toIdxNonFungible]).to.be.bignumber.equal(nftNotOwnerBalance);
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await token.safeBatchTransferFrom.sendTransactionAsync(from, to, tokenTypesToTransfer, valuesToTransfer, callbackData, {from}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances after transfer
const balancesAfterTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesAfterTransfer[fromIdxFungible]).to.be.bignumber.equal(balancesBeforeTransfer[fromIdxFungible].minus(fungibleValueToTransfer));
expect(balancesAfterTransfer[toIdxFungible]).to.be.bignumber.equal(balancesBeforeTransfer[toIdxFungible].plus(fungibleValueToTransfer));
expect(balancesAfterTransfer[fromIdxNonFungible]).to.be.bignumber.equal(nftNotOwnerBalance);
expect(balancesAfterTransfer[toIdxNonFungible]).to.be.bignumber.equal(nftOwnerBalance);
});
it('should trigger callback if transferring to a contract', async () => {
// setup test parameters
const from = spender;
const to = erc1155Receiver.address;
const fromIdxFungible = 0;
const toIdxFungible = 1;
const fromIdxNonFungible = 2;
const toIdxNonFungible = 3;
const participatingOwners = [from, to, from, to];
const participatingTokens = [dummyFungibleTokenType, dummyFungibleTokenType, dummyNft, dummyNft];
const tokenTypesToTransfer = [dummyFungibleTokenType, dummyNft];
const fungibleValueToTransfer = new BigNumber(200);
const nonFungibleValueToTransfer = new BigNumber(1);
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
const callbackData = '0x01020304';
// check balances before transfer
const balancesBeforeTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesBeforeTransfer[fromIdxFungible]).to.be.bignumber.equal(spenderInitialBalance);
expect(balancesBeforeTransfer[toIdxFungible]).to.be.bignumber.equal(receiverInitialBalance);
expect(balancesBeforeTransfer[fromIdxNonFungible]).to.be.bignumber.equal(nftOwnerBalance);
expect(balancesBeforeTransfer[toIdxNonFungible]).to.be.bignumber.equal(nftNotOwnerBalance);
// execute transfer
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
await token.safeBatchTransferFrom.sendTransactionAsync(from, to, tokenTypesToTransfer, valuesToTransfer, callbackData, {from})
);
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
// check callback logs
expect(receiverLog.args.operator).to.be.equal(from);
expect(receiverLog.args.from).to.be.equal(from);
expect(receiverLog.args.tokenIds.length).to.be.equal(2);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokenTypesToTransfer[0]);
expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(tokenTypesToTransfer[1]);
expect(receiverLog.args.tokenValues.length).to.be.equal(2);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(valuesToTransfer[0]);
expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(valuesToTransfer[1]);
expect(receiverLog.args.data).to.be.deep.equal(callbackData);
// check balances after transfer
const balancesAfterTransfer = await token.balanceOfBatch.callAsync(participatingOwners, participatingTokens);
expect(balancesAfterTransfer[fromIdxFungible]).to.be.bignumber.equal(balancesBeforeTransfer[fromIdxFungible].minus(fungibleValueToTransfer));
expect(balancesAfterTransfer[toIdxFungible]).to.be.bignumber.equal(balancesBeforeTransfer[toIdxFungible].plus(fungibleValueToTransfer));
expect(balancesAfterTransfer[fromIdxNonFungible]).to.be.bignumber.equal(nftNotOwnerBalance);
expect(balancesAfterTransfer[toIdxNonFungible]).to.be.bignumber.equal(nftOwnerBalance);
});
});
});
// 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,15 @@
{
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/DummyERC1155Receiver.json",
"generated-artifacts/DummyERC1155Token.json",
"generated-artifacts/ERC1155.json",
"generated-artifacts/ERC1155Mintable.json",
"generated-artifacts/IERC1155.json",
"generated-artifacts/IERC1155Receiver.json",
"generated-artifacts/InvalidERC1155Receiver.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

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