|
| 1 | +pragma solidity ^0.5.0; |
| 2 | + |
| 3 | +import "./IERC1155.sol"; |
| 4 | +import "./IERC1155Receiver.sol"; |
| 5 | +import "../../math/SafeMath.sol"; |
| 6 | +import "../../utils/Address.sol"; |
| 7 | +import "../../introspection/ERC165.sol"; |
| 8 | + |
| 9 | +/** |
| 10 | + * @title Standard ERC1155 token |
| 11 | + * |
| 12 | + * @dev Implementation of the basic standard multi-token. |
| 13 | + * See https://eips.ethereum.org/EIPS/eip-1155 |
| 14 | + * Originally based on code by Enjin: https://github.com/enjin/erc-1155 |
| 15 | + */ |
| 16 | +contract ERC1155 is ERC165, IERC1155 |
| 17 | +{ |
| 18 | + using SafeMath for uint256; |
| 19 | + using Address for address; |
| 20 | + |
| 21 | + // Mapping from token ID to account balances |
| 22 | + mapping (uint256 => mapping(address => uint256)) private _balances; |
| 23 | + |
| 24 | + // Mapping from account to operator approvals |
| 25 | + mapping (address => mapping(address => bool)) private _operatorApprovals; |
| 26 | + |
| 27 | + constructor() |
| 28 | + public |
| 29 | + { |
| 30 | + _registerInterface( |
| 31 | + ERC1155(0).safeTransferFrom.selector ^ |
| 32 | + ERC1155(0).safeBatchTransferFrom.selector ^ |
| 33 | + ERC1155(0).balanceOf.selector ^ |
| 34 | + ERC1155(0).balanceOfBatch.selector ^ |
| 35 | + ERC1155(0).setApprovalForAll.selector ^ |
| 36 | + ERC1155(0).isApprovedForAll.selector |
| 37 | + ); |
| 38 | + } |
| 39 | + |
| 40 | + /** |
| 41 | + @dev Get the specified address' balance for token with specified ID. |
| 42 | +
|
| 43 | + Attempting to query the zero account for a balance will result in a revert. |
| 44 | +
|
| 45 | + @param account The address of the token holder |
| 46 | + @param id ID of the token |
| 47 | + @return The account's balance of the token type requested |
| 48 | + */ |
| 49 | + function balanceOf(address account, uint256 id) public view returns (uint256) { |
| 50 | + require(account != address(0), "ERC1155: balance query for the zero address"); |
| 51 | + return _balances[id][account]; |
| 52 | + } |
| 53 | + |
| 54 | + /** |
| 55 | + @dev Get the balance of multiple account/token pairs. |
| 56 | +
|
| 57 | + If any of the query accounts is the zero account, this query will revert. |
| 58 | +
|
| 59 | + @param accounts The addresses of the token holders |
| 60 | + @param ids IDs of the tokens |
| 61 | + @return Balances for each account and token id pair |
| 62 | + */ |
| 63 | + function balanceOfBatch( |
| 64 | + address[] memory accounts, |
| 65 | + uint256[] memory ids |
| 66 | + ) |
| 67 | + public |
| 68 | + view |
| 69 | + returns (uint256[] memory) |
| 70 | + { |
| 71 | + require(accounts.length == ids.length, "ERC1155: accounts and IDs must have same lengths"); |
| 72 | + |
| 73 | + uint256[] memory batchBalances = new uint256[](accounts.length); |
| 74 | + |
| 75 | + for (uint256 i = 0; i < accounts.length; ++i) { |
| 76 | + require(accounts[i] != address(0), "ERC1155: some address in batch balance query is zero"); |
| 77 | + batchBalances[i] = _balances[ids[i]][accounts[i]]; |
| 78 | + } |
| 79 | + |
| 80 | + return batchBalances; |
| 81 | + } |
| 82 | + |
| 83 | + /** |
| 84 | + * @dev Sets or unsets the approval of a given operator. |
| 85 | + * |
| 86 | + * An operator is allowed to transfer all tokens of the sender on their behalf. |
| 87 | + * |
| 88 | + * Because an account already has operator privileges for itself, this function will revert |
| 89 | + * if the account attempts to set the approval status for itself. |
| 90 | + * |
| 91 | + * @param operator address to set the approval |
| 92 | + * @param approved representing the status of the approval to be set |
| 93 | + */ |
| 94 | + function setApprovalForAll(address operator, bool approved) external { |
| 95 | + require(msg.sender != operator, "ERC1155: cannot set approval status for self"); |
| 96 | + _operatorApprovals[msg.sender][operator] = approved; |
| 97 | + emit ApprovalForAll(msg.sender, operator, approved); |
| 98 | + } |
| 99 | + |
| 100 | + /** |
| 101 | + @notice Queries the approval status of an operator for a given account. |
| 102 | + @param account The account of the Tokens |
| 103 | + @param operator Address of authorized operator |
| 104 | + @return True if the operator is approved, false if not |
| 105 | + */ |
| 106 | + function isApprovedForAll(address account, address operator) public view returns (bool) { |
| 107 | + return _operatorApprovals[account][operator]; |
| 108 | + } |
| 109 | + |
| 110 | + /** |
| 111 | + @dev Transfers `value` amount of an `id` from the `from` address to the `to` address specified. |
| 112 | + Caller must be approved to manage the tokens being transferred out of the `from` account. |
| 113 | + If `to` is a smart contract, will call `onERC1155Received` on `to` and act appropriately. |
| 114 | + @param from Source address |
| 115 | + @param to Target address |
| 116 | + @param id ID of the token type |
| 117 | + @param value Transfer amount |
| 118 | + @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver |
| 119 | + */ |
| 120 | + function safeTransferFrom( |
| 121 | + address from, |
| 122 | + address to, |
| 123 | + uint256 id, |
| 124 | + uint256 value, |
| 125 | + bytes calldata data |
| 126 | + ) |
| 127 | + external |
| 128 | + { |
| 129 | + require(to != address(0), "ERC1155: target address must be non-zero"); |
| 130 | + require( |
| 131 | + from == msg.sender || isApprovedForAll(from, msg.sender) == true, |
| 132 | + "ERC1155: need operator approval for 3rd party transfers" |
| 133 | + ); |
| 134 | + |
| 135 | + _balances[id][from] = _balances[id][from].sub(value, "ERC1155: insufficient balance for transfer"); |
| 136 | + _balances[id][to] = _balances[id][to].add(value); |
| 137 | + |
| 138 | + emit TransferSingle(msg.sender, from, to, id, value); |
| 139 | + |
| 140 | + _doSafeTransferAcceptanceCheck(msg.sender, from, to, id, value, data); |
| 141 | + } |
| 142 | + |
| 143 | + /** |
| 144 | + @dev Transfers `values` amount(s) of `ids` from the `from` address to the |
| 145 | + `to` address specified. Caller must be approved to manage the tokens being |
| 146 | + transferred out of the `from` account. If `to` is a smart contract, will |
| 147 | + call `onERC1155BatchReceived` on `to` and act appropriately. |
| 148 | + @param from Source address |
| 149 | + @param to Target address |
| 150 | + @param ids IDs of each token type |
| 151 | + @param values Transfer amounts per token type |
| 152 | + @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver |
| 153 | + */ |
| 154 | + function safeBatchTransferFrom( |
| 155 | + address from, |
| 156 | + address to, |
| 157 | + uint256[] calldata ids, |
| 158 | + uint256[] calldata values, |
| 159 | + bytes calldata data |
| 160 | + ) |
| 161 | + external |
| 162 | + { |
| 163 | + require(ids.length == values.length, "ERC1155: IDs and values must have same lengths"); |
| 164 | + require(to != address(0), "ERC1155: target address must be non-zero"); |
| 165 | + require( |
| 166 | + from == msg.sender || isApprovedForAll(from, msg.sender) == true, |
| 167 | + "ERC1155: need operator approval for 3rd party transfers" |
| 168 | + ); |
| 169 | + |
| 170 | + for (uint256 i = 0; i < ids.length; ++i) { |
| 171 | + uint256 id = ids[i]; |
| 172 | + uint256 value = values[i]; |
| 173 | + |
| 174 | + _balances[id][from] = _balances[id][from].sub( |
| 175 | + value, |
| 176 | + "ERC1155: insufficient balance of some token type for transfer" |
| 177 | + ); |
| 178 | + _balances[id][to] = _balances[id][to].add(value); |
| 179 | + } |
| 180 | + |
| 181 | + emit TransferBatch(msg.sender, from, to, ids, values); |
| 182 | + |
| 183 | + _doSafeBatchTransferAcceptanceCheck(msg.sender, from, to, ids, values, data); |
| 184 | + } |
| 185 | + |
| 186 | + /** |
| 187 | + * @dev Internal function to mint an amount of a token with the given ID |
| 188 | + * @param to The address that will own the minted token |
| 189 | + * @param id ID of the token to be minted |
| 190 | + * @param value Amount of the token to be minted |
| 191 | + * @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver |
| 192 | + */ |
| 193 | + function _mint(address to, uint256 id, uint256 value, bytes memory data) internal { |
| 194 | + require(to != address(0), "ERC1155: mint to the zero address"); |
| 195 | + |
| 196 | + _balances[id][to] = _balances[id][to].add(value); |
| 197 | + emit TransferSingle(msg.sender, address(0), to, id, value); |
| 198 | + |
| 199 | + _doSafeTransferAcceptanceCheck(msg.sender, address(0), to, id, value, data); |
| 200 | + } |
| 201 | + |
| 202 | + /** |
| 203 | + * @dev Internal function to batch mint amounts of tokens with the given IDs |
| 204 | + * @param to The address that will own the minted token |
| 205 | + * @param ids IDs of the tokens to be minted |
| 206 | + * @param values Amounts of the tokens to be minted |
| 207 | + * @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver |
| 208 | + */ |
| 209 | + function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal { |
| 210 | + require(to != address(0), "ERC1155: batch mint to the zero address"); |
| 211 | + require(ids.length == values.length, "ERC1155: minted IDs and values must have same lengths"); |
| 212 | + |
| 213 | + for(uint i = 0; i < ids.length; i++) { |
| 214 | + _balances[ids[i]][to] = values[i].add(_balances[ids[i]][to]); |
| 215 | + } |
| 216 | + |
| 217 | + emit TransferBatch(msg.sender, address(0), to, ids, values); |
| 218 | + |
| 219 | + _doSafeBatchTransferAcceptanceCheck(msg.sender, address(0), to, ids, values, data); |
| 220 | + } |
| 221 | + |
| 222 | + /** |
| 223 | + * @dev Internal function to burn an amount of a token with the given ID |
| 224 | + * @param account Account which owns the token to be burnt |
| 225 | + * @param id ID of the token to be burnt |
| 226 | + * @param value Amount of the token to be burnt |
| 227 | + */ |
| 228 | + function _burn(address account, uint256 id, uint256 value) internal { |
| 229 | + require(account != address(0), "ERC1155: attempting to burn tokens on zero account"); |
| 230 | + |
| 231 | + _balances[id][account] = _balances[id][account].sub( |
| 232 | + value, |
| 233 | + "ERC1155: attempting to burn more than balance" |
| 234 | + ); |
| 235 | + emit TransferSingle(msg.sender, account, address(0), id, value); |
| 236 | + } |
| 237 | + |
| 238 | + /** |
| 239 | + * @dev Internal function to batch burn an amounts of tokens with the given IDs |
| 240 | + * @param account Account which owns the token to be burnt |
| 241 | + * @param ids IDs of the tokens to be burnt |
| 242 | + * @param values Amounts of the tokens to be burnt |
| 243 | + */ |
| 244 | + function _burnBatch(address account, uint256[] memory ids, uint256[] memory values) internal { |
| 245 | + require(account != address(0), "ERC1155: attempting to burn batch of tokens on zero account"); |
| 246 | + require(ids.length == values.length, "ERC1155: burnt IDs and values must have same lengths"); |
| 247 | + |
| 248 | + for(uint i = 0; i < ids.length; i++) { |
| 249 | + _balances[ids[i]][account] = _balances[ids[i]][account].sub( |
| 250 | + values[i], |
| 251 | + "ERC1155: attempting to burn more than balance for some token" |
| 252 | + ); |
| 253 | + } |
| 254 | + |
| 255 | + emit TransferBatch(msg.sender, account, address(0), ids, values); |
| 256 | + } |
| 257 | + |
| 258 | + function _doSafeTransferAcceptanceCheck( |
| 259 | + address operator, |
| 260 | + address from, |
| 261 | + address to, |
| 262 | + uint256 id, |
| 263 | + uint256 value, |
| 264 | + bytes memory data |
| 265 | + ) |
| 266 | + internal |
| 267 | + { |
| 268 | + if(to.isContract()) { |
| 269 | + require( |
| 270 | + IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) == |
| 271 | + IERC1155Receiver(to).onERC1155Received.selector, |
| 272 | + "ERC1155: got unknown value from onERC1155Received" |
| 273 | + ); |
| 274 | + } |
| 275 | + } |
| 276 | + |
| 277 | + function _doSafeBatchTransferAcceptanceCheck( |
| 278 | + address operator, |
| 279 | + address from, |
| 280 | + address to, |
| 281 | + uint256[] memory ids, |
| 282 | + uint256[] memory values, |
| 283 | + bytes memory data |
| 284 | + ) |
| 285 | + internal |
| 286 | + { |
| 287 | + if(to.isContract()) { |
| 288 | + require( |
| 289 | + IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) == |
| 290 | + IERC1155Receiver(to).onERC1155BatchReceived.selector, |
| 291 | + "ERC1155: got unknown value from onERC1155BatchReceived" |
| 292 | + ); |
| 293 | + } |
| 294 | + } |
| 295 | +} |
0 commit comments