Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Initialize | 9587068 | 22 hrs ago | IN | 0 S | 0.01366194 |
Loading...
Loading
Contract Name:
AlchemyGovernance
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./AlchemyGameUpgradeableV2.sol"; import "./AlchemyTournament.sol"; contract AlchemyGovernance is Initializable, OwnableUpgradeable { struct ElementListing { address lister; uint256 feePaid; uint256 totalVotes; mapping(address => uint256) voterShares; mapping(address => bool) hasVoted; address[] voters; bool isActive; } struct Epoch { uint256 startTime; uint256 endTime; uint256 totalRewardPool; uint256[] elementIds; uint256[] winners; mapping(uint256 => ElementListing) elements; bool isFinalized; } IERC20 public rewardToken; AlchemyGameUpgradeableV2 public game; AlchemyTournament public tournament; uint256 public constant BONUS_PERCENTAGE = 50; uint256 public epochDuration; uint256 public currentEpoch; uint256 public minimumListingFee; bool public paused; uint256 public constant FEE_DECAY = 10; mapping(uint256 => Epoch) public epochs; mapping(uint256 => mapping(uint256 => mapping(address => uint256))) public voterRewards; event ElementListed(address indexed lister, uint256 elementId, uint256 fee); event Voted(address indexed voter, uint256 indexed epoch, uint256 elementId, uint256 amount); event EpochFinalized(uint256 indexed epoch, uint256[] winningElements); event RewardsDeposited(uint256 amount); event RewardsWithdrawn(uint256 amount); event VoteCancelled(address indexed voter, uint256 indexed epoch, uint256 elementId, uint256 amount); function initialize( address _game, address _tournament, address _rewardToken, uint256 _epochDuration, uint256 _minimumFee ) public initializer { __Ownable_init(msg.sender); game = AlchemyGameUpgradeableV2(_game); tournament = AlchemyTournament(_tournament); rewardToken = IERC20(_rewardToken); epochDuration = _epochDuration; minimumListingFee = _minimumFee; currentEpoch = 1; epochs[1].startTime = block.timestamp; epochs[1].endTime = block.timestamp + epochDuration; } modifier whenNotPaused() { require(!paused, "Contract paused"); _; } function setPaused(bool _paused) external onlyOwner { paused = _paused; } function listElement(uint256 elementId, uint256 fee) external whenNotPaused { require(fee >= minimumListingFee, "Fee too low"); (, , bool discovered, , ) = game.elements(elementId); require(discovered, "Element not found"); uint256 nextEpoch = currentEpoch + 1; require(!epochs[nextEpoch].isFinalized, "Epoch closed"); ElementListing storage element = epochs[nextEpoch].elements[elementId]; require(element.lister == address(0), "Element already listed"); rewardToken.transferFrom(msg.sender, address(this), fee); element.lister = msg.sender; element.feePaid = fee; element.totalVotes = 0; element.isActive = true; element.voters = new address[](0); epochs[nextEpoch].elementIds.push(elementId); epochs[nextEpoch].totalRewardPool += fee; emit ElementListed(msg.sender, elementId, fee); } function vote(uint256 elementId, uint256 votingPower) external whenNotPaused { Epoch storage epoch = epochs[currentEpoch]; require(block.timestamp <= epoch.endTime, "Voting period ended"); ElementListing storage element = epoch.elements[elementId]; require(element.isActive, "Invalid element"); require(votingPower > 0, "Voting power must > 0"); rewardToken.transferFrom(msg.sender, address(this), votingPower); if (!element.hasVoted[msg.sender]) { element.voters.push(msg.sender); element.hasVoted[msg.sender] = true; } element.totalVotes += votingPower; element.voterShares[msg.sender] += votingPower; emit Voted(msg.sender, currentEpoch, elementId, votingPower); } function cancelVote(uint256 elementId) external whenNotPaused { Epoch storage epoch = epochs[currentEpoch]; require(block.timestamp <= epoch.endTime, "Voting ended"); ElementListing storage element = epoch.elements[elementId]; require(element.isActive, "Element not active"); uint256 voterShare = element.voterShares[msg.sender]; require(voterShare > 0, "No vote"); element.totalVotes -= voterShare; element.voterShares[msg.sender] = 0; rewardToken.transfer(msg.sender, voterShare); emit VoteCancelled(msg.sender, currentEpoch, elementId, voterShare); } function finalizeEpoch() external onlyOwner { Epoch storage epoch = epochs[currentEpoch]; require(block.timestamp > epoch.endTime, "Epoch ongoing"); require(!epoch.isFinalized, "Already finalized"); if (epoch.elementIds.length == 0) { // No elements: Finalize and reset epoch.isFinalized = true; _setupNextEpoch(new uint256[](0)); emit EpochFinalized(currentEpoch, new uint256[](0)); currentEpoch++; return; } uint256[] memory winners = _selectTopElements(3); epoch.winners = winners; // Calculate total votes from winners uint256 totalVotes; for (uint256 i = 0; i < winners.length; i++) { totalVotes += epochs[currentEpoch].elements[winners[i]].totalVotes; } if (totalVotes == 0) { // No votes: Skip distribution, finalize, and carry over elements _setupNextEpoch(winners); epoch.isFinalized = true; emit EpochFinalized(currentEpoch, winners); currentEpoch++; return; } else { // Distribute rewards as usual uint256 bonusRewards = (epoch.totalRewardPool * BONUS_PERCENTAGE) / 100; uint256 totalRewards = epoch.totalRewardPool + bonusRewards; _distributeRewards(winners, totalRewards); _setupNextEpoch(winners); } epoch.isFinalized = true; emit EpochFinalized(currentEpoch, winners); currentEpoch++; } function _selectTopElements(uint256 count) internal view returns (uint256[] memory) { uint256[] memory elements = epochs[currentEpoch].elementIds; uint256 loopCount = count; if (elements.length < loopCount) { loopCount = elements.length; } uint256[] memory winners = new uint256[](loopCount); // Adjust winners array length uint256[] memory votes = new uint256[](elements.length); // Collect votes for (uint256 i = 0; i < elements.length; i++) { votes[i] = epochs[currentEpoch].elements[elements[i]].totalVotes; } // Simple selection sort up to loopCount for (uint256 i = 0; i < loopCount; i++) { uint256 maxIndex = i; for (uint256 j = i + 1; j < elements.length; j++) { if (votes[j] > votes[maxIndex]) { maxIndex = j; } } if (maxIndex != i && votes[maxIndex] > 0) { (elements[i], elements[maxIndex]) = (elements[maxIndex], elements[i]); (votes[i], votes[maxIndex]) = (votes[maxIndex], votes[i]); } winners[i] = elements[i]; } return winners; } function _distributeRewards(uint256[] memory winners, uint256 totalRewards) internal { uint256 totalVotes; for (uint256 i = 0; i < winners.length; i++) { totalVotes += epochs[currentEpoch].elements[winners[i]].totalVotes; } require(totalVotes > 0, "No votes"); for (uint256 i = 0; i < winners.length; i++) { ElementListing storage element = epochs[currentEpoch].elements[winners[i]]; uint256 elementReward = (totalRewards * element.totalVotes) / totalVotes; uint256 voterRewardsTotal = (elementReward * 80) / 100; for (uint256 j = 0; j < element.voters.length; j++) { address voter = element.voters[j]; uint256 share = (element.voterShares[voter] * voterRewardsTotal) / element.totalVotes; if (share > 0) { voterRewards[currentEpoch][winners[i]][voter] += share; } } if (elementReward - voterRewardsTotal > 0) { rewardToken.transfer(element.lister, elementReward - voterRewardsTotal); } } } function claimVoterRewards(uint256 epoch, uint256 elementId) external { uint256 amount = voterRewards[epoch][elementId][msg.sender]; require(amount > 0, "No rewards"); voterRewards[epoch][elementId][msg.sender] = 0; rewardToken.transfer(msg.sender, amount); } function depositRewards(uint256 amount) external onlyOwner { rewardToken.transferFrom(msg.sender, address(this), amount); emit RewardsDeposited(amount); } function withdrawExcessRewards(uint256 amount) external onlyOwner { uint256 contractBalance = rewardToken.balanceOf(address(this)); uint256 lockedRewards = _calculateLockedRewards(); require(contractBalance - lockedRewards >= amount, "Insufficient funds"); rewardToken.transfer(owner(), amount); emit RewardsWithdrawn(amount); } // Getters function getEpochElements(uint256 epoch) external view returns (uint256[] memory) { return epochs[epoch].elementIds; } function getEpochWinners(uint256 epoch) external view returns (uint256[] memory) { require(epoch <= currentEpoch, "Invalid epoch"); require(epochs[epoch].isFinalized, "Epoch not finalized"); return epochs[epoch].winners; } function getVoterShare(address voter, uint256 epoch, uint256 elementId) external view returns (uint256) { return epochs[epoch].elements[elementId].voterShares[voter]; } function getElementDetails(uint256 epoch, uint256 elementId) public view returns ( address lister, uint256 feePaid, uint256 totalVotes, bool isActive ) { ElementListing storage listing = epochs[epoch].elements[elementId]; return (listing.lister, listing.feePaid, listing.totalVotes, listing.isActive); } function getElementVoters(uint256 epoch, uint256 elementId) public view returns (address[] memory) { return epochs[epoch].elements[elementId].voters; } function hasVoted(uint256 epoch, uint256 elementId, address voter) public view returns (bool) { return epochs[epoch].elements[elementId].hasVoted[voter]; } function getVoterShares(uint256 epoch, uint256 elementId, address voter) public view returns (uint256) { return epochs[epoch].elements[elementId].voterShares[voter]; } function _setupNextEpoch(uint256[] memory winners) internal { uint256 nextEpoch = currentEpoch + 1; epochs[nextEpoch].startTime = block.timestamp; epochs[nextEpoch].endTime = block.timestamp + epochDuration; for (uint256 i = 0; i < epochs[currentEpoch].elementIds.length; i++) { uint256 elementId = epochs[currentEpoch].elementIds[i]; bool isWinner = false; for (uint256 j = 0; j < winners.length; j++) { if (winners[j] == elementId) { isWinner = true; break; } } if (!isWinner) { ElementListing storage currentElement = epochs[currentEpoch].elements[elementId]; ElementListing storage newElement = epochs[nextEpoch].elements[elementId]; // Only carry over if not already listed if (newElement.lister == address(0)) { newElement.lister = currentElement.lister; newElement.feePaid = currentElement.feePaid * (100 - FEE_DECAY) / 100; newElement.isActive = true; newElement.totalVotes = 0; newElement.voters = new address[](0); epochs[nextEpoch].elementIds.push(elementId); epochs[nextEpoch].totalRewardPool += newElement.feePaid; } } } } function _calculateLockedRewards() internal view returns (uint256) { uint256 locked; for (uint256 i = 1; i <= currentEpoch; i++) { if (!epochs[i].isFinalized) { locked += epochs[i].totalRewardPool; } } return locked; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly { $.slot := OwnableStorageLocation } } /** * @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. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { 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) { OwnableStorage storage $ = _getOwnableStorage(); 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 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 { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/ERC1155.sol) pragma solidity ^0.8.20; import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {IERC1155MetadataURI} from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol"; import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ERC165Upgradeable} from "../../utils/introspection/ERC165Upgradeable.sol"; import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; import {IERC1155Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 * Originally based on code by Enjin: https://github.com/enjin/erc-1155 */ abstract contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155, IERC1155MetadataURI, IERC1155Errors { using Arrays for uint256[]; using Arrays for address[]; /// @custom:storage-location erc7201:openzeppelin.storage.ERC1155 struct ERC1155Storage { mapping(uint256 id => mapping(address account => uint256)) _balances; mapping(address account => mapping(address operator => bool)) _operatorApprovals; // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json string _uri; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC1155")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ERC1155StorageLocation = 0x88be536d5240c274a3b1d3a1be54482fd9caa294f08c62a7cde569f49a3c4500; function _getERC1155Storage() private pure returns (ERC1155Storage storage $) { assembly { $.slot := ERC1155StorageLocation } } /** * @dev See {_setURI}. */ function __ERC1155_init(string memory uri_) internal onlyInitializing { __ERC1155_init_unchained(uri_); } function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing { _setURI(uri_); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) { return interfaceId == type(IERC1155).interfaceId || interfaceId == type(IERC1155MetadataURI).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC1155MetadataURI-uri}. * * This implementation returns the same URI for *all* token types. It relies * on the token type ID substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * Clients calling this function must replace the `\{id\}` substring with the * actual token type ID. */ function uri(uint256 /* id */) public view virtual returns (string memory) { ERC1155Storage storage $ = _getERC1155Storage(); return $._uri; } /** * @dev See {IERC1155-balanceOf}. */ function balanceOf(address account, uint256 id) public view virtual returns (uint256) { ERC1155Storage storage $ = _getERC1155Storage(); return $._balances[id][account]; } /** * @dev See {IERC1155-balanceOfBatch}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch( address[] memory accounts, uint256[] memory ids ) public view virtual returns (uint256[] memory) { if (accounts.length != ids.length) { revert ERC1155InvalidArrayLength(ids.length, accounts.length); } uint256[] memory batchBalances = new uint256[](accounts.length); for (uint256 i = 0; i < accounts.length; ++i) { batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i)); } return batchBalances; } /** * @dev See {IERC1155-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC1155-isApprovedForAll}. */ function isApprovedForAll(address account, address operator) public view virtual returns (bool) { ERC1155Storage storage $ = _getERC1155Storage(); return $._operatorApprovals[account][operator]; } /** * @dev See {IERC1155-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual { address sender = _msgSender(); if (from != sender && !isApprovedForAll(from, sender)) { revert ERC1155MissingApprovalForAll(sender, from); } _safeTransferFrom(from, to, id, value, data); } /** * @dev See {IERC1155-safeBatchTransferFrom}. */ function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data ) public virtual { address sender = _msgSender(); if (from != sender && !isApprovedForAll(from, sender)) { revert ERC1155MissingApprovalForAll(sender, from); } _safeBatchTransferFrom(from, to, ids, values, data); } /** * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from` * (or `to`) is the zero address. * * Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise. * * Requirements: * * - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received} * or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value. * - `ids` and `values` must have the same length. * * NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead. */ function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual { ERC1155Storage storage $ = _getERC1155Storage(); if (ids.length != values.length) { revert ERC1155InvalidArrayLength(ids.length, values.length); } address operator = _msgSender(); for (uint256 i = 0; i < ids.length; ++i) { uint256 id = ids.unsafeMemoryAccess(i); uint256 value = values.unsafeMemoryAccess(i); if (from != address(0)) { uint256 fromBalance = $._balances[id][from]; if (fromBalance < value) { revert ERC1155InsufficientBalance(from, fromBalance, value, id); } unchecked { // Overflow not possible: value <= fromBalance $._balances[id][from] = fromBalance - value; } } if (to != address(0)) { $._balances[id][to] += value; } } if (ids.length == 1) { uint256 id = ids.unsafeMemoryAccess(0); uint256 value = values.unsafeMemoryAccess(0); emit TransferSingle(operator, from, to, id, value); } else { emit TransferBatch(operator, from, to, ids, values); } } /** * @dev Version of {_update} that performs the token acceptance check by calling * {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it * contains code (eg. is a smart contract at the moment of execution). * * IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any * update to the contract state after this function would break the check-effect-interaction pattern. Consider * overriding {_update} instead. */ function _updateWithAcceptanceCheck( address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data ) internal virtual { _update(from, to, ids, values); if (to != address(0)) { address operator = _msgSender(); if (ids.length == 1) { uint256 id = ids.unsafeMemoryAccess(0); uint256 value = values.unsafeMemoryAccess(0); _doSafeTransferAcceptanceCheck(operator, from, to, id, value, data); } else { _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data); } } } /** * @dev Transfers a `value` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - `from` must have a balance of tokens of type `id` of at least `value` amount. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal { if (to == address(0)) { revert ERC1155InvalidReceiver(address(0)); } if (from == address(0)) { revert ERC1155InvalidSender(address(0)); } (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value); _updateWithAcceptanceCheck(from, to, ids, values, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. * - `ids` and `values` must have the same length. */ function _safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data ) internal { if (to == address(0)) { revert ERC1155InvalidReceiver(address(0)); } if (from == address(0)) { revert ERC1155InvalidSender(address(0)); } _updateWithAcceptanceCheck(from, to, ids, values, data); } /** * @dev Sets a new URI for all token types, by relying on the token type ID * substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * By this mechanism, any occurrence of the `\{id\}` substring in either the * URI or any of the values in the JSON file at said URI will be replaced by * clients with the token type ID. * * For example, the `https://token-cdn-domain/\{id\}.json` URI would be * interpreted by clients as * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` * for token type ID 0x4cce0. * * See {uri}. * * Because these URIs cannot be meaningfully represented by the {URI} event, * this function emits no events. */ function _setURI(string memory newuri) internal virtual { ERC1155Storage storage $ = _getERC1155Storage(); $._uri = newuri; } /** * @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _mint(address to, uint256 id, uint256 value, bytes memory data) internal { if (to == address(0)) { revert ERC1155InvalidReceiver(address(0)); } (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value); _updateWithAcceptanceCheck(address(0), to, ids, values, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `values` must have the same length. * - `to` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal { if (to == address(0)) { revert ERC1155InvalidReceiver(address(0)); } _updateWithAcceptanceCheck(address(0), to, ids, values, data); } /** * @dev Destroys a `value` amount of tokens of type `id` from `from` * * Emits a {TransferSingle} event. * * Requirements: * * - `from` cannot be the zero address. * - `from` must have at least `value` amount of tokens of type `id`. */ function _burn(address from, uint256 id, uint256 value) internal { if (from == address(0)) { revert ERC1155InvalidSender(address(0)); } (uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value); _updateWithAcceptanceCheck(from, address(0), ids, values, ""); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. * * Emits a {TransferBatch} event. * * Requirements: * * - `from` cannot be the zero address. * - `from` must have at least `value` amount of tokens of type `id`. * - `ids` and `values` must have the same length. */ function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal { if (from == address(0)) { revert ERC1155InvalidSender(address(0)); } _updateWithAcceptanceCheck(from, address(0), ids, values, ""); } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the zero address. */ function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { ERC1155Storage storage $ = _getERC1155Storage(); if (operator == address(0)) { revert ERC1155InvalidOperator(address(0)); } $._operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address * if it contains code at the moment of execution. */ function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 value, bytes memory data ) private { if (to.code.length > 0) { try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) { // Tokens rejected revert ERC1155InvalidReceiver(to); } } catch (bytes memory reason) { if (reason.length == 0) { // non-ERC1155Receiver implementer revert ERC1155InvalidReceiver(to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } } /** * @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address * if it contains code at the moment of execution. */ function _doSafeBatchTransferAcceptanceCheck( address operator, address from, address to, uint256[] memory ids, uint256[] memory values, bytes memory data ) private { if (to.code.length > 0) { try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns ( bytes4 response ) { if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { // Tokens rejected revert ERC1155InvalidReceiver(to); } } catch (bytes memory reason) { if (reason.length == 0) { // non-ERC1155Receiver implementer revert ERC1155InvalidReceiver(to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } } /** * @dev Creates an array in memory with only one value for each of the elements provided. */ function _asSingletonArrays( uint256 element1, uint256 element2 ) private pure returns (uint256[] memory array1, uint256[] memory array2) { /// @solidity memory-safe-assembly assembly { // Load the free memory pointer array1 := mload(0x40) // Set array length to 1 mstore(array1, 1) // Store the single element at the next word after the length (where content starts) mstore(add(array1, 0x20), element1) // Repeat for next array locating it right after the first array array2 := add(array1, 0x40) mstore(array2, 1) mstore(add(array2, 0x20), element2) // Update the free memory pointer by pointing after the second array mstore(0x40, add(array2, 0x40)) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } 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; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165Upgradeable is Initializable, IERC165 { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @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 ReentrancyGuardUpgradeable is Initializable { // 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; /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard struct ReentrancyGuardStorage { uint256 _status; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { assembly { $.slot := ReentrancyGuardStorageLocation } } /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); $._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 { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // On the first call to nonReentrant, _status will be NOT_ENTERED if ($._status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail $._status = ENTERED; } function _nonReentrantAfter() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // 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) { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); return $._status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/IERC1155MetadataURI.sol) pragma solidity ^0.8.20; import {IERC1155} from "../IERC1155.sol"; /** * @dev Interface of the optional ERC1155MetadataExtension interface, as defined * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. */ interface IERC1155MetadataURI is IERC1155 { /** * @dev Returns the URI for token type `id`. * * If the `\{id\}` substring is present in the URI, it must be replaced by * clients with the actual token type ID. */ function uri(uint256 id) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the value of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch( address[] calldata accounts, uint256[] calldata ids ) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155Received} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `value` amount. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments. * * Requirements: * * - `ids` and `values` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Interface that must be implemented by smart contracts in order to receive * ERC-1155 token transfers. */ interface IERC1155Receiver is IERC165 { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol) pragma solidity ^0.8.20; import {StorageSlot} from "./StorageSlot.sol"; import {Math} from "./math/Math.sol"; /** * @dev Collection of functions related to array types. */ library Arrays { using StorageSlot for bytes32; /** * @dev Searches a sorted `array` and returns the first index that contains * a value greater or equal to `element`. If no such index exists (i.e. all * values in the array are strictly less than `element`), the array length is * returned. Time complexity O(log n). * * `array` is expected to be sorted in ascending order, and to contain no * repeated elements. */ function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { uint256 low = 0; uint256 high = array.length; if (high == 0) { return 0; } while (low < high) { uint256 mid = Math.average(low, high); // Note that mid will always be strictly less than high (i.e. it will be a valid array index) // because Math.average rounds towards zero (it does integer division with truncation). if (unsafeAccess(array, mid).value > element) { high = mid; } else { low = mid + 1; } } // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. if (low > 0 && unsafeAccess(array, low - 1).value == element) { return low - 1; } else { return low; } } /** * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. * * WARNING: Only use if you are certain `pos` is lower than the array length. */ function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) { bytes32 slot; // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. /// @solidity memory-safe-assembly assembly { mstore(0, arr.slot) slot := add(keccak256(0, 0x20), pos) } return slot.getAddressSlot(); } /** * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. * * WARNING: Only use if you are certain `pos` is lower than the array length. */ function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) { bytes32 slot; // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. /// @solidity memory-safe-assembly assembly { mstore(0, arr.slot) slot := add(keccak256(0, 0x20), pos) } return slot.getBytes32Slot(); } /** * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. * * WARNING: Only use if you are certain `pos` is lower than the array length. */ function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) { bytes32 slot; // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. /// @solidity memory-safe-assembly assembly { mstore(0, arr.slot) slot := add(keccak256(0, 0x20), pos) } return slot.getUint256Slot(); } /** * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. * * WARNING: Only use if you are certain `pos` is lower than the array length. */ function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) { assembly { res := mload(add(add(arr, 0x20), mul(pos, 0x20))) } } /** * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. * * WARNING: Only use if you are certain `pos` is lower than the array length. */ function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) { assembly { res := mload(add(add(arr, 0x20), mul(pos, 0x20))) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.20; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the Merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates Merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** *@dev The multiproof provided is not valid. */ error MerkleProofInvalidMultiproof(); /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Sorts the pair (a, b) and hashes the result. */ function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } /** * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory. */ function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * 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[EIP 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC1967 implementation slot: * ```solidity * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { uint256 localValue = value; bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal * representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; contract AlchemyGameUpgradeableV2 is Initializable, ERC1155Upgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable { uint256 public totalNFT; uint256 public baseFee; address public fundAddress; bytes32 public merkleRoot; mapping(uint256 => Element) public elements; mapping(uint256 => mapping(uint256 => uint256)) public combinations; mapping(address => bool) public hasClaimedAirdrop; mapping(address => RoyaltyInfo) public creatorRoyalties; uint256 public nextElementId; uint256 public elementSubmissionFee; uint256 public royaltyPercentage; mapping(uint256 => Combination) public combinationList; uint256 public combinationCount; bool private combinationsMigrated; struct Element { uint256 id; uint256 rarity; bool discovered; address creator; bool isUserCreated; } struct RoyaltyInfo { uint256 accumulatedRoyalties; bool isActive; } event ElementDiscovered(address indexed player, uint256 elementId); event NewElementCreated(address indexed creator, uint256 elementId, uint256 rarity); event NewCombinationAdded(address indexed creator, uint256 element1, uint256 element2, uint256 result); event RoyaltiesClaimed(address indexed creator, uint256 amount); struct Combination { uint256 element1; uint256 element2; uint256 result; } // Initialize function updated with new parameters function initializeV2( uint256 _elementSubmissionFee, uint256 _royaltyPercentage, uint256 _nextElementId ) public reinitializer(2) { require(_royaltyPercentage <= 50, "Royalty cannot exceed 50%"); elementSubmissionFee = _elementSubmissionFee; royaltyPercentage = _royaltyPercentage; // Only set nextElementId if it hasn't been set yet (to avoid overwriting) if (nextElementId == 0) { nextElementId = _nextElementId; // Set to 47 during initialization } // Initialize new state variables combinationCount = 0; combinationsMigrated = false; } // Function to set the fund address function setFundAddress(address _account) external onlyOwner { fundAddress = _account; } // Function to set the base minting fee function setBaseFee(uint256 _baseFee) external onlyOwner { baseFee = _baseFee; } // Function to set the Merkle root for airdrop verification function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner { merkleRoot = _merkleRoot; } // New function to set element submission fee function setElementSubmissionFee(uint256 _fee) external onlyOwner { elementSubmissionFee = _fee; } // New function to set royalty percentage function setRoyaltyPercentage(uint256 _percentage) external onlyOwner { require(_percentage <= 50, "Royalty cannot exceed 50%"); royaltyPercentage = _percentage; } // Mint standard elements function mintStandardElements() external payable nonReentrant { require(balanceOf(msg.sender, 0) == 0, "Already owns Water"); require(balanceOf(msg.sender, 1) == 0, "Already owns Air"); require(balanceOf(msg.sender, 2) == 0, "Already owns Fire"); require(balanceOf(msg.sender, 3) == 0, "Already owns Earth"); require(msg.value >= baseFee * 4, "Insufficient ETH sent"); (bool sent, ) = fundAddress.call{value: msg.value}(""); require(sent, "Failed to send ETH"); _mint(msg.sender, 0, 1, ""); // Water _mint(msg.sender, 1, 1, ""); // Air _mint(msg.sender, 2, 1, ""); // Fire _mint(msg.sender, 3, 1, ""); // Earth totalNFT += 4; elements[0].discovered = true; elements[1].discovered = true; elements[2].discovered = true; elements[3].discovered = true; } // Claim airdrop using Merkle proof function claimAirdrop(bytes32[] calldata _merkleProof) external nonReentrant { require(!hasClaimedAirdrop[msg.sender], "Airdrop already claimed"); bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); require(MerkleProof.verify(_merkleProof, merkleRoot, leaf), "Invalid proof"); hasClaimedAirdrop[msg.sender] = true; _mint(msg.sender, 0, 1, ""); // Water _mint(msg.sender, 1, 1, ""); // Air _mint(msg.sender, 2, 1, ""); // Fire _mint(msg.sender, 3, 1, ""); // Earth totalNFT += 4; } // New function for users to submit elements function submitElement(uint256 rarity) external payable nonReentrant { require(msg.value >= elementSubmissionFee, "Insufficient submission fee"); require(rarity > 0 && rarity <= 10, "Invalid rarity range"); uint256 newElementId = nextElementId++; elements[newElementId] = Element({ id: newElementId, rarity: rarity, discovered: true, // User-created elements start as discovered creator: msg.sender, isUserCreated: true }); // Initialize royalty tracking if first element if (!creatorRoyalties[msg.sender].isActive) { creatorRoyalties[msg.sender].isActive = true; } // Transfer submission fee to fund address (bool sent, ) = fundAddress.call{value: msg.value}(""); require(sent, "Failed to send submission fee"); emit NewElementCreated(msg.sender, newElementId, rarity); } // New function for users to submit combinations function submitCombination(uint256 element1, uint256 element2, uint256 result) external { require(elements[element1].discovered, "Element 1 not discovered"); require(elements[element2].discovered, "Element 2 not discovered"); require(elements[result].discovered, "Result element not discovered"); require(combinations[element1][element2] == 0, "Combination already exists"); require(combinations[result][element1] == 0 && combinations[result][element2] == 0,"Combination creates a cycle"); // Only creator of the result element can add combinations using it require( elements[result].creator == msg.sender || !elements[result].isUserCreated, "Not authorized to use this result element" ); _addCombination(element1, element2, result); emit NewCombinationAdded(msg.sender, element1, element2, result); } // Modified mint function to handle royalties function mint(uint256 element1, uint256 element2) external payable nonReentrant { require(balanceOf(msg.sender, element1) > 0, "Insufficient balance of element1"); require(balanceOf(msg.sender, element2) > 0, "Insufficient balance of element2"); uint256 newElement = combinations[element1][element2]; require(newElement != 0, "Invalid combination"); Element storage elem = elements[newElement]; uint256 fee = calculateFee(elem.rarity); require(msg.value >= fee, "Insufficient ETH sent"); // Handle royalty distribution if element was created by a user if (elem.isUserCreated && elem.creator != address(0)) { uint256 royaltyAmount = (fee * royaltyPercentage) / 100; uint256 fundAmount = fee - royaltyAmount; // Update creator's royalty balance creatorRoyalties[elem.creator].accumulatedRoyalties += royaltyAmount; // Transfer remaining amount to fund address (bool sent, ) = fundAddress.call{value: fundAmount}(""); require(sent, "Failed to send ETH to fund"); } else { // If not user-created, all fees go to fund address (bool sent, ) = fundAddress.call{value: fee}(""); require(sent, "Failed to send ETH to fund"); } _mint(msg.sender, newElement, 1, ""); totalNFT++; if (!elem.discovered) { elem.discovered = true; emit ElementDiscovered(msg.sender, newElement); } } // New function for creators to claim their royalties function claimRoyalties() external nonReentrant { RoyaltyInfo storage royaltyInfo = creatorRoyalties[msg.sender]; require(royaltyInfo.isActive, "No royalties to claim"); require(royaltyInfo.accumulatedRoyalties > 0, "No royalties accumulated"); uint256 amount = royaltyInfo.accumulatedRoyalties; royaltyInfo.accumulatedRoyalties = 0; (bool sent, ) = msg.sender.call{value: amount}(""); require(sent, "Failed to send royalties"); emit RoyaltiesClaimed(msg.sender, amount); } // Function to calculate fee based on rarity function calculateFee(uint256 rarity) public view returns (uint256) { return baseFee * (rarity + 1); } // Migration function function migrateCombinations() external onlyOwner { require(!combinationsMigrated, "Combinations already migrated"); // Loop through all possible pairs of elements for (uint256 i = 0; i < nextElementId; i++) { for (uint256 j = 0; j < nextElementId; j++) { uint256 result = combinations[i][j]; if (result != 0) { // Add the combination to the combinationList combinationList[combinationCount] = Combination({ element1: i, element2: j, result: result }); combinationCount++; } } } // Mark migration as complete combinationsMigrated = true; } // Initialize elements and combinations function _initializeElements() private { // Add basic elements _addElement(0, 1); _addElement(1, 1); _addElement(2, 1); _addElement(3, 1); // Add intermediate elements _addElement(4, 2); _addElement(5, 2); _addElement(6, 2); _addElement(7, 2); _addElement(8, 2); _addElement(9, 3); _addElement(10, 3); _addElement(11, 3); _addElement(12, 3); _addElement(13, 3); _addElement(14, 4); _addElement(15, 4); _addElement(16, 4); _addElement(17, 5); _addElement(18, 5); _addElement(19, 6); _addElement(20, 7); _addElement(21, 8); _addElement(22, 9); _addElement(23, 10); _addElement(24, 7); // Smart Contract _addElement(25, 6); // Token _addElement(26, 8); // Dapp _addElement(27, 9); // DAO _addElement(28, 8); // Cryptocurrency _addElement(29, 7); // Exchange _addElement(30, 8); // Liquidity Pool _addElement(31, 9); // Yield Farming _addElement(32, 6); // Mining _addElement(33, 7); // Proof of Work _addElement(34, 8); // Consensus _addElement(35, 8); // Governance Token _addElement(36, 9); // DeFi _addElement(37, 10); // Decentralized Exchange _addElement(38, 7); // Wallet _addElement(39, 8); // Private Key _addElement(40, 7); // Public Key _addElement(41, 8); // Digital Signature _addElement(42, 9); // Identity Verification _addElement(43, 4); // Circuit _addElement(44, 6); // Network _addElement(45, 7); // Node _addElement(46, 8); // Validator // Add combinations _addCombination(0, 1, 7); // Water + Air = Rain _addCombination(0, 2, 4); // Water + Fire = Steam _addCombination(0, 3, 8); // Water + Earth = Mud _addCombination(1, 2, 5); // Air + Fire = Energy _addCombination(1, 10, 11); // Air + Rock = Sand _addCombination(2, 3, 6); // Fire + Earth = Lava _addCombination(2, 10, 12); // Fire + Rock = Metal _addCombination(2, 11, 13); // Fire + Sand = Glass _addCombination(3, 7, 9); // Earth + Rain = Plant _addCombination(6, 1, 10); // Lava + Air = Rock _addCombination(8, 9, 14); // Mud + Plant = Swamp _addCombination(13, 13, 15); // Glass + Glass = Eyeglasses _addCombination(5, 12, 16); // Energy + Metal = Electricity _addCombination(5, 8, 17); // Energy + Mud = Life _addCombination(17, 3, 18); // Life + Earth = Human _addCombination(15, 18, 19); // Eyeglasses + Human = Nerd _addCombination(16, 19, 20); // Electricity + Nerd = Computer _addCombination(20, 20, 21); // Computer + Computer = Internet _addCombination(20, 21, 22); // Computer + Internet = Blockchain _addCombination(22, 19, 23); // Blockchain + Nerd = Bitcoin _addCombination(22, 17, 24); // Blockchain + Life = Smart Contract _addCombination(24, 12, 25); // Smart Contract + Metal = Token _addCombination(24, 5, 26); // Smart Contract + Energy = Dapp _addCombination(26, 9, 27); // Dapp + Plant = DAO _addCombination(23, 24, 28); // Bitcoin + Smart Contract = Cryptocurrency _addCombination(28, 11, 29); // Cryptocurrency + Sand = Exchange _addCombination(29, 25, 30); // Exchange + Token = Liquidity Pool _addCombination(30, 16, 31); // Liquidity Pool + Electricity = Yield Farming _addCombination(23, 5, 32); // Bitcoin + Energy = Mining _addCombination(32, 6, 33); // Mining + Lava = Proof of Work _addCombination(33, 17, 34); // Proof of Work + Life = Consensus _addCombination(34, 25, 35); // Consensus + Token = Governance Token _addCombination(35, 26, 36); // Governance Token + Dapp = DeFi _addCombination(36, 14, 37); // DeFi + Swamp = Decentralized Exchange _addCombination(28, 22, 38); // Cryptocurrency + Blockchain = Wallet _addCombination(38, 16, 39); // Wallet + Electricity = Private Key _addCombination(39, 19, 40); // Private Key + Nerd = Public Key _addCombination(40, 10, 41); // Public Key + Rock = Digital Signature _addCombination(41, 24, 42); // Digital Signature + Smart Contract = Identity Verification } // Owner can add a new element function addElement(uint256 id, uint256 rarity) public onlyOwner { _addElement(id, rarity); } // Owner can add a new combination function addCombination(uint256 element1, uint256 element2, uint256 result) public onlyOwner { require(combinations[element1][element2] == 0, "Combination already exists"); _addCombination(element1, element2, result); } // Internal function to add elements function _addElement(uint256 id, uint256 rarity) private { elements[id] = Element({ id: id, rarity: rarity, discovered: false, creator: address(0), isUserCreated: false }); } // Internal function to add combinations function _addCombination(uint256 element1, uint256 element2, uint256 result) private { combinations[element1][element2] = result; combinations[element2][element1] = result; // Add the combination to the combinationList combinationList[combinationCount] = Combination({ element1: element1, element2: element2, result: result }); combinationCount++; } // Returns total supply of NFTs function totalSupply() public view returns (uint256) { return totalNFT; } // Returns details of an element function elementDetails(uint256 id) public view returns (uint256, uint256, bool, address, bool) { Element memory elem = elements[id]; return (elem.id, elem.rarity, elem.discovered, elem.creator, elem.isUserCreated); } // URI for element metadata function uri(uint256 tokenId) public view override returns (string memory) { return string(abi.encodePacked(super.uri(tokenId), Strings.toString(tokenId), ".json")); } // New view function to get creator's royalty info function getCreatorRoyalties(address creator) external view returns (uint256, bool) { RoyaltyInfo memory royaltyInfo = creatorRoyalties[creator]; return (royaltyInfo.accumulatedRoyalties, royaltyInfo.isActive); } // Function to get all elements created by a specific creator function getCreatorElements(address creator) external view returns (uint256[] memory) { uint256 count = 0; // First count elements by this creator for (uint256 i = 0; i < nextElementId; i++) { if (elements[i].creator == creator) { count++; } } // Create and fill array uint256[] memory creatorElements = new uint256[](count); uint256 index = 0; for (uint256 i = 0; i < nextElementId; i++) { if (elements[i].creator == creator) { creatorElements[index] = i; index++; } } return creatorElements; } function getAllCombinations() external view returns (Combination[] memory) { Combination[] memory allCombinations = new Combination[](combinationCount); for (uint256 i = 0; i < combinationCount; i++) { allCombinations[i] = combinationList[i]; } return allCombinations; } function hasCombination(uint256 element1, uint256 element2) external view returns (bool) { return combinations[element1][element2] != 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./AlchemyGameUpgradeableV2.sol"; interface IAlchemyGovernance { function currentEpoch() external view returns (uint256); function getEpochWinners(uint256 epoch) external view returns (uint256[] memory); } contract AlchemyTournament is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, IERC1155Receiver { struct StakingSession { uint256 startTime; uint256 endTime; uint256 totalRewards; uint256 totalStaked; uint256 distributedRewards; // (Optional) if you were tracking claimed rewards mapping(uint256 => uint256) elementStakes; mapping(uint256 => uint256) elementWeight; // NEW: total weight for each element mapping(address => mapping(uint256 => uint256)) userStakes; mapping(uint256 => bool) validElements; uint256[] winners; mapping(address => uint256) claimedRewards; mapping(address => mapping(uint256 => uint256)) userStakeTime; } AlchemyGameUpgradeableV2 public gameNFT; IERC20 public rewardToken; IAlchemyGovernance public governance; uint256 public currentSession; uint256 public sessionDuration; uint256 public governanceEpochOffset; mapping(uint256 => StakingSession) public sessions; event SessionStarted(uint256 sessionId, uint256[] elements); event Staked(address indexed user, uint256 sessionId, uint256 elementId, uint256 amount); event Withdrawn(address indexed user, uint256 sessionId, uint256 elementId, uint256 amount); event RewardsDeposited(uint256 sessionId, uint256 amount); event RewardsClaimed(address indexed user, uint256 sessionId, uint256 amount); function initialize( address _gameNFT, address _rewardToken, address _governance, uint256 _sessionDuration, uint256 _epochOffset ) public initializer { __Ownable_init(msg.sender); __ReentrancyGuard_init(); gameNFT = AlchemyGameUpgradeableV2(_gameNFT); rewardToken = IERC20(_rewardToken); governance = IAlchemyGovernance(_governance); sessionDuration = _sessionDuration; governanceEpochOffset = _epochOffset; } function startNewSession() external onlyOwner { uint256 governanceEpoch = governance.currentEpoch() - governanceEpochOffset; uint256[] memory winners = governance.getEpochWinners(governanceEpoch); currentSession++; StakingSession storage session = sessions[currentSession]; session.startTime = block.timestamp; session.endTime = block.timestamp + sessionDuration; for (uint256 i = 0; i < winners.length; i++) { uint256 elementId = winners[i]; session.validElements[elementId] = true; session.winners.push(elementId); } emit SessionStarted(currentSession, winners); } function stake(uint256 elementId, uint256 amount) external { StakingSession storage session = sessions[currentSession]; require(block.timestamp < session.endTime, "Session ended"); require(session.validElements[elementId], "Invalid element"); gameNFT.safeTransferFrom(msg.sender, address(this), elementId, amount, ""); uint256 prevStake = session.userStakes[msg.sender][elementId]; uint256 oldDepositTime = session.userStakeTime[msg.sender][elementId]; // may be 0 if none uint256 newWeightedDepositTime; uint256 newStake = prevStake + amount; if (prevStake == 0) { newWeightedDepositTime = block.timestamp; } else { newWeightedDepositTime = (prevStake * oldDepositTime + amount * block.timestamp) / newStake; } session.userStakeTime[msg.sender][elementId] = newWeightedDepositTime; uint256 oldUserWeight = prevStake * (session.endTime - oldDepositTime); uint256 newUserWeight = newStake * (session.endTime - newWeightedDepositTime); session.elementWeight[elementId] += (newUserWeight - oldUserWeight); session.elementStakes[elementId] += amount; session.userStakes[msg.sender][elementId] = newStake; session.totalStaked += amount; emit Staked(msg.sender, currentSession, elementId, amount); } function depositRewards(uint256 sessionId, uint256 amount) external onlyOwner { require(sessionId <= currentSession, "Invalid session"); rewardToken.transferFrom(msg.sender, address(this), amount); sessions[sessionId].totalRewards += amount; emit RewardsDeposited(sessionId, amount); } function claimRewardsAndWithdraw(uint256 sessionId) external nonReentrant { StakingSession storage session = sessions[sessionId]; require(block.timestamp > session.endTime, "Session ongoing"); uint256 claimable = _calculateClaimable(msg.sender, sessionId); require(claimable > 0, "No rewards"); session.claimedRewards[msg.sender] += claimable; rewardToken.transfer(msg.sender, claimable); emit RewardsClaimed(msg.sender, sessionId, claimable); for (uint256 i = 0; i < session.winners.length; i++) { uint256 elementId = session.winners[i]; uint256 stakedAmount = session.userStakes[msg.sender][elementId]; if (stakedAmount > 0) { session.userStakes[msg.sender][elementId] = 0; session.elementStakes[elementId] -= stakedAmount; session.totalStaked -= stakedAmount; gameNFT.safeTransferFrom(address(this), msg.sender, elementId, stakedAmount, ""); emit Withdrawn(msg.sender, sessionId, elementId, stakedAmount); } } } function _calculateClaimable(address user, uint256 sessionId) internal view returns (uint256) { StakingSession storage session = sessions[sessionId]; require(session.totalStaked > 0, "No stakes"); uint256 totalEntitlement = 0; for (uint256 i = 0; i < session.winners.length && i < 3; i++) { uint256 elementId = session.winners[i]; uint256 userStake = session.userStakes[user][elementId]; if (userStake > 0) { uint256 depositTime = session.userStakeTime[user][elementId]; uint256 userWeight = userStake * (session.endTime - depositTime); uint256 totalWeight = session.elementWeight[elementId]; uint256 elementRewardPool; if (i == 0) { // First winner gets 50% elementRewardPool = (session.totalRewards * 50) / 100; } else if (i == 1) { // Second winner gets 30% elementRewardPool = (session.totalRewards * 30) / 100; } else if (i == 2) { // Third winner gets 20% elementRewardPool = (session.totalRewards * 20) / 100; } uint256 rewardForElement = (userWeight * elementRewardPool) / totalWeight; totalEntitlement += rewardForElement; } } if (totalEntitlement <= session.claimedRewards[user]) { return 0; } return totalEntitlement - session.claimedRewards[user]; } function getSessionElements(uint256 sessionId) external view returns (uint256[] memory) { StakingSession storage session = sessions[sessionId]; return session.winners; } function getSessionInfo(uint256 sessionId) public view returns ( uint256 startTime, uint256 endTime, uint256 totalRewards, uint256 totalStaked ) { StakingSession storage session = sessions[sessionId]; return (session.startTime, session.endTime, session.totalRewards, session.totalStaked); } function getUserStake( uint256 sessionId, address user, uint256 elementId ) external view returns (uint256) { return sessions[sessionId].userStakes[user][elementId]; } function calculateClaimable(address user, uint256 sessionId) external view returns (uint256) { return _calculateClaimable(user, sessionId); } // IERC1155Receiver interface implementations. function onERC1155Received( address, /* operator */ address, /* from */ uint256, /* id */ uint256, /* value */ bytes memory /* data */ ) public pure override returns (bytes4) { return this.onERC1155Received.selector; } function onERC1155BatchReceived( address, /* operator */ address, /* from */ uint256[] memory, /* ids */ uint256[] memory, /* values */ bytes memory /* data */ ) public pure override returns (bytes4) { return this.onERC1155BatchReceived.selector; } /// @notice Implementation of ERC165's supportsInterface. function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId; } /// @notice Recover any ERC20 tokens mistakenly sent to the contract. function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner { IERC20(tokenAddress).transfer(owner(), tokenAmount); } /// @notice Recover any ERC1155 tokens mistakenly sent to the contract. function recoverERC1155(address tokenAddress, uint256 tokenId, uint256 amount) external onlyOwner { IERC1155(tokenAddress).safeTransferFrom(address(this), owner(), tokenId, amount, ""); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lister","type":"address"},{"indexed":false,"internalType":"uint256","name":"elementId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ElementListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"winningElements","type":"uint256[]"}],"name":"EpochFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"elementId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VoteCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"elementId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Voted","type":"event"},{"inputs":[],"name":"BONUS_PERCENTAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DECAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"elementId","type":"uint256"}],"name":"cancelVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"elementId","type":"uint256"}],"name":"claimVoterRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epochDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochs","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"totalRewardPool","type":"uint256"},{"internalType":"bool","name":"isFinalized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalizeEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"game","outputs":[{"internalType":"contract AlchemyGameUpgradeableV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"elementId","type":"uint256"}],"name":"getElementDetails","outputs":[{"internalType":"address","name":"lister","type":"address"},{"internalType":"uint256","name":"feePaid","type":"uint256"},{"internalType":"uint256","name":"totalVotes","type":"uint256"},{"internalType":"bool","name":"isActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"elementId","type":"uint256"}],"name":"getElementVoters","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochElements","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochWinners","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"voter","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"elementId","type":"uint256"}],"name":"getVoterShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"elementId","type":"uint256"},{"internalType":"address","name":"voter","type":"address"}],"name":"getVoterShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"elementId","type":"uint256"},{"internalType":"address","name":"voter","type":"address"}],"name":"hasVoted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_game","type":"address"},{"internalType":"address","name":"_tournament","type":"address"},{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"uint256","name":"_epochDuration","type":"uint256"},{"internalType":"uint256","name":"_minimumFee","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"elementId","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"listElement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minimumListingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tournament","outputs":[{"internalType":"contract AlchemyTournament","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"elementId","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"voterRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawExcessRewards","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b506125a8806100206000396000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c806371715de611610104578063a6b63eb8116100a2578063c6b61e4c11610071578063c6b61e4c14610483578063dce06315146104dd578063f2fde38b1461055b578063f7c618c11461056e57600080fd5b8063a6b63eb814610437578063b384abef1461044a578063bacbe2da1461045d578063c3fe3e281461047057600080fd5b80638bdf67f2116100de5780638bdf67f2146103c35780638c715fa9146103d65780638d0272e5146103e95780638da5cb5b1461042f57600080fd5b806371715de61461039f57806376671808146103b257806382ae9ef7146103bb57600080fd5b8063268423781161017c5780634ff0876a1161014b5780634ff0876a146103615780635c975abb1461036a5780636df4ef9814610377578063715018a61461039757600080fd5b8063268423781461033557806335ed3fd61461033e57806336a79f00146103465780633fb69abf1461035957600080fd5b806316c38b3c116101b857806316c38b3c1461029c578063174e34f5146102b15780631e0197e2146102f75780631f69f45e1461032257600080fd5b806301f96a71146101df57806302bbba53146102235780630d39a1d314610243575b600080fd5b6102106101ed366004612220565b600860209081526000938452604080852082529284528284209052825290205481565b6040519081526020015b60405180910390f35b610236610231366004612259565b610581565b60405161021a919061227b565b61028c610251366004612220565b600083815260076020908152604080832085845260050182528083206001600160a01b038516845260040190915290205460ff169392505050565b604051901515815260200161021a565b6102af6102aa3660046122d6565b6105ff565b005b6102106102bf3660046122fa565b600082815260076020908152604080832084845260050182528083206001600160a01b03871684526003019091529020549392505050565b60025461030a906001600160a01b031681565b6040516001600160a01b03909116815260200161021a565b6102af610330366004612259565b61061a565b61021060055481565b610210603281565b6102af61035436600461232f565b61071b565b610210600a81565b61021060035481565b60065461028c9060ff1681565b61038a61038536600461232f565b6108af565b60405161021a9190612348565b6102af6109ad565b6102af6103ad366004612259565b6109c1565b61021060045481565b6102af610cfd565b6102af6103d136600461232f565b610fcf565b61038a6103e436600461232f565b611089565b6102106103f7366004612220565b600083815260076020908152604080832085845260050182528083206001600160a01b03851684526003019091529020549392505050565b61030a6110ec565b6102af610445366004612380565b61111a565b6102af610458366004612259565b6112d8565b6102af61046b36600461232f565b611554565b60015461030a906001600160a01b031681565b6104bb61049136600461232f565b60076020526000908152604090208054600182015460028301546006909301549192909160ff1684565b604080519485526020850193909352918301521515606082015260800161021a565b61052f6104eb366004612259565b60009182526007602090815260408084209284526005909201905290208054600182015460028301546006909301546001600160a01b039092169390929160ff1690565b604080516001600160a01b0390951685526020850193909352918301521515606082015260800161021a565b6102af6105693660046123db565b61174a565b60005461030a906001600160a01b031681565b600082815260076020908152604080832084845260059081018352928190209092018054835181840281018401909452808452606093928301828280156105f157602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116105d3575b505050505090505b92915050565b610607611788565b6006805460ff1916911515919091179055565b600082815260086020908152604080832084845282528083203384529091529020548061067b5760405162461bcd60e51b815260206004820152600a6024820152694e6f207265776172647360b01b60448201526064015b60405180910390fd5b600083815260086020908152604080832085845282528083203380855292528083208390559154915163a9059cbb60e01b81526004810191909152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af11580156106f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071591906123f8565b50505050565b610723611788565b600080546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561076c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107909190612415565b9050600061079c6117ba565b9050826107a98284612444565b10156107ec5760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e742066756e647360701b6044820152606401610672565b6000546001600160a01b031663a9059cbb6108056110ec565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018690526044016020604051808303816000875af1158015610852573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087691906123f8565b506040518381527f7c087d7d3fa0d567fd03d7feb4cd3edacd2a08580cdbb2bc2f86e671279ea01d9060200160405180910390a1505050565b60606004548211156108f35760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840cae0dec6d609b1b6044820152606401610672565b60008281526007602052604090206006015460ff1661094a5760405162461bcd60e51b8152602060048201526013602482015272115c1bd8da081b9bdd08199a5b985b1a5e9959606a1b6044820152606401610672565b600082815260076020908152604091829020600401805483518184028101840190945280845290918301828280156109a157602002820191906000526020600020905b81548152602001906001019080831161098d575b50505050509050919050565b6109b5611788565b6109bf6000611819565b565b60065460ff16156109e45760405162461bcd60e51b815260040161067290612457565b600554811015610a245760405162461bcd60e51b815260206004820152600b60248201526a46656520746f6f206c6f7760a81b6044820152606401610672565b60015460405163e0d64b3760e01b8152600481018490526000916001600160a01b03169063e0d64b379060240160a060405180830381865afa158015610a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a929190612480565b50509250505080610ad95760405162461bcd60e51b8152602060048201526011602482015270115b195b595b9d081b9bdd08199bdd5b99607a1b6044820152606401610672565b60006004546001610aea91906124e1565b60008181526007602052604090206006015490915060ff1615610b3e5760405162461bcd60e51b815260206004820152600c60248201526b115c1bd8da0818db1bdcd95960a21b6044820152606401610672565b6000818152600760209081526040808320878452600501909152902080546001600160a01b031615610bab5760405162461bcd60e51b8152602060048201526016602482015275115b195b595b9d08185b1c9958591e481b1a5cdd195960521b6044820152606401610672565b6000546040516323b872dd60e01b8152336004820152306024820152604481018690526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2691906123f8565b5080546001600160a01b03191633178155600180820185905560006002830181905560068301805460ff19169092179091556040805191825260208201908190529051610c77916005840191612156565b50600082815260076020908152604082206003810180546001810182559084529183209091018790558382526002018054869290610cb69084906124e1565b9091555050604080518681526020810186905233917ff758c05d390770eb7d9710201a0626a5f4c9dd12bd336a72eeea6af3c593d897910160405180910390a25050505050565b610d05611788565b600454600090815260076020526040902060018101544211610d595760405162461bcd60e51b815260206004820152600d60248201526c45706f6368206f6e676f696e6760981b6044820152606401610672565b600681015460ff1615610da25760405162461bcd60e51b8152602060048201526011602482015270105b1c9958591e48199a5b985b1a5e9959607a1b6044820152606401610672565b6003810154600003610e365760068101805460ff1916600117905560408051600080825260208201909252610dd7915061188a565b600454604080516000815260208101918290527f0134e2180e3869aa7f850d9533f2225d637dfbdc224a1f7eb6bd7f2448d05a8291610e169190612348565b60405180910390a260048054906000610e2e8361250a565b919050555050565b6000610e426003611aa0565b8051909150610e5a90600484019060208401906121bb565b506000805b8251811015610ecd576007600060045481526020019081526020016000206005016000848381518110610e9457610e94612523565b602002602001015181526020019081526020016000206002015482610eb991906124e1565b915080610ec58161250a565b915050610e5f565b5080600003610f4357610edf8261188a565b60068301805460ff191660011790556004546040517f0134e2180e3869aa7f850d9533f2225d637dfbdc224a1f7eb6bd7f2448d05a8290610f21908590612348565b60405180910390a260048054906000610f398361250a565b9190505550505050565b6000606460328560020154610f589190612539565b610f629190612550565b90506000818560020154610f7691906124e1565b9050610f828482611dfd565b610f8b8461188a565b505060068301805460ff191660011790556004546040517f0134e2180e3869aa7f850d9533f2225d637dfbdc224a1f7eb6bd7f2448d05a8290610f21908590612348565b610fd7611788565b6000546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af115801561102e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105291906123f8565b506040518181527f4e9221f2cca6ca0397acc6004ea0b716798254f5abcf53924fab34f0373e5d4e9060200160405180910390a150565b6000818152600760209081526040918290206003018054835181840281018401909452808452606093928301828280156109a1576020028201919060005260206000209081548152602001906001019080831161098d5750505050509050919050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff166000811580156111605750825b905060008267ffffffffffffffff16600114801561117d5750303b155b90508115801561118b575080155b156111a95760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156111d357845460ff60401b1916600160401b1785555b6111dc336120f4565b600180546001600160a01b03808d166001600160a01b0319928316178355600280548d831690841617905560008054918c1691909216178155600389905560058890556004829055526007602052427fb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b82881905561125a9088906124e1565b600160005260076020527fb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b8295583156112cc57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b60065460ff16156112fb5760405162461bcd60e51b815260040161067290612457565b600454600090815260076020526040902060018101544211156113565760405162461bcd60e51b8152602060048201526013602482015272159bdd1a5b99c81c195c9a5bd908195b991959606a1b6044820152606401610672565b60008381526005820160205260409020600681015460ff166113ac5760405162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a5908195b195b595b9d608a1b6044820152606401610672565b600083116113f45760405162461bcd60e51b81526020600482015260156024820152740566f74696e6720706f776572206d757374203e203605c1b6044820152606401610672565b6000546040516323b872dd60e01b8152336004820152306024820152604481018590526001600160a01b03909116906323b872dd906064016020604051808303816000875af115801561144b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146f91906123f8565b5033600090815260048201602052604090205460ff166114d057600581018054600181810183556000928352602080842090920180546001600160a01b031916339081179091558352600484019091526040909120805460ff191690911790555b828160020160008282546114e491906124e1565b90915550503360009081526003820160205260408120805485929061150a9084906124e1565b9091555050600454604080518681526020810186905233917fc32b42768a47a585121e9b8d7a2ab9d3f34c326a192dee11ee1732e3d18313f391015b60405180910390a350505050565b60065460ff16156115775760405162461bcd60e51b815260040161067290612457565b600454600090815260076020526040902060018101544211156115cb5760405162461bcd60e51b815260206004820152600c60248201526b159bdd1a5b99c8195b99195960a21b6044820152606401610672565b60008281526005820160205260409020600681015460ff166116245760405162461bcd60e51b8152602060048201526012602482015271456c656d656e74206e6f742061637469766560701b6044820152606401610672565b3360009081526003820160205260409020548061166d5760405162461bcd60e51b81526020600482015260076024820152664e6f20766f746560c81b6044820152606401610672565b808260020160008282546116819190612444565b90915550503360008181526003840160205260408082208290559054905163a9059cbb60e01b81526004810192909252602482018390526001600160a01b03169063a9059cbb906044016020604051808303816000875af11580156116ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170e91906123f8565b50600454604080518681526020810184905233917f306f08cf27c015a5ba2da6ad1a9f3d25e739cd265e8564bcd97fb9a51a8640569101611546565b611752611788565b6001600160a01b03811661177c57604051631e4fbdf760e01b815260006004820152602401610672565b61178581611819565b50565b336117916110ec565b6001600160a01b0316146109bf5760405163118cdaa760e01b8152336004820152602401610672565b60008060015b60045481116118135760008181526007602052604090206006015460ff16611801576000818152600760205260409020600201546117fe90836124e1565b91505b8061180b8161250a565b9150506117c0565b50919050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6000600454600161189b91906124e1565b600081815260076020526040902042908190556003549192506118be91906124e1565b6000828152600760205260408120600101919091555b600454600090815260076020526040902060030154811015611a9b57600454600090815260076020526040812060030180548390811061191657611916612523565b906000526020600020015490506000805b855181101561196b578286828151811061194357611943612523565b602002602001015103611959576001915061196b565b806119638161250a565b915050611927565b5080611a86576004546000908152600760208181526040808420868552600590810183528185208986529383528185208786520190915290912080546001600160a01b0316611a8357815481546001600160a01b0319166001600160a01b0390911617815560646119dd600a82612444565b83600101546119ec9190612539565b6119f69190612550565b60018281019190915560068201805460ff191690911790556000600282018190556040805191825260208201908190529051611a36916005840191612156565b506000868152600760209081526040822060038101805460018181018355918552928420909201879055908301548883526002909101805491929091611a7d9084906124e1565b90915550505b50505b50508080611a939061250a565b9150506118d4565b505050565b6004546000908152600760209081526040808320600301805482518185028101850190935280835260609493830182828015611afb57602002820191906000526020600020905b815481526020019060010190808311611ae7575b5050505050905060008390508082511015611b14575080515b60008167ffffffffffffffff811115611b2f57611b2f6124f4565b604051908082528060200260200182016040528015611b58578160200160208202803683370190505b5090506000835167ffffffffffffffff811115611b7757611b776124f4565b604051908082528060200260200182016040528015611ba0578160200160208202803683370190505b50905060005b8451811015611c24576007600060045481526020019081526020016000206005016000868381518110611bdb57611bdb612523565b6020026020010151815260200190815260200160002060020154828281518110611c0757611c07612523565b602090810291909101015280611c1c8161250a565b915050611ba6565b5060005b83811015611df257806000611c3e8260016124e1565b90505b8651811015611c9a57838281518110611c5c57611c5c612523565b6020026020010151848281518110611c7657611c76612523565b60200260200101511115611c88578091505b80611c928161250a565b915050611c41565b50818114158015611cc457506000838281518110611cba57611cba612523565b6020026020010151115b15611da857858181518110611cdb57611cdb612523565b6020026020010151868381518110611cf557611cf5612523565b6020026020010151878481518110611d0f57611d0f612523565b60200260200101888481518110611d2857611d28612523565b6020026020010182815250828152505050828181518110611d4b57611d4b612523565b6020026020010151838381518110611d6557611d65612523565b6020026020010151848481518110611d7f57611d7f612523565b60200260200101858481518110611d9857611d98612523565b6020908102919091010191909152525b858281518110611dba57611dba612523565b6020026020010151848381518110611dd457611dd4612523565b60209081029190910101525080611dea8161250a565b915050611c28565b509095945050505050565b6000805b8351811015611e6f576007600060045481526020019081526020016000206005016000858381518110611e3657611e36612523565b602002602001015181526020019081526020016000206002015482611e5b91906124e1565b915080611e678161250a565b915050611e01565b5060008111611eab5760405162461bcd60e51b81526020600482015260086024820152674e6f20766f74657360c01b6044820152606401610672565b60005b83518110156107155760006007600060045481526020019081526020016000206005016000868481518110611ee557611ee5612523565b602002602001015181526020019081526020016000209050600083826002015486611f109190612539565b611f1a9190612550565b905060006064611f2b836050612539565b611f359190612550565b905060005b6005840154811015612038576000846005018281548110611f5d57611f5d612523565b600091825260208083209091015460028801546001600160a01b0390911680845260038901909252604083205491935090611f99908690612539565b611fa39190612550565b90508015612023578060086000600454815260200190815260200160002060008c8a81518110611fd557611fd5612523565b602002602001015181526020019081526020016000206000846001600160a01b03166001600160a01b03168152602001908152602001600020600082825461201d91906124e1565b90915550505b505080806120309061250a565b915050611f3a565b5060006120458284612444565b11156120de5760005483546001600160a01b039182169163a9059cbb911661206d8486612444565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156120b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120dc91906123f8565b505b50505080806120ec9061250a565b915050611eae565b6120fc612105565b6117858161214e565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166109bf57604051631afcd79f60e31b815260040160405180910390fd5b611752612105565b8280548282559060005260206000209081019282156121ab579160200282015b828111156121ab57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190612176565b506121b79291506121f6565b5090565b8280548282559060005260206000209081019282156121ab579160200282015b828111156121ab5782518255916020019190600101906121db565b5b808211156121b757600081556001016121f7565b6001600160a01b038116811461178557600080fd5b60008060006060848603121561223557600080fd5b8335925060208401359150604084013561224e8161220b565b809150509250925092565b6000806040838503121561226c57600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156122bc5783516001600160a01b031683529284019291840191600101612297565b50909695505050505050565b801515811461178557600080fd5b6000602082840312156122e857600080fd5b81356122f3816122c8565b9392505050565b60008060006060848603121561230f57600080fd5b833561231a8161220b565b95602085013595506040909401359392505050565b60006020828403121561234157600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156122bc57835183529284019291840191600101612364565b600080600080600060a0868803121561239857600080fd5b85356123a38161220b565b945060208601356123b38161220b565b935060408601356123c38161220b565b94979396509394606081013594506080013592915050565b6000602082840312156123ed57600080fd5b81356122f38161220b565b60006020828403121561240a57600080fd5b81516122f3816122c8565b60006020828403121561242757600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156105f9576105f961242e565b6020808252600f908201526e10dbdb9d1c9858dd081c185d5cd959608a1b604082015260600190565b600080600080600060a0868803121561249857600080fd5b855194506020860151935060408601516124b1816122c8565b60608701519093506124c28161220b565b60808701519092506124d3816122c8565b809150509295509295909350565b808201808211156105f9576105f961242e565b634e487b7160e01b600052604160045260246000fd5b60006001820161251c5761251c61242e565b5060010190565b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176105f9576105f961242e565b60008261256d57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122086b7bb01284011b33735e965cc813f7452c9edf33337fa10485173236c4fee6d64736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101da5760003560e01c806371715de611610104578063a6b63eb8116100a2578063c6b61e4c11610071578063c6b61e4c14610483578063dce06315146104dd578063f2fde38b1461055b578063f7c618c11461056e57600080fd5b8063a6b63eb814610437578063b384abef1461044a578063bacbe2da1461045d578063c3fe3e281461047057600080fd5b80638bdf67f2116100de5780638bdf67f2146103c35780638c715fa9146103d65780638d0272e5146103e95780638da5cb5b1461042f57600080fd5b806371715de61461039f57806376671808146103b257806382ae9ef7146103bb57600080fd5b8063268423781161017c5780634ff0876a1161014b5780634ff0876a146103615780635c975abb1461036a5780636df4ef9814610377578063715018a61461039757600080fd5b8063268423781461033557806335ed3fd61461033e57806336a79f00146103465780633fb69abf1461035957600080fd5b806316c38b3c116101b857806316c38b3c1461029c578063174e34f5146102b15780631e0197e2146102f75780631f69f45e1461032257600080fd5b806301f96a71146101df57806302bbba53146102235780630d39a1d314610243575b600080fd5b6102106101ed366004612220565b600860209081526000938452604080852082529284528284209052825290205481565b6040519081526020015b60405180910390f35b610236610231366004612259565b610581565b60405161021a919061227b565b61028c610251366004612220565b600083815260076020908152604080832085845260050182528083206001600160a01b038516845260040190915290205460ff169392505050565b604051901515815260200161021a565b6102af6102aa3660046122d6565b6105ff565b005b6102106102bf3660046122fa565b600082815260076020908152604080832084845260050182528083206001600160a01b03871684526003019091529020549392505050565b60025461030a906001600160a01b031681565b6040516001600160a01b03909116815260200161021a565b6102af610330366004612259565b61061a565b61021060055481565b610210603281565b6102af61035436600461232f565b61071b565b610210600a81565b61021060035481565b60065461028c9060ff1681565b61038a61038536600461232f565b6108af565b60405161021a9190612348565b6102af6109ad565b6102af6103ad366004612259565b6109c1565b61021060045481565b6102af610cfd565b6102af6103d136600461232f565b610fcf565b61038a6103e436600461232f565b611089565b6102106103f7366004612220565b600083815260076020908152604080832085845260050182528083206001600160a01b03851684526003019091529020549392505050565b61030a6110ec565b6102af610445366004612380565b61111a565b6102af610458366004612259565b6112d8565b6102af61046b36600461232f565b611554565b60015461030a906001600160a01b031681565b6104bb61049136600461232f565b60076020526000908152604090208054600182015460028301546006909301549192909160ff1684565b604080519485526020850193909352918301521515606082015260800161021a565b61052f6104eb366004612259565b60009182526007602090815260408084209284526005909201905290208054600182015460028301546006909301546001600160a01b039092169390929160ff1690565b604080516001600160a01b0390951685526020850193909352918301521515606082015260800161021a565b6102af6105693660046123db565b61174a565b60005461030a906001600160a01b031681565b600082815260076020908152604080832084845260059081018352928190209092018054835181840281018401909452808452606093928301828280156105f157602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116105d3575b505050505090505b92915050565b610607611788565b6006805460ff1916911515919091179055565b600082815260086020908152604080832084845282528083203384529091529020548061067b5760405162461bcd60e51b815260206004820152600a6024820152694e6f207265776172647360b01b60448201526064015b60405180910390fd5b600083815260086020908152604080832085845282528083203380855292528083208390559154915163a9059cbb60e01b81526004810191909152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af11580156106f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071591906123f8565b50505050565b610723611788565b600080546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561076c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107909190612415565b9050600061079c6117ba565b9050826107a98284612444565b10156107ec5760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e742066756e647360701b6044820152606401610672565b6000546001600160a01b031663a9059cbb6108056110ec565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018690526044016020604051808303816000875af1158015610852573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087691906123f8565b506040518381527f7c087d7d3fa0d567fd03d7feb4cd3edacd2a08580cdbb2bc2f86e671279ea01d9060200160405180910390a1505050565b60606004548211156108f35760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840cae0dec6d609b1b6044820152606401610672565b60008281526007602052604090206006015460ff1661094a5760405162461bcd60e51b8152602060048201526013602482015272115c1bd8da081b9bdd08199a5b985b1a5e9959606a1b6044820152606401610672565b600082815260076020908152604091829020600401805483518184028101840190945280845290918301828280156109a157602002820191906000526020600020905b81548152602001906001019080831161098d575b50505050509050919050565b6109b5611788565b6109bf6000611819565b565b60065460ff16156109e45760405162461bcd60e51b815260040161067290612457565b600554811015610a245760405162461bcd60e51b815260206004820152600b60248201526a46656520746f6f206c6f7760a81b6044820152606401610672565b60015460405163e0d64b3760e01b8152600481018490526000916001600160a01b03169063e0d64b379060240160a060405180830381865afa158015610a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a929190612480565b50509250505080610ad95760405162461bcd60e51b8152602060048201526011602482015270115b195b595b9d081b9bdd08199bdd5b99607a1b6044820152606401610672565b60006004546001610aea91906124e1565b60008181526007602052604090206006015490915060ff1615610b3e5760405162461bcd60e51b815260206004820152600c60248201526b115c1bd8da0818db1bdcd95960a21b6044820152606401610672565b6000818152600760209081526040808320878452600501909152902080546001600160a01b031615610bab5760405162461bcd60e51b8152602060048201526016602482015275115b195b595b9d08185b1c9958591e481b1a5cdd195960521b6044820152606401610672565b6000546040516323b872dd60e01b8152336004820152306024820152604481018690526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2691906123f8565b5080546001600160a01b03191633178155600180820185905560006002830181905560068301805460ff19169092179091556040805191825260208201908190529051610c77916005840191612156565b50600082815260076020908152604082206003810180546001810182559084529183209091018790558382526002018054869290610cb69084906124e1565b9091555050604080518681526020810186905233917ff758c05d390770eb7d9710201a0626a5f4c9dd12bd336a72eeea6af3c593d897910160405180910390a25050505050565b610d05611788565b600454600090815260076020526040902060018101544211610d595760405162461bcd60e51b815260206004820152600d60248201526c45706f6368206f6e676f696e6760981b6044820152606401610672565b600681015460ff1615610da25760405162461bcd60e51b8152602060048201526011602482015270105b1c9958591e48199a5b985b1a5e9959607a1b6044820152606401610672565b6003810154600003610e365760068101805460ff1916600117905560408051600080825260208201909252610dd7915061188a565b600454604080516000815260208101918290527f0134e2180e3869aa7f850d9533f2225d637dfbdc224a1f7eb6bd7f2448d05a8291610e169190612348565b60405180910390a260048054906000610e2e8361250a565b919050555050565b6000610e426003611aa0565b8051909150610e5a90600484019060208401906121bb565b506000805b8251811015610ecd576007600060045481526020019081526020016000206005016000848381518110610e9457610e94612523565b602002602001015181526020019081526020016000206002015482610eb991906124e1565b915080610ec58161250a565b915050610e5f565b5080600003610f4357610edf8261188a565b60068301805460ff191660011790556004546040517f0134e2180e3869aa7f850d9533f2225d637dfbdc224a1f7eb6bd7f2448d05a8290610f21908590612348565b60405180910390a260048054906000610f398361250a565b9190505550505050565b6000606460328560020154610f589190612539565b610f629190612550565b90506000818560020154610f7691906124e1565b9050610f828482611dfd565b610f8b8461188a565b505060068301805460ff191660011790556004546040517f0134e2180e3869aa7f850d9533f2225d637dfbdc224a1f7eb6bd7f2448d05a8290610f21908590612348565b610fd7611788565b6000546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af115801561102e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105291906123f8565b506040518181527f4e9221f2cca6ca0397acc6004ea0b716798254f5abcf53924fab34f0373e5d4e9060200160405180910390a150565b6000818152600760209081526040918290206003018054835181840281018401909452808452606093928301828280156109a1576020028201919060005260206000209081548152602001906001019080831161098d5750505050509050919050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff166000811580156111605750825b905060008267ffffffffffffffff16600114801561117d5750303b155b90508115801561118b575080155b156111a95760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156111d357845460ff60401b1916600160401b1785555b6111dc336120f4565b600180546001600160a01b03808d166001600160a01b0319928316178355600280548d831690841617905560008054918c1691909216178155600389905560058890556004829055526007602052427fb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b82881905561125a9088906124e1565b600160005260076020527fb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b8295583156112cc57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b60065460ff16156112fb5760405162461bcd60e51b815260040161067290612457565b600454600090815260076020526040902060018101544211156113565760405162461bcd60e51b8152602060048201526013602482015272159bdd1a5b99c81c195c9a5bd908195b991959606a1b6044820152606401610672565b60008381526005820160205260409020600681015460ff166113ac5760405162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a5908195b195b595b9d608a1b6044820152606401610672565b600083116113f45760405162461bcd60e51b81526020600482015260156024820152740566f74696e6720706f776572206d757374203e203605c1b6044820152606401610672565b6000546040516323b872dd60e01b8152336004820152306024820152604481018590526001600160a01b03909116906323b872dd906064016020604051808303816000875af115801561144b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146f91906123f8565b5033600090815260048201602052604090205460ff166114d057600581018054600181810183556000928352602080842090920180546001600160a01b031916339081179091558352600484019091526040909120805460ff191690911790555b828160020160008282546114e491906124e1565b90915550503360009081526003820160205260408120805485929061150a9084906124e1565b9091555050600454604080518681526020810186905233917fc32b42768a47a585121e9b8d7a2ab9d3f34c326a192dee11ee1732e3d18313f391015b60405180910390a350505050565b60065460ff16156115775760405162461bcd60e51b815260040161067290612457565b600454600090815260076020526040902060018101544211156115cb5760405162461bcd60e51b815260206004820152600c60248201526b159bdd1a5b99c8195b99195960a21b6044820152606401610672565b60008281526005820160205260409020600681015460ff166116245760405162461bcd60e51b8152602060048201526012602482015271456c656d656e74206e6f742061637469766560701b6044820152606401610672565b3360009081526003820160205260409020548061166d5760405162461bcd60e51b81526020600482015260076024820152664e6f20766f746560c81b6044820152606401610672565b808260020160008282546116819190612444565b90915550503360008181526003840160205260408082208290559054905163a9059cbb60e01b81526004810192909252602482018390526001600160a01b03169063a9059cbb906044016020604051808303816000875af11580156116ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170e91906123f8565b50600454604080518681526020810184905233917f306f08cf27c015a5ba2da6ad1a9f3d25e739cd265e8564bcd97fb9a51a8640569101611546565b611752611788565b6001600160a01b03811661177c57604051631e4fbdf760e01b815260006004820152602401610672565b61178581611819565b50565b336117916110ec565b6001600160a01b0316146109bf5760405163118cdaa760e01b8152336004820152602401610672565b60008060015b60045481116118135760008181526007602052604090206006015460ff16611801576000818152600760205260409020600201546117fe90836124e1565b91505b8061180b8161250a565b9150506117c0565b50919050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6000600454600161189b91906124e1565b600081815260076020526040902042908190556003549192506118be91906124e1565b6000828152600760205260408120600101919091555b600454600090815260076020526040902060030154811015611a9b57600454600090815260076020526040812060030180548390811061191657611916612523565b906000526020600020015490506000805b855181101561196b578286828151811061194357611943612523565b602002602001015103611959576001915061196b565b806119638161250a565b915050611927565b5080611a86576004546000908152600760208181526040808420868552600590810183528185208986529383528185208786520190915290912080546001600160a01b0316611a8357815481546001600160a01b0319166001600160a01b0390911617815560646119dd600a82612444565b83600101546119ec9190612539565b6119f69190612550565b60018281019190915560068201805460ff191690911790556000600282018190556040805191825260208201908190529051611a36916005840191612156565b506000868152600760209081526040822060038101805460018181018355918552928420909201879055908301548883526002909101805491929091611a7d9084906124e1565b90915550505b50505b50508080611a939061250a565b9150506118d4565b505050565b6004546000908152600760209081526040808320600301805482518185028101850190935280835260609493830182828015611afb57602002820191906000526020600020905b815481526020019060010190808311611ae7575b5050505050905060008390508082511015611b14575080515b60008167ffffffffffffffff811115611b2f57611b2f6124f4565b604051908082528060200260200182016040528015611b58578160200160208202803683370190505b5090506000835167ffffffffffffffff811115611b7757611b776124f4565b604051908082528060200260200182016040528015611ba0578160200160208202803683370190505b50905060005b8451811015611c24576007600060045481526020019081526020016000206005016000868381518110611bdb57611bdb612523565b6020026020010151815260200190815260200160002060020154828281518110611c0757611c07612523565b602090810291909101015280611c1c8161250a565b915050611ba6565b5060005b83811015611df257806000611c3e8260016124e1565b90505b8651811015611c9a57838281518110611c5c57611c5c612523565b6020026020010151848281518110611c7657611c76612523565b60200260200101511115611c88578091505b80611c928161250a565b915050611c41565b50818114158015611cc457506000838281518110611cba57611cba612523565b6020026020010151115b15611da857858181518110611cdb57611cdb612523565b6020026020010151868381518110611cf557611cf5612523565b6020026020010151878481518110611d0f57611d0f612523565b60200260200101888481518110611d2857611d28612523565b6020026020010182815250828152505050828181518110611d4b57611d4b612523565b6020026020010151838381518110611d6557611d65612523565b6020026020010151848481518110611d7f57611d7f612523565b60200260200101858481518110611d9857611d98612523565b6020908102919091010191909152525b858281518110611dba57611dba612523565b6020026020010151848381518110611dd457611dd4612523565b60209081029190910101525080611dea8161250a565b915050611c28565b509095945050505050565b6000805b8351811015611e6f576007600060045481526020019081526020016000206005016000858381518110611e3657611e36612523565b602002602001015181526020019081526020016000206002015482611e5b91906124e1565b915080611e678161250a565b915050611e01565b5060008111611eab5760405162461bcd60e51b81526020600482015260086024820152674e6f20766f74657360c01b6044820152606401610672565b60005b83518110156107155760006007600060045481526020019081526020016000206005016000868481518110611ee557611ee5612523565b602002602001015181526020019081526020016000209050600083826002015486611f109190612539565b611f1a9190612550565b905060006064611f2b836050612539565b611f359190612550565b905060005b6005840154811015612038576000846005018281548110611f5d57611f5d612523565b600091825260208083209091015460028801546001600160a01b0390911680845260038901909252604083205491935090611f99908690612539565b611fa39190612550565b90508015612023578060086000600454815260200190815260200160002060008c8a81518110611fd557611fd5612523565b602002602001015181526020019081526020016000206000846001600160a01b03166001600160a01b03168152602001908152602001600020600082825461201d91906124e1565b90915550505b505080806120309061250a565b915050611f3a565b5060006120458284612444565b11156120de5760005483546001600160a01b039182169163a9059cbb911661206d8486612444565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156120b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120dc91906123f8565b505b50505080806120ec9061250a565b915050611eae565b6120fc612105565b6117858161214e565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166109bf57604051631afcd79f60e31b815260040160405180910390fd5b611752612105565b8280548282559060005260206000209081019282156121ab579160200282015b828111156121ab57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190612176565b506121b79291506121f6565b5090565b8280548282559060005260206000209081019282156121ab579160200282015b828111156121ab5782518255916020019190600101906121db565b5b808211156121b757600081556001016121f7565b6001600160a01b038116811461178557600080fd5b60008060006060848603121561223557600080fd5b8335925060208401359150604084013561224e8161220b565b809150509250925092565b6000806040838503121561226c57600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156122bc5783516001600160a01b031683529284019291840191600101612297565b50909695505050505050565b801515811461178557600080fd5b6000602082840312156122e857600080fd5b81356122f3816122c8565b9392505050565b60008060006060848603121561230f57600080fd5b833561231a8161220b565b95602085013595506040909401359392505050565b60006020828403121561234157600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156122bc57835183529284019291840191600101612364565b600080600080600060a0868803121561239857600080fd5b85356123a38161220b565b945060208601356123b38161220b565b935060408601356123c38161220b565b94979396509394606081013594506080013592915050565b6000602082840312156123ed57600080fd5b81356122f38161220b565b60006020828403121561240a57600080fd5b81516122f3816122c8565b60006020828403121561242757600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156105f9576105f961242e565b6020808252600f908201526e10dbdb9d1c9858dd081c185d5cd959608a1b604082015260600190565b600080600080600060a0868803121561249857600080fd5b855194506020860151935060408601516124b1816122c8565b60608701519093506124c28161220b565b60808701519092506124d3816122c8565b809150509295509295909350565b808201808211156105f9576105f961242e565b634e487b7160e01b600052604160045260246000fd5b60006001820161251c5761251c61242e565b5060010190565b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176105f9576105f961242e565b60008261256d57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122086b7bb01284011b33735e965cc813f7452c9edf33337fa10485173236c4fee6d64736f6c63430008140033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.