// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 3 of 6: ERC1155.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Minimalist and gas efficient standard ERC1155 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)abstractcontractERC1155{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransferSingle(addressindexed operator,
addressindexedfrom,
addressindexed to,
uint256 id,
uint256 amount
);
eventTransferBatch(addressindexed operator,
addressindexedfrom,
addressindexed to,
uint256[] ids,
uint256[] amounts
);
eventApprovalForAll(addressindexed owner,
addressindexed operator,
bool approved
);
eventURI(string value, uint256indexed id);
/*//////////////////////////////////////////////////////////////
ERC1155 STORAGE
//////////////////////////////////////////////////////////////*/mapping(address=>mapping(uint256=>uint256)) public balanceOf;
mapping(address=>mapping(address=>bool)) public _isApprovedForAll;
/*//////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/functionuri(uint256 id) publicviewvirtualreturns (stringmemory);
/*//////////////////////////////////////////////////////////////
ERC1155 LOGIC
//////////////////////////////////////////////////////////////*/functionisApprovedForAll(address owner,
address operator
) publicviewvirtualreturns (bool) {
return _isApprovedForAll[owner][operator];
}
functionsetApprovalForAll(address operator, bool approved) publicvirtual{
_isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
uint256 amount,
bytescalldata data
) publicvirtual{
require(
msg.sender==from|| _isApprovedForAll[from][msg.sender],
"NOT_AUTHORIZED"
);
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155Received(
msg.sender,
from,
id,
amount,
data
) == ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
functionsafeBatchTransferFrom(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytescalldata data
) publicvirtual{
require(ids.length== amounts.length, "LENGTH_MISMATCH");
require(
msg.sender==from|| _isApprovedForAll[from][msg.sender],
"NOT_AUTHORIZED"
);
// Storing these outside the loop saves ~15 gas per iteration.uint256 id;
uint256 amount;
for (uint256 i =0; i < ids.length; ) {
id = ids[i];
amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
// An array can't have a total length// larger than the max uint256 value.unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(
msg.sender,
from,
ids,
amounts,
data
) == ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
functionbalanceOfBatch(address[] calldata owners,
uint256[] calldata ids
) publicviewvirtualreturns (uint256[] memory balances) {
require(owners.length== ids.length, "LENGTH_MISMATCH");
balances =newuint256[](owners.length);
// Unchecked because the only math done is incrementing// the array index counter which cannot possibly overflow.unchecked {
for (uint256 i =0; i < owners.length; ++i) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/functionsupportsInterface(bytes4 interfaceId
) publicviewvirtualreturns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0xd9b67a26||// ERC165 Interface ID for ERC1155
interfaceId ==0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to,
uint256 id,
uint256 amount,
bytesmemory data
) internalvirtual{
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155Received(
msg.sender,
address(0),
id,
amount,
data
) == ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function_batchMint(address to,
uint256[] memory ids,
uint256[] memory amounts,
bytesmemory data
) internalvirtual{
uint256 idsLength = ids.length; // Saves MLOADs.require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i =0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
// An array can't have a total length// larger than the max uint256 value.unchecked {
++i;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(
msg.sender,
address(0),
ids,
amounts,
data
) == ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function_batchBurn(addressfrom,
uint256[] memory ids,
uint256[] memory amounts
) internalvirtual{
uint256 idsLength = ids.length; // Saves MLOADs.require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i =0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
// An array can't have a total length// larger than the max uint256 value.unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function_burn(addressfrom, uint256 id, uint256 amount) internalvirtual{
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)abstractcontractERC1155TokenReceiver{
functiononERC1155Received(address,
address,
uint256,
uint256,
bytescalldata) externalvirtualreturns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
functiononERC1155BatchReceived(address,
address,
uint256[] calldata,
uint256[] calldata,
bytescalldata) externalvirtualreturns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
}
Contract Source Code
File 4 of 6: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)pragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 5 of 6: Pausable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)pragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/abstractcontractPausableisContext{
/**
* @dev Emitted when the pause is triggered by `account`.
*/eventPaused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/eventUnpaused(address account);
boolprivate _paused;
/**
* @dev Initializes the contract in unpaused state.
*/constructor() {
_paused =false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/modifierwhenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/modifierwhenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/functionpaused() publicviewvirtualreturns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/function_requireNotPaused() internalviewvirtual{
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/function_requirePaused() internalviewvirtual{
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/function_pause() internalvirtualwhenNotPaused{
_paused =true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/function_unpause() internalvirtualwhenPaused{
_paused =false;
emit Unpaused(_msgSender());
}
}
Contract Source Code
File 6 of 6: Token.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.9;import"./Auth.sol";
import"./ERC1155.sol";
import"@openzeppelin/contracts/security/Pausable.sol";
import"@openzeppelin/contracts/access/Ownable.sol";
// import "hardhat/console.sol";structTokenConfig {
bool added;
bool canMint;
bool canBurn;
uint256 supplyLimit;
}
contractTokenisERC1155, Pausable, Ownable, Auth{
stringpublic name;
stringpublic symbol;
stringpublic contractURI;
stringprivate _uri;
mapping(address=>bool) private _approvalAllowlist;
uint16publicconstant ROLE_ADD_FT =1<<0;
uint16publicconstant ROLE_MODIFY_FT =1<<1;
uint16publicconstant ROLE_MINT_FT =1<<2;
uint16publicconstant ROLE_MINT_NFT =1<<3;
uint16publicconstant ROLE_BATCH_MINT_NFT =1<<4;
uint16publicconstant ROLE_BURN_FT =1<<5;
uint16publicconstant ROLE_BURN_NFT =1<<6;
uint16publicconstant ROLE_BATCH_BURN_NFT =1<<7;
uint16publicconstant ROLE_REFRESH_METADATA =1<<8;
uint16publicconstant ROLE_SET_PAUSED =1<<9;
uint16publicconstant ROLE_BYPASS_PAUSE =1<<10;
uint256publicconstant FUNGIBLE_TOKEN_UPPER_BOUND =10_000;
mapping(uint256=> TokenConfig) private _added;
mapping(uint256=>uint256) private _minted;
mapping(uint256=>uint256) private _burned;
constructor(stringmemory name_,
stringmemory symbol_,
stringmemory contractURI_,
stringmemory uri_
) ERC1155() {
setMetadata(name_, symbol_, contractURI_, uri_);
// Contract owner gets all roles by default. (11 roles, so the mask is 2^12 - 1 = 0b111_1111_1111.)
setRole(msg.sender, (1<<12) -1);
}
functionsetMetadata(stringmemory name_,
stringmemory symbol_,
stringmemory contractURI_,
stringmemory uri_
) publiconlyOwner{
name = name_;
symbol = symbol_;
contractURI = contractURI_;
_uri = uri_;
}
functionuri(uint256) publicviewoverride(ERC1155) returns (stringmemory) {
return _uri;
}
functionsetApprovalAllowlist(address operator,
bool approved
) publiconlyOwner{
_approvalAllowlist[operator] = approved;
}
functionisApprovedForAll(address owner,
address operator
) publicviewoverride(ERC1155) returns (bool) {
if (_approvalAllowlist[operator] ==true) {
returntrue;
}
returnsuper.isApprovedForAll(owner, operator);
}
functionsetPaused(bool b) publicrequireRole(ROLE_SET_PAUSED) {
if (b) {
require(b &&!paused(), "Contract is already paused");
_pause();
return;
}
require(!b && paused(), "Contract is not paused");
_unpause();
}
function_isFungible(uint256 id) internalpurereturns (bool) {
return id < FUNGIBLE_TOKEN_UPPER_BOUND;
}
function_supplyLimit(uint256 id) internalviewreturns (uint256) {
if (!_isFungible(id)) {
return1;
}
return _added[id].supplyLimit;
}
functionsupplyLimit(uint256 id) publicviewreturns (uint256) {
return _supplyLimit(id);
}
functiontotalSupply(uint256 id) publicviewreturns (uint256) {
return _minted[id] - _burned[id];
}
functionaddFT(uint256 id,
uint256 supplyLimit_,
bool canMint_,
bool canBurn_
) publicrequireRole(ROLE_ADD_FT) {
require(_added[id].added ==false, "Token already added.");
_added[id] = TokenConfig(true, canMint_, canBurn_, supplyLimit_);
emit TransferSingle(_msgSender(), address(0), address(0), id, 0);
}
functionmodifyFT(uint256 id,
uint256 supplyLimit_,
bool canMint_,
bool canBurn_
) publicrequireRole(ROLE_MODIFY_FT) {
require(_added[id].added ==true, "Token not added.");
_added[id] = TokenConfig(true, canMint_, canBurn_, supplyLimit_);
}
functionmintFT(address to,
uint256 tokenID,
uint256 quantity
) publicrequireRole(ROLE_MINT_FT) {
require(_isFungible(tokenID), "Token is not fungible.");
require(_added[tokenID].added, "Token type not added.");
require(_added[tokenID].canMint, "Token cannot be minted.");
require(
supplyLimit(tokenID) ==0||
(_minted[tokenID] + quantity <= supplyLimit(tokenID)),
"Mint would exceed supply limit."
);
_mint(to, tokenID, quantity, "");
_minted[tokenID] += quantity;
}
functionmintNFT(address to,
uint256 tokenID
) publicrequireRole(ROLE_MINT_NFT) {
require(!_isFungible(tokenID), "Token is fungible.");
require(_minted[tokenID] ==0, "Token is already minted.");
_minted[tokenID]++;
_mint(to, tokenID, 1, "");
}
functionbatchMintNFT(address to,
uint256[] calldata ids
) publicrequireRole(ROLE_BATCH_MINT_NFT) {
for (uint256 i =0; i < ids.length; i++) {
uint256 id = ids[i];
require(!_isFungible(id), "Token is fungible.");
require(_minted[id] ==0, "Token is already minted.");
}
for (uint256 i =0; i < ids.length; i++) {
uint256 id = ids[i];
_minted[id]++;
_mint(to, id, 1, "");
}
}
functionburnFT(address owner,
uint256 tokenID,
uint256 quantity
) publicrequireRole(ROLE_BURN_FT) {
require(_isFungible(tokenID), "Token is not fungible.");
require(_added[tokenID].added, "Token type not added.");
require(_added[tokenID].canBurn, "Token cannot be burned.");
_burn(owner, tokenID, quantity);
}
functionburnNFT(address owner,
uint256 tokenID
) publicrequireRole(ROLE_BURN_NFT) {
require(!_isFungible(tokenID), "Token is fungible.");
require(_minted[tokenID] ==1, "Token is not minted.");
_burned[tokenID]++;
_burn(owner, tokenID, 1);
}
functionbatchBurnNFT(address owner,
uint256[] calldata ids
) publicrequireRole(ROLE_BATCH_BURN_NFT) {
for (uint256 i =0; i < ids.length; i++) {
uint256 id = ids[i];
require(!_isFungible(id), "Token is fungible.");
require(_minted[id] ==1, "Token is not minted.");
}
for (uint256 i =0; i < ids.length; i++) {
uint256 id = ids[i];
_burned[id]++;
_burn(owner, id, 1);
}
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
uint256 amount,
bytescalldata data
) publicoverride(ERC1155) {
if (paused()) {
if (!_hasRole(_msgSender(), ROLE_BYPASS_PAUSE)) {
revert("Token is paused");
}
}
returnsuper.safeTransferFrom(from, to, id, amount, data);
}
functionsafeBatchTransferFrom(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytescalldata data
) publicoverride(ERC1155) {
if (paused()) {
if (!_hasRole(_msgSender(), ROLE_BYPASS_PAUSE)) {
revert("Token is paused");
}
}
returnsuper.safeBatchTransferFrom(from, to, ids, amounts, data);
}
eventMetadataUpdate(uint256 _tokenId);
eventBatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
functionupdateMetadata(uint256 id
) publicrequireRole(ROLE_REFRESH_METADATA) {
emit MetadataUpdate(id);
}
functionupdateAllMetadata() publicrequireRole(ROLE_REFRESH_METADATA) {
emit BatchMetadataUpdate(0, type(uint256).max);
}
functionsetRole(address operator, uint16 mask) publiconlyOwner{
_setRole(operator, mask);
}
functionhasRole(address operator, uint16 role) publicviewreturns (bool) {
return _hasRole(operator, role);
}
function_repeat(uint256 value,
uint256 length
) privatepurereturns (uint256[] memory) {
uint256[] memory array =newuint256[](length);
for (uint256 i =0; i < length; i++) {
array[i] = value;
}
return array;
}
}