Skip to content

Instantly share code, notes, and snippets.

@nohehf
Created May 1, 2022 08:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nohehf/3a1116e47d932bb9477bbc5332e61a9a to your computer and use it in GitHub Desktop.
Save nohehf/3a1116e47d932bb9477bbc5332e61a9a to your computer and use it in GitHub Desktop.
// 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