Permalink
Branch: master
Find file Copy path
180 lines (148 sloc) 8.02 KB
pragma solidity ^0.5.4;
import "./SafeMath.sol";
import "./Address.sol";
import "./IERC1155TokenReceiver.sol";
import "./IERC1155.sol";
// A sample implementation of core ERC1155 function.
contract ERC1155 is IERC1155, ERC165
{
using SafeMath for uint256;
using Address for address;
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
// id => (owner => balance)
mapping (uint256 => mapping(address => uint256)) internal balances;
// owner => (operator => approved)
mapping (address => mapping(address => bool)) internal operatorApproval;
/////////////////////////////////////////// ERC165 //////////////////////////////////////////////
/*
bytes4(keccak256('supportsInterface(bytes4)'));
*/
bytes4 constant private INTERFACE_SIGNATURE_ERC165 = 0x01ffc9a7;
/*
bytes4(keccak256("safeTransferFrom(address,address,uint256,uint256,bytes)")) ^
bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)")) ^
bytes4(keccak256("balanceOf(address,uint256)")) ^
bytes4(keccak256("balanceOfBatch(address[],uint256[])")) ^
bytes4(keccak256("setApprovalForAll(address,bool)")) ^
bytes4(keccak256("isApprovedForAll(address,address)"));
*/
bytes4 constant private INTERFACE_SIGNATURE_ERC1155 = 0xd9b67a26;
function supportsInterface(bytes4 _interfaceId)
public
view
returns (bool) {
if (_interfaceId == INTERFACE_SIGNATURE_ERC165 ||
_interfaceId == INTERFACE_SIGNATURE_ERC1155) {
return true;
}
return false;
}
/////////////////////////////////////////// ERC1155 //////////////////////////////////////////////
/**
@notice Transfers value amount of an _id from the _from address to the _to addresses specified. Each parameter array should be the same length, with each index correlating.
@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 `_id` is not a valid token ID.
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 addresses
@param _to Target addresses
@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 {
require(_to != address(0x0), "_to must be non-zero.");
require(_from == msg.sender || operatorApproval[_from][msg.sender] == true, "Need operator approval for 3rd party transfers.");
// SafeMath will throw with insuficient funds _from
// or if _id is not valid (balance will be 0)
balances[_id][_from] = balances[_id][_from].sub(_value);
balances[_id][_to] = _value.add(balances[_id][_to]);
emit TransferSingle(msg.sender, _from, _to, _id, _value);
if (_to.isContract()) {
require(IERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _id, _value, _data) == ERC1155_RECEIVED, "Receiver contract did not accept the transfer.");
}
}
/**
@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 any of the `_ids` is not a valid token ID.
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 address
@param _to Target address
@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 {
// MUST Throw on errors
require(_to != address(0x0), "destination address must be non-zero.");
require(_ids.length == _values.length, "_ids and _values array lenght must match.");
require(_from == msg.sender || operatorApproval[_from][msg.sender] == true, "Need operator approval for 3rd party transfers.");
for (uint256 i = 0; i < _ids.length; ++i) {
uint256 id = _ids[i];
uint256 value = _values[i];
// SafeMath will throw with insuficient funds _from
// or if _id is not valid (balance will be 0)
balances[id][_from] = balances[id][_from].sub(value);
balances[id][_to] = value.add(balances[id][_to]);
}
// MUST emit event
emit TransferBatch(msg.sender, _from, _to, _ids, _values);
// Now that the balances are updated,
// call onERC1155BatchReceived if the destination is a contract
if (_to.isContract()) {
require(IERC1155TokenReceiver(_to).onERC1155BatchReceived(msg.sender, _from, _ids, _values, _data) == ERC1155_BATCH_RECEIVED, "Receiver contract did not accept the transfer.");
}
}
/**
@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) {
// The balance of any account can be calculated from the Transfer events history.
// However, since we need to keep the balances to validate transfer request,
// there is no extra cost to also privide a querry function.
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) {
require(_owners.length == _ids.length);
uint256[] memory balances_ = new uint256[](_owners.length);
for (uint256 i = 0; i < _owners.length; ++i) {
balances_[i] = balances[_ids[i]][_owners[i]];
}
return balances_;
}
/**
@notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
@dev MUST emit the ApprovalForAll event on success.
@param _operator Address to 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];
}
}