// File: contracts/interfaces/IERC1155Metadata.sol
pragma solidity 0.7.4;
interface IERC1155Metadata {
event URI(string _uri, uint256 indexed _id);
/****************************************|
| Functions |
|_______________________________________*/
/**
* @notice A distinct Uniform Resource Identifier (URI) for a given token.
* @dev URIs are defined in RFC 3986.
* URIs are assumed to be deterministically generated based on token ID
* Token IDs are assumed to be represented in their hex format in URIs
* @return URI string
*/
function uri(uint256 _id) external view returns (string memory);
}
// File: contracts/utils/Ownable.sol
pragma solidity 0.7.4;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner_;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () {
_owner_ = msg.sender;
emit OwnershipTransferred(address(0), _owner_);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == _owner_, "Ownable#onlyOwner: SENDER_IS_NOT_OWNER");
_;
}
/**
* @notice Transfers the ownership of the contract to new address
* @param _newOwner Address of the new owner
*/
function transferOwnership(address _newOwner) public onlyOwner {
require(_newOwner != address(0), "Ownable#transferOwnership: INVALID_ADDRESS");
emit OwnershipTransferred(_owner_, _newOwner);
_owner_ = _newOwner;
}
/**
* @notice Returns the address of the owner.
*/
function owner() public view returns (address) {
return _owner_;
}
}
// File: contracts/utils/Address.sol
pragma solidity 0.7.4;
/**
* Utility library of inline functions on addresses
*/
library Address {
// Default hash for EOA accounts returned by extcodehash
bytes32 constant internal ACCOUNT_HASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract.
* @param _address address of the account to check
* @return Whether the target address is a contract
*/
function isContract(address _address) internal view returns (bool) {
bytes32 codehash;
// 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 or if it has a non-zero code hash or account hash
assembly { codehash := extcodehash(_address) }
return (codehash != 0x0 && codehash != ACCOUNT_HASH);
}
}
// File: contracts/interfaces/IERC165.sol
pragma solidity 0.7.4;
/**
* @title ERC165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
*/
interface IERC165 {
/**
* @notice Query if a contract implements an interface
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas
* @param _interfaceId The interface identifier, as specified in ERC-165
*/
function supportsInterface(bytes4 _interfaceId)
external
view
returns (bool);
}
// File: contracts/utils/ERC165.sol
pragma solidity 0.7.4;
abstract contract ERC165 is IERC165 {
/**
* @notice Query if a contract implements an interface
* @param _interfaceID The interface identifier, as specified in ERC-165
* @return `true` if the contract implements `_interfaceID`
*/
function supportsInterface(bytes4 _interfaceID) virtual override public view returns (bool) {
return _interfaceID == this.supportsInterface.selector;
}
}
// File: contracts/interfaces/IERC1155.sol
pragma solidity 0.7.4;
interface IERC1155 is IERC165 {
/****************************************|
| Events |
|_______________________________________*/
/**
* @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, including zero amount transfers as well as minting or burning
* Operator MUST be msg.sender
* When minting/creating tokens, the `_from` field MUST be set to `0x0`
* When burning/destroying tokens, the `_to` field MUST be set to `0x0`
* The total amount transferred from address 0x0 minus the total amount transferred to 0x0 may be used by clients and exchanges to be added to the "circulating supply" for a given token ID
* To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the TransferSingle event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_amount` of 0
*/
event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _amount);
/**
* @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, including zero amount transfers as well as minting or burning
* Operator MUST be msg.sender
* When minting/creating tokens, the `_from` field MUST be set to `0x0`
* When burning/destroying tokens, the `_to` field MUST be set to `0x0`
* The total amount transferred from address 0x0 minus the total amount transferred to 0x0 may be used by clients and exchanges to be added to the "circulating supply" for a given token ID
* To broadcast the existence of multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_amount` of 0
*/
event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _amounts);
/**
* @dev MUST emit when an approval is updated
*/
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/****************************************|
| Functions |
|_______________________________________*/
/**
* @notice Transfers 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 `_amount` 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 amount 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 _amount Transfered amount
* @param _data Additional data with no specified format, sent in call to `_to`
*/
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes calldata _data) external;
/**
* @notice Send multiple types of Tokens from the _from address to the _to address (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 `_amounts`
* MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_amounts` 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 amount is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* Transfers and events MUST occur in the array order they were submitted (_ids[0] before _ids[1], etc)
* @param _from Source addresses
* @param _to Target addresses
* @param _ids IDs of each token type
* @param _amounts 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 _amounts, bytes calldata _data) external;
/**
* @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 (i.e. balance for each (owner, id) pair)
*/
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
/**
* @notice Enable or disable approval for a third party ("operator") to manage all of 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 isOperator True if the operator is approved, false if not
*/
function isApprovedForAll(address _owner, address _operator) external view returns (bool isOperator);
}
// File: contracts/interfaces/IERC1155TokenReceiver.sol
pragma solidity 0.7.4;
/**
* @dev ERC-1155 interface for accepting safe transfers.
*/
interface IERC1155TokenReceiver {
/**
* @notice Handle the receipt of a single ERC1155 token type
* @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated
* This function MAY throw to revert and reject the transfer
* Return of other amount than the magic value MUST result in the transaction being reverted
* Note: The token contract address is always the message sender
* @param _operator The address which called the `safeTransferFrom` function
* @param _from The address which previously owned the token
* @param _id The id of the token being transferred
* @param _amount 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 _amount, bytes calldata _data) external returns(bytes4);
/**
* @notice Handle the receipt of multiple ERC1155 token types
* @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated
* This function MAY throw to revert and reject the transfer
* Return of other amount than the magic value WILL result in the transaction being reverted
* Note: The token contract address is always the message sender
* @param _operator The address which called the `safeBatchTransferFrom` function
* @param _from The address which previously owned the token
* @param _ids An array containing ids of each token being transferred
* @param _amounts An array containing amounts of each token being transferred
* @param _data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
*/
function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _amounts, bytes calldata _data) external returns(bytes4);
}
// File: contracts/utils/SafeMath.sol
pragma solidity 0.7.4;
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath#mul: OVERFLOW");
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath#div: DIVISION_BY_ZERO");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath#sub: UNDERFLOW");
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath#add: OVERFLOW");
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath#mod: DIVISION_BY_ZERO");
return a % b;
}
}
// File: contracts/ERC1155PackedBalance.sol
pragma solidity 0.7.4;
/**
* @dev Implementation of Multi-Token Standard contract. This implementation of the ERC-1155 standard
* utilizes the fact that balances of different token ids can be concatenated within individual
* uint256 storage slots. This allows the contract to batch transfer tokens more efficiently at
* the cost of limiting the maximum token balance each address can hold. This limit is
* 2^IDS_BITS_SIZE, which can be adjusted below. In practice, using IDS_BITS_SIZE smaller than 16
* did not lead to major efficiency gains.
*/
contract ERC1155PackedBalance is IERC1155, ERC165 {
using SafeMath for uint256;
using Address for address;
/***********************************|
| Variables and Events |
|__________________________________*/
// onReceive function signatures
bytes4 constant internal ERC1155_RECEIVED_VALUE = 0xf23a6e61;
bytes4 constant internal ERC1155_BATCH_RECEIVED_VALUE = 0xbc197c81;
// Constants regarding bin sizes for balance packing
// IDS_BITS_SIZE **MUST** be a power of 2 (e.g. 2, 4, 8, 16, 32, 64, 128)
uint256 internal constant IDS_BITS_SIZE = 32; // Max balance amount in bits per token ID
uint256 internal constant IDS_PER_UINT256 = 256 / IDS_BITS_SIZE; // Number of ids per uint256
// Operations for _updateIDBalance
enum Operations { Add, Sub }
// Token IDs balances ; balances[address][id] => balance (using array instead of mapping for efficiency)
mapping (address => mapping(uint256 => uint256)) internal balances;
// Operators
mapping (address => mapping(address => bool)) internal operators;
/***********************************|
| Public Transfer Functions |
|__________________________________*/
/**
* @notice Transfers amount amount of an _id from the _from address to the _to address specified
* @param _from Source address
* @param _to Target address
* @param _id ID of the token type
* @param _amount Transfered amount
* @param _data Additional data with no specified format, sent in call to `_to`
*/
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes memory _data)
public override
{
// Requirements
require((msg.sender == _from) || isApprovedForAll(_from, msg.sender), "ERC1155PackedBalance#safeTransferFrom: INVALID_OPERATOR");
require(_to != address(0),"ERC1155PackedBalance#safeTransferFrom: INVALID_RECIPIENT");
// require(_amount <= balances); Not necessary since checked with _viewUpdateBinValue() checks
_safeTransferFrom(_from, _to, _id, _amount);
_callonERC1155Received(_from, _to, _id, _amount, gasleft(), _data);
}
/**
* @notice Send multiple types of Tokens from the _from address to the _to address (with safety call)
* @dev Arrays should be sorted so that all ids in a same storage slot are adjacent (more efficient)
* @param _from Source addresses
* @param _to Target addresses
* @param _ids IDs of each token type
* @param _amounts 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[] memory _ids, uint256[] memory _amounts, bytes memory _data)
public override
{
// Requirements
require((msg.sender == _from) || isApprovedForAll(_from, msg.sender), "ERC1155PackedBalance#safeBatchTransferFrom: INVALID_OPERATOR");
require(_to != address(0),"ERC1155PackedBalance#safeBatchTransferFrom: INVALID_RECIPIENT");
_safeBatchTransferFrom(_from, _to, _ids, _amounts);
_callonERC1155BatchReceived(_from, _to, _ids, _amounts, gasleft(), _data);
}
/***********************************|
| Internal Transfer Functions |
|__________________________________*/
/**
* @notice Transfers amount amount of an _id from the _from address to the _to address specified
* @param _from Source address
* @param _to Target address
* @param _id ID of the token type
* @param _amount Transfered amount
*/
function _safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount)
internal
{
//Update balances
_updateIDBalance(_from, _id, _amount, Operations.Sub); // Subtract amount from sender
_updateIDBalance(_to, _id, _amount, Operations.Add); // Add amount to recipient
// Emit event
emit TransferSingle(msg.sender, _from, _to, _id, _amount);
}
/**
* @notice Verifies if receiver is contract and if so, calls (_to).onERC1155Received(...)
*/
function _callonERC1155Received(address _from, address _to, uint256 _id, uint256 _amount, uint256 _gasLimit, bytes memory _data)
internal
{
// Check if recipient is contract
if (_to.isContract()) {
bytes4 retval = IERC1155TokenReceiver(_to).onERC1155Received{gas:_gasLimit}(msg.sender, _from, _id, _amount, _data);
require(retval == ERC1155_RECEIVED_VALUE, "ERC1155PackedBalance#_callonERC1155Received: INVALID_ON_RECEIVE_MESSAGE");
}
}
/**
* @notice Send multiple types of Tokens from the _from address to the _to address (with safety call)
* @dev Arrays should be sorted so that all ids in a same storage slot are adjacent (more efficient)
* @param _from Source addresses
* @param _to Target addresses
* @param _ids IDs of each token type
* @param _amounts Transfer amounts per token type
*/
function _safeBatchTransferFrom(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts)
internal
{
uint256 nTransfer = _ids.length; // Number of transfer to execute
require(nTransfer == _amounts.length, "ERC1155PackedBalance#_safeBatchTransferFrom: INVALID_ARRAYS_LENGTH");
if (_from != _to && nTransfer > 0) {
// Load first bin and index where the token ID balance exists
(uint256 bin, uint256 index) = getIDBinIndex(_ids[0]);
// Balance for current bin in memory (initialized with first transfer)
uint256 balFrom = _viewUpdateBinValue(balances[_from][bin], index, _amounts[0], Operations.Sub);
uint256 balTo = _viewUpdateBinValue(balances[_to][bin], index, _amounts[0], Operations.Add);
// Last bin updated
uint256 lastBin = bin;
for (uint256 i = 1; i < nTransfer; i++) {
(bin, index) = getIDBinIndex(_ids[i]);
// If new bin
if (bin != lastBin) {
// Update storage balance of previous bin
balances[_from][lastBin] = balFrom;
balances[_to][lastBin] = balTo;
balFrom = balances[_from][bin];
balTo = balances[_to][bin];
// Bin will be the most recent bin
lastBin = bin;
}
// Update memory balance
balFrom = _viewUpdateBinValue(balFrom, index, _amounts[i], Operations.Sub);
balTo = _viewUpdateBinValue(balTo, index, _amounts[i], Operations.Add);
}
// Update storage of the last bin visited
balances[_from][bin] = balFrom;
balances[_to][bin] = balTo;
// If transfer to self, just make sure all amounts are valid
} else {
for (uint256 i = 0; i < nTransfer; i++) {
require(balanceOf(_from, _ids[i]) >= _amounts[i], "ERC1155PackedBalance#_safeBatchTransferFrom: UNDERFLOW");
}
}
// Emit event
emit TransferBatch(msg.sender, _from, _to, _ids, _amounts);
}
/**
* @notice Verifies if receiver is contract and if so, calls (_to).onERC1155BatchReceived(...)
*/
function _callonERC1155BatchReceived(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts, uint256 _gasLimit, bytes memory _data)
internal
{
// Pass data if recipient is contract
if (_to.isContract()) {
bytes4 retval = IERC1155TokenReceiver(_to).onERC1155BatchReceived{gas: _gasLimit}(msg.sender, _from, _ids, _amounts, _data);
require(retval == ERC1155_BATCH_RECEIVED_VALUE, "ERC1155PackedBalance#_callonERC1155BatchReceived: INVALID_ON_RECEIVE_MESSAGE");
}
}
/***********************************|
| Operator Functions |
|__________________________________*/
/**
* @notice Enable or disable approval for a third party ("operator") to manage all of caller's tokens
* @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 override
{
// Update operator status
operators[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 isOperator True if the operator is approved, false if not
*/
function isApprovedForAll(address _owner, address _operator)
public override view returns (bool isOperator)
{
return operators[_owner][_operator];
}
/***********************************|
| Public Balance Functions |
|__________________________________*/
/**
* @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)
public override view returns (uint256)
{
uint256 bin;
uint256 index;
//Get bin and index of _id
(bin, index) = getIDBinIndex(_id);
return getValueInBin(balances[_owner][bin], index);
}
/**
* @notice Get the balance of multiple account/token pairs
* @param _owners The addresses of the token holders (sorted owners will lead to less gas usage)
* @param _ids ID of the Tokens (sorted ids will lead to less gas usage
* @return The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair)
*/
function balanceOfBatch(address[] memory _owners, uint256[] memory _ids)
public override view returns (uint256[] memory)
{
uint256 n_owners = _owners.length;
require(n_owners == _ids.length, "ERC1155PackedBalance#balanceOfBatch: INVALID_ARRAY_LENGTH");
// First values
(uint256 bin, uint256 index) = getIDBinIndex(_ids[0]);
uint256 balance_bin = balances[_owners[0]][bin];
uint256 last_bin = bin;
// Initialization
uint256[] memory batchBalances = new uint256[](n_owners);
batchBalances[0] = getValueInBin(balance_bin, index);
// Iterate over each owner and token ID
for (uint256 i = 1; i < n_owners; i++) {
(bin, index) = getIDBinIndex(_ids[i]);
// SLOAD if bin changed for the same owner or if owner changed
if (bin != last_bin || _owners[i-1] != _owners[i]) {
balance_bin = balances[_owners[i]][bin];
last_bin = bin;
}
batchBalances[i] = getValueInBin(balance_bin, index);
}
return batchBalances;
}
/***********************************|
| Packed Balance Functions |
|__________________________________*/
/**
* @notice Update the balance of a id for a given address
* @param _address Address to update id balance
* @param _id Id to update balance of
* @param _amount Amount to update the id balance
* @param _operation Which operation to conduct :
* Operations.Add: Add _amount to id balance
* Operations.Sub: Substract _amount from id balance
*/
function _updateIDBalance(address _address, uint256 _id, uint256 _amount, Operations _operation)
internal
{
uint256 bin;
uint256 index;
// Get bin and index of _id
(bin, index) = getIDBinIndex(_id);
// Update balance
balances[_address][bin] = _viewUpdateBinValue(balances[_address][bin], index, _amount, _operation);
}
/**
* @notice Update a value in _binValues
* @param _binValues Uint256 containing values of size IDS_BITS_SIZE (the token balances)
* @param _index Index of the value in the provided bin
* @param _amount Amount to update the id balance
* @param _operation Which operation to conduct :
* Operations.Add: Add _amount to value in _binValues at _index
* Operations.Sub: Substract _amount from value in _binValues at _index
*/
function _viewUpdateBinValue(uint256 _binValues, uint256 _index, uint256 _amount, Operations _operation)
internal pure returns (uint256 newBinValues)
{
uint256 shift = IDS_BITS_SIZE * _index;
uint256 mask = (uint256(1) << IDS_BITS_SIZE) - 1;
if (_operation == Operations.Add) {
newBinValues = _binValues + (_amount << shift);
require(newBinValues >= _binValues, "ERC1155PackedBalance#_viewUpdateBinValue: OVERFLOW");
require(
((_binValues >> shift) & mask) + _amount < 2**IDS_BITS_SIZE, // Checks that no other id changed
"ERC1155PackedBalance#_viewUpdateBinValue: OVERFLOW"
);
} else if (_operation == Operations.Sub) {
newBinValues = _binValues - (_amount << shift);
require(newBinValues <= _binValues, "ERC1155PackedBalance#_viewUpdateBinValue: UNDERFLOW");
require(
((_binValues >> shift) & mask) >= _amount, // Checks that no other id changed
"ERC1155PackedBalance#_viewUpdateBinValue: UNDERFLOW"
);
} else {
revert("ERC1155PackedBalance#_viewUpdateBinValue: INVALID_BIN_WRITE_OPERATION"); // Bad operation
}
return newBinValues;
}
/**
* @notice Return the bin number and index within that bin where ID is
* @param _id Token id
* @return bin index (Bin number, ID"s index within that bin)
*/
function getIDBinIndex(uint256 _id)
public pure returns (uint256 bin, uint256 index)
{
bin = _id / IDS_PER_UINT256;
index = _id % IDS_PER_UINT256;
return (bin, index);
}
/**
* @notice Return amount in _binValues at position _index
* @param _binValues uint256 containing the balances of IDS_PER_UINT256 ids
* @param _index Index at which to retrieve amount
* @return amount at given _index in _bin
*/
function getValueInBin(uint256 _binValues, uint256 _index)
public pure returns (uint256)
{
// require(_index < IDS_PER_UINT256) is not required since getIDBinIndex ensures `_index < IDS_PER_UINT256`
// Mask to retrieve data for a given binData
uint256 mask = (uint256(1) << IDS_BITS_SIZE) - 1;
// Shift amount
uint256 rightShift = IDS_BITS_SIZE * _index;
return (_binValues >> rightShift) & mask;
}
/***********************************|
| ERC165 Functions |
|__________________________________*/
/**
* @notice Query if a contract implements an interface
* @param _interfaceID The interface identifier, as specified in ERC-165
* @return `true` if the contract implements `_interfaceID` and
*/
function supportsInterface(bytes4 _interfaceID) public override(ERC165, IERC165) virtual view returns (bool) {
if (_interfaceID == type(IERC1155).interfaceId) {
return true;
}
return super.supportsInterface(_interfaceID);
}
}
// File: contracts/ERC1155MintBurnPackedBalance.sol
pragma solidity 0.7.4;
/**
* @dev Multi-Fungible Tokens with minting and burning methods. These methods assume
* a parent contract to be executed as they are `internal` functions.
*/
contract ERC1155MintBurnPackedBalance is ERC1155PackedBalance {
/****************************************|
| Minting Functions |
|_______________________________________*/
/**
* @notice Mint _amount of tokens of a given id
* @param _to The address to mint tokens to
* @param _id Token id to mint
* @param _amount The amount to be minted
* @param _data Data to pass if receiver is contract
*/
function _mint(address _to, uint256 _id, uint256 _amount, bytes memory _data)
internal
{
//Add _amount
_updateIDBalance(_to, _id, _amount, Operations.Add); // Add amount to recipient
// Emit event
emit TransferSingle(msg.sender, address(0x0), _to, _id, _amount);
// Calling onReceive method if recipient is contract
_callonERC1155Received(address(0x0), _to, _id, _amount, gasleft(), _data);
}
/**
* @notice Mint tokens for each (_ids[i], _amounts[i]) pair
* @param _to The address to mint tokens to
* @param _ids Array of ids to mint
* @param _amounts Array of amount of tokens to mint per id
* @param _data Data to pass if receiver is contract
*/
function _batchMint(address _to, uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data)
internal
{
require(_ids.length == _amounts.length, "ERC1155MintBurnPackedBalance#_batchMint: INVALID_ARRAYS_LENGTH");
if (_ids.length > 0) {
// Load first bin and index where the token ID balance exists
(uint256 bin, uint256 index) = getIDBinIndex(_ids[0]);
// Balance for current bin in memory (initialized with first transfer)
uint256 balTo = _viewUpdateBinValue(balances[_to][bin], index, _amounts[0], Operations.Add);
// Number of transfer to execute
uint256 nTransfer = _ids.length;
// Last bin updated
uint256 lastBin = bin;
for (uint256 i = 1; i < nTransfer; i++) {
(bin, index) = getIDBinIndex(_ids[i]);
// If new bin
if (bin != lastBin) {
// Update storage balance of previous bin
balances[_to][lastBin] = balTo;
balTo = balances[_to][bin];
// Bin will be the most recent bin
lastBin = bin;
}
// Update memory balance
balTo = _viewUpdateBinValue(balTo, index, _amounts[i], Operations.Add);
}
// Update storage of the last bin visited
balances[_to][bin] = balTo;
}
// //Emit event
emit TransferBatch(msg.sender, address(0x0), _to, _ids, _amounts);
// Calling onReceive method if recipient is contract
_callonERC1155BatchReceived(address(0x0), _to, _ids, _amounts, gasleft(), _data);
}
/****************************************|
| Burning Functions |
|_______________________________________*/
/**
* @notice Burn _amount of tokens of a given token id
* @param _from The address to burn tokens from
* @param _id Token id to burn
* @param _amount The amount to be burned
*/
function _burn(address _from, uint256 _id, uint256 _amount)
internal
{
// Substract _amount
_updateIDBalance(_from, _id, _amount, Operations.Sub);
// Emit event
emit TransferSingle(msg.sender, _from, address(0x0), _id, _amount);
}
/**
* @notice Burn tokens of given token id for each (_ids[i], _amounts[i]) pair
* @dev This batchBurn method does not implement the most efficient way of updating
* balances to reduce the potential bug surface as this function is expected to
* be less common than transfers. EIP-2200 makes this method significantly
* more efficient already for packed balances.
* @param _from The address to burn tokens from
* @param _ids Array of token ids to burn
* @param _amounts Array of the amount to be burned
*/
function _batchBurn(address _from, uint256[] memory _ids, uint256[] memory _amounts)
internal
{
// Number of burning to execute
uint256 nBurn = _ids.length;
require(nBurn == _amounts.length, "ERC1155MintBurnPackedBalance#batchBurn: INVALID_ARRAYS_LENGTH");
// Executing all burning
for (uint256 i = 0; i < nBurn; i++) {
// Update storage balance
_updateIDBalance(_from, _ids[i], _amounts[i], Operations.Sub); // Add amount to recipient
}
// Emit batch burn event
emit TransferBatch(msg.sender, _from, address(0x0), _ids, _amounts);
}
}
// File: contracts/ResistanceClub.sol
pragma solidity 0.7.4;
contract ResistanceClub is ERC1155MintBurnPackedBalance, Ownable, IERC1155Metadata {
uint256 public constant RED = 0;
uint256 public constant BEIGE = 1;
mapping (uint => string) private _metadata;
string private _contractMetadataURI;
uint96 royaltyFeesInBips;
address royaltyAddress;
function setMetadata(uint _tokenId, string calldata newMetadata) public onlyOwner {
_metadata[_tokenId] = newMetadata;
}
function setContractMetadataURI(string memory contractMetadataURI) public onlyOwner {
_contractMetadataURI = contractMetadataURI;
}
function contractURI() public view returns (string memory) {
return _contractMetadataURI;
}
function uri (uint _tokenId) override public view returns (string memory) {
return _metadata[_tokenId];
}
function name() public pure returns (string memory) {
return "Resistance Club";
}
function symbol() public pure returns (string memory) {
return "RESISTANCE";
}
function setRoyaltyInfo(address _receiver, uint96 _royaltyFeesInBips) public onlyOwner {
royaltyAddress = _receiver;
royaltyFeesInBips = _royaltyFeesInBips;
}
function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
external
view
virtual
returns (address, uint256)
{
return (royaltyAddress, calculateRoyalty(_salePrice));
}
function calculateRoyalty(uint256 _salePrice) view public returns (uint256) {
return (_salePrice / 10000) * royaltyFeesInBips;
}
function supportsInterface(bytes4 interfaceId) public view override returns (bool){
return interfaceId == 0x2a55205a || super.supportsInterface(interfaceId);
}
constructor() ERC1155MintBurnPackedBalance() {
_mint(address(0x2DADCD5248FB059a47192b8b182c8811E0ECDc9e), RED, 50, "");
_mint(address(0x2DADCD5248FB059a47192b8b182c8811E0ECDc9e), BEIGE, 100, "");
_metadata[0] = "ipfs://QmQXaU23fXTbM3BCkj4811pyAgcZAf8ukT14wskJYkPr2r";
_metadata[1] = "ipfs://QmehLQ8vmizor9uEXE2HDwh3rCBfJ8BPTmUECg81udon8n";
_contractMetadataURI = "ipfs://QmVa6y1jqCAbXH9HG6GjHATnjch5Cqmj799SM34yfZbsZy";
royaltyFeesInBips = 1000;
royaltyAddress = 0x2DADCD5248FB059a47192b8b182c8811E0ECDc9e;
}
}
{
"compilationTarget": {
"ResistanceClub.sol": "ResistanceClub"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":false,"internalType":"bool","name":"_approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"_uri","type":"string"},{"indexed":true,"internalType":"uint256","name":"_id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[],"name":"BEIGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_owners","type":"address[]"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"calculateRoyalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getIDBinIndex","outputs":[{"internalType":"uint256","name":"bin","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_binValues","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getValueInBin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"isOperator","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contractMetadataURI","type":"string"}],"name":"setContractMetadataURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"string","name":"newMetadata","type":"string"}],"name":"setMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint96","name":"_royaltyFeesInBips","type":"uint96"}],"name":"setRoyaltyInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]