pragma solidity ^0.5.0; import "./IERC1155.sol"; import "./IERC1155TokenReceiver.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; import "openzeppelin-solidity/contracts/utils/Address.sol"; import "openzeppelin-solidity/contracts/introspection/ERC165.sol"; /** * @title Standard ERC1155 token * * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 * Originally based on code by Enjin: https://github.com/enjin/erc-1155 */ contract ERC1155 is ERC165, IERC1155 { using SafeMath for uint256; using Address for address; // Mapping from token ID to owner balances mapping (uint256 => mapping(address => uint256)) private _balances; // Mapping from owner to operator approvals mapping (address => mapping(address => bool)) private _operatorApprovals; constructor() public { _registerInterface( ERC1155(0).safeTransferFrom.selector ^ ERC1155(0).safeBatchTransferFrom.selector ^ ERC1155(0).balanceOf.selector ^ ERC1155(0).balanceOfBatch.selector ^ ERC1155(0).setApprovalForAll.selector ^ ERC1155(0).isApprovedForAll.selector ); } /** @dev Get the specified address' balance for token with specified ID. @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) public view returns (uint256) { require(owner != address(0), "ERC1155: balance query for the zero address"); return _balances[id][owner]; } /** @dev Get the balance of multiple account/token pairs @param owners The addresses of the token holders @param ids IDs of the tokens @return Balances for each owner and token id pair */ function balanceOfBatch( address[] memory owners, uint256[] memory ids ) public view returns (uint256[] memory) { require(owners.length == ids.length, "ERC1155: owners and IDs must have same lengths"); uint256[] memory batchBalances = new uint256[](owners.length); for (uint256 i = 0; i < owners.length; ++i) { require(owners[i] != address(0), "ERC1155: some address in batch balance query is zero"); batchBalances[i] = _balances[ids[i]][owners[i]]; } return batchBalances; } /** * @dev Sets or unsets the approval of a given operator * An operator is allowed to transfer all tokens of the sender on their behalf * @param operator address to set the approval * @param approved representing the status of the approval to be set */ function setApprovalForAll(address operator, bool approved) external { _operatorApprovals[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 _operatorApprovals[owner][operator]; } /** @dev Transfers `value` amount of an `id` from the `from` address to the `to` address specified. Caller must be approved to manage the tokens being transferred out of the `from` account. If `to` is a smart contract, will call `onERC1155Received` on `to` and act appropriately. @param from Source address @param to Target address @param id ID of the token type @param value Transfer amount @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver */ function safeTransferFrom( address from, address to, uint256 id, uint256 value, bytes calldata data ) external { require(to != address(0), "ERC1155: target address must be non-zero"); require( from == msg.sender || _operatorApprovals[from][msg.sender] == true, "ERC1155: need operator approval for 3rd party transfers." ); _balances[id][from] = _balances[id][from].sub(value); _balances[id][to] = value.add(_balances[id][to]); emit TransferSingle(msg.sender, from, to, id, value); _doSafeTransferAcceptanceCheck(msg.sender, from, to, id, value, data); } /** @dev Transfers `values` amount(s) of `ids` from the `from` address to the `to` address specified. Caller must be approved to manage the tokens being transferred out of the `from` account. If `to` is a smart contract, will call `onERC1155BatchReceived` on `to` and act appropriately. @param from Source address @param to Target address @param ids IDs of each token type @param values Transfer amounts per token type @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external { require(ids.length == values.length, "ERC1155: IDs and values must have same lengths"); require(to != address(0), "ERC1155: target address must be non-zero"); require( from == msg.sender || _operatorApprovals[from][msg.sender] == true, "ERC1155: need operator approval for 3rd party transfers." ); for (uint256 i = 0; i < ids.length; ++i) { uint256 id = ids[i]; uint256 value = values[i]; _balances[id][from] = _balances[id][from].sub(value); _balances[id][to] = value.add(_balances[id][to]); } emit TransferBatch(msg.sender, from, to, ids, values); _doSafeBatchTransferAcceptanceCheck(msg.sender, from, to, ids, values, data); } /** * @dev Internal function to mint an amount of a token with the given ID * @param to The address that will own the minted token * @param id ID of the token to be minted * @param value Amount of the token to be minted * @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver */ function _mint(address to, uint256 id, uint256 value, bytes memory data) internal { require(to != address(0), "ERC1155: mint to the zero address"); _balances[id][to] = value.add(_balances[id][to]); emit TransferSingle(msg.sender, address(0), to, id, value); _doSafeTransferAcceptanceCheck(msg.sender, address(0), to, id, value, data); } /** * @dev Internal function to burn an amount of a token with the given ID * @param owner Account which owns the token to be burnt * @param id ID of the token to be burnt * @param value Amount of the token to be burnt */ function _burn(address owner, uint256 id, uint256 value) internal { _balances[id][owner] = _balances[id][owner].sub(value); emit TransferSingle(msg.sender, owner, address(0), id, value); } function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 value, bytes memory data ) internal { if(to.isContract()) { require( IERC1155TokenReceiver(to).onERC1155Received(operator, from, id, value, data) == IERC1155TokenReceiver(to).onERC1155Received.selector, "ERC1155: got unknown value from onERC1155Received" ); } } function _doSafeBatchTransferAcceptanceCheck( address operator, address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data ) internal { if(to.isContract()) { require( IERC1155TokenReceiver(to).onERC1155BatchReceived(operator, from, ids, values, data) == IERC1155TokenReceiver(to).onERC1155BatchReceived.selector, "ERC1155: got unknown value from onERC1155BatchReceived" ); } } }