Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"WantedTributes.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.29;\r
\r
// _ __ __ __ \r
// | | / /___ _____ / /____ ____/ / \r
// | | /| / / __ `/ __ \/ __/ _ \/ __ / \r
// | |/ |/ / /_/ / / / / /_/ __/ /_/ / \r
// |__/|__/\__,_/_/ /_/\__/\___/\__,_/ \r
// ______ _ __ __ \r
// /_ __/____(_) /_ __ __/ /____ _____\r
// / / / ___/ / __ \/ / / / __/ _ \/ ___/\r
// / / / / / / /_/ / /_/ / /_/ __(__ ) \r
// /_/ /_/ /_/_.___/\__,_/\__/\___/____/ \r
//\r
// Creator: MadV0x\r
// Deployer: Syntetik Labs\r
\r
import { ERC1155TransferValidator } from "src/lib/ERC1155TransferValidator.sol";\r
import { ICreatorToken, ILegacyCreatorToken } from "src/interfaces/ICreatorToken.sol";\r
import { ReentrancyGuard } from "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";\r
import { ITransferValidator1155 } from "src/interfaces/ITransferValidator.sol";\r
import { ERC1155Airdrops } from "WANTED/lib/ERC1155Airdrops.sol";\r
import { WCTDropErrorsAndEvents } from "WANTED/lib/WCTDropErrorsAndEvents.sol";\r
\r
/**\r
* @title WantedTributes: WANTED Cyborgs Tributes (WCT).\r
* @notice Implements ERC1155Airdrops: Enables batch transfers of ERC1155 tokens.\r
* Implements Limit Break's Creator Token Standards transfer\r
* validation for security and royalty enforcement.\r
* Implements Approvals restrictions : Only the owner or admin can approve operators \r
* until the contract is unlocked or ownership has been renounced.\r
* Implements Admin role management: Allows assignment of an admin address with\r
* privileged permissions. \r
* @notice Implements Royalties Gamification: Dynamic royalty mechanisms, \r
* active only if/after ownership has been renounced.\r
**/\r
\r
contract WantedTributes is ERC1155Airdrops, ERC1155TransferValidator, ReentrancyGuard, WCTDropErrorsAndEvents {\r
\r
address public Admin;\r
address public WCTMaster;\r
uint256 public tokenControlledByMaster;\r
bool public marketsUnLocked = false;\r
\r
constructor(\r
string memory _name,\r
string memory _symbol)\r
ERC1155Airdrops(_name, _symbol) {\r
}\r
\r
/**\r
* @dev ICreatorToken Interface implementation.\r
*/\r
function supportsInterface(bytes4 interfaceId) public view override(ERC1155Airdrops) returns (bool) {\r
return\r
interfaceId == type(ICreatorToken).interfaceId ||\r
interfaceId == type(ILegacyCreatorToken).interfaceId ||\r
interfaceId == 0x49064906 || // ERC-4906\r
super.supportsInterface(interfaceId);\r
}\r
\r
/**\r
* @dev Internal helper function to verify if the caller is either the contract owner or the designated admin.\r
* Reverts with `NotOwnerNorAdmin` error if unauthorized.\r
*/ \r
function _isOwnerOrAdmin() private view {\r
if(msg.sender != owner() && msg.sender != Admin) {\r
revert NotAdminOrOwner();\r
}\r
}\r
\r
/**\r
* @notice Grants the Admin role to a specified account. Use zero address to revoke.\r
* @dev Can only be called by the owner or current admin.\r
*\r
* @param newAdmin The address to assign as Admin. Use `address(0)` to revoke the role.\r
*/\r
function grantAdminRole(address newAdmin) external {\r
_isOwnerOrAdmin();\r
Admin = newAdmin; // Grant the role to an address\r
emit ContractAdminRoleGranted(newAdmin);\r
}\r
\r
/**\r
* @notice Returns the selector of the transfer validation function used by the contract.\r
* @dev Indicates whether the function is a view or modifies state.\r
*\r
* @return functionSignature The function selector of `validateTransfer`.\r
* @return isViewFunction Boolean indicating if the function is a view function (false in this case).\r
*/\r
function getTransferValidationFunction() external override pure returns (bytes4 functionSignature, bool isViewFunction) {\r
functionSignature = ITransferValidator1155.validateTransfer.selector;\r
isViewFunction = false;\r
}\r
\r
/**\r
* @notice Sets the address of the transfer validator contract (Admin only).\r
* @dev The transfer validator enforces custom rules on token transfers.\r
*\r
* @param newValidator The address of the new transfer validator contract.\r
*/\r
function setTransferValidator(address newValidator) external override {\r
_isOwnerOrAdmin();\r
super._setTransferValidator(newValidator);\r
}\r
\r
/**\r
* @dev Internal hook called before any token transfer, including minting.\r
* Enforces royalty or transfer restrictions via an external validator contract, if set.\r
*\r
* Note Derived from Limit Break's ERC1155-C implementation.\r
* Allows configurable transfer validation logics for on-chain royalty enforcement,\r
* operator filtering, and marketplace restrictions.\r
* \r
* Requirements:\r
* - If both `from` and `to` are non-zero (i.e., not mint or burn), the transfer validator (if configured)\r
* is called to validate the transfer.\r
*/\r
function _beforeTokenTransfers(\r
address from,\r
address to,\r
uint256 id,\r
uint256 quantity,\r
bytes memory //*data//* \r
) internal override {\r
if (from != address(0) && to != address(0)) {\r
// Call the transfer validator if one is set.\r
address transferValidator = _transferValidator;\r
if (transferValidator != address(0)) {\r
ITransferValidator1155(transferValidator).validateTransfer(\r
msg.sender,\r
from,\r
to,\r
id,\r
quantity\r
);\r
}\r
}\r
super._beforeTokenTransfers(from, to, id, quantity,"");\r
}\r
\r
/**\r
* @notice Updates the royalty recipient address and royalty fee basis points (Admin only).\r
* @dev Reverts if the royalty address is zero or if the basis points exceed 10,000 (100%).\r
*\r
* @param RoyaltyAddress The address to receive royalty payments.\r
* @param RoyaltyFeesInBips The royalty fee in basis points (1 basis point = 0.01%).\r
*/\r
function setRoyaltyInfo(address RoyaltyAddress, uint96 RoyaltyFeesInBips) external virtual {\r
_isOwnerOrAdmin();\r
// Revert if the new royalty address is the zero address.\r
if (RoyaltyAddress == address(0)) {\r
revert RoyaltyAddressCannotBeZeroAddress();\r
}\r
\r
// Revert if the new basis points is greater than 10_000.\r
if (RoyaltyFeesInBips > 10_000) {\r
revert InvalidRoyaltyBasisPoints(RoyaltyFeesInBips);\r
}\r
\r
// Set the new royalty info.\r
_setDefaultRoyalty(RoyaltyAddress, RoyaltyFeesInBips);\r
\r
// Emit an event with the updated params.\r
emit RoyaltyInfoUpdated(RoyaltyAddress, RoyaltyFeesInBips);\r
}\r
\r
/**\r
* @dev Overrides ERC1155 `setApprovalForAll` to restrict operator approvals\r
* while the contract remains locked or ownership has not been renounced.\r
* Until then, only the owner or Admin can grant approvals, enforcing a\r
* temporary soulbound-like behavior.\r
*\r
* @param operator The address to grant or revoke approval for as an operator.\r
* @param approved Boolean indicating the approval status (`true` = approve, `false` = revoke).\r
*/\r
function setApprovalForAll(address operator, bool approved) public virtual override {\r
\r
if (!marketsUnLocked || owner() != address(0) || msg.sender != owner() || msg.sender != Admin) {\r
revert ContractNotUnLocked();\r
}\r
\r
super.setApprovalForAll(operator, approved);\r
}\r
\r
/**\r
* @notice Permanently unlocks contract-wide approvals.\r
* @dev Can only be called once by the contract owner or admin.\r
* Sets `marketsUnLocked` to true, enabling unrestricted approvals.\r
* Reverts if the contract is already unlocked.\r
*/\r
function unlockMarkets() external {\r
require(!marketsUnLocked, "unlocked");\r
_isOwnerOrAdmin();\r
marketsUnLocked = true;\r
}\r
\r
/**\r
* @notice On-chain gamification and community engagement feature:\r
* Allows a user to claim the title of "WCT Leader" for a specific tokenId by owning\r
* more than 0.5% of that token’s total supply and exceeding the previous claimer's balance.\r
* Claiming temporarily transfers the token’s royalty share to the new leader until someone else claims it.\r
* \r
* Activation requires that the contract ownership has been renounced (owner == address(0)).\r
*\r
* @param tokenId The specific token ID for which the WCT Leader title is being claimed.\r
*\r
* Reverts if:\r
* - Contract ownership is not renounced.\r
* - The token does not exist or is controlled by the WCTMaster.\r
* - Caller does not hold a sufficient quantity (<1%) or does not exceed the previous leader’s balance.\r
*/\r
function claimWCTLeader(uint256 tokenId) external nonReentrant {\r
\r
if(owner() != address(0)) {\r
revert OwnershipNotRenounced();\r
}\r
\r
if(totalSupply(tokenId) == 0 || getWCTLeader(tokenId) == WCTMaster) {\r
revert TokenControlledByWCTMasterOrDoNotExist();\r
} \r
RoyaltyInfo storage _royaltyInfo = _tokenRoyaltyInfo[tokenId];\r
\r
// Claimer must hold >0.5% of the token supply and more tokenId than previous claimer.\r
if(balanceOf(msg.sender, tokenId) < (totalSupply(tokenId) * 1/100) \r
&& balanceOf(msg.sender, tokenId) <= balanceOf(_royaltyInfo.receiver, tokenId)) {\r
revert NotMajorityTokenOwner(msg.sender, tokenId);\r
}\r
// Set the new royalty info to the default contract royalties.\r
uint96 royalty = _defaultRoyaltyInfo.royaltyFraction;\r
_setTokenRoyalty(tokenId, msg.sender, royalty);\r
\r
// Emit an event with the updated params.\r
emit TokenRoyaltyInfoUpdated(tokenId, msg.sender, royalty);\r
emit WCTLeaderClaimed(msg.sender, tokenId);\r
}\r
\r
/**\r
* @notice Returns the current WCT Leader (royalty recipient) for the specified tokenId.\r
* @param tokenId The token ID to query.\r
* @return The address of the current WCT Leader for that token.\r
*/\r
function getWCTLeader(uint256 tokenId) public view returns (address) {\r
return _tokenRoyaltyInfo[tokenId].receiver;\r
}\r
\r
/**\r
* @notice On-chain gamification: Claim the title of **WCT Master** by owning a significant share of WCT tokens.\r
* @dev Caller becomes the global contract Leader ("WCT Master") and takes over any one tokenId’s royalties.\r
* Can only be claimed if contract ownership has been renounced (`owner == address(0)`).\r
* WCT Master must:\r
* - Own more than 0.05% of the total WCT supply.\r
* - Hold more WCT tokens than the previous WCT Master.\r
* - Not already be the current WCT Master.\r
*\r
* The newly claimed tokenId will be assigned to the WCT Master for royalty control.\r
* If a previous token was controlled by the WCT Master, its royalty will be reset to default.\r
*\r
* @param tokenId The tokenId for which the caller wants to take over royalty control.\r
*\r
* Reverts if:\r
* - Contract ownership is not renounced.\r
* - tokenId is invalid or already controlled.\r
* - Caller doesn't meet the supply ownership conditions.\r
*/\r
function claimWCTMaster(uint256 tokenId) external nonReentrant {\r
\r
if(owner() != address(0)) {\r
revert OwnershipNotRenounced();\r
}\r
\r
if(tokenId > totalTokenIdsMinted || WCTMaster == msg.sender) {\r
revert WCTMasterAlreadyClaimedorTokenDoNotExist();\r
} \r
\r
// Claimer must hold >0.05% of the total WCTs supply and more WCTs than previous Master.\r
if(totalBalanceOf(msg.sender) < (globalSupply() *5/10000)\r
&& totalBalanceOf(msg.sender) <= totalBalanceOf(WCTMaster)) {\r
revert NotWCTMaster(msg.sender);\r
}\r
\r
uint96 royalty = _defaultRoyaltyInfo.royaltyFraction;\r
// Reset previous tokenId to default royalties.\r
if (tokenControlledByMaster != 0) {\r
_setTokenRoyalty(tokenControlledByMaster, _defaultRoyaltyInfo.receiver, royalty);\r
emit TokenRoyaltyInfoUpdated(tokenControlledByMaster, _defaultRoyaltyInfo.receiver, royalty);\r
tokenControlledByMaster = tokenId;\r
} \r
\r
// Set the new tokenroyalty info to the WCTLeader's address.\r
WCTMaster = msg.sender;\r
_setTokenRoyalty(tokenId, WCTMaster, royalty);\r
\r
// Emit an event with the updated params.\r
emit TokenRoyaltyInfoUpdated(tokenId, WCTMaster, royalty);\r
emit WCTMasterClaimed(WCTMaster, tokenId);\r
}\r
\r
/**\r
* @notice Burn a specific amount of tokens of a given tokenId from an account (irreversible).\r
* @dev Caller must be the token owner or an approved operator.\r
*\r
* @param account The address from which tokens will be burned.\r
* @param tokenId The token ID to burn.\r
* @param amount The number of tokens to burn.\r
*/\r
function burn(address account, uint256 tokenId, uint256 amount) external {\r
require(account == msg.sender || isApprovedForAll(account, msg.sender), "Not authorized");\r
_burn(account, tokenId, amount);\r
}\r
\r
/**\r
* @notice Withdraw all POL from the contract to the owner's address.\r
* @dev Only callable by the contract owner. Reverts if no balance is available or if the transfer fails.\r
*/ \r
function withdraw() external nonReentrant {\r
_isOwnerOrAdmin();\r
uint256 balance = address(this).balance;\r
require(balance > 0, "No funds to withdraw");\r
\r
(bool success, ) = payable(msg.sender).call{value: balance}("");\r
require(success, "Withdrawal to owner failed");\r
}\r
\r
}"
},
"WANTED/lib/WCTDropErrorsAndEvents.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.29;\r
\r
interface WCTDropErrorsAndEvents {\r
\r
/**\r
* @dev Emit an event when Manager Role is granted.\r
*/\r
event ContractAdminRoleGranted(address account);\r
\r
/**\r
* @dev Emit an event when the royalties info is updated.\r
*/\r
event RoyaltyInfoUpdated(address receiver, uint256 bps);\r
\r
/**\r
* @dev Emit an event when the token royalties info is updated.\r
*/\r
event TokenRoyaltyInfoUpdated (uint256 tokenId, address receiver, uint256 royalties);\r
\r
/**\r
* @dev Emit an event when a Token's Leadership is claimed.\r
*/\r
event WCTLeaderClaimed(address account, uint256 tokenId);\r
\r
/**\r
* @dev Emit an event when the Contract's Master Position is claimed.\r
*/\r
event WCTMasterClaimed(address account, uint256 tokenId);\r
\r
/**\r
* @dev Emit an event when the Contract's metadata is Frozen.\r
*/\r
event MetadataFrozen();\r
\r
/**\r
* @dev Revert if the royalty basis points is greater than 10_000.\r
*/\r
error InvalidRoyaltyBasisPoints(uint256 basisPoints);\r
\r
/**\r
* @dev Revert if the royalty address is being set to the zero address.\r
*/\r
error RoyaltyAddressCannotBeZeroAddress();\r
\r
/**\r
* @dev Revert if the caller is not Majority holder.\r
*/\r
error NotMajorityTokenOwner(address caller, uint256 tokenId);\r
\r
/**\r
* @dev Revert if the caller is not eligible as Contract Master.\r
*/\r
error NotWCTMaster(address account);\r
\r
/**\r
* @dev Revert if caller is not Admin or Owner.\r
*/\r
error NotAdminOrOwner();\r
\r
/**\r
* @dev Revert if the Contract isn't unlocked.\r
*/\r
error ContractNotUnLocked();\r
\r
/**\r
* @dev Revert if ownership not renounced.\r
*/\r
error OwnershipNotRenounced();\r
\r
/**\r
* @dev Revert if the caller isn't eligible as Token Leader.\r
*/\r
error TokenControlledByWCTMasterOrDoNotExist();\r
\r
/**\r
* @dev Revert if the caller isn't eligible as WCT Master.\r
*/\r
error WCTMasterAlreadyClaimedorTokenDoNotExist();\r
\r
}"
},
"WANTED/lib/ERC1155Airdrops.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.29;\r
\r
import { IERC1155MetadataURI } from ".deps/npm/@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";\r
import { ERC1155Utils } from ".deps/npm/@openzeppelin/contracts/token/ERC1155/utils/ERC1155Utils.sol";\r
import { ERC1155Supply } from ".deps/npm/@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";\r
import { Context } from ".deps/npm/@openzeppelin/contracts/utils/Context.sol";\r
import { IERC165, ERC165 } from ".deps/npm/@openzeppelin/contracts/utils/introspection/ERC165.sol";\r
import { Arrays } from ".deps/npm/@openzeppelin/contracts/utils/Arrays.sol";\r
import { Strings } from "lib/openzeppelin-contracts/contracts/utils/Strings.sol";\r
import { ERC2981 } from "@openzeppelin/contracts/token/common/ERC2981.sol";\r
import { IERC2981 } from "lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol";\r
import { ERC1155AirdropsErrorsAndEvents } from "WANTED/lib/ERC1155AirdropsErrorsAndEvents.sol";\r
import { Ownable } from ".deps/npm/@openzeppelin/contracts/access/Ownable.sol";\r
\r
/**\r
* @title ERC1155Airdrops\r
* @custom:creator MadV0x\r
* @notice Implements mintFor of newTokenId to an address.\r
* Implements batchMintFor of newTokenIds to an address. \r
* Implements bulkDropTo (Bulk transfers tokenId to a list of addresses). \r
* Implements batchDropTo (Batch transfers tokenIds to a list of addresses). \r
* Implements totalBalanceOf (returns global balance of an address for all tokenIds).\r
* Implements totalSupply (returns the total supply of all tokens in the contract).\r
* Implements getTokenIdsByWallet to return all tokenIds owned by a wallet.\r
* Implements isContractFrozen: freeze metadata and minting functions.\r
* IMPORTANT: Ownable library not implemented (all mint functions public).\r
**/\r
\r
contract ERC1155Airdrops is \r
Context,\r
Ownable, \r
ERC165, \r
IERC1155MetadataURI,\r
ERC1155AirdropsErrorsAndEvents, \r
ERC2981 {\r
\r
string public name;\r
string public symbol;\r
uint256 public totalTokenIdsMinted;\r
\r
/// @notice returns the contract URI for contract Metadata.\r
string contractURI;\r
\r
/// @notice Check if contract (Metadata + Mint) is frozen. \r
/// @dev emit an event when frozen.\r
bool public isContractFrozen = false;\r
event contractFrozen();\r
\r
//@dev Mapping to store the uri for each tokenId\r
mapping(uint256 => string) private _tokenURI;\r
\r
//@dev mapping tokenId balances to addresses\r
mapping(uint256 id => mapping(address account => uint256)) private _balances;\r
\r
//@dev mapping of approvals\r
mapping(address account => mapping(address operator => bool)) private _operatorApprovals;\r
\r
//@dev Mapping to store the total supply of each tokenId\r
mapping(uint256 => uint256) private _totalSupply;\r
\r
constructor(\r
string memory _name,\r
string memory _symbol) Ownable(msg.sender) {\r
name = _name;\r
symbol = _symbol;\r
}\r
\r
/**\r
* @dev See {IERC165-supportsInterface}.\r
*/\r
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165, ERC2981) returns (bool) {\r
return\r
interfaceId == type(IERC165).interfaceId ||\r
interfaceId == type(IERC1155MetadataURI).interfaceId ||\r
interfaceId == type(IERC2981).interfaceId ||\r
interfaceId == 0x49064906 || // ERC-4906\r
super.supportsInterface(interfaceId);\r
}\r
\r
/**\r
* @notice Mint a new TokenId with a given supply.\r
* @dev Allows minting the next sequential tokenId with specified supply and URI.\r
* @param recipient The address receiving the minted tokens.\r
* @param supply The number of tokens to mint for this tokenId.\r
* @param tokenURI The URI associated with the new tokenId.\r
*/\r
function mintFor(address recipient, uint256 supply, string memory tokenURI) onlyOwner public virtual {\r
\r
if(isContractFrozen) {revert ContractFrozen();} \r
\r
uint256 nextTokenId = totalTokenIdsMinted + 1;\r
\r
// Mint the tokens to the caller (owner or authorized address)\r
_mint(recipient, nextTokenId, supply, "");\r
_tokenURI[nextTokenId] = tokenURI;\r
totalTokenIdsMinted += 1;\r
\r
// Update the total supply for the tokenId\r
_totalSupply[nextTokenId] += supply;\r
\r
// Emit an event for the minting\r
emit Minted(recipient, nextTokenId, supply);\r
}\r
\r
/**\r
* @notice Batch mint multiple new token IDs of given supplies to a recipient.\r
* @param recipient The address to receive the minted tokens.\r
* @param supplies The number of tokens to mint for each new tokenId.\r
* @param tokenURIs The URIs corresponding to each new tokenId.\r
*/\r
function batchMintFor(address recipient, uint256[] calldata supplies, string[] memory tokenURIs) onlyOwner public virtual {\r
\r
if(isContractFrozen) {revert ContractFrozen();} \r
\r
if (supplies.length <= 0) {revert SuppliesMissing();}\r
if (supplies.length != tokenURIs.length) {revert DataLengthMismatch();}\r
\r
uint256 nextTokenId = totalTokenIdsMinted + 1;\r
uint256[] memory tokenIds = new uint256[](supplies.length);\r
\r
// Create new token IDs based on the nextTokenId\r
for (uint256 i = 0; i < supplies.length; i++) {\r
tokenIds[i] = nextTokenId; // Assign current nextTokenId\r
_totalSupply[nextTokenId] += supplies[i]; // Update the supply for the current tokenId\r
_tokenURI[nextTokenId] = tokenURIs[i]; // Set the URI for the current tokenId\r
nextTokenId++; // Increment nextTokenId after using it\r
totalTokenIdsMinted++; // Increment the totalTokenIdsMinted\r
}\r
// Mint the tokens to the recipient.\r
_mintBatch(recipient, tokenIds, supplies, "");\r
\r
// Emit an event for the minting.\r
emit BatchMinted(recipient, tokenIds, supplies);\r
}\r
\r
/**\r
* @notice Transfers amounts of a specified tokenId to recipients.\r
* @dev Caller must own sufficient balance of `tokenId` to cover total amounts.\r
* @param recipients Array of recipient addresses.\r
* @param tokenId Token ID to airdrop. Must be minted and exist.\r
* @param amounts Array of amounts corresponding to each recipient.\r
*\r
* Requirements:\r
* - `recipients` and `amounts` arrays must be of the same length and not empty.\r
* - Caller must have enough balance of `tokenId` to cover the sum of `amounts`.\r
*/\r
function bulkDropTo(\r
address[] calldata recipients, \r
uint256 tokenId, \r
uint256[] calldata amounts\r
) public virtual {\r
uint256 recipientsCount = recipients.length;\r
uint256 totalAmount = 0;\r
\r
// Ensure there are recipients\r
if (recipientsCount == 0) {revert NoRecipients();} \r
if (tokenId > totalTokenIdsMinted) {revert TokenDoesNotExist();}\r
if (recipientsCount != amounts.length) {\r
revert ERC1155InvalidArrayLength(recipientsCount, amounts.length);\r
}\r
\r
uint256 remaining = balanceOf(msg.sender, tokenId);\r
\r
for (uint256 i = 0; i < recipients.length; i++) {\r
if (remaining < amounts[i]) {revert LowTokenBalance();} \r
remaining -= amounts[i];\r
_safeTransferFrom(msg.sender, recipients[i], tokenId, amounts[i], "");\r
totalAmount += amounts[i];\r
}\r
\r
// Emit an event for the airdrop\r
emit BulkDropped(msg.sender, tokenId, totalAmount, recipientsCount);\r
}\r
\r
/**\r
* @notice Transfers amounts of different tokenIds to recipients.\r
* @param recipients List of recipient addresses.\r
* @param tokenIds List of token IDs corresponding to amounts and recipients.\r
* @param amounts List of token amounts for each tokenId to send to respective recipient.\r
*/\r
function batchDropTo(\r
address[] calldata recipients, \r
uint256[] calldata tokenIds, \r
uint256[] calldata amounts\r
) public virtual {\r
uint256 recipientsCount = recipients.length;\r
uint256 totalAmount = 0;\r
uint256 length = recipients.length;\r
\r
// Ensure there are recipients\r
if (recipientsCount == 0) {revert NoRecipients();} \r
if (length != tokenIds.length \r
&& length != amounts.length) {revert DataLengthMismatch();}\r
\r
// Loop through the recipients and transfer amount to each one\r
for (uint256 i = 0; i < recipientsCount; i++) {\r
address recipient = recipients[i];\r
uint256 amount = amounts[i];\r
uint256 tokenId = tokenIds[i];\r
if (tokenId > totalTokenIdsMinted) {revert TokenDoesNotExist();}\r
if (balanceOf(msg.sender, tokenId) < amount) {revert LowTokenBalance();} \r
totalAmount += amount;\r
// Transfer the specified amount of tokenId to the recipient\r
_safeTransferFrom(msg.sender, recipient, tokenId, amount, "");\r
}\r
\r
// Emit an event for the batch airdrop\r
emit BatchDropped(msg.sender, tokenIds, totalAmount, recipientsCount);\r
}\r
\r
/**\r
* @notice Returns the total supply of a given tokenId.\r
* @param tokenId The tokenId to query.\r
*/\r
function totalSupply(uint256 tokenId) public virtual view returns (uint256) {\r
return _totalSupply[tokenId];\r
}\r
\r
/**\r
* @notice Returns the total supply of all tokenIds (cumulative, global) in the contract.\r
* @dev Iterates through all minted token IDs and sums their individual total supplies.\r
* May be expensive in gas if called on-chain when many token IDs exist.\r
*/\r
function globalSupply() public virtual view returns (uint256) {\r
uint256 cumulSupply = 0;\r
for(uint256 i = 1; i <= totalTokenIdsMinted; i++ ) {\r
cumulSupply += _totalSupply[i];\r
}\r
return cumulSupply;\r
}\r
\r
/**\r
* @notice Returns all token IDs owned by an address.\r
* @dev Iterates over all minted token IDs to check ownership.\r
* Uses two passes: first counts tokens owned, second fills the result array.\r
* This can be gas-heavy and fail if totalTokenIdsMinted is large; \r
*/\r
function getTokenIdsByWallet(address wallet) external view returns (uint256[] memory) {\r
uint256[] memory temp = new uint256[](totalTokenIdsMinted);\r
uint256 count = 0;\r
\r
for (uint256 i = 1; i <= totalTokenIdsMinted; i++) {\r
if (balanceOf(wallet, i) > 0) {\r
temp[count] = i;\r
count++;\r
}\r
}\r
\r
// Create a fixed-size array to return\r
uint256[] memory tokenIds = new uint256[](count);\r
for (uint256 j = 0; j < count; j++) {\r
tokenIds[j] = temp[j];\r
}\r
\r
return tokenIds;\r
}\r
\r
/**\r
* @notice Returns the URI of a given tokenId.\r
* @dev Reverts if tokenId does not exist.\r
*/\r
function uri(uint256 tokenId) public view returns (string memory) {\r
return _tokenURI[tokenId];\r
}\r
\r
/**\r
* @dev Sets the URI for a specific token.\r
* @param tokenId The token ID to update.\r
* @param newTokenURI The new URI string to set.\r
*/\r
function setTokenURI(uint256 tokenId, string memory newTokenURI) public onlyOwner virtual {\r
if(isContractFrozen) {revert ContractFrozen();}\r
if (tokenId > totalTokenIdsMinted) {revert TokenDoesNotExist();}\r
_tokenURI[tokenId] = newTokenURI;\r
emit TokenURIUpdated(tokenId, newTokenURI);\r
}\r
\r
/**\r
* @notice Sets the contract URI for contract metadata.\r
*\r
* @param newContractURI The new contract URI.\r
*/\r
function setContractURI(string calldata newContractURI) public onlyOwner virtual {\r
if(isContractFrozen) {revert ContractFrozen();}\r
\r
// Set the new contract URI.\r
contractURI = newContractURI;\r
\r
// Emit an event with the update.\r
emit ContractURIUpdated(newContractURI);\r
}\r
\r
/**\r
* @notice Freeze Metadata + Mint (only Owner). \r
* @dev This Action is irreversible.\r
*/\r
function freezeContract() external onlyOwner virtual {\r
// Freeze contract\r
isContractFrozen = true;\r
\r
// Emit an event with the update.\r
emit contractFrozen();\r
}\r
\r
/**\r
* @notice Emit an event notifying metadata updates for\r
* a range of token ids, according to EIP-4906.\r
*\r
* @param fromTokenId The start token id.\r
* @param toTokenId The end token id.\r
*/\r
function emitBatchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId) external {\r
// Emit an event with the update.\r
emit BatchMetadataUpdate(fromTokenId, toTokenId);\r
}\r
\r
/**\r
* @dev See {IERC1155-balanceOf}.\r
*/\r
function totalBalanceOf(address account) public view returns (uint256) {\r
uint256 cumulBalance = 0;\r
for(uint256 i = 1; i <= totalTokenIdsMinted; i++ ) {\r
cumulBalance = cumulBalance + balanceOf(account, i);\r
}\r
return cumulBalance;\r
}\r
\r
/**\r
* @dev See {IERC1155-balanceOf}.\r
*/\r
function balanceOf(address account, uint256 tokenId) public view virtual returns (uint256) {\r
return _balances[tokenId][account];\r
}\r
\r
/**\r
* @dev See {IERC1155-balanceOfBatch}.\r
*\r
* Requirements:\r
*\r
* - `accounts` and `ids` must have the same length.\r
*/\r
function balanceOfBatch(\r
address[] memory accounts,\r
uint256[] memory ids\r
) public view virtual returns (uint256[] memory) {\r
if (accounts.length != ids.length) {\r
revert ERC1155InvalidArrayLength(ids.length, accounts.length);\r
}\r
\r
uint256[] memory batchBalances = new uint256[](accounts.length);\r
\r
for (uint256 i = 0; i < accounts.length; ++i) {\r
batchBalances[i] = balanceOf(accounts[i], ids[i]);\r
}\r
\r
return batchBalances;\r
}\r
\r
/**\r
* @dev See {IERC1155-setApprovalForAll}.\r
*/\r
function setApprovalForAll(address operator, bool approved) public virtual {\r
_setApprovalForAll(_msgSender(), operator, approved);\r
}\r
\r
/**\r
* @dev See {IERC1155-isApprovedForAll}.\r
*/\r
function isApprovedForAll(address account, address operator) public view virtual returns (bool) {\r
return _operatorApprovals[account][operator];\r
}\r
\r
/**\r
* @dev See {IERC1155-safeTransferFrom}.\r
*/\r
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {\r
address sender = _msgSender();\r
if (from != sender && !isApprovedForAll(from, sender)) {\r
revert ERC1155MissingApprovalForAll(sender, from);\r
}\r
_beforeTokenTransfers(from, to, id, value, data);\r
_safeTransferFrom(from, to, id, value, data);\r
}\r
\r
/**\r
* @dev See {IERC1155-safeBatchTransferFrom}.\r
*/\r
function safeBatchTransferFrom(\r
address from,\r
address to,\r
uint256[] memory ids,\r
uint256[] memory values,\r
bytes memory data\r
) public virtual {\r
address sender = _msgSender();\r
if (from != sender && !isApprovedForAll(from, sender)) {\r
revert ERC1155MissingApprovalForAll(sender, from);\r
}\r
for (uint256 i = 1; i <= totalTokenIdsMinted; i++ ) {\r
_beforeTokenTransfers(from, to, i, values[i], data);\r
}\r
_safeBatchTransferFrom(from, to, ids, values, data);\r
}\r
\r
/**\r
* @dev Hook that is called before any token transfer.\r
* This includes minting.\r
*/\r
function _beforeTokenTransfers(\r
address from,\r
address to,\r
uint256 id,\r
uint256 value,\r
bytes memory //*data//* \r
) internal virtual {\r
}\r
\r
/**\r
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`\r
* (or `to`) is the zero address.\r
*\r
* Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.\r
*\r
* Requirements:\r
*\r
* - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}\r
* or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.\r
* - `ids` and `values` must have the same length.\r
*\r
* NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.\r
*/\r
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {\r
if (ids.length != values.length) {\r
revert ERC1155InvalidArrayLength(ids.length, values.length);\r
}\r
\r
address operator = _msgSender();\r
\r
for (uint256 i = 0; i < ids.length; ++i) {\r
uint256 id = ids[i];\r
uint256 value = values[i];\r
\r
if (from != address(0)) {\r
uint256 fromBalance = _balances[id][from];\r
if (fromBalance < value) {\r
revert ERC1155InsufficientBalance(from, fromBalance, value, id);\r
}\r
unchecked {\r
// Overflow not possible: value <= fromBalance\r
_balances[id][from] = fromBalance - value;\r
}\r
}\r
\r
if (to != address(0)) {\r
_balances[id][to] += value;\r
}\r
}\r
\r
if (ids.length == 1) {\r
uint256 id = ids[0];\r
uint256 value = values[0];\r
emit TransferSingle(operator, from, to, id, value);\r
} else {\r
emit TransferBatch(operator, from, to, ids, values);\r
}\r
}\r
\r
/**\r
* @dev Version of {_update} that performs the token acceptance check by calling\r
* {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it\r
* contains code (eg. is a smart contract at the moment of execution).\r
*\r
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any\r
* update to the contract state after this function would break the check-effect-interaction pattern. Consider\r
* overriding {_update} instead.\r
*/\r
function _updateWithAcceptanceCheck(\r
address from,\r
address to,\r
uint256[] memory ids,\r
uint256[] memory values,\r
bytes memory data\r
) internal virtual {\r
_update(from, to, ids, values);\r
if (to != address(0)) {\r
address operator = _msgSender();\r
if (ids.length == 1) {\r
uint256 id = ids[0];\r
uint256 value = values[0];\r
ERC1155Utils.checkOnERC1155Received(operator, from, to, id, value, data);\r
} else {\r
ERC1155Utils.checkOnERC1155BatchReceived(operator, from, to, ids, values, data);\r
}\r
}\r
}\r
\r
/**\r
* @dev Transfers a `value` tokens of token type `id` from `from` to `to`.\r
*\r
* Emits a {TransferSingle} event.\r
*\r
* Requirements:\r
*\r
* - `to` cannot be the zero address.\r
* - `from` must have a balance of tokens of type `id` of at least `value` amount.\r
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the\r
* acceptance magic value.\r
*/\r
function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal {\r
if (to == address(0)) {\r
revert ERC1155InvalidReceiver(address(0));\r
}\r
if (from == address(0)) {\r
revert ERC1155InvalidSender(address(0));\r
}\r
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);\r
_updateWithAcceptanceCheck(from, to, ids, values, data);\r
}\r
\r
/**\r
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.\r
*\r
* Emits a {TransferBatch} event.\r
*\r
* Requirements:\r
*\r
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the\r
* acceptance magic value.\r
* - `ids` and `values` must have the same length.\r
*/\r
function _safeBatchTransferFrom(\r
address from,\r
address to,\r
uint256[] memory ids,\r
uint256[] memory values,\r
bytes memory data\r
) internal {\r
if (to == address(0)) {\r
revert ERC1155InvalidReceiver(address(0));\r
}\r
if (from == address(0)) {\r
revert ERC1155InvalidSender(address(0));\r
}\r
_updateWithAcceptanceCheck(from, to, ids, values, data);\r
}\r
\r
\r
/**\r
* @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.\r
*\r
* Emits a {TransferSingle} event.\r
*\r
* Requirements:\r
*\r
* - `to` cannot be the zero address.\r
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the\r
* acceptance magic value.\r
*/\r
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal virtual {\r
if (to == address(0)) {\r
revert ERC1155InvalidReceiver(address(0));\r
}\r
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);\r
_updateWithAcceptanceCheck(address(0), to, ids, values, data);\r
}\r
\r
/**\r
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.\r
*\r
* Emits a {TransferBatch} event.\r
*\r
* Requirements:\r
*\r
* - `ids` and `values` must have the same length.\r
* - `to` cannot be the zero address.\r
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the\r
* acceptance magic value.\r
*/\r
function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {\r
if (to == address(0)) {\r
revert ERC1155InvalidReceiver(address(0));\r
}\r
_updateWithAcceptanceCheck(address(0), to, ids, values, data);\r
}\r
\r
/**\r
* @dev Destroys a `value` amount of tokens of type `id` from `from`\r
*\r
* Emits a {TransferSingle} event.\r
*\r
* Requirements:\r
*\r
* - `from` cannot be the zero address.\r
* - `from` must have at least `value` amount of tokens of type `id`.\r
*/\r
function _burn(address from, uint256 id, uint256 value) internal {\r
if (from == address(0)) {\r
revert ERC1155InvalidSender(address(0));\r
}\r
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);\r
_updateWithAcceptanceCheck(from, address(0), ids, values, "");\r
_totalSupply[id] -= value;\r
}\r
\r
/**\r
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.\r
*\r
* Emits a {TransferBatch} event.\r
*\r
* Requirements:\r
*\r
* - `from` cannot be the zero address.\r
* - `from` must have at least `value` amount of tokens of type `id`.\r
* - `ids` and `values` must have the same length.\r
*/\r
function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal {\r
if (from == address(0)) {\r
revert ERC1155InvalidSender(address(0));\r
}\r
_updateWithAcceptanceCheck(from, address(0), ids, values, "");\r
for(uint256 i = 0; i < ids.length; i++) {\r
_totalSupply[ids[i]] -= values[i];\r
}\r
}\r
\r
/**\r
* @dev Approve `operator` to operate on all of `owner` tokens\r
*\r
* Emits an {ApprovalForAll} event.\r
*\r
* Requirements:\r
*\r
* - `operator` cannot be the zero address.\r
*/\r
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {\r
if (operator == address(0)) {\r
revert ERC1155InvalidOperator(address(0));\r
}\r
_operatorApprovals[owner][operator] = approved;\r
emit ApprovalForAll(owner, operator, approved);\r
}\r
\r
/**\r
* @dev Creates an array in memory with only one value for each of the elements provided.\r
*/\r
function _asSingletonArrays(\r
uint256 element1,\r
uint256 element2\r
) private pure returns (uint256[] memory array1, uint256[] memory array2) {\r
assembly ("memory-safe") {\r
// Load the free memory pointer\r
array1 := mload(0x40)\r
// Set array length to 1\r
mstore(array1, 1)\r
// Store the single element at the next word after the length (where content starts)\r
mstore(add(array1, 0x20), element1)\r
\r
// Repeat for next array locating it right after the first array\r
array2 := add(array1, 0x40)\r
mstore(array2, 1)\r
mstore(add(array2, 0x20), element2)\r
\r
// Update the free memory pointer by pointing after the second array\r
mstore(0x40, add(array2, 0x40))\r
}\r
}\r
\r
}"
},
"src/interfaces/ITransferValidator.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.17;\r
\r
interface ITransferValidator721 {\r
/// @notice Ensure that a transfer has been authorized for a specific tokenId\r
function validateTransfer(\r
address caller,\r
address from,\r
address to,\r
uint256 tokenId\r
) external view;\r
}\r
\r
interface ITransferValidator1155 {\r
/// @notice Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and reduce the transferable amount remaining\r
function validateTransfer(\r
address caller,\r
address from,\r
address to,\r
uint256 tokenId,\r
uint256 amount\r
) external;\r
}\r
"
},
"lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
"
},
"src/interfaces/ICreatorToken.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.4;\r
\r
interface ICreatorToken {\r
event TransferValidatorUpdated(address oldValidator, address newValidator);\r
\r
function getTransferValidator() external view returns (address validator);\r
\r
function getTransferValidationFunction()\r
external\r
view\r
returns (bytes4 functionSignature, bool isViewFunction);\r
\r
function setTransferValidator(address validator) external;\r
}\r
\r
interface ILegacyCreatorToken {\r
event TransferValidatorUpdated(address oldValidator, address newValidator);\r
\r
function getTransferValidator() external view returns (address validator);\r
\r
function setTransferValidator(address validator) external;\r
}\r
"
},
"src/lib/ERC1155TransferValidator.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.17;\r
\r
import { ICreatorToken } from "../interfaces/ICreatorToken.sol";\r
\r
/**\r
* @title ERC721TransferValidator\r
* @notice Functionality to use a transfer validator.\r
*/\r
abstract contract ERC1155TransferValidator is ICreatorToken {\r
/// @dev Store the transfer validator. The null address means no transfer validator is set.\r
address internal _transferValidator;\r
\r
/// @notice Revert with an error if the transfer validator is being set to the same address.\r
error SameTransferValidator();\r
\r
/// @notice Returns the currently active transfer validator.\r
/// The null address means no transfer validator is set.\r
function getTransferValidator() external view returns (address) {\r
return _transferValidator;\r
}\r
\r
/// @notice Set the transfer validator.\r
/// The external method that uses this must include access control.\r
function _setTransferValidator(address newValidator) internal virtual {\r
address oldValidator = _transferValidator;\r
if (oldValidator == newValidator) {\r
revert SameTransferValidator();\r
}\r
_transferValidator = newValidator;\r
emit TransferValidatorUpdated(oldValidator, newValidator);\r
}\r
}"
},
".deps/npm/@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public payable virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"WANTED/lib/ERC1155AirdropsErrorsAndEvents.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.29;\r
\r
/**\r
* @dev Standard ERC-1155 Errors\r
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.\r
*/\r
\r
interface ERC1155AirdropsErrorsAndEvents {\r
\r
/**\r
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\r
* @param sender Address whose tokens are being transferred.\r
* @param balance Current balance for the interacting account.\r
* @param needed Minimum amount required to perform a transfer.\r
* @param tokenId Identifier number of a token.\r
*/\r
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);\r
\r
/**\r
* @dev Indicates a failure with the token `sender`. Used in transfers.\r
* @param sender Address whose tokens are being transferred.\r
*/\r
error ERC1155InvalidSender(address sender);\r
\r
/**\r
* @dev Indicates a failure with the token `receiver`. Used in transfers.\r
* @param receiver Address to which tokens are being transferred.\r
*/\r
error ERC1155InvalidReceiver(address receiver);\r
\r
/**\r
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.\r
* @param operator Address that may be allowed to operate on tokens without being their owner.\r
* @param owner Address of the current owner of a token.\r
*/\r
error ERC1155MissingApprovalForAll(address operator, address owner);\r
\r
/**\r
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\r
* @param approver Address initiating an approval operation.\r
*/\r
error ERC1155InvalidApprover(address approver);\r
\r
/**\r
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.\r
* @param operator Address that may be allowed to operate on tokens without being their owner.\r
*/\r
error ERC1155InvalidOperator(address operator);\r
\r
/**\r
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.\r
* Used in batch transfers.\r
* @param idsLength Length of the array of token identifiers\r
* @param valuesLength Length of the array of token amounts\r
*/\r
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);\r
\r
// @dev Thrown when a referenced token does not exist.\r
error TokenDoesNotExist();\r
\r
// @dev Thrown when initial token supply values are missing or invalid.\r
error SuppliesMissing();\r
\r
// @dev Thrown when the provided data arrays (e.g., IDs and supplies) have mismatched lengths.\r
error DataLengthMismatch();\r
\r
// @dev Thrown when no recipient addresses are provided where required.\r
error NoRecipients();\r
\r
// @dev Thrown when an action is attempted on a frozen contract.\r
error ContractFrozen();\r
\r
// @dev Thrown when an account's token balance is too low to perform the requested operation.\r
error LowTokenBalance();\r
\r
// @dev Event to notify new token minting.\r
event Minted(address account, uint256 indexed tokenId, uint256 supply);\r
\r
// @dev Event to notify new tokenIds are batch minted (single recipient, several tokenIds).\r
event BatchMinted(address account, uint256[] indexed tokenIds, uint256[] supplies);\r
\r
// @dev Event when tokenId is bulk Airdropped (to multiple recipients).\r
event BulkDropped(address from, uint256 indexed tokenId, uint256 totalAmount, uint256 recipientsCount);\r
\r
// @dev Event when multiple tokenIds are batch Airdropped (to multiple recipients).\r
event BatchDropped(address from, uint256[] indexed tokenIds, uint256 totalAmount, uint256 recipientsCount);\r
\r
/**\r
* @dev Emit an event for token metadata reveals/updates,\r
* according to EIP-4906.\r
*\r
* @param _fromTokenId The start token id.\r
* @param _toTokenId The end token id.\r
*/\r
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);\r
\r
/**\r
* @dev Emit an event when the URI of a token is updated.\r
*/\r
event TokenURIUpdated(uint256 tokenId, string newTokentURI);\r
\r
/**\r
* @dev Emit an event when the contract URI is updated.\r
*/\r
event ContractURIUpdated(string newContractURI);\r
\r
\r
}"
},
"lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
}
"
},
"@openzeppelin/contracts/token/common/ERC2981.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/common/ERC2981.sol)
pragma solidity ^0.8.20;
import {IERC2981} from "../../interfaces/IERC2981.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
*
* Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
* specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
*
* Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
* fee is specified in basis points by default.
*
* IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
* https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the ERC. Marketplaces are expected to
* voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
*/
abstract contract ERC2981 is IERC2981, ERC165 {
struct RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
RoyaltyInfo internal _defaultRoyaltyInfo;
mapping(uint256 tokenId => RoyaltyInfo) internal _tokenRoyaltyInfo;
/**
* @dev The default royalty set is invalid (eg. (numerator / denominator) >= 1).
*/
error ERC2981InvalidDefaultRoyalty(uint256 numerator, uint256 denominator);
/**
* @dev The default royalty receiver is invalid.
*/
error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);
/**
* @dev The royalty set for a specific `tokenId` is invalid (eg. (numerator / denominator) >= 1).
*/
error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);
/**
* @dev The royalty receiver for `tokenId` is invalid.
*/
error ERC2981InvalidTokenRoyaltyReceiver(uint256 tokenId, address receiver);
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
/// @inheritdoc IERC2981
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) public view virtual returns (address receiver, uint256 amount) {
RoyaltyInfo storage _royaltyInfo = _tokenRoyaltyInfo[tokenId];
address royaltyReceiver = _royaltyInfo.receiver;
uint96 royaltyFraction = _royaltyInfo.royaltyFraction;
if (royaltyReceiver == address(0)) {
royaltyReceiver = _defaultRoyaltyInfo.receiver;
royaltyFraction = _defaultRoyaltyInfo.royaltyFraction;
}
uint256 royaltyAmount = (salePrice * royaltyFraction) / _feeDenominator();
return (royaltyReceiver, royaltyAmount);
}
/**
* @notice Returns the address that receives royalties.
*/
function royaltyAddress() external view returns (address) {
return _defaultRoyaltyInfo.receiver;
}
/**
* @notice Returns the royalty basis points out of 10_000.
*/
function royaltyBasisPoints() external view returns (uint256) {
return _defaultRoyaltyInfo.royaltyFraction;
}
/**
* @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
* fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
* override.
*/
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
/**
* @dev Sets the royalty information that all ids in this contract will default to.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
uint256 denominator = _feeDenominator();
if (feeNumerator > denominator) {
// Royalty fee will exceed the sale price
revert ERC2981InvalidDefaultRoyalty(feeNumerator, denominator);
}
if (receiver == address(0)) {
revert ERC2981InvalidDefaultRoyaltyReceiver(address(0));
}
_defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Removes default royalty information.
*/
function _deleteDefaultRoyalty() internal virtual {
delete _defaultRoyaltyInfo;
}
/**
* @dev Sets the royalty information for a specific token id, overriding the global default.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
uint256 denominator = _feeDenominator();
if (feeNumerator > denominator) {
// Royalty fee will exceed the sale price
revert ERC2981InvalidTokenRoyalty(tokenId, feeNumerator, denominator);
}
if (receiver == address(0)) {
revert ERC2981InvalidTokenRoyaltyReceiver(tokenId, address(0));
}
_tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Resets royalty information for the token id back to the global default.
*/
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
delete _tokenRoyaltyInfo[tokenId];\
Submitted on: 2025-10-24 13:27:49
Comments
Log in to comment.
No comments yet.