Split tokens package into erc20 and erc721
This commit is contained in:
3
contracts/erc721/.solhintignore
Normal file
3
contracts/erc721/.solhintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
contracts/tokens/ZRXToken/ERC20Token_v1.sol
|
||||
contracts/tokens/ZRXToken/Token_v1.sol
|
||||
contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol
|
||||
56
contracts/erc721/CHANGELOG.json
Normal file
56
contracts/erc721/CHANGELOG.json
Normal file
@@ -0,0 +1,56 @@
|
||||
[
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Upgrade the bignumber.js to v8.0.2",
|
||||
"pr": 1517
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1547747677,
|
||||
"version": "1.0.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1547561734,
|
||||
"version": "1.0.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1547225310,
|
||||
"version": "1.0.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1547040760,
|
||||
"version": "1.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1544741676,
|
||||
"version": "1.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
26
contracts/erc721/CHANGELOG.md
Normal file
26
contracts/erc721/CHANGELOG.md
Normal file
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.6 - _January 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.5 - _January 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.4 - _January 11, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.3 - _January 9, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.2 - _December 13, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
16
contracts/erc721/DEPLOYS.json
Normal file
16
contracts/erc721/DEPLOYS.json
Normal file
@@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"name": "ZRXToken",
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "protocol v1 deploy",
|
||||
"networks": {
|
||||
"1": "0xe41d2489571d322189246dafa5ebde1f4699f498",
|
||||
"3": "0xff67881f8d12f372d91baae9752eb3631ff0ed00",
|
||||
"42": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
74
contracts/erc721/README.md
Normal file
74
contracts/erc721/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
## Token contracts
|
||||
|
||||
Token smart contracts that are used in the 0x protocol. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [CHANGELOG](./CHANGELOG.json) of this package.
|
||||
|
||||
## Usage
|
||||
|
||||
Token contracts that make up and interact with version 2.0.0 of the protocol can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories:
|
||||
|
||||
- [tokens](./contracts/tokens)
|
||||
- This directory contains implementations of different tokens and token standards, including [wETH](https://weth.io/), ZRX, [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), and [ERC721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md).
|
||||
- [test](./contracts/test)
|
||||
- This directory contains mocks and other contracts that are used solely for testing contracts within the other directories.
|
||||
|
||||
## 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-tokens yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-tokens 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/erc721/compiler.json
Normal file
30
contracts/erc721/compiler.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"compilerSettings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"DummyERC721Receiver",
|
||||
"InvalidERC721Receiver",
|
||||
"DummyERC721Token",
|
||||
"ERC721Token",
|
||||
"IERC721Receiver",
|
||||
"IERC721Token",
|
||||
"MintableERC721Token"
|
||||
]
|
||||
}
|
||||
277
contracts/erc721/contracts/src/ERC721Token.sol
Normal file
277
contracts/erc721/contracts/src/ERC721Token.sol
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
|
||||
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.4.24;
|
||||
|
||||
import "./interfaces/IERC721Token.sol";
|
||||
import "./interfaces/IERC721Receiver.sol";
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
|
||||
|
||||
contract ERC721Token is
|
||||
IERC721Token,
|
||||
SafeMath
|
||||
{
|
||||
// Function selector for ERC721Receiver.onERC721Received
|
||||
// 0x150b7a02
|
||||
bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
|
||||
|
||||
// Mapping of tokenId => owner
|
||||
mapping (uint256 => address) internal owners;
|
||||
|
||||
// Mapping of tokenId => approved address
|
||||
mapping (uint256 => address) internal approvals;
|
||||
|
||||
// Mapping of owner => number of tokens owned
|
||||
mapping (address => uint256) internal balances;
|
||||
|
||||
// Mapping of owner => operator => approved
|
||||
mapping (address => mapping (address => bool)) internal operatorApprovals;
|
||||
|
||||
/// @notice Transfers the ownership of an NFT from one address to another address
|
||||
/// @dev Throws unless `msg.sender` is the current owner, an authorized
|
||||
/// operator, or the approved address for this NFT. Throws if `_from` is
|
||||
/// not the current owner. Throws if `_to` is the zero address. Throws if
|
||||
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
|
||||
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
|
||||
/// `onERC721Received` on `_to` and throws if the return value is not
|
||||
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
/// @param _data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _tokenId,
|
||||
bytes _data
|
||||
)
|
||||
external
|
||||
{
|
||||
transferFrom(
|
||||
_from,
|
||||
_to,
|
||||
_tokenId
|
||||
);
|
||||
|
||||
uint256 receiverCodeSize;
|
||||
assembly {
|
||||
receiverCodeSize := extcodesize(_to)
|
||||
}
|
||||
if (receiverCodeSize > 0) {
|
||||
bytes4 selector = IERC721Receiver(_to).onERC721Received(
|
||||
msg.sender,
|
||||
_from,
|
||||
_tokenId,
|
||||
_data
|
||||
);
|
||||
require(
|
||||
selector == ERC721_RECEIVED,
|
||||
"ERC721_INVALID_SELECTOR"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Transfers the ownership of an NFT from one address to another address
|
||||
/// @dev This works identically to the other function with an extra data parameter,
|
||||
/// except this function just sets data to "".
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
function safeTransferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _tokenId
|
||||
)
|
||||
external
|
||||
{
|
||||
transferFrom(
|
||||
_from,
|
||||
_to,
|
||||
_tokenId
|
||||
);
|
||||
|
||||
uint256 receiverCodeSize;
|
||||
assembly {
|
||||
receiverCodeSize := extcodesize(_to)
|
||||
}
|
||||
if (receiverCodeSize > 0) {
|
||||
bytes4 selector = IERC721Receiver(_to).onERC721Received(
|
||||
msg.sender,
|
||||
_from,
|
||||
_tokenId,
|
||||
""
|
||||
);
|
||||
require(
|
||||
selector == ERC721_RECEIVED,
|
||||
"ERC721_INVALID_SELECTOR"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Change or reaffirm the approved address for an NFT
|
||||
/// @dev The zero address indicates there is no approved address.
|
||||
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
|
||||
/// operator of the current owner.
|
||||
/// @param _approved The new approved NFT controller
|
||||
/// @param _tokenId The NFT to approve
|
||||
function approve(address _approved, uint256 _tokenId)
|
||||
external
|
||||
{
|
||||
address owner = ownerOf(_tokenId);
|
||||
require(
|
||||
msg.sender == owner || isApprovedForAll(owner, msg.sender),
|
||||
"ERC721_INVALID_SENDER"
|
||||
);
|
||||
|
||||
approvals[_tokenId] = _approved;
|
||||
emit Approval(
|
||||
owner,
|
||||
_approved,
|
||||
_tokenId
|
||||
);
|
||||
}
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage
|
||||
/// all of `msg.sender`'s assets
|
||||
/// @dev Emits the ApprovalForAll event. The contract MUST allow
|
||||
/// multiple operators per owner.
|
||||
/// @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
|
||||
{
|
||||
operatorApprovals[msg.sender][_operator] = _approved;
|
||||
emit ApprovalForAll(
|
||||
msg.sender,
|
||||
_operator,
|
||||
_approved
|
||||
);
|
||||
}
|
||||
|
||||
/// @notice Count all NFTs assigned to an owner
|
||||
/// @dev NFTs assigned to the zero address are considered invalid, and this
|
||||
/// function throws for queries about the zero address.
|
||||
/// @param _owner An address for whom to query the balance
|
||||
/// @return The number of NFTs owned by `_owner`, possibly zero
|
||||
function balanceOf(address _owner)
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
require(
|
||||
_owner != address(0),
|
||||
"ERC721_ZERO_OWNER"
|
||||
);
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
|
||||
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
|
||||
/// THEY MAY BE PERMANENTLY LOST
|
||||
/// @dev Throws unless `msg.sender` is the current owner, an authorized
|
||||
/// operator, or the approved address for this NFT. Throws if `_from` is
|
||||
/// not the current owner. Throws if `_to` is the zero address. Throws if
|
||||
/// `_tokenId` is not a valid NFT.
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
function transferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _tokenId
|
||||
)
|
||||
public
|
||||
{
|
||||
require(
|
||||
_to != address(0),
|
||||
"ERC721_ZERO_TO_ADDRESS"
|
||||
);
|
||||
|
||||
address owner = ownerOf(_tokenId);
|
||||
require(
|
||||
_from == owner,
|
||||
"ERC721_OWNER_MISMATCH"
|
||||
);
|
||||
|
||||
address spender = msg.sender;
|
||||
address approvedAddress = getApproved(_tokenId);
|
||||
require(
|
||||
spender == owner ||
|
||||
isApprovedForAll(owner, spender) ||
|
||||
approvedAddress == spender,
|
||||
"ERC721_INVALID_SPENDER"
|
||||
);
|
||||
|
||||
if (approvedAddress != address(0)) {
|
||||
approvals[_tokenId] = address(0);
|
||||
}
|
||||
|
||||
owners[_tokenId] = _to;
|
||||
balances[_from] = safeSub(balances[_from], 1);
|
||||
balances[_to] = safeAdd(balances[_to], 1);
|
||||
|
||||
emit Transfer(
|
||||
_from,
|
||||
_to,
|
||||
_tokenId
|
||||
);
|
||||
}
|
||||
|
||||
/// @notice Find the owner of an NFT
|
||||
/// @dev NFTs assigned to zero address are considered invalid, and queries
|
||||
/// about them do throw.
|
||||
/// @param _tokenId The identifier for an NFT
|
||||
/// @return The address of the owner of the NFT
|
||||
function ownerOf(uint256 _tokenId)
|
||||
public
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
address owner = owners[_tokenId];
|
||||
require(
|
||||
owner != address(0),
|
||||
"ERC721_ZERO_OWNER"
|
||||
);
|
||||
return owner;
|
||||
}
|
||||
|
||||
/// @notice Get the approved address for a single NFT
|
||||
/// @dev Throws if `_tokenId` is not a valid NFT.
|
||||
/// @param _tokenId The NFT to find the approved address for
|
||||
/// @return The approved address for this NFT, or the zero address if there is none
|
||||
function getApproved(uint256 _tokenId)
|
||||
public
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return approvals[_tokenId];
|
||||
}
|
||||
|
||||
/// @notice Query if an address is an authorized operator for another address
|
||||
/// @param _owner The address that owns the NFTs
|
||||
/// @param _operator The address that acts on behalf of the owner
|
||||
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
|
||||
function isApprovedForAll(address _owner, address _operator)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return operatorApprovals[_owner][_operator];
|
||||
}
|
||||
}
|
||||
82
contracts/erc721/contracts/src/MintableERC721Token.sol
Normal file
82
contracts/erc721/contracts/src/MintableERC721Token.sol
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
|
||||
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.4.24;
|
||||
|
||||
import "./ERC721Token.sol";
|
||||
|
||||
|
||||
contract MintableERC721Token is
|
||||
ERC721Token
|
||||
{
|
||||
/// @dev Function to mint a new token
|
||||
/// Reverts if the given token ID already exists
|
||||
/// @param _to Address of the beneficiary that will own the minted token
|
||||
/// @param _tokenId ID of the token to be minted by the msg.sender
|
||||
function _mint(address _to, uint256 _tokenId)
|
||||
internal
|
||||
{
|
||||
require(
|
||||
_to != address(0),
|
||||
"ERC721_ZERO_TO_ADDRESS"
|
||||
);
|
||||
|
||||
address owner = owners[_tokenId];
|
||||
require(
|
||||
owner == address(0),
|
||||
"ERC721_OWNER_ALREADY_EXISTS"
|
||||
);
|
||||
|
||||
owners[_tokenId] = _to;
|
||||
balances[_to] = safeAdd(balances[_to], 1);
|
||||
|
||||
emit Transfer(
|
||||
address(0),
|
||||
_to,
|
||||
_tokenId
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Function to burn a token
|
||||
/// Reverts if the given token ID doesn't exist
|
||||
/// @param _owner Owner of token with given token ID
|
||||
/// @param _tokenId ID of the token to be burned by the msg.sender
|
||||
function _burn(address _owner, uint256 _tokenId)
|
||||
internal
|
||||
{
|
||||
require(
|
||||
_owner != address(0),
|
||||
"ERC721_ZERO_OWNER_ADDRESS"
|
||||
);
|
||||
|
||||
address owner = owners[_tokenId];
|
||||
require(
|
||||
owner == _owner,
|
||||
"ERC721_OWNER_MISMATCH"
|
||||
);
|
||||
|
||||
owners[_tokenId] = address(0);
|
||||
balances[_owner] = safeSub(balances[_owner], 1);
|
||||
|
||||
emit Transfer(
|
||||
_owner,
|
||||
address(0),
|
||||
_tokenId
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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.4.24;
|
||||
|
||||
|
||||
contract IERC721Receiver {
|
||||
|
||||
/// @notice Handle the receipt of an NFT
|
||||
/// @dev The ERC721 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("onERC721Received(address,address,uint256,bytes)"))`
|
||||
/// unless throwing
|
||||
function onERC721Received(
|
||||
address _operator,
|
||||
address _from,
|
||||
uint256 _tokenId,
|
||||
bytes _data
|
||||
)
|
||||
external
|
||||
returns (bytes4);
|
||||
}
|
||||
158
contracts/erc721/contracts/src/interfaces/IERC721Token.sol
Normal file
158
contracts/erc721/contracts/src/interfaces/IERC721Token.sol
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
|
||||
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.4.24;
|
||||
|
||||
|
||||
contract IERC721Token {
|
||||
|
||||
/// @dev This emits when ownership of any NFT changes by any mechanism.
|
||||
/// This event emits when NFTs are created (`from` == 0) and destroyed
|
||||
/// (`to` == 0). Exception: during contract creation, any number of NFTs
|
||||
/// may be created and assigned without emitting Transfer. At the time of
|
||||
/// any transfer, the approved address for that NFT (if any) is reset to none.
|
||||
event Transfer(
|
||||
address indexed _from,
|
||||
address indexed _to,
|
||||
uint256 indexed _tokenId
|
||||
);
|
||||
|
||||
/// @dev This emits when the approved address for an NFT is changed or
|
||||
/// reaffirmed. The zero address indicates there is no approved address.
|
||||
/// When a Transfer event emits, this also indicates that the approved
|
||||
/// address for that NFT (if any) is reset to none.
|
||||
event Approval(
|
||||
address indexed _owner,
|
||||
address indexed _approved,
|
||||
uint256 indexed _tokenId
|
||||
);
|
||||
|
||||
/// @dev This emits when an operator is enabled or disabled for an owner.
|
||||
/// The operator can manage all NFTs of the owner.
|
||||
event ApprovalForAll(
|
||||
address indexed _owner,
|
||||
address indexed _operator,
|
||||
bool _approved
|
||||
);
|
||||
|
||||
/// @notice Transfers the ownership of an NFT from one address to another address
|
||||
/// @dev Throws unless `msg.sender` is the current owner, an authorized
|
||||
/// perator, or the approved address for this NFT. Throws if `_from` is
|
||||
/// not the current owner. Throws if `_to` is the zero address. Throws if
|
||||
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
|
||||
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
|
||||
/// `onERC721Received` on `_to` and throws if the return value is not
|
||||
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
/// @param _data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _tokenId,
|
||||
bytes _data
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Transfers the ownership of an NFT from one address to another address
|
||||
/// @dev This works identically to the other function with an extra data parameter,
|
||||
/// except this function just sets data to "".
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
function safeTransferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _tokenId
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Change or reaffirm the approved address for an NFT
|
||||
/// @dev The zero address indicates there is no approved address.
|
||||
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
|
||||
/// operator of the current owner.
|
||||
/// @param _approved The new approved NFT controller
|
||||
/// @param _tokenId The NFT to approve
|
||||
function approve(address _approved, uint256 _tokenId)
|
||||
external;
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage
|
||||
/// all of `msg.sender`'s assets
|
||||
/// @dev Emits the ApprovalForAll event. The contract MUST allow
|
||||
/// multiple operators per owner.
|
||||
/// @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 Count all NFTs assigned to an owner
|
||||
/// @dev NFTs assigned to the zero address are considered invalid, and this
|
||||
/// function throws for queries about the zero address.
|
||||
/// @param _owner An address for whom to query the balance
|
||||
/// @return The number of NFTs owned by `_owner`, possibly zero
|
||||
function balanceOf(address _owner)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
|
||||
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
|
||||
/// THEY MAY BE PERMANENTLY LOST
|
||||
/// @dev Throws unless `msg.sender` is the current owner, an authorized
|
||||
/// operator, or the approved address for this NFT. Throws if `_from` is
|
||||
/// not the current owner. Throws if `_to` is the zero address. Throws if
|
||||
/// `_tokenId` is not a valid NFT.
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
function transferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _tokenId
|
||||
)
|
||||
public;
|
||||
|
||||
/// @notice Find the owner of an NFT
|
||||
/// @dev NFTs assigned to zero address are considered invalid, and queries
|
||||
/// about them do throw.
|
||||
/// @param _tokenId The identifier for an NFT
|
||||
/// @return The address of the owner of the NFT
|
||||
function ownerOf(uint256 _tokenId)
|
||||
public
|
||||
view
|
||||
returns (address);
|
||||
|
||||
/// @notice Get the approved address for a single NFT
|
||||
/// @dev Throws if `_tokenId` is not a valid NFT.
|
||||
/// @param _tokenId The NFT to find the approved address for
|
||||
/// @return The approved address for this NFT, or the zero address if there is none
|
||||
function getApproved(uint256 _tokenId)
|
||||
public
|
||||
view
|
||||
returns (address);
|
||||
|
||||
/// @notice Query if an address is an authorized operator for another address
|
||||
/// @param _owner The address that owns the NFTs
|
||||
/// @param _operator The address that acts on behalf of the owner
|
||||
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
|
||||
function isApprovedForAll(address _owner, address _operator)
|
||||
public
|
||||
view
|
||||
returns (bool);
|
||||
}
|
||||
67
contracts/erc721/contracts/test/DummyERC721Receiver.sol
Normal file
67
contracts/erc721/contracts/test/DummyERC721Receiver.sol
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.4.24;
|
||||
|
||||
import "../src/interfaces/IERC721Receiver.sol";
|
||||
|
||||
|
||||
contract DummyERC721Receiver is
|
||||
IERC721Receiver
|
||||
{
|
||||
// Function selector for ERC721Receiver.onERC721Received
|
||||
// 0x150b7a02
|
||||
bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
|
||||
|
||||
event TokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 tokenId,
|
||||
bytes data
|
||||
);
|
||||
|
||||
/// @notice Handle the receipt of an NFT
|
||||
/// @dev The ERC721 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("onERC721Received(address,address,uint256,bytes)"))`
|
||||
/// unless throwing
|
||||
function onERC721Received(
|
||||
address _operator,
|
||||
address _from,
|
||||
uint256 _tokenId,
|
||||
bytes _data
|
||||
)
|
||||
external
|
||||
returns (bytes4)
|
||||
{
|
||||
emit TokenReceived(
|
||||
_operator,
|
||||
_from,
|
||||
_tokenId,
|
||||
_data
|
||||
);
|
||||
return ERC721_RECEIVED;
|
||||
}
|
||||
}
|
||||
63
contracts/erc721/contracts/test/DummyERC721Token.sol
Normal file
63
contracts/erc721/contracts/test/DummyERC721Token.sol
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
|
||||
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.4.24;
|
||||
|
||||
import "../src/MintableERC721Token.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract DummyERC721Token is
|
||||
Ownable,
|
||||
MintableERC721Token
|
||||
{
|
||||
string public name;
|
||||
string public symbol;
|
||||
|
||||
constructor (
|
||||
string _name,
|
||||
string _symbol
|
||||
)
|
||||
public
|
||||
{
|
||||
name = _name;
|
||||
symbol = _symbol;
|
||||
}
|
||||
|
||||
/// @dev Function to mint a new token
|
||||
/// Reverts if the given token ID already exists
|
||||
/// @param _to Address of the beneficiary that will own the minted token
|
||||
/// @param _tokenId ID of the token to be minted by the msg.sender
|
||||
function mint(address _to, uint256 _tokenId)
|
||||
external
|
||||
{
|
||||
_mint(_to, _tokenId);
|
||||
}
|
||||
|
||||
/// @dev Function to burn a token
|
||||
/// Reverts if the given token ID doesn't exist or not called by contract owner
|
||||
/// @param _owner Owner of token with given token ID
|
||||
/// @param _tokenId ID of the token to be burned by the msg.sender
|
||||
function burn(address _owner, uint256 _tokenId)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
_burn(_owner, _tokenId);
|
||||
}
|
||||
}
|
||||
66
contracts/erc721/contracts/test/InvalidERC721Receiver.sol
Normal file
66
contracts/erc721/contracts/test/InvalidERC721Receiver.sol
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
|
||||
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.4.24;
|
||||
|
||||
import "../src/interfaces/IERC721Receiver.sol";
|
||||
|
||||
|
||||
contract InvalidERC721Receiver is
|
||||
IERC721Receiver
|
||||
{
|
||||
// Actual function signature is `onERC721Received(address,address,uint256,bytes)`
|
||||
bytes4 constant internal INVALID_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,uint256,bytes)"));
|
||||
|
||||
event TokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 tokenId,
|
||||
bytes data
|
||||
);
|
||||
|
||||
/// @notice Handle the receipt of an NFT
|
||||
/// @dev The ERC721 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("onERC721Received(address,address,uint256,bytes)"))`
|
||||
/// unless throwing
|
||||
function onERC721Received(
|
||||
address _operator,
|
||||
address _from,
|
||||
uint256 _tokenId,
|
||||
bytes _data
|
||||
)
|
||||
external
|
||||
returns (bytes4)
|
||||
{
|
||||
emit TokenReceived(
|
||||
_operator,
|
||||
_from,
|
||||
_tokenId,
|
||||
_data
|
||||
);
|
||||
return INVALID_ERC721_RECEIVED;
|
||||
}
|
||||
}
|
||||
89
contracts/erc721/package.json
Normal file
89
contracts/erc721/package.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc721",
|
||||
"version": "1.0.6",
|
||||
"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",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "generated-artifacts/@(DummyERC721Receiver|InvalidERC721Receiver|DummyERC721Token|ERC721Token|IERC721Receiver|IERC721Token|MintableERC721Token).json"
|
||||
},
|
||||
"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": "^1.0.22",
|
||||
"@0x/contracts-test-utils": "^2.0.1",
|
||||
"@0x/dev-utils": "^1.0.24",
|
||||
"@0x/sol-compiler": "^2.0.2",
|
||||
"@0x/subproviders": "^2.1.11",
|
||||
"@0x/tslint-config": "^2.0.2",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"@types/yargs": "^10.0.0",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereumjs-abi": "0.6.5",
|
||||
"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",
|
||||
"yargs": "^10.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^3.0.13",
|
||||
"@0x/contracts-exchange-libs": "^1.0.6",
|
||||
"@0x/contracts-utils": "^1.0.6",
|
||||
"@0x/order-utils": "^3.1.2",
|
||||
"@0x/types": "^1.5.2",
|
||||
"@0x/typescript-typings": "^3.0.8",
|
||||
"@0x/utils": "^3.0.1",
|
||||
"@0x/web3-wrapper": "^3.2.4",
|
||||
"@types/js-combinatorics": "^0.5.29",
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereum-types": "^1.1.6",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.5"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
20
contracts/erc721/src/artifacts/index.ts
Normal file
20
contracts/erc721/src/artifacts/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyERC721Receiver from '../../generated-artifacts/DummyERC721Receiver.json';
|
||||
import * as DummyERC721Token from '../../generated-artifacts/DummyERC721Token.json';
|
||||
import * as ERC721Token from '../../generated-artifacts/ERC721Token.json';
|
||||
import * as IERC721Receiver from '../../generated-artifacts/IERC721Receiver.json';
|
||||
import * as IERC721Token from '../../generated-artifacts/IERC721Token.json';
|
||||
import * as InvalidERC721Receiver from '../../generated-artifacts/InvalidERC721Receiver.json';
|
||||
import * as MintableERC721Token from '../../generated-artifacts/MintableERC721Token.json';
|
||||
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
export const artifacts = {
|
||||
DummyERC721Receiver: DummyERC721Receiver as ContractArtifact,
|
||||
InvalidERC721Receiver: InvalidERC721Receiver as ContractArtifact,
|
||||
DummyERC721Token: DummyERC721Token as ContractArtifact,
|
||||
ERC721Token: ERC721Token as ContractArtifact,
|
||||
IERC721Receiver: IERC721Receiver as ContractArtifact,
|
||||
IERC721Token: IERC721Token as ContractArtifact,
|
||||
MintableERC721Token: MintableERC721Token as ContractArtifact,
|
||||
};
|
||||
2
contracts/erc721/src/index.ts
Normal file
2
contracts/erc721/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './wrappers';
|
||||
export * from './artifacts';
|
||||
7
contracts/erc721/src/wrappers/index.ts
Normal file
7
contracts/erc721/src/wrappers/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from '../../generated-wrappers/mintable_erc721_token';
|
||||
export * from '../../generated-wrappers/invalid_erc721_receiver';
|
||||
export * from '../../generated-wrappers/i_erc721_token';
|
||||
export * from '../../generated-wrappers/i_erc721_receiver';
|
||||
export * from '../../generated-wrappers/erc721_token';
|
||||
export * from '../../generated-wrappers/dummy_erc721_token';
|
||||
export * from '../../generated-wrappers/dummy_erc721_receiver';
|
||||
282
contracts/erc721/test/erc721_token.ts
Normal file
282
contracts/erc721/test/erc721_token.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
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 {
|
||||
artifacts,
|
||||
DummyERC721ReceiverContract,
|
||||
DummyERC721ReceiverTokenReceivedEventArgs,
|
||||
DummyERC721TokenContract,
|
||||
DummyERC721TokenTransferEventArgs,
|
||||
InvalidERC721ReceiverContract,
|
||||
} from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('ERC721Token', () => {
|
||||
let owner: string;
|
||||
let spender: string;
|
||||
let token: DummyERC721TokenContract;
|
||||
let erc721Receiver: DummyERC721ReceiverContract;
|
||||
let logDecoder: LogDecoder;
|
||||
const tokenId = new BigNumber(1);
|
||||
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 DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
);
|
||||
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await token.mint.sendTransactionAsync(owner, tokenId, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('transferFrom', () => {
|
||||
it('should revert if the tokenId is not owner', async () => {
|
||||
const from = owner;
|
||||
const to = erc721Receiver.address;
|
||||
const unownedTokenId = new BigNumber(2);
|
||||
await expectTransactionFailedAsync(
|
||||
token.transferFrom.sendTransactionAsync(from, to, unownedTokenId),
|
||||
RevertReason.Erc721ZeroOwner,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring to a null address', async () => {
|
||||
const from = owner;
|
||||
const to = constants.NULL_ADDRESS;
|
||||
await expectTransactionFailedAsync(
|
||||
token.transferFrom.sendTransactionAsync(from, to, tokenId),
|
||||
RevertReason.Erc721ZeroToAddress,
|
||||
);
|
||||
});
|
||||
it('should revert if the from address does not own the token', async () => {
|
||||
const from = spender;
|
||||
const to = erc721Receiver.address;
|
||||
await expectTransactionFailedAsync(
|
||||
token.transferFrom.sendTransactionAsync(from, to, tokenId),
|
||||
RevertReason.Erc721OwnerMismatch,
|
||||
);
|
||||
});
|
||||
it('should revert if spender does not own the token, is not approved, and is not approved for all', async () => {
|
||||
const from = owner;
|
||||
const to = erc721Receiver.address;
|
||||
await expectTransactionFailedAsync(
|
||||
token.transferFrom.sendTransactionAsync(from, to, tokenId, { from: spender }),
|
||||
RevertReason.Erc721InvalidSpender,
|
||||
);
|
||||
});
|
||||
it('should transfer the token if called by owner', async () => {
|
||||
const from = owner;
|
||||
const to = erc721Receiver.address;
|
||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await token.transferFrom.sendTransactionAsync(from, to, tokenId),
|
||||
);
|
||||
const newOwner = await token.ownerOf.callAsync(tokenId);
|
||||
expect(newOwner).to.be.equal(to);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
|
||||
expect(log.args._from).to.be.equal(from);
|
||||
expect(log.args._to).to.be.equal(to);
|
||||
expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
|
||||
});
|
||||
it('should transfer the token if spender is approved for all', async () => {
|
||||
const isApproved = true;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await token.setApprovalForAll.sendTransactionAsync(spender, isApproved),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
const from = owner;
|
||||
const to = erc721Receiver.address;
|
||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await token.transferFrom.sendTransactionAsync(from, to, tokenId),
|
||||
);
|
||||
const newOwner = await token.ownerOf.callAsync(tokenId);
|
||||
expect(newOwner).to.be.equal(to);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
|
||||
expect(log.args._from).to.be.equal(from);
|
||||
expect(log.args._to).to.be.equal(to);
|
||||
expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
|
||||
});
|
||||
it('should transfer the token if spender is individually approved', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await token.approve.sendTransactionAsync(spender, tokenId),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
const from = owner;
|
||||
const to = erc721Receiver.address;
|
||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await token.transferFrom.sendTransactionAsync(from, to, tokenId),
|
||||
);
|
||||
const newOwner = await token.ownerOf.callAsync(tokenId);
|
||||
expect(newOwner).to.be.equal(to);
|
||||
|
||||
const approvedAddress = await token.getApproved.callAsync(tokenId);
|
||||
expect(approvedAddress).to.be.equal(constants.NULL_ADDRESS);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
|
||||
expect(log.args._from).to.be.equal(from);
|
||||
expect(log.args._to).to.be.equal(to);
|
||||
expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
|
||||
});
|
||||
});
|
||||
describe('safeTransferFrom without data', () => {
|
||||
it('should transfer token to a non-contract address if called by owner', async () => {
|
||||
const from = owner;
|
||||
const to = spender;
|
||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
|
||||
);
|
||||
const newOwner = await token.ownerOf.callAsync(tokenId);
|
||||
expect(newOwner).to.be.equal(to);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
|
||||
expect(log.args._from).to.be.equal(from);
|
||||
expect(log.args._to).to.be.equal(to);
|
||||
expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
|
||||
});
|
||||
it('should revert if transferring to a contract address without onERC721Received', async () => {
|
||||
const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
);
|
||||
const from = owner;
|
||||
const to = contract.address;
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
|
||||
);
|
||||
});
|
||||
it('should revert if onERC721Received does not return the correct value', async () => {
|
||||
const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.InvalidERC721Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
const from = owner;
|
||||
const to = invalidErc721Receiver.address;
|
||||
await expectTransactionFailedAsync(
|
||||
token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
|
||||
RevertReason.Erc721InvalidSelector,
|
||||
);
|
||||
});
|
||||
it('should transfer to contract and call onERC721Received with correct return value', async () => {
|
||||
const from = owner;
|
||||
const to = erc721Receiver.address;
|
||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
|
||||
);
|
||||
const newOwner = await token.ownerOf.callAsync(tokenId);
|
||||
expect(newOwner).to.be.equal(to);
|
||||
const transferLog = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<DummyERC721ReceiverTokenReceivedEventArgs>;
|
||||
expect(transferLog.args._from).to.be.equal(from);
|
||||
expect(transferLog.args._to).to.be.equal(to);
|
||||
expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId);
|
||||
expect(receiverLog.args.operator).to.be.equal(owner);
|
||||
expect(receiverLog.args.from).to.be.equal(from);
|
||||
expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId);
|
||||
expect(receiverLog.args.data).to.be.equal(constants.NULL_BYTES);
|
||||
});
|
||||
});
|
||||
describe('safeTransferFrom with data', () => {
|
||||
const data = '0x0102030405060708090a0b0c0d0e0f';
|
||||
it('should transfer token to a non-contract address if called by owner', async () => {
|
||||
const from = owner;
|
||||
const to = spender;
|
||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
|
||||
);
|
||||
const newOwner = await token.ownerOf.callAsync(tokenId);
|
||||
expect(newOwner).to.be.equal(to);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
|
||||
expect(log.args._from).to.be.equal(from);
|
||||
expect(log.args._to).to.be.equal(to);
|
||||
expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
|
||||
});
|
||||
it('should revert if transferring to a contract address without onERC721Received', async () => {
|
||||
const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
);
|
||||
const from = owner;
|
||||
const to = contract.address;
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
|
||||
);
|
||||
});
|
||||
it('should revert if onERC721Received does not return the correct value', async () => {
|
||||
const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.InvalidERC721Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
const from = owner;
|
||||
const to = invalidErc721Receiver.address;
|
||||
await expectTransactionFailedAsync(
|
||||
token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
|
||||
RevertReason.Erc721InvalidSelector,
|
||||
);
|
||||
});
|
||||
it('should transfer to contract and call onERC721Received with correct return value', async () => {
|
||||
const from = owner;
|
||||
const to = erc721Receiver.address;
|
||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
|
||||
);
|
||||
const newOwner = await token.ownerOf.callAsync(tokenId);
|
||||
expect(newOwner).to.be.equal(to);
|
||||
const transferLog = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<DummyERC721ReceiverTokenReceivedEventArgs>;
|
||||
expect(transferLog.args._from).to.be.equal(from);
|
||||
expect(transferLog.args._to).to.be.equal(to);
|
||||
expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId);
|
||||
expect(receiverLog.args.operator).to.be.equal(owner);
|
||||
expect(receiverLog.args.from).to.be.equal(from);
|
||||
expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId);
|
||||
expect(receiverLog.args.data).to.be.equal(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:enable:no-unnecessary-type-assertion
|
||||
17
contracts/erc721/test/global_hooks.ts
Normal file
17
contracts/erc721/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();
|
||||
});
|
||||
19
contracts/erc721/tsconfig.json
Normal file
19
contracts/erc721/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": ".",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"./generated-artifacts/DummyERC721Receiver.json",
|
||||
"./generated-artifacts/InvalidERC721Receiver.json",
|
||||
"./generated-artifacts/DummyERC721Token.json",
|
||||
"./generated-artifacts/ERC721Token.json",
|
||||
"./generated-artifacts/IERC721Receiver.json",
|
||||
"./generated-artifacts/IERC721Token.json",
|
||||
"./generated-artifacts/MintableERC721Token.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
6
contracts/erc721/tslint.json
Normal file
6
contracts/erc721/tslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user