Merge pull request #1657 from 0xProject/feature/contracts/erc1155MinimalImplementation
erc1155 minimal implementation
This commit is contained in:
@@ -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
2
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,6 +39,7 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
||||
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
|
||||
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
||||
| [`@0x/contracts-erc721`](/contracts/erc721) | [](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
|
||||
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
|
||||
| [`@0x/contracts-exchange`](/contracts/exchange) | [](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
|
||||
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
|
||||
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
|
||||
|
||||
11
contracts/erc1155/CHANGELOG.json
Normal file
11
contracts/erc1155/CHANGELOG.json
Normal file
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Created ERC1155 contracts package",
|
||||
"pr": 1657
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
6
contracts/erc1155/CHANGELOG.md
Normal file
6
contracts/erc1155/CHANGELOG.md
Normal 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
|
||||
1
contracts/erc1155/DEPLOYS.json
Normal file
1
contracts/erc1155/DEPLOYS.json
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
73
contracts/erc1155/README.md
Normal file
73
contracts/erc1155/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
## ERC1155 Tokens
|
||||
|
||||
This package contains implementations of various [ERC1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md) tokens. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-erc1155 --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-erc1155 yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-erc1155 yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Testing options
|
||||
|
||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
||||
30
contracts/erc1155/compiler.json
Normal file
30
contracts/erc1155/compiler.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"artifactsDir": "generated-artifacts",
|
||||
"contractsDir": "contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "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"
|
||||
]
|
||||
}
|
||||
247
contracts/erc1155/contracts/src/ERC1155.sol
Normal file
247
contracts/erc1155/contracts/src/ERC1155.sol
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Address.sol";
|
||||
import "./interfaces/IERC1155.sol";
|
||||
import "./interfaces/IERC1155Receiver.sol";
|
||||
import "./MixinNonFungibleToken.sol";
|
||||
|
||||
|
||||
contract ERC1155 is
|
||||
SafeMath,
|
||||
IERC1155,
|
||||
MixinNonFungibleToken
|
||||
{
|
||||
using Address for address;
|
||||
|
||||
// selectors for receiver callbacks
|
||||
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
|
||||
|
||||
// id => (owner => balance)
|
||||
mapping (uint256 => mapping(address => uint256)) internal balances;
|
||||
|
||||
// owner => (operator => approved)
|
||||
mapping (address => mapping(address => bool)) internal operatorApproval;
|
||||
|
||||
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
|
||||
/// @dev MUST emit TransferSingle event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
|
||||
/// @param from Source address
|
||||
/// @param to Target address
|
||||
/// @param id ID of the token type
|
||||
/// @param value Transfer amount
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
to != address(0x0),
|
||||
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
|
||||
);
|
||||
require(
|
||||
from == msg.sender || operatorApproval[from][msg.sender] == true,
|
||||
"INSUFFICIENT_ALLOWANCE"
|
||||
);
|
||||
|
||||
// perform transfer
|
||||
if (isNonFungible(id)) {
|
||||
require(
|
||||
value == 1,
|
||||
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
|
||||
);
|
||||
require(
|
||||
nfOwners[id] == from,
|
||||
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
|
||||
);
|
||||
nfOwners[id] = to;
|
||||
// You could keep balance of NF type in base type id like so:
|
||||
// uint256 baseType = getNonFungibleBaseType(_id);
|
||||
// balances[baseType][_from] = balances[baseType][_from].safeSub(_value);
|
||||
// balances[baseType][_to] = balances[baseType][_to].safeAdd(_value);
|
||||
} else {
|
||||
balances[id][from] = safeSub(balances[id][from], value);
|
||||
balances[id][to] = safeAdd(balances[id][to], value);
|
||||
}
|
||||
emit TransferSingle(msg.sender, from, to, id, value);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (to.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155Received(
|
||||
msg.sender,
|
||||
from,
|
||||
id,
|
||||
value,
|
||||
data
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
|
||||
/// @dev MUST emit TransferBatch event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if length of `_ids` is not the same as length of `_values`.
|
||||
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
|
||||
/// @param from Source addresses
|
||||
/// @param to Target addresses
|
||||
/// @param ids IDs of each token type
|
||||
/// @param values Transfer amounts per token type
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
to != address(0x0),
|
||||
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
|
||||
);
|
||||
require(
|
||||
ids.length == values.length,
|
||||
"TOKEN_AND_VALUES_LENGTH_MISMATCH"
|
||||
);
|
||||
|
||||
// Only supporting a global operator approval allows us to do
|
||||
// only 1 check and not to touch storage to handle allowances.
|
||||
require(
|
||||
from == msg.sender || operatorApproval[from][msg.sender] == true,
|
||||
"INSUFFICIENT_ALLOWANCE"
|
||||
);
|
||||
|
||||
// perform transfers
|
||||
for (uint256 i = 0; i < ids.length; ++i) {
|
||||
// Cache value to local variable to reduce read costs.
|
||||
uint256 id = ids[i];
|
||||
uint256 value = values[i];
|
||||
|
||||
if (isNonFungible(id)) {
|
||||
require(
|
||||
value == 1,
|
||||
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
|
||||
);
|
||||
require(
|
||||
nfOwners[id] == from,
|
||||
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
|
||||
);
|
||||
nfOwners[id] = to;
|
||||
} else {
|
||||
balances[id][from] = safeSub(balances[id][from], value);
|
||||
balances[id][to] = safeAdd(balances[id][to], value);
|
||||
}
|
||||
}
|
||||
emit TransferBatch(msg.sender, from, to, ids, values);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (to.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155BatchReceived(
|
||||
msg.sender,
|
||||
from,
|
||||
ids,
|
||||
values,
|
||||
data
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_BATCH_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
|
||||
/// @dev MUST emit the ApprovalForAll event on success.
|
||||
/// @param operator Address to add to the set of authorized operators
|
||||
/// @param approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address operator, bool approved) external {
|
||||
operatorApproval[msg.sender][operator] = approved;
|
||||
emit ApprovalForAll(msg.sender, operator, approved);
|
||||
}
|
||||
|
||||
/// @notice Queries the approval status of an operator for a given owner.
|
||||
/// @param owner The owner of the Tokens
|
||||
/// @param operator Address of authorized operator
|
||||
/// @return True if the operator is approved, false if not
|
||||
function isApprovedForAll(address owner, address operator) external view returns (bool) {
|
||||
return operatorApproval[owner][operator];
|
||||
}
|
||||
|
||||
/// @notice Get the balance of an account's Tokens.
|
||||
/// @param owner The address of the token holder
|
||||
/// @param id ID of the Token
|
||||
/// @return The _owner's balance of the Token type requested
|
||||
function balanceOf(address owner, uint256 id) external view returns (uint256) {
|
||||
if (isNonFungibleItem(id)) {
|
||||
return nfOwners[id] == owner ? 1 : 0;
|
||||
}
|
||||
return balances[id][owner];
|
||||
}
|
||||
|
||||
/// @notice Get the balance of multiple account/token pairs
|
||||
/// @param owners The addresses of the token holders
|
||||
/// @param ids ID of the Tokens
|
||||
/// @return The _owner's balance of the Token types requested
|
||||
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory balances_) {
|
||||
// sanity check
|
||||
require(
|
||||
owners.length == ids.length,
|
||||
"OWNERS_AND_IDS_MUST_HAVE_SAME_LENGTH"
|
||||
);
|
||||
|
||||
// get balances
|
||||
balances_ = new uint256[](owners.length);
|
||||
for (uint256 i = 0; i < owners.length; ++i) {
|
||||
uint256 id = ids[i];
|
||||
if (isNonFungibleItem(id)) {
|
||||
balances_[i] = nfOwners[id] == owners[i] ? 1 : 0;
|
||||
} else {
|
||||
balances_[i] = balances[id][owners[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return balances_;
|
||||
}
|
||||
}
|
||||
173
contracts/erc1155/contracts/src/ERC1155Mintable.sol
Normal file
173
contracts/erc1155/contracts/src/ERC1155Mintable.sol
Normal file
@@ -0,0 +1,173 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "./ERC1155.sol";
|
||||
import "./interfaces/IERC1155Mintable.sol";
|
||||
|
||||
|
||||
/// @dev Mintable form of ERC1155
|
||||
/// Shows how easy it is to mint new items
|
||||
contract ERC1155Mintable is
|
||||
IERC1155Mintable,
|
||||
ERC1155
|
||||
{
|
||||
|
||||
/// token nonce
|
||||
uint256 internal nonce;
|
||||
|
||||
/// mapping from token to creator
|
||||
mapping (uint256 => address) public creators;
|
||||
|
||||
/// mapping from token to max index
|
||||
mapping (uint256 => uint256) public maxIndex;
|
||||
|
||||
/// asserts token is owned by msg.sender
|
||||
modifier creatorOnly(uint256 _id) {
|
||||
require(creators[_id] == msg.sender);
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev creates a new token
|
||||
/// @param uri URI of token
|
||||
/// @param isNF is non-fungible token
|
||||
/// @return type_ of token (a unique identifier)
|
||||
function create(
|
||||
string calldata uri,
|
||||
bool isNF
|
||||
)
|
||||
external
|
||||
returns (uint256 type_)
|
||||
{
|
||||
// Store the type in the upper 128 bits
|
||||
type_ = (++nonce << 128);
|
||||
|
||||
// Set a flag if this is an NFI.
|
||||
if (isNF) {
|
||||
type_ = type_ | TYPE_NF_BIT;
|
||||
}
|
||||
|
||||
// This will allow restricted access to creators.
|
||||
creators[type_] = msg.sender;
|
||||
|
||||
// emit a Transfer event with Create semantic to help with discovery.
|
||||
emit TransferSingle(
|
||||
msg.sender,
|
||||
address(0x0),
|
||||
address(0x0),
|
||||
type_,
|
||||
0
|
||||
);
|
||||
|
||||
if (bytes(uri).length > 0) {
|
||||
emit URI(uri, type_);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev mints fungible tokens
|
||||
/// @param id token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
/// @param quantities amounts of minted tokens
|
||||
function mintFungible(
|
||||
uint256 id,
|
||||
address[] calldata to,
|
||||
uint256[] calldata quantities
|
||||
)
|
||||
external
|
||||
creatorOnly(id)
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
isFungible(id),
|
||||
"TRIED_TO_MINT_FUNGIBLE_FOR_NON_FUNGIBLE_TOKEN"
|
||||
);
|
||||
|
||||
// mint tokens
|
||||
for (uint256 i = 0; i < to.length; ++i) {
|
||||
// cache to reduce number of loads
|
||||
address dst = to[i];
|
||||
uint256 quantity = quantities[i];
|
||||
|
||||
// Grant the items to the caller
|
||||
balances[id][dst] = safeAdd(quantity, balances[id][dst]);
|
||||
|
||||
// Emit the Transfer/Mint event.
|
||||
// the 0x0 source address implies a mint
|
||||
// It will also provide the circulating supply info.
|
||||
emit TransferSingle(
|
||||
msg.sender,
|
||||
address(0x0),
|
||||
dst,
|
||||
id,
|
||||
quantity
|
||||
);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (dst.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
id,
|
||||
quantity,
|
||||
""
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev mints a non-fungible token
|
||||
/// @param type_ token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
function mintNonFungible(
|
||||
uint256 type_,
|
||||
address[] calldata to
|
||||
)
|
||||
external
|
||||
creatorOnly(type_)
|
||||
{
|
||||
// No need to check this is a nf type rather than an id since
|
||||
// creatorOnly() will only let a type pass through.
|
||||
require(
|
||||
isNonFungible(type_),
|
||||
"TRIED_TO_MINT_NON_FUNGIBLE_FOR_FUNGIBLE_TOKEN"
|
||||
);
|
||||
|
||||
// Index are 1-based.
|
||||
uint256 index = maxIndex[type_] + 1;
|
||||
|
||||
for (uint256 i = 0; i < to.length; ++i) {
|
||||
// cache to reduce number of loads
|
||||
address dst = to[i];
|
||||
uint256 id = type_ | index + i;
|
||||
|
||||
nfOwners[id] = dst;
|
||||
|
||||
// You could use base-type id to store NF type balances if you wish.
|
||||
// balances[_type][dst] = quantity.safeAdd(balances[_type][dst]);
|
||||
|
||||
emit TransferSingle(msg.sender, address(0x0), dst, id, 1);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (dst.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
id,
|
||||
1,
|
||||
""
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// record the `maxIndex` of this nft type
|
||||
// this allows us to mint more nft's of this type in a subsequent call.
|
||||
maxIndex[type_] = safeAdd(to.length, maxIndex[type_]);
|
||||
}
|
||||
}
|
||||
76
contracts/erc1155/contracts/src/MixinNonFungibleToken.sol
Normal file
76
contracts/erc1155/contracts/src/MixinNonFungibleToken.sol
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./mixins/MNonFungibleToken.sol";
|
||||
|
||||
|
||||
contract MixinNonFungibleToken is
|
||||
MNonFungibleToken
|
||||
{
|
||||
/// Use a split bit implementation.
|
||||
/// Store the type in the upper 128 bits..
|
||||
uint256 constant internal TYPE_MASK = uint256(uint128(~0)) << 128;
|
||||
|
||||
/// ..and the non-fungible index in the lower 128
|
||||
uint256 constant internal NF_INDEX_MASK = uint128(~0);
|
||||
|
||||
/// The top bit is a flag to tell if this is a NFI.
|
||||
uint256 constant internal TYPE_NF_BIT = 1 << 255;
|
||||
|
||||
/// mapping of nft to owner
|
||||
mapping (uint256 => address) internal nfOwners;
|
||||
|
||||
/// @dev Returns true if token is non-fungible
|
||||
function isNonFungible(uint256 id) public pure returns(bool) {
|
||||
return id & TYPE_NF_BIT == TYPE_NF_BIT;
|
||||
}
|
||||
|
||||
/// @dev Returns true if token is fungible
|
||||
function isFungible(uint256 id) public pure returns(bool) {
|
||||
return id & TYPE_NF_BIT == 0;
|
||||
}
|
||||
|
||||
/// @dev Returns index of non-fungible token
|
||||
function getNonFungibleIndex(uint256 id) public pure returns(uint256) {
|
||||
return id & NF_INDEX_MASK;
|
||||
}
|
||||
|
||||
/// @dev Returns base type of non-fungible token
|
||||
function getNonFungibleBaseType(uint256 id) public pure returns(uint256) {
|
||||
return id & TYPE_MASK;
|
||||
}
|
||||
|
||||
/// @dev Returns true if input is base-type of a non-fungible token
|
||||
function isNonFungibleBaseType(uint256 id) public pure returns(bool) {
|
||||
// A base type has the NF bit but does not have an index.
|
||||
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK == 0);
|
||||
}
|
||||
|
||||
/// @dev Returns true if input is a non-fungible token
|
||||
function isNonFungibleItem(uint256 id) public pure returns(bool) {
|
||||
// A base type has the NF bit but does has an index.
|
||||
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0);
|
||||
}
|
||||
|
||||
/// @dev returns owner of a non-fungible token
|
||||
function ownerOf(uint256 id) public view returns (address) {
|
||||
return nfOwners[id];
|
||||
}
|
||||
}
|
||||
152
contracts/erc1155/contracts/src/interfaces/IERC1155.sol
Normal file
152
contracts/erc1155/contracts/src/interfaces/IERC1155.sol
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
/// @title ERC-1155 Multi Token Standard
|
||||
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
|
||||
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
|
||||
interface IERC1155 {
|
||||
|
||||
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
||||
/// including zero value transfers as well as minting or burning.
|
||||
/// Operator will always be msg.sender.
|
||||
/// Either event from address `0x0` signifies a minting operation.
|
||||
/// An event to address `0x0` signifies a burning or melting operation.
|
||||
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
|
||||
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
|
||||
/// To define a token ID with no initial balance, the contract SHOULD emit the TransferSingle event
|
||||
/// from `0x0` to `0x0`, with the token creator as `_operator`.
|
||||
event TransferSingle(
|
||||
address indexed operator,
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 id,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
||||
/// including zero value transfers as well as minting or burning.
|
||||
///Operator will always be msg.sender.
|
||||
/// Either event from address `0x0` signifies a minting operation.
|
||||
/// An event to address `0x0` signifies a burning or melting operation.
|
||||
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
|
||||
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
|
||||
/// To define multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event
|
||||
/// from `0x0` to `0x0`, with the token creator as `_operator`.
|
||||
event TransferBatch(
|
||||
address indexed operator,
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256[] ids,
|
||||
uint256[] values
|
||||
);
|
||||
|
||||
/// @dev MUST emit when an approval is updated.
|
||||
event ApprovalForAll(
|
||||
address indexed owner,
|
||||
address indexed operator,
|
||||
bool approved
|
||||
);
|
||||
|
||||
/// @dev MUST emit when the URI is updated for a token ID.
|
||||
/// URIs are defined in RFC 3986.
|
||||
/// The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata JSON Schema".
|
||||
event URI(
|
||||
string value,
|
||||
uint256 indexed id
|
||||
);
|
||||
|
||||
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
|
||||
/// @dev MUST emit TransferSingle event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
|
||||
/// @param from Source address
|
||||
/// @param to Target address
|
||||
/// @param id ID of the token type
|
||||
/// @param value Transfer amount
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
|
||||
/// @dev MUST emit TransferBatch event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if length of `_ids` is not the same as length of `_values`.
|
||||
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
|
||||
/// @param from Source addresses
|
||||
/// @param to Target addresses
|
||||
/// @param ids IDs of each token type
|
||||
/// @param values Transfer amounts per token type
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
|
||||
/// @dev MUST emit the ApprovalForAll event on success.
|
||||
/// @param operator Address to add to the set of authorized operators
|
||||
/// @param approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address operator, bool approved) external;
|
||||
|
||||
/// @notice Queries the approval status of an operator for a given owner.
|
||||
/// @param owner The owner of the Tokens
|
||||
/// @param operator Address of authorized operator
|
||||
/// @return True if the operator is approved, false if not
|
||||
function isApprovedForAll(address owner, address operator) external view returns (bool);
|
||||
|
||||
/// @notice Get the balance of an account's Tokens.
|
||||
/// @param owner The address of the token holder
|
||||
/// @param id ID of the Token
|
||||
/// @return The _owner's balance of the Token type requested
|
||||
function balanceOf(address owner, uint256 id) external view returns (uint256);
|
||||
|
||||
/// @notice Get the balance of multiple account/token pairs
|
||||
/// @param owners The addresses of the token holders
|
||||
/// @param ids ID of the Tokens
|
||||
/// @return The _owner's balance of the Token types requested
|
||||
function balanceOfBatch(
|
||||
address[] calldata owners,
|
||||
uint256[] calldata ids
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory balances_);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./IERC1155.sol";
|
||||
|
||||
|
||||
/// @dev Mintable form of ERC1155
|
||||
/// Shows how easy it is to mint new items
|
||||
contract IERC1155Mintable is
|
||||
IERC1155
|
||||
{
|
||||
|
||||
/// @dev creates a new token
|
||||
/// @param uri URI of token
|
||||
/// @param isNF is non-fungible token
|
||||
/// @return _type of token (a unique identifier)
|
||||
function create(
|
||||
string calldata uri,
|
||||
bool isNF
|
||||
)
|
||||
external
|
||||
returns (uint256 type_);
|
||||
|
||||
/// @dev mints fungible tokens
|
||||
/// @param id token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
/// @param quantities amounts of minted tokens
|
||||
function mintFungible(
|
||||
uint256 id,
|
||||
address[] calldata to,
|
||||
uint256[] calldata quantities
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev mints a non-fungible token
|
||||
/// @param type_ token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
function mintNonFungible(
|
||||
uint256 type_,
|
||||
address[] calldata to
|
||||
)
|
||||
external;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
interface IERC1155Receiver {
|
||||
|
||||
/// @notice Handle the receipt of a single ERC1155 token type
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
///transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param id An array containing the ids of the token being transferred
|
||||
/// @param value An array containing the amount of tokens being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155Received(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4);
|
||||
|
||||
/// @notice Handle the receipt of multiple ERC1155 token types
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
/// transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param ids An array containing ids of each token being transferred
|
||||
/// @param values An array containing amounts of each token being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155BatchReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4);
|
||||
}
|
||||
44
contracts/erc1155/contracts/src/mixins/MNonFungibleToken.sol
Normal file
44
contracts/erc1155/contracts/src/mixins/MNonFungibleToken.sol
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract MNonFungibleToken {
|
||||
|
||||
/// @dev Returns true if token is non-fungible
|
||||
function isNonFungible(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev Returns true if token is fungible
|
||||
function isFungible(uint256 _d) public pure returns(bool);
|
||||
|
||||
/// @dev Returns index of non-fungible token
|
||||
function getNonFungibleIndex(uint256 id) public pure returns(uint256);
|
||||
|
||||
/// @dev Returns base type of non-fungible token
|
||||
function getNonFungibleBaseType(uint256 id) public pure returns(uint256);
|
||||
|
||||
/// @dev Returns true if input is base-type of a non-fungible token
|
||||
function isNonFungibleBaseType(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev Returns true if input is a non-fungible token
|
||||
function isNonFungibleItem(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev returns owner of a non-fungible token
|
||||
function ownerOf(uint256 id) public view returns (address);
|
||||
}
|
||||
126
contracts/erc1155/contracts/test/DummyERC1155Receiver.sol
Normal file
126
contracts/erc1155/contracts/test/DummyERC1155Receiver.sol
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../src/interfaces/IERC1155Receiver.sol";
|
||||
|
||||
|
||||
contract DummyERC1155Receiver is
|
||||
IERC1155Receiver
|
||||
{
|
||||
|
||||
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
|
||||
bool internal shouldRejectTransfer;
|
||||
|
||||
event TokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 tokenId,
|
||||
uint256 tokenValue,
|
||||
bytes data
|
||||
);
|
||||
|
||||
event BatchTokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] tokenIds,
|
||||
uint256[] tokenValues,
|
||||
bytes data
|
||||
);
|
||||
|
||||
constructor () public {
|
||||
shouldRejectTransfer = false;
|
||||
}
|
||||
|
||||
/// @notice Handle the receipt of a single ERC1155 token type
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
///transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param id An array containing the ids of the token being transferred
|
||||
/// @param value An array containing the amount of tokens being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155Received(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4)
|
||||
{
|
||||
if (shouldRejectTransfer) {
|
||||
revert("TRANSFER_REJECTED");
|
||||
}
|
||||
emit TokenReceived(
|
||||
operator,
|
||||
from,
|
||||
id,
|
||||
value,
|
||||
data
|
||||
);
|
||||
return ERC1155_RECEIVED;
|
||||
}
|
||||
|
||||
/// @notice Handle the receipt of multiple ERC1155 token types
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
/// transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param ids An array containing ids of each token being transferred
|
||||
/// @param values An array containing amounts of each token being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155BatchReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns (bytes4)
|
||||
{
|
||||
if (shouldRejectTransfer) {
|
||||
revert("TRANSFER_REJECTED");
|
||||
}
|
||||
emit BatchTokenReceived(
|
||||
operator,
|
||||
from,
|
||||
ids,
|
||||
values,
|
||||
data
|
||||
);
|
||||
return ERC1155_BATCH_RECEIVED;
|
||||
}
|
||||
|
||||
// @dev If set to true then all future transfers will be rejected.
|
||||
function setRejectTransferFlag(bool _shouldRejectTransfer) external {
|
||||
shouldRejectTransfer = _shouldRejectTransfer;
|
||||
}
|
||||
}
|
||||
82
contracts/erc1155/package.json
Normal file
82
contracts/erc1155/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc1155",
|
||||
"version": "1.0.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"
|
||||
}
|
||||
}
|
||||
25
contracts/erc1155/src/artifacts.ts
Normal file
25
contracts/erc1155/src/artifacts.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyERC1155Receiver from '../generated-artifacts/DummyERC1155Receiver.json';
|
||||
import * as ERC1155 from '../generated-artifacts/ERC1155.json';
|
||||
import * as ERC1155Mintable from '../generated-artifacts/ERC1155Mintable.json';
|
||||
import * as IERC1155 from '../generated-artifacts/IERC1155.json';
|
||||
import * as IERC1155Mintable from '../generated-artifacts/IERC1155Mintable.json';
|
||||
import * as IERC1155Receiver from '../generated-artifacts/IERC1155Receiver.json';
|
||||
import * as MixinNonFungibleToken from '../generated-artifacts/MixinNonFungibleToken.json';
|
||||
import * as MNonFungibleToken from '../generated-artifacts/MNonFungibleToken.json';
|
||||
export const artifacts = {
|
||||
DummyERC1155Receiver: DummyERC1155Receiver as ContractArtifact,
|
||||
ERC1155: ERC1155 as ContractArtifact,
|
||||
MNonFungibleToken: MNonFungibleToken as ContractArtifact,
|
||||
ERC1155Mintable: ERC1155Mintable as ContractArtifact,
|
||||
MixinNonFungibleToken: MixinNonFungibleToken as ContractArtifact,
|
||||
IERC1155Mintable: IERC1155Mintable as ContractArtifact,
|
||||
IERC1155Receiver: IERC1155Receiver as ContractArtifact,
|
||||
IERC1155: IERC1155 as ContractArtifact,
|
||||
};
|
||||
3
contracts/erc1155/src/index.ts
Normal file
3
contracts/erc1155/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './wrappers';
|
||||
export * from './artifacts';
|
||||
export { Erc1155Wrapper } from '../test/utils/erc1155_wrapper';
|
||||
13
contracts/erc1155/src/wrappers.ts
Normal file
13
contracts/erc1155/src/wrappers.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/dummy_erc1155_receiver';
|
||||
export * from '../generated-wrappers/erc1155';
|
||||
export * from '../generated-wrappers/erc1155_mintable';
|
||||
export * from '../generated-wrappers/i_erc1155_mintable';
|
||||
export * from '../generated-wrappers/i_erc1155_receiver';
|
||||
export * from '../generated-wrappers/ierc1155';
|
||||
export * from '../generated-wrappers/m_non_fungible_token';
|
||||
export * from '../generated-wrappers/mixin_non_fungible_token';
|
||||
492
contracts/erc1155/test/erc1155_token.ts
Normal file
492
contracts/erc1155/test/erc1155_token.ts
Normal file
@@ -0,0 +1,492 @@
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||
DummyERC1155ReceiverContract,
|
||||
ERC1155MintableContract,
|
||||
} from '../src';
|
||||
|
||||
import { Erc1155Wrapper } from './utils/erc1155_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('ERC1155Token', () => {
|
||||
// constant values used in transfer tests
|
||||
const nftOwnerBalance = new BigNumber(1);
|
||||
const nftNotOwnerBalance = new BigNumber(0);
|
||||
const spenderInitialFungibleBalance = new BigNumber(500);
|
||||
const receiverInitialFungibleBalance = new BigNumber(0);
|
||||
const fungibleValueToTransfer = spenderInitialFungibleBalance.div(2);
|
||||
const nonFungibleValueToTransfer = nftOwnerBalance;
|
||||
const receiverCallbackData = '0x01020304';
|
||||
// tokens & addresses
|
||||
let owner: string;
|
||||
let spender: string;
|
||||
let delegatedSpender: string;
|
||||
let receiver: string;
|
||||
let erc1155Contract: ERC1155MintableContract;
|
||||
let erc1155Receiver: DummyERC1155ReceiverContract;
|
||||
let nonFungibleToken: BigNumber;
|
||||
let erc1155Wrapper: Erc1155Wrapper;
|
||||
let fungibleToken: BigNumber;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
// deploy erc1155 contract & receiver
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[owner, spender, delegatedSpender] = accounts;
|
||||
erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC1155Mintable,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC1155Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
receiver = erc1155Receiver.address;
|
||||
// create wrapper & mint erc1155 tokens
|
||||
erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, provider, owner);
|
||||
fungibleToken = await erc1155Wrapper.mintFungibleTokensAsync([spender], spenderInitialFungibleBalance);
|
||||
let nonFungibleTokens: BigNumber[];
|
||||
[, nonFungibleTokens] = await erc1155Wrapper.mintNonFungibleTokensAsync([spender]);
|
||||
nonFungibleToken = nonFungibleTokens[0];
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('safeTransferFrom', () => {
|
||||
it('should transfer fungible token if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
fungibleToken,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valueToTransfer),
|
||||
receiverInitialFungibleBalance.plus(valueToTransfer),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should transfer non-fungible token if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = nonFungibleToken;
|
||||
const valueToTransfer = nonFungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should trigger callback if transferring to a contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
spenderInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
const tx = await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
expect(tx.logs.length).to.be.equal(2);
|
||||
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
|
||||
// check callback logs
|
||||
const expectedCallbackLog = {
|
||||
operator: spender,
|
||||
from: spender,
|
||||
tokenId: tokenToTransfer,
|
||||
tokenValue: valueToTransfer,
|
||||
data: receiverCallbackData,
|
||||
};
|
||||
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
|
||||
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
|
||||
expect(receiverLog.args.tokenId).to.be.bignumber.equal(expectedCallbackLog.tokenId);
|
||||
expect(receiverLog.args.tokenValue).to.be.bignumber.equal(expectedCallbackLog.tokenValue);
|
||||
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valueToTransfer),
|
||||
receiverInitialFungibleBalance.plus(valueToTransfer),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should throw if transfer reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = spenderInitialFungibleBalance.plus(1);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.Uint256Underflow,
|
||||
);
|
||||
});
|
||||
it('should throw if callback reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// set receiver to reject balances
|
||||
const shouldRejectTransfer = true;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.TransferRejected,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('batchSafeTransferFrom', () => {
|
||||
it('should transfer fungible tokens if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer non-fungible token if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [nonFungibleToken];
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer mix of fungible / non-fungible tokens if called by token owner', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken, nonFungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should trigger callback if transferring to a contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken, nonFungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const tx = await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
expect(tx.logs.length).to.be.equal(2);
|
||||
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
|
||||
// check callback logs
|
||||
const expectedCallbackLog = {
|
||||
operator: spender,
|
||||
from: spender,
|
||||
tokenIds: tokensToTransfer,
|
||||
tokenValues: valuesToTransfer,
|
||||
data: receiverCallbackData,
|
||||
};
|
||||
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
|
||||
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.equal(2);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[0]);
|
||||
expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[1]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.equal(2);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[0]);
|
||||
expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[1]);
|
||||
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should throw if transfer reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)];
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.Uint256Underflow,
|
||||
);
|
||||
});
|
||||
it('should throw if callback reverts', async () => {
|
||||
// setup test parameters
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// set receiver to reject balances
|
||||
const shouldRejectTransfer = true;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: spender },
|
||||
),
|
||||
RevertReason.TransferRejected,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('setApprovalForAll', () => {
|
||||
it('should transfer token via safeTransferFrom if called by approved account', async () => {
|
||||
// set approval
|
||||
const isApprovedForAll = true;
|
||||
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.true();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
delegatedSpender,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valueToTransfer),
|
||||
receiverInitialFungibleBalance.plus(valueToTransfer),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should throw if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => {
|
||||
// check approval not set
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.false();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleToken;
|
||||
const valueToTransfer = fungibleValueToTransfer;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokenToTransfer,
|
||||
valueToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: delegatedSpender },
|
||||
),
|
||||
RevertReason.InsufficientAllowance,
|
||||
);
|
||||
});
|
||||
it('should transfer token via safeBatchTransferFrom if called by approved account', async () => {
|
||||
// set approval
|
||||
const isApprovedForAll = true;
|
||||
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.true();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155Wrapper.safeBatchTransferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
delegatedSpender,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
||||
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should throw if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => {
|
||||
// check approval not set
|
||||
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
||||
expect(isApprovedForAllCheck).to.be.false();
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = [fungibleToken];
|
||||
const valuesToTransfer = [fungibleValueToTransfer];
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
spender,
|
||||
receiver,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
{ from: delegatedSpender },
|
||||
),
|
||||
RevertReason.InsufficientAllowance,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:enable:no-unnecessary-type-assertion
|
||||
17
contracts/erc1155/test/global_hooks.ts
Normal file
17
contracts/erc1155/test/global_hooks.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { env, EnvVars } from '@0x/dev-utils';
|
||||
|
||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||
before('start web3 provider', () => {
|
||||
provider.start();
|
||||
});
|
||||
after('generate coverage report', async () => {
|
||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
||||
await coverageSubprovider.writeCoverageAsync();
|
||||
}
|
||||
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
|
||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
||||
await profilerSubprovider.writeProfilerOutputAsync();
|
||||
}
|
||||
provider.stop();
|
||||
});
|
||||
159
contracts/erc1155/test/utils/erc1155_wrapper.ts
Normal file
159
contracts/erc1155/test/utils/erc1155_wrapper.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { constants, LogDecoder } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155MintableContract, ERC1155TransferSingleEventArgs } from '../../src';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
export class Erc1155Wrapper {
|
||||
private readonly _erc1155Contract: ERC1155MintableContract;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _contractOwner: string;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
|
||||
constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) {
|
||||
this._erc1155Contract = contractInstance;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._contractOwner = contractOwner;
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
||||
}
|
||||
public getContract(): ERC1155MintableContract {
|
||||
return this._erc1155Contract;
|
||||
}
|
||||
public async getBalancesAsync(owners: string[], tokens: BigNumber[]): Promise<BigNumber[]> {
|
||||
const balances = await this._erc1155Contract.balanceOfBatch.callAsync(owners, tokens);
|
||||
return balances;
|
||||
}
|
||||
public async safeTransferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
token: BigNumber,
|
||||
value: BigNumber,
|
||||
callbackData?: string,
|
||||
delegatedSpender?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const spender = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
|
||||
const callbackDataHex = _.isUndefined(callbackData) ? '0x' : callbackData;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.safeTransferFrom.sendTransactionAsync(from, to, token, value, callbackDataHex, {
|
||||
from: spender,
|
||||
}),
|
||||
);
|
||||
return tx;
|
||||
}
|
||||
public async safeBatchTransferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
tokens: BigNumber[],
|
||||
values: BigNumber[],
|
||||
callbackData?: string,
|
||||
delegatedSpender?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const spender = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
|
||||
const callbackDataHex = _.isUndefined(callbackData) ? '0x' : callbackData;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.safeBatchTransferFrom.sendTransactionAsync(
|
||||
from,
|
||||
to,
|
||||
tokens,
|
||||
values,
|
||||
callbackDataHex,
|
||||
{ from: spender },
|
||||
),
|
||||
);
|
||||
return tx;
|
||||
}
|
||||
public async mintFungibleTokensAsync(
|
||||
beneficiaries: string[],
|
||||
tokenAmounts: BigNumber | BigNumber[],
|
||||
): Promise<BigNumber> {
|
||||
const tokenUri = 'dummyFungibleToken';
|
||||
const tokenIsNonFungible = false;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
);
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
||||
const token = createFungibleTokenLog.args.id;
|
||||
const tokenAmountsAsArray = _.isArray(tokenAmounts) ? tokenAmounts : [];
|
||||
if (!_.isArray(tokenAmounts)) {
|
||||
_.each(_.range(0, beneficiaries.length), () => {
|
||||
tokenAmountsAsArray.push(tokenAmounts);
|
||||
});
|
||||
}
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await this._erc1155Contract.mintFungible.sendTransactionAsync(token, beneficiaries, tokenAmountsAsArray, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return token;
|
||||
}
|
||||
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
|
||||
const tokenUri = 'dummyNonFungibleToken';
|
||||
const tokenIsNonFungible = true;
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
);
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
||||
const token = createFungibleTokenLog.args.id;
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await this._erc1155Contract.mintNonFungible.sendTransactionAsync(token, beneficiaries, {
|
||||
from: this._contractOwner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const encodedNftIds: BigNumber[] = [];
|
||||
const nftIdBegin = 1;
|
||||
const nftIdEnd = beneficiaries.length + 1;
|
||||
const nftIdRange = _.range(nftIdBegin, nftIdEnd);
|
||||
_.each(nftIdRange, (nftId: number) => {
|
||||
const encodedNftId = token.plus(nftId);
|
||||
encodedNftIds.push(encodedNftId);
|
||||
});
|
||||
return [token, encodedNftIds];
|
||||
}
|
||||
public async setApprovalForAllAsync(
|
||||
owner: string,
|
||||
beneficiary: string,
|
||||
isApproved: boolean,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this._erc1155Contract.setApprovalForAll.sendTransactionAsync(beneficiary, isApproved, {
|
||||
from: owner,
|
||||
}),
|
||||
);
|
||||
return tx;
|
||||
}
|
||||
public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise<boolean> {
|
||||
const isApprovedForAll = await this._erc1155Contract.isApprovedForAll.callAsync(owner, beneficiary);
|
||||
return isApprovedForAll;
|
||||
}
|
||||
public async assertBalancesAsync(
|
||||
owners: string[],
|
||||
tokens: BigNumber[],
|
||||
expectedBalances: BigNumber[],
|
||||
): Promise<void> {
|
||||
const ownersExtended: string[] = [];
|
||||
let tokensExtended: BigNumber[] = [];
|
||||
_.each(owners, (owner: string) => {
|
||||
tokensExtended = tokensExtended.concat(tokens);
|
||||
_.each(_.range(0, tokens.length), () => {
|
||||
ownersExtended.push(owner);
|
||||
});
|
||||
});
|
||||
const balances = await this.getBalancesAsync(ownersExtended, tokensExtended);
|
||||
_.each(balances, (balance: BigNumber, i: number) => {
|
||||
expect(balance, `${ownersExtended[i]}${tokensExtended[i]}`).to.be.bignumber.equal(expectedBalances[i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
16
contracts/erc1155/tsconfig.json
Normal file
16
contracts/erc1155/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DummyERC1155Receiver.json",
|
||||
"generated-artifacts/ERC1155.json",
|
||||
"generated-artifacts/ERC1155Mintable.json",
|
||||
"generated-artifacts/IERC1155.json",
|
||||
"generated-artifacts/IERC1155Mintable.json",
|
||||
"generated-artifacts/IERC1155Receiver.json",
|
||||
"generated-artifacts/MNonFungibleToken.json",
|
||||
"generated-artifacts/MixinNonFungibleToken.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
6
contracts/erc1155/tslint.json
Normal file
6
contracts/erc1155/tslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,10 @@
|
||||
{
|
||||
"note": "Upgrade contracts to Solidity 0.5.5",
|
||||
"pr": 1682
|
||||
},
|
||||
{
|
||||
"note": "Added Address.sol with test for whether or not an address is a contract",
|
||||
"pr": 1657
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/Address.sol",
|
||||
"src/LibBytes.sol",
|
||||
"src/Ownable.sol",
|
||||
"src/ReentrancyGuard.sol",
|
||||
|
||||
47
contracts/utils/contracts/src/Address.sol
Normal file
47
contracts/utils/contracts/src/Address.sol
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* Utility library of inline functions on addresses
|
||||
*/
|
||||
library Address {
|
||||
|
||||
/**
|
||||
* Returns whether the target address is a contract
|
||||
* @dev This function will return false if invoked during the constructor of a contract,
|
||||
* as the code is not actually created until after the constructor finishes.
|
||||
* @param account address of the account to check
|
||||
* @return whether the target address is a contract
|
||||
*/
|
||||
function isContract(address account) internal view returns (bool) {
|
||||
uint256 size;
|
||||
// XXX Currently there is no better way to check if there is a contract in an address
|
||||
// than to check the size of the code at that address.
|
||||
// See https://ethereum.stackexchange.com/a/14016/36603
|
||||
// for more details about how this works.
|
||||
// TODO Check this again before the Serenity release, because all addresses will be
|
||||
// contracts then.
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly { size := extcodesize(account) }
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,7 +46,7 @@
|
||||
"lint": "wsrun lint $PKG --fast-exit --parallel --exclude-missing"
|
||||
},
|
||||
"config": {
|
||||
"contractsPackages": "@0x/contracts-asset-proxy @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-extensions @0x/contracts-multisig @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-coordinator",
|
||||
"contractsPackages": "@0x/contracts-asset-proxy @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-extensions @0x/contracts-multisig @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-coordinator",
|
||||
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
|
||||
"packagesWithDocPages": "0x.js connect json-schemas subproviders web3-wrapper contract-wrappers order-utils order-watcher sol-compiler sol-coverage sol-profiler sol-trace ethereum-types asset-buyer migrations",
|
||||
"ignoreDependencyVersions": "@types/styled-components @types/node",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.2.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added ERC1155 revert reasons",
|
||||
"pr": 1657
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.2.0",
|
||||
"changes": [
|
||||
|
||||
@@ -274,6 +274,18 @@ export enum RevertReason {
|
||||
InvalidApprovalSignature = 'INVALID_APPROVAL_SIGNATURE',
|
||||
ApprovalExpired = 'APPROVAL_EXPIRED',
|
||||
InvalidOrigin = 'INVALID_ORIGIN',
|
||||
// ERC1155
|
||||
AmountEqualToOneRequired = 'AMOUNT_EQUAL_TO_ONE_REQUIRED',
|
||||
BadReceiverReturnValue = 'BAD_RECEIVER_RETURN_VALUE',
|
||||
CannotTransferToAddressZero = 'CANNOT_TRANSFER_TO_ADDRESS_ZERO',
|
||||
InsufficientAllowance = 'INSUFFICIENT_ALLOWANCE',
|
||||
NFTNotOwnedByFromAddress = 'NFT_NOT_OWNED_BY_FROM_ADDRESS',
|
||||
OwnersAndIdsMustHaveSameLength = 'OWNERS_AND_IDS_MUST_HAVE_SAME_LENGTH',
|
||||
TokenAndValuesLengthMismatch = 'TOKEN_AND_VALUES_LENGTH_MISMATCH',
|
||||
TriedToMintFungibleForNonFungibleToken = 'TRIED_TO_MINT_FUNGIBLE_FOR_NON_FUNGIBLE_TOKEN',
|
||||
TriedToMintNonFungibleForFungibleToken = 'TRIED_TO_MINT_NON_FUNGIBLE_FOR_FUNGIBLE_TOKEN',
|
||||
TransferRejected = 'TRANSFER_REJECTED',
|
||||
Uint256Underflow = 'UINT256_UNDERFLOW',
|
||||
}
|
||||
|
||||
export enum StatusCodes {
|
||||
|
||||
Reference in New Issue
Block a user