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": {
"WantedVault.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 { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";\r
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";\r
import { IERC721Receiver } from "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol";\r
import { Ownable } from ".deps/npm/@openzeppelin/contracts/access/Ownable.sol";\r
\r
/**\r
* @title WANTED Cyborgs - Vesting Vault.\r
* @notice Implements immutable vesting schedules for WANTED NFTs premint Allocations.\r
* Multibeneficiaries with cliffs and linear monthly releases.\r
* @dev Implements IERC721Receiver to receive ERC721 NFTs from a contract safely.\r
*/\r
\r
contract WantedVault is ReentrancyGuard, IERC721Receiver, Ownable {\r
IERC721 public WANTEDcontract; \r
\r
/// @notice beneficiaries group\r
address public ArtistWallet;\r
address public OperationsWallet;\r
address public TreasuryWallet;\r
address public MarketingWallet ; \r
\r
/// @notice Global vesting start timestamp (set at deployment)\r
uint256 public vestingStart;\r
\r
uint256 constant SECONDS_IN_A_MONTH = 2592000; // 30 days = 2592000 sec\r
uint256 constant cliff = 3 * SECONDS_IN_A_MONTH;\r
\r
/**\r
* @dev Vesting config for a beneficiary\r
* @param beneficiary: The account allowed to claim NFTs from this schedule.\r
* @param fromId: First tokenId allocated to the beneficiary.\r
* @param toId: Last tokenId allocated to the beneficiary.\r
* @param cliff: The delay (in seconds) before vesting starts.\r
* @param perMonth: Number of NFTs released per month after cliff.\r
*/\r
struct Schedule {\r
address beneficiary;\r
uint256 fromId;\r
uint256 toId;\r
uint256 cliff; \r
uint256 perMonth; \r
}\r
\r
Schedule[] internal schedules;\r
\r
/// @dev Marks whether a specific tokenId has been claimed.\r
mapping(uint256 => bool) public claimed;\r
\r
/// @dev Tracks how many NFTs each beneficiary has claimed.\r
mapping(address => uint256) public claimedCount;\r
\r
/// @dev Tracks whether a token has been deposited into the vault.\r
mapping(uint256 => bool) public deposited;\r
\r
/// @notice Emitted when an account claims a batch of NFTs.\r
/// @param beneficiary The account that claimed NFTs.\r
/// @param count Number of NFTs claimed.\r
/// @param tokenIds List of tokenIds claimed.\r
event BatchClaimed(address indexed beneficiary, uint256 count, uint256[] tokenIds);\r
\r
constructor(address _wantedContract) Ownable(msg.sender) {\r
WANTEDcontract = IERC721(_wantedContract);\r
\r
ArtistWallet = 0xEC4bB69B40cC3Bb44a7c903A3A06a3203Ac41E42;\r
OperationsWallet = 0x451e5d30Fa83e19A35A2A01E55Ff1D2630968352;\r
TreasuryWallet = 0x9d953ecd8782c7D589365736335305e98df1e6cC;\r
MarketingWallet = 0xE4d96514386B650B165Ff67DcCe2484113AEB65e;\r
\r
// Define vesting schedules per beneficiary\r
schedules.push(Schedule(ArtistWallet, 1, 100, cliff, 3)); // 3 months cliff, 3 per month (40 months)\r
schedules.push(Schedule(OperationsWallet, 101, 300, 2*cliff, 6)); // 6 months cliff, 6 per month (40 months)\r
schedules.push(Schedule(TreasuryWallet, 301, 600, 2*cliff, 9)); // 6 months cliff, 9 per month (40 months)\r
schedules.push(Schedule(MarketingWallet, 601, 1000, cliff, 11)); // 3 months cliff, 11 per month (40 months)\r
}\r
\r
/**\r
* @notice Allows a beneficiary to claim all eligible NFTs at once based on their vesting schedule.\r
* \r
* @dev \r
* - A "Schedule" defines the vesting conditions for a beneficiary (fromId, toId, cliff, perMonth).\r
* - This function enforces a time-based cliff and linear monthly vesting.\r
* - Calculates how many NFTs the caller is allowed to claim at this point in time.\r
* - Transfers only deposited and unclaimed tokenIds within the beneficiary’s allowed range.\r
* - Updates claim tracking to prevent double-claims.\r
*\r
* Requirements:\r
* - Caller must be a registered beneficiary with a valid schedule.\r
* - The cliff period must have passed.\r
* - At least one unclaimed token must be available based on elapsed time.\r
* - Token must have been previously deposited and not yet claimed.\r
*\r
* Emits:\r
* - {BatchClaimed} with number of tokens claimed and their token IDs.\r
*/\r
function batchClaimAll() external nonReentrant {\r
Schedule memory schedule = getSchedule(msg.sender);\r
require(schedule.beneficiary != address(0), "Not beneficiary");\r
\r
uint256 timePassed = block.timestamp - vestingStart;\r
require(timePassed >= schedule.cliff, "Cliff not reached");\r
\r
uint256 periods = (timePassed - schedule.cliff) / SECONDS_IN_A_MONTH;\r
uint256 totalClaimable = periods * schedule.perMonth;\r
uint256 maxClaimable = schedule.toId - schedule.fromId + 1;\r
if (totalClaimable >= maxClaimable) {totalClaimable = maxClaimable;}\r
uint256 toClaim = totalClaimable - claimedCount[msg.sender];\r
require(toClaim > 0, "Nothing to claim");\r
\r
uint256 claimedNow = 0;\r
uint256[] memory batch = new uint256[](toClaim);\r
\r
for (uint256 tokenId = schedule.fromId; tokenId <= schedule.toId && claimedNow < toClaim; tokenId++) {\r
if (!claimed[tokenId] && deposited[tokenId]) {\r
claimed[tokenId] = true;\r
WANTEDcontract.safeTransferFrom(address(this), msg.sender, tokenId);\r
batch[claimedNow] = tokenId;\r
claimedNow++;\r
}\r
}\r
\r
require(claimedNow > 0, "No eligible tokens");\r
claimedCount[msg.sender] += claimedNow;\r
emit BatchClaimed(msg.sender, claimedNow, batch);\r
}\r
\r
/**\r
* @notice Allows a beneficiary to claim an amount of unvested NFTs.\r
* \r
* @dev \r
* - A "Schedule" defines the vesting conditions for a beneficiary (fromId, toId, cliff, perMonth).\r
* - This function enforces a time-based cliff and linear monthly vesting.\r
* - Verifies the amount asked is unvested and claimable.\r
* - Transfers the claimed amount of unvested tokenIds to the beneficiary.\r
* - Updates claim tracking to prevent double-claims.\r
*\r
* Requirements:\r
* - Caller must be a registered beneficiary with a valid schedule.\r
* - The cliff period must have passed.\r
* - At least one unclaimed token must be available based on elapsed time.\r
* - Token must have been previously deposited and not yet claimed.\r
* - Amount claimed should not be higher than the unvested NFTs amount.\r
*\r
* Emits:\r
* - {BatchClaimed} with number of tokens claimed and their token IDs.\r
*\r
* @param amountToClaim The number of NFTs the beneficiary wants to claim.\r
*/\r
function claimAmount(uint256 amountToClaim) external nonReentrant {\r
Schedule memory schedule = getSchedule(msg.sender);\r
require(schedule.beneficiary != address(0), "Not beneficiary");\r
\r
uint256 timePassed = block.timestamp - vestingStart;\r
require(timePassed >= schedule.cliff, "Cliff not reached");\r
\r
uint256 periods = (timePassed - schedule.cliff) / SECONDS_IN_A_MONTH;\r
uint256 totalClaimable = periods * schedule.perMonth;\r
uint256 maxClaimable = schedule.toId - schedule.fromId + 1;\r
\r
if (totalClaimable >= maxClaimable) {totalClaimable = maxClaimable;}\r
\r
uint256 toClaim = totalClaimable - claimedCount[msg.sender];\r
require(toClaim > 0, "Nothing to claim");\r
require(amountToClaim <= toClaim, "Amount > Available");\r
\r
uint256 claimedNow = 0;\r
uint256[] memory batch = new uint256[](amountToClaim);\r
\r
for (uint256 tokenId = schedule.fromId; tokenId <= schedule.toId && claimedNow < amountToClaim; tokenId++) {\r
if (!claimed[tokenId] && deposited[tokenId]) {\r
claimed[tokenId] = true;\r
WANTEDcontract.safeTransferFrom(address(this), msg.sender, tokenId);\r
batch[claimedNow] = tokenId;\r
claimedNow++;\r
}\r
}\r
\r
require(claimedNow > 0, "No eligible tokens");\r
claimedCount[msg.sender] += claimedNow;\r
emit BatchClaimed(msg.sender, claimedNow, batch);\r
}\r
\r
/**\r
* @notice Returns the vesting schedule assigned to a specific beneficiary.\r
*\r
* @dev \r
* - Iterates over all defined schedules to find the one matching the given account.\r
* - If found, returns the full Schedule struct (includes range, cliff, and monthly vesting rate).\r
* - If not found, returns an empty Schedule (beneficiary = address(0)).\r
*\r
* @param account The address for which to retrieve the schedule.\r
* @return The Schedule struct associated with the address, or a zeroed struct if none exists.\r
*/\r
function getSchedule(address account) public view returns (Schedule memory) {\r
for (uint256 i = 0; i < schedules.length; i++) {\r
if (schedules[i].beneficiary == account) {\r
return schedules[i];\r
}\r
}\r
return Schedule(address(0), 0, 0, 0, 0);\r
}\r
\r
/**\r
* @notice Calculates how many NFTs a beneficiary is currently eligible to claim.\r
*\r
* @dev \r
* - Returns 0 if the account is not assigned a schedule or the cliff period hasn't passed.\r
* - Computes how many full vesting periods (months) have passed since the cliff.\r
* - Multiplies passed periods by the per-month vesting rate.\r
* - Caps the total claimable to the maximum NFTs assigned in the vesting schedule.\r
* - Subtracts the number of NFTs already claimed to get the final claimable amount.\r
*\r
* @param account The address of the beneficiary.\r
* @return Number of NFTs currently available to be claimed by the beneficiary.\r
*/\r
function getClaimable(address account) external view returns (uint256) {\r
Schedule memory schedule = getSchedule(account);\r
\r
if (\r
schedule.beneficiary == address(0) ||\r
block.timestamp < vestingStart + schedule.cliff\r
) {\r
return 0;\r
}\r
\r
uint256 elapsed = block.timestamp - vestingStart - schedule.cliff;\r
uint256 periods = elapsed / SECONDS_IN_A_MONTH;\r
uint256 totalClaimable = periods * schedule.perMonth;\r
\r
uint256 maxClaimable = schedule.toId - schedule.fromId + 1;\r
if (totalClaimable > maxClaimable) {\r
totalClaimable = maxClaimable;\r
}\r
\r
uint256 alreadyClaimed = claimedCount[account];\r
if (alreadyClaimed >= totalClaimable) { return 0; }\r
\r
return totalClaimable - alreadyClaimed;\r
}\r
\r
/**\r
* @notice Returns the remaining time and timestamp until the next vesting unlock.\r
* @dev \r
* - Calculates how many full vesting periods (months) have elapsed since `vestingStart`.\r
* - If all 40 (37 + 3 cliff) vesting periods have passed, returns 0 indicating vesting is complete.\r
*\r
* @return timeRemaining Seconds remaining until the next unlock or 0 if complete.\r
*/\r
function timeBeforeNextUnlock() external view returns (uint256) {\r
\r
uint256 timePassed = block.timestamp - vestingStart;\r
uint256 periods = timePassed / SECONDS_IN_A_MONTH;\r
\r
if (timePassed <= cliff) {\r
return cliff - timePassed;\r
}\r
\r
if (periods >= 40) {\r
// Vesting complete: no more unlocks\r
return (0);\r
}\r
\r
uint256 nextUnlockTime = vestingStart + (periods + 1) * SECONDS_IN_A_MONTH;\r
uint256 timeRemaining = nextUnlockTime - block.timestamp;\r
\r
return timeRemaining;\r
}\r
\r
/**\r
* @notice Updates the wallet address associated with the artist.\r
*\r
* @dev \r
* - Only callable by the contract owner.\r
* - Reverts if the new address is the zero address.\r
* - Internally calls `_updateBeneficiary()` to handle bookkeeping or external integrations.\r
* - Updates the `ArtistWallet` state variable to the new address.\r
*\r
* @param newWallet The new address to be set as the artist's wallet.\r
*/\r
function updateArtistWallet(address newWallet) external onlyOwner {\r
require(newWallet != address(0), "Invalid address");\r
_updateBeneficiary(ArtistWallet, newWallet);\r
ArtistWallet = newWallet;\r
}\r
\r
/**\r
* @notice Updates the wallet address designated for operations.\r
*\r
* @dev \r
* - Only callable by the contract owner.\r
* - Reverts if the new wallet address is the zero address.\r
* - Calls `_updateBeneficiary()` to handle necessary state updates or mappings.\r
* - Updates the `OperationsWallet` state variable to the new address.\r
*\r
* @param newWallet The new wallet address to assign for operations-related funds or permissions.\r
*/\r
function updateOperationsWallet(address newWallet) external onlyOwner {\r
require(newWallet != address(0), "Invalid address");\r
_updateBeneficiary(OperationsWallet, newWallet);\r
OperationsWallet = newWallet;\r
}\r
\r
/**\r
* @notice Updates the wallet address designated for Treasury.\r
*\r
* @dev \r
* - Only callable by the contract owner.\r
* - Reverts if the new wallet address is the zero address.\r
* - Calls `_updateBeneficiary()` to handle necessary state updates or mappings.\r
* - Updates the `TreasuryWallet` state variable to the new address.\r
*\r
* @param newWallet The new wallet address to assign for Treasury-related funds or permissions.\r
*/\r
function updateTreasuryWallet(address newWallet) external onlyOwner {\r
require(newWallet != address(0), "Invalid address");\r
_updateBeneficiary(TreasuryWallet, newWallet);\r
TreasuryWallet = newWallet;\r
}\r
\r
/**\r
* @notice Updates the wallet address designated for Marketing.\r
*\r
* @dev \r
* - Only callable by the contract owner.\r
* - Reverts if the new wallet address is the zero address.\r
* - Calls `_updateBeneficiary()` to handle necessary state updates or mappings.\r
* - Updates the `MarketingsWallet` state variable to the new address.\r
*\r
* @param newWallet The new wallet address to assign for Marketing-related funds or permissions.\r
*/\r
function updateMarketingWallet(address newWallet) external onlyOwner {\r
require(newWallet != address(0), "Invalid address");\r
_updateBeneficiary(MarketingWallet, newWallet);\r
MarketingWallet = newWallet;\r
}\r
\r
/**\r
* @dev Internal utility to update the beneficiary address within a vesting schedule.\r
*\r
* @dev\r
* - Iterates through all existing schedules.\r
* - If a schedule is found for `oldAddr`, it updates the beneficiary to `newAddr`.\r
* - Transfers the claimed count to the new address.\r
* - Resets the claimed count for the old address to zero.\r
* - Stops after the first match (assuming unique beneficiaries).\r
*\r
* @param oldAddr The previous beneficiary address to be replaced.\r
* @param newAddr The new address to assign as beneficiary.\r
*/\r
function _updateBeneficiary(address oldAddr, address newAddr) private {\r
for (uint256 i = 0; i < schedules.length; i++) {\r
if (schedules[i].beneficiary == oldAddr) {\r
schedules[i].beneficiary = newAddr;\r
claimedCount[newAddr] = claimedCount[oldAddr];\r
claimedCount[oldAddr] = 0;\r
break;\r
}\r
}\r
}\r
\r
/**\r
* @notice Handles the receipt of a WANTED ERC721 token.\r
* @dev This function is automatically called when an ERC721 token is transferred\r
* to this contract via `safeTransferFrom`. It:\r
* - Ensures the token comes from the expected WANTED contract.\r
* - Validates the tokenId is within an acceptable range.\r
* - Flags the token as deposited for future vesting logic.\r
* - Initializes the vesting start time if this is the first token received.\r
*\r
* @param tokenId The unique ID of the token being received.\r
* @return Selector confirming the token was received correctly.\r
*/\r
function onERC721Received(\r
address /* operator */,\r
address /* from */,\r
uint256 tokenId,\r
bytes calldata /* data */\r
) external override returns (bytes4) {\r
require(msg.sender == address(WANTEDcontract), "Only accepts WANTED NFTs from contract");\r
require(tokenId <= 1000, "Invalid tokenId"); \r
deposited[tokenId] = true;\r
\r
// vestingStart on first deposit\r
if (vestingStart == 0) {\r
vestingStart = block.timestamp;\r
}\r
\r
return this.onERC721Received.selector;\r
}\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);
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
"
},
"@openzeppelin/contracts/security/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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, _status will be _NOT_ENTERED
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;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
"
},
"@openzeppelin/contracts/token/ERC721/IERC721.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol)
pragma solidity >=0.6.2;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
"
},
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
".deps/npm/@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-21 10:39:29
Comments
Log in to comment.
No comments yet.