This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.9; | |
import {LibStorage, AppStorage, ArtefactType} from "./LibStorage.sol"; | |
import "@solidstate/contracts/token/ERC1155/base/ERC1155BaseStorage.sol"; | |
import "@solidstate/contracts/token/ERC1155/base/ERC1155BaseInternal.sol"; | |
// import {ERC1155Facet} from "../facets/ERC1155Facet.sol"; | |
import "../facets/ERC1155Facet.sol"; | |
// Copy of the internal functions defined in: @solidstate/contracts/token/ERC1155/base/ERC1155BaseInternal.sol & associated events | |
// This is made to be able to call internal functions of the ERC1155Facet within different contracts | |
// The code had to be slightly modified: Added events from the corresponding interface and removed " " keyword of functions | |
// NOTE: This SHOULD be simplified by solidstate by providing a library. | |
/** | |
* @title Base ERC1155 internal functions | |
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) | |
*/ | |
library LibERC1155Internal { | |
// COPY OF THE EVENTS: | |
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); | |
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values); | |
event ApprovalForAll(address indexed account, address indexed operator, bool approved); | |
using AddressUtils for address; | |
/** | |
* @notice query the balance of given token held by given address | |
* @param account address to query | |
* @param id token to query | |
* @return token balance | |
*/ | |
function _balanceOf(address account, uint256 id) internal view returns (uint256) { | |
require(account != address(0), "ERC1155: balance query for the zero address"); | |
return ERC1155BaseStorage.layout().balances[id][account]; | |
} | |
/** | |
* @notice mint given quantity of tokens for given address | |
* @dev ERC1155Receiver implementation is not checked | |
* @param account beneficiary of minting | |
* @param id token ID | |
* @param amount quantity of tokens to mint | |
* @param data data payload | |
*/ | |
function _mint( | |
address account, | |
uint256 id, | |
uint256 amount, | |
bytes memory data | |
) internal { | |
require(account != address(0), "ERC1155: mint to the zero address"); | |
_beforeTokenTransfer(msg.sender, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data); | |
ERC1155BaseStorage.layout().balances[id][account] += amount; | |
emit TransferSingle(msg.sender, address(0), account, id, amount); | |
} | |
/** | |
* @notice mint given quantity of tokens for given address | |
* @param account beneficiary of minting | |
* @param id token ID | |
* @param amount quantity of tokens to mint | |
* @param data data payload | |
*/ | |
function _safeMint( | |
address account, | |
uint256 id, | |
uint256 amount, | |
bytes memory data | |
) internal { | |
_mint(account, id, amount, data); | |
_doSafeTransferAcceptanceCheck(msg.sender, address(0), account, id, amount, data); | |
} | |
/** | |
* @notice mint batch of tokens for given address | |
* @dev ERC1155Receiver implementation is not checked | |
* @param account beneficiary of minting | |
* @param ids list of token IDs | |
* @param amounts list of quantities of tokens to mint | |
* @param data data payload | |
*/ | |
function _mintBatch( | |
address account, | |
uint256[] memory ids, | |
uint256[] memory amounts, | |
bytes memory data | |
) internal { | |
require(account != address(0), "ERC1155: mint to the zero address"); | |
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); | |
_beforeTokenTransfer(msg.sender, address(0), account, ids, amounts, data); | |
mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; | |
for (uint256 i; i < ids.length; ) { | |
balances[ids[i]][account] += amounts[i]; | |
unchecked { | |
i++; | |
} | |
} | |
emit TransferBatch(msg.sender, address(0), account, ids, amounts); | |
} | |
/** | |
* @notice mint batch of tokens for given address | |
* @param account beneficiary of minting | |
* @param ids list of token IDs | |
* @param amounts list of quantities of tokens to mint | |
* @param data data payload | |
*/ | |
function _safeMintBatch( | |
address account, | |
uint256[] memory ids, | |
uint256[] memory amounts, | |
bytes memory data | |
) internal { | |
_mintBatch(account, ids, amounts, data); | |
_doSafeBatchTransferAcceptanceCheck(msg.sender, address(0), account, ids, amounts, data); | |
} | |
/** | |
* @notice burn given quantity of tokens held by given address | |
* @param account holder of tokens to burn | |
* @param id token ID | |
* @param amount quantity of tokens to burn | |
*/ | |
function _burn( | |
address account, | |
uint256 id, | |
uint256 amount | |
) internal { | |
require(account != address(0), "ERC1155: burn from the zero address"); | |
_beforeTokenTransfer(msg.sender, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), ""); | |
mapping(address => uint256) storage balances = ERC1155BaseStorage.layout().balances[id]; | |
unchecked { | |
require(balances[account] >= amount, "ERC1155: burn amount exceeds balances"); | |
balances[account] -= amount; | |
} | |
emit TransferSingle(msg.sender, account, address(0), id, amount); | |
} | |
/** | |
* @notice burn given batch of tokens held by given address | |
* @param account holder of tokens to burn | |
* @param ids token IDs | |
* @param amounts quantities of tokens to burn | |
*/ | |
function _burnBatch( | |
address account, | |
uint256[] memory ids, | |
uint256[] memory amounts | |
) internal { | |
require(account != address(0), "ERC1155: burn from the zero address"); | |
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); | |
_beforeTokenTransfer(msg.sender, account, address(0), ids, amounts, ""); | |
mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; | |
unchecked { | |
for (uint256 i; i < ids.length; i++) { | |
uint256 id = ids[i]; | |
require(balances[id][account] >= amounts[i], "ERC1155: burn amount exceeds balance"); | |
balances[id][account] -= amounts[i]; | |
} | |
} | |
emit TransferBatch(msg.sender, account, address(0), ids, amounts); | |
} | |
/** | |
* @notice transfer tokens between given addresses | |
* @dev ERC1155Receiver implementation is not checked | |
* @param operator executor of transfer | |
* @param sender sender of tokens | |
* @param recipient receiver of tokens | |
* @param id token ID | |
* @param amount quantity of tokens to transfer | |
* @param data data payload | |
*/ | |
function _transfer( | |
address operator, | |
address sender, | |
address recipient, | |
uint256 id, | |
uint256 amount, | |
bytes memory data | |
) internal { | |
require(recipient != address(0), "ERC1155: transfer to the zero address"); | |
_beforeTokenTransfer(operator, sender, recipient, _asSingletonArray(id), _asSingletonArray(amount), data); | |
mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; | |
unchecked { | |
uint256 senderBalance = balances[id][sender]; | |
require(senderBalance >= amount, "ERC1155: insufficient balances for transfer"); | |
balances[id][sender] = senderBalance - amount; | |
} | |
balances[id][recipient] += amount; | |
emit TransferSingle(operator, sender, recipient, id, amount); | |
} | |
/** | |
* @notice transfer tokens between given addresses | |
* @param operator executor of transfer | |
* @param sender sender of tokens | |
* @param recipient receiver of tokens | |
* @param id token ID | |
* @param amount quantity of tokens to transfer | |
* @param data data payload | |
*/ | |
function _safeTransfer( | |
address operator, | |
address sender, | |
address recipient, | |
uint256 id, | |
uint256 amount, | |
bytes memory data | |
) internal { | |
_transfer(operator, sender, recipient, id, amount, data); | |
_doSafeTransferAcceptanceCheck(operator, sender, recipient, id, amount, data); | |
} | |
/** | |
* @notice transfer batch of tokens between given addresses | |
* @dev ERC1155Receiver implementation is not checked | |
* @param operator executor of transfer | |
* @param sender sender of tokens | |
* @param recipient receiver of tokens | |
* @param ids token IDs | |
* @param amounts quantities of tokens to transfer | |
* @param data data payload | |
*/ | |
function _transferBatch( | |
address operator, | |
address sender, | |
address recipient, | |
uint256[] memory ids, | |
uint256[] memory amounts, | |
bytes memory data | |
) internal { | |
require(recipient != address(0), "ERC1155: transfer to the zero address"); | |
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); | |
_beforeTokenTransfer(operator, sender, recipient, ids, amounts, data); | |
mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; | |
for (uint256 i; i < ids.length; ) { | |
uint256 token = ids[i]; | |
uint256 amount = amounts[i]; | |
unchecked { | |
uint256 senderBalance = balances[token][sender]; | |
require(senderBalance >= amount, "ERC1155: insufficient balances for transfer"); | |
balances[token][sender] = senderBalance - amount; | |
i++; | |
} | |
// balance increase cannot be unchecked because ERC1155Base neither tracks nor validates a totalSupply | |
balances[token][recipient] += amount; | |
} | |
emit TransferBatch(operator, sender, recipient, ids, amounts); | |
} | |
/** | |
* @notice transfer batch of tokens between given addresses | |
* @param operator executor of transfer | |
* @param sender sender of tokens | |
* @param recipient receiver of tokens | |
* @param ids token IDs | |
* @param amounts quantities of tokens to transfer | |
* @param data data payload | |
*/ | |
function _safeTransferBatch( | |
address operator, | |
address sender, | |
address recipient, | |
uint256[] memory ids, | |
uint256[] memory amounts, | |
bytes memory data | |
) internal { | |
_transferBatch(operator, sender, recipient, ids, amounts, data); | |
_doSafeBatchTransferAcceptanceCheck(operator, sender, recipient, ids, amounts, data); | |
} | |
/** | |
* @notice wrap given element in array of length 1 | |
* @param element element to wrap | |
* @return singleton array | |
*/ | |
function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { | |
uint256[] memory array = new uint256[](1); | |
array[0] = element; | |
return array; | |
} | |
/** | |
* @notice revert if applicable transfer recipient is not valid ERC1155Receiver | |
* @param operator executor of transfer | |
* @param from sender of tokens | |
* @param to receiver of tokens | |
* @param id token ID | |
* @param amount quantity of tokens to transfer | |
* @param data data payload | |
*/ | |
function _doSafeTransferAcceptanceCheck( | |
address operator, | |
address from, | |
address to, | |
uint256 id, | |
uint256 amount, | |
bytes memory data | |
) private { | |
if (to.isContract()) { | |
try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { | |
require(response == IERC1155Receiver.onERC1155Received.selector, "ERC1155: ERC1155Receiver rejected tokens"); | |
} catch Error(string memory reason) { | |
revert(reason); | |
} catch { | |
revert("ERC1155: transfer to non ERC1155Receiver implementer"); | |
} | |
} | |
} | |
/** | |
* @notice revert if applicable transfer recipient is not valid ERC1155Receiver | |
* @param operator executor of transfer | |
* @param from sender of tokens | |
* @param to receiver of tokens | |
* @param ids token IDs | |
* @param amounts quantities of tokens to transfer | |
* @param data data payload | |
*/ | |
function _doSafeBatchTransferAcceptanceCheck( | |
address operator, | |
address from, | |
address to, | |
uint256[] memory ids, | |
uint256[] memory amounts, | |
bytes memory data | |
) private { | |
if (to.isContract()) { | |
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (bytes4 response) { | |
require(response == IERC1155Receiver.onERC1155BatchReceived.selector, "ERC1155: ERC1155Receiver rejected tokens"); | |
} catch Error(string memory reason) { | |
revert(reason); | |
} catch { | |
revert("ERC1155: transfer to non ERC1155Receiver implementer"); | |
} | |
} | |
} | |
/** | |
* @notice ERC1155 hook, called before all transfers including mint and burn | |
* @dev function should be overridden and new implementation must call super | |
* @dev called for both single and batch transfers | |
* @param operator executor of transfer | |
* @param from sender of tokens | |
* @param to receiver of tokens | |
* @param ids token IDs | |
* @param amounts quantities of tokens to transfer | |
* @param data data payload | |
*/ | |
function _beforeTokenTransfer( | |
address operator, | |
address from, | |
address to, | |
uint256[] memory ids, | |
uint256[] memory amounts, | |
bytes memory data | |
) internal {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment