Source Code
Latest 25 from a total of 22,645 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Complete Transmu... | 81946072 | 2 days ago | IN | 0 POL | 0.10948623 | ||||
| Start Transmutat... | 81945002 | 2 days ago | IN | 0 POL | 0.24621953 | ||||
| Complete Transmu... | 81418411 | 15 days ago | IN | 0 POL | 0.03636018 | ||||
| Complete Transmu... | 81418358 | 15 days ago | IN | 0 POL | 0.0450152 | ||||
| Start Transmutat... | 81410935 | 15 days ago | IN | 0 POL | 0.27994724 | ||||
| Start Transmutat... | 81410796 | 15 days ago | IN | 0 POL | 0.45855604 | ||||
| Complete Transmu... | 78576047 | 81 days ago | IN | 0 POL | 0.02290756 | ||||
| Start Transmutat... | 78576020 | 81 days ago | IN | 0 POL | 0.03525747 | ||||
| Complete Transmu... | 78413010 | 84 days ago | IN | 0 POL | 0.02409372 | ||||
| Start Transmutat... | 78412987 | 84 days ago | IN | 0 POL | 0.03454223 | ||||
| Complete Transmu... | 78412952 | 84 days ago | IN | 0 POL | 0.0254698 | ||||
| Start Transmutat... | 78412929 | 84 days ago | IN | 0 POL | 0.03564332 | ||||
| Complete Transmu... | 78412883 | 84 days ago | IN | 0 POL | 0.02424973 | ||||
| Start Transmutat... | 78412858 | 84 days ago | IN | 0 POL | 0.03721588 | ||||
| Complete Transmu... | 78288861 | 87 days ago | IN | 0 POL | 0.00751051 | ||||
| Start Transmutat... | 78288839 | 87 days ago | IN | 0 POL | 0.0108014 | ||||
| Complete Transmu... | 78288543 | 87 days ago | IN | 0 POL | 0.00737946 | ||||
| Start Transmutat... | 78288512 | 87 days ago | IN | 0 POL | 0.01080139 | ||||
| Complete Transmu... | 78202597 | 89 days ago | IN | 0 POL | 0.00737946 | ||||
| Start Transmutat... | 78202577 | 89 days ago | IN | 0 POL | 0.0108615 | ||||
| Complete Transmu... | 78202564 | 89 days ago | IN | 0 POL | 0.00737946 | ||||
| Start Transmutat... | 78202545 | 89 days ago | IN | 0 POL | 0.01043238 | ||||
| Complete Transmu... | 78202537 | 89 days ago | IN | 0 POL | 0.00729546 | ||||
| Start Transmutat... | 78202518 | 89 days ago | IN | 0 POL | 0.01163627 | ||||
| Complete Transmu... | 78202213 | 89 days ago | IN | 0 POL | 0.00752496 |
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x8Cf5f19e...8343ca5F0 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
MagicAlchemyTransmutationRelic
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {ReentrancyGuardTransient} from "openzeppelin-contracts/contracts/utils/ReentrancyGuardTransient.sol";
import {EnumerableSet} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";
import {VRFConsumerBaseV2Plus} from "chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {IMagicAlchemyMendelefFlask} from "./interfaces/IMagicAlchemyMendelefFlask.sol";
import {IMagicAlchemyTransmutationResult} from "./interfaces/IMagicAlchemyTransmutationResult.sol";
import {MagicAlchemyFlaskAggregator} from "./utils/MagicAlchemyFlaskAggregator.sol";
import {MagicAlchemyRarity as Rarity} from "./utils/MagicAlchemyRarity.sol";
/// @title MagicAlchemyTransmutationRelic
/// @notice This contract handles the conversion ("transmutation") of Mendelef flasks into new forms by using randomness provided by Chainlink VRF.
/// @dev The contract integrates with a Magic Alchemy Mendelef Flask contract and two separate transmutation result contracts (embryo flaks and ugly broth).
/// It uses a VRF mechanism to determine the outcome of a transmutation process. In addition, it leverages pausable and non-reentrant modifiers to enhance security.
contract MagicAlchemyTransmutationRelic is
VRFConsumerBaseV2Plus,
Context,
ReentrancyGuardTransient,
Pausable,
MagicAlchemyFlaskAggregator
{
/// @notice Enum representing the various possible outcomes of a Mendelef flask transmutation.
/// @dev Undefined indicates an uninitialized or non-existent transmutation state.
/// DifferentiationInProgress means a VRF request has been made and the outcome is pending.
/// BecomesEmbryoFlask indicates a successful transmutation into a embryo flask token.
/// BecomesUglyBroth indicates a successful transmutation into an ugly broth token.
/// Failed denotes that the transmutation process did not complete successfully.
enum Status {
Undefined,
DifferentiationInProgress,
BecomesEmbryoFlask,
BecomesUglyBroth,
Failed
}
/* -------------------------------------------------------------------------- */
/* EVENTS */
/* -------------------------------------------------------------------------- */
/// @notice Emitted when a transmutation process is started for a given Mendelef flask.
/// @param player The address of the player who initiated the transmutation.
/// @param mendelefFlaskId The unique identifier of the Mendelef flask that is being transmutated.
/// @param vrfRequestId The unique identifier for the Chainlink VRF request that will determine the outcome.
event TransmutationStarted(address indexed player, uint256 indexed mendelefFlaskId, uint256 vrfRequestId);
/// @notice Emitted when a transmutation process is successfully completed.
/// @param player The address of the player who received the transmutation result.
/// @param mendelefFlaskId The ID of the source Mendelef flask token used in the transmutation.
/// @param resultTokenContract The address of the transmutation result contract (either embryo flask or ugly broth).
/// @param resultTokenId The token ID of the minted transmutation result token.
/// @param rarity The rarity level associated with the resulting token.
event TransmutationCompleted(
address indexed player,
uint256 indexed mendelefFlaskId,
IMagicAlchemyTransmutationResult indexed resultTokenContract,
uint256 resultTokenId,
Rarity rarity
);
/// @notice Emitted when the VRF response has been processed and the outcome of the transmutation is determined.
/// @param player The address of the player associated with the VRF request.
/// @param mendelefFlaskId The Mendelef flask ID for which the outcome was determined.
/// @param round The round number associated with the Mendelef flask.
/// @param rarity The rarity of the Mendelef flask involved.
/// @param status The final status after processing the random result
event TransmutationDifferentiated(
address indexed player, uint256 indexed mendelefFlaskId, uint256 indexed round, Rarity rarity, Status status
);
/// @notice Emitted when a Mendelef flask is returned (extracted) to a player, typically after a failed transmutation.
/// @param to The address that receives the Mendelef flask.
/// @param flaskId The Mendelef flask ID being extracted.
event MendelefFlaskExtracted(address indexed to, uint256 indexed flaskId);
/// @notice Emitted when the embryo flask transmutation result contract is setup.
/// @param embryo flask The address of the embryo flask transmutation result contract.
event EmbryoFlaskSetup(IMagicAlchemyTransmutationResult embryo);
/// @notice Emitted when the ugly broth transmutation result contract is setup.
/// @param uglyBroth The address of the ugly broth transmutation result contract.
event UglyBrothSetup(IMagicAlchemyTransmutationResult uglyBroth);
/// @notice Emitted when the VRF subscription ID is set up.
/// @param vrfSubscriptionId The VRF subscription identifier.
event VrfSubscriptionIdSetup(uint256 indexed vrfSubscriptionId);
/// @notice Emitted when the VRF key hash is set up.
/// @param vrfKeyHash The key hash used for VRF requests.
event VrfKeyHashSetup(bytes32 indexed vrfKeyHash);
/// @notice Emitted when the VRF callback gas limit is set up.
/// @param vrfCallbackGasLimit The gas limit used for VRF callback functions.
event VrfCallbackGasLimitSetup(uint32 indexed vrfCallbackGasLimit);
/// @notice Emitted when the number of VRF request confirmations is set up.
/// @param vrfRequestConfirmations The number of confirmations required for a VRF request.
event VrfRequestConfirmationsSetup(uint16 indexed vrfRequestConfirmations);
/* -------------------------------------------------------------------------- */
/* ERRORS */
/* -------------------------------------------------------------------------- */
/// @notice Reverts when a provided transmutation result contract does not implement the required interface.
error InvalidTransmutationResultInterface();
/// @notice Reverts if the Mendelef flask is not registered or its status is undefined.
error MendelefFlaskNotFound();
/// @notice Reverts when an attempt is made to start a transmutation on a Mendelef flask that is already in process.
error TransmutationAlreadyStarted();
/// @notice Reverts when a transmutation process is still in the pending state and its outcome is not yet determined.
error TransmutationStillUndifferentiated();
/// @notice Reverts when the transmutation process has failed.
error TransmutationFailed();
/// @notice Reverts when an operation is attempted on a Mendelef flask that has already been transmutated.
/// @param currentStatus The current transmutation status of the Mendelef flask.
error TransmutationAlreadyDifferentiated(Status currentStatus);
/// @notice Reverts when extraction is attempted before the allowed block number.
/// @dev This protects against draining the VRF Subscription balance by ensuring that extraction
/// only occurs after a safe block threshold.
/// @param minAllowedBlockNumber The minimum block number at which extraction is permitted.
/// @param actualBlockNumber The current block number when extraction is attempted.
error EarlyExtraction(uint256 minAllowedBlockNumber, uint256 actualBlockNumber);
/// @notice Reverts when the caller is not the last recorded owner of a Mendelef flask during an extraction operation.
/// @param lastOwner The last owner recorded for the Mendelef flask.
error PlayerIsNotLastOwner(address lastOwner);
/// @notice Reverts when the message sender is not the owner of the Mendelef flask.
/// @param sender The address that initiated the transaction.
/// @param player The actual owner of the Mendelef flask.
error SenderIsNotOwner(address sender, address player);
/// @notice Thrown when an index provided for an owner's Mendelef flask list is out of bounds.
/// @param lastOwner The address of the owner whose Mendelef flask list is being accessed.
/// @param index The invalid index that was attempted.
error LastOwnerMendelefFlaskIndexOutOfBounds(address lastOwner, uint256 index);
/* -------------------------------------------------------------------------- */
/* STATE VARIABLES */
/* -------------------------------------------------------------------------- */
using EnumerableSet for EnumerableSet.UintSet;
/// @notice Data structure for storing details of a VRF request associated with a transmutation.
/// @param player The address that initiated the VRF request.
/// @param round The round number associated with the Mendelef flask.
/// @param mendelefFlaskId The Mendelef flask ID involved.
/// @param rarity The rarity level of the Mendelef flask.
struct VRFRequestData {
address player;
uint256 round;
uint256 mendelefFlaskId;
Rarity rarity;
}
mapping(Rarity rarity => uint256 quota) private _embryoFlaskQuotas;
mapping(uint256 tokenId => Status status) private _statuses;
mapping(uint256 tokenId => address lastOwner) private _mendelefFlaskLastOwners;
mapping(address lastOwner => EnumerableSet.UintSet flasks) private _lastOwnerMendelefFlaskLists;
mapping(uint256 round => mapping(Rarity rarity => uint256 amount)) private _remainingEmbryoFlasksPlusOne;
mapping(uint256 requestId => VRFRequestData data) private _vrfRequestData;
mapping(uint256 tokenId => uint256 blockNumber) private _mendelefFlaskStartTransmutationBlockNumbers;
IMagicAlchemyTransmutationResult private _embryoFlask;
IMagicAlchemyTransmutationResult private _uglyBroth;
uint256 private _vrfSubscriptionId;
bytes32 private _vrfKeyHash;
uint32 private _vrfCallbackGasLimit;
uint16 private _vrfRequestConfirmations;
/* -------------------------------------------------------------------------- */
/* CONSTRUCTOR */
/* -------------------------------------------------------------------------- */
/// @dev Sets the initial embryo flask and ugly broth contracts, pauses the contract upon deployment, and configures VRF parameters.
/// @param commonEmbryoFlaskQuota_ The number of embryo flask tokens available for common rarity Mendelef flasks per round.
/// @param rareEmbryoFlaskQuota_ The number of embryo flask tokens available for rare rarity Mendelef flasks per round.
/// @param epicEmbryoFlaskQuota_ The number of embryo flask tokens available for epic rarity Mendelef flasks per round.
/// @param mendelefFlask_ The address of the Magic Alchemy Mendelef Flask contract.
/// @param uglyBroth_ The address of the ugly broth transmutation result contract.
/// @param embryoFlask_ The address of the embryo flask transmutation result contract.
/// @param vrfCoordinator_ The address of the Chainlink VRF coordinator.
/// @param vrfSubscriptionId_ The Chainlink VRF subscription ID.
/// @param vrfKeyHash_ The Chainlink VRF key hash.
/// @param vrfCallbackGasLimit_ The gas limit for VRF callback functions.
/// @param vrfRequestConfirmations_ The number of confirmations for VRF requests.
constructor(
uint256 commonEmbryoFlaskQuota_,
uint256 rareEmbryoFlaskQuota_,
uint256 epicEmbryoFlaskQuota_,
IMagicAlchemyMendelefFlask mendelefFlask_,
IMagicAlchemyTransmutationResult uglyBroth_,
IMagicAlchemyTransmutationResult embryoFlask_,
address vrfCoordinator_,
uint256 vrfSubscriptionId_,
bytes32 vrfKeyHash_,
uint32 vrfCallbackGasLimit_,
uint16 vrfRequestConfirmations_
) VRFConsumerBaseV2Plus(vrfCoordinator_) Pausable() MagicAlchemyFlaskAggregator(mendelefFlask_) {
_pause();
_setEmbryoFlask(embryoFlask_);
_setUglyBroth(uglyBroth_);
_setVrfSubscriptionId(vrfSubscriptionId_);
_setVrfKeyHash(vrfKeyHash_);
_setVrfCallbackGasLimit(vrfCallbackGasLimit_);
_setVrfRequestConfirmations(vrfRequestConfirmations_);
_embryoFlaskQuotas[Rarity.Common] = commonEmbryoFlaskQuota_;
_embryoFlaskQuotas[Rarity.Rare] = rareEmbryoFlaskQuota_;
_embryoFlaskQuotas[Rarity.Epic] = epicEmbryoFlaskQuota_;
}
/* -------------------------------------------------------------------------- */
/* VIEW FUNCTIONS */
/* -------------------------------------------------------------------------- */
/// @notice Returns the currently set embryo flask contract.
/// @return The embryo flask instance.
function embryoFlask() public view returns (IMagicAlchemyTransmutationResult) {
return _embryoFlask;
}
/// @notice Returns the currently set ugly broth contract.
/// @return The ugly broth instance.
function uglyBroth() public view returns (IMagicAlchemyTransmutationResult) {
return _uglyBroth;
}
/// @notice Returns the Chainlink VRF subscription ID.
/// @return The subscription ID used to fund VRF requests.
function vrfSubscriptionId() public view returns (uint256) {
return _vrfSubscriptionId;
}
/// @notice Returns the Chainlink VRF key hash.
/// @return The Chainlink VRF key hash.
function vrfKeyHash() public view returns (bytes32) {
return _vrfKeyHash;
}
/// @notice Returns the gas limit for VRF callback functions.
/// @return The callback gas limit.
function vrfCallbackGasLimit() public view returns (uint32) {
return _vrfCallbackGasLimit;
}
/// @notice Returns the number of confirmations required for VRF requests.
/// @return The number of confirmations.
function vrfRequestConfirmations() public view returns (uint16) {
return _vrfRequestConfirmations;
}
/// @notice Returns the current transmutation status of a given Mendelef flask.
/// @dev Reverts if the Mendelef flask status is undefined.
/// @param mendelefFlaskId_ The unique identifier of the Mendelef flask.
/// @return The current Status enum value of the Mendelef flask's transmutation process.
function statusOf(uint256 mendelefFlaskId_) public view returns (Status) {
if (_statuses[mendelefFlaskId_] == Status.Undefined) {
revert MendelefFlaskNotFound();
}
return _statuses[mendelefFlaskId_];
}
/// @notice Returns the current transmutation status of a given Mendelef flask.
/// @dev Reverts if the Mendelef flask is not registered.
/// @param mendelefFlaskId_ The unique identifier of the Mendelef flask.
/// @return The current Status enum value of the Mendelef flask's transmutation process.
function mendelefFlaskLastOwnerOf(uint256 mendelefFlaskId_) public view returns (address) {
if (_mendelefFlaskLastOwners[mendelefFlaskId_] == address(0)) {
revert MendelefFlaskNotFound();
}
return _mendelefFlaskLastOwners[mendelefFlaskId_];
}
/// @notice Returns the number of Mendelef flasks originally owned by an address before
/// being transferred to the relic via startTransmutation.
/// @param lastOwner_ The address of the owner.
/// @return The count of Mendelef flasks recorded for the specified owner.
function lastOwnerMendelefFlaskCount(address lastOwner_) public view returns (uint256) {
return _lastOwnerMendelefFlaskLists[lastOwner_].length();
}
/// @notice Returns the Mendelef flask ID from an owner's list at a specified index, representing flasks
/// originally owned before being transferred to the relic via startTransmutation.
/// @dev Reverts if the provided index is out of bounds for the owner's list.
/// @param lastOwner_ The address of the owner.
/// @param index_ The index in the owner's Mendelef flask list.
/// @return The Mendelef flask ID at the given index.
function mendelefFlaskOfLastOwnerByIndex(address lastOwner_, uint256 index_) public view returns (uint256) {
if (index_ >= lastOwnerMendelefFlaskCount(lastOwner_)) {
revert LastOwnerMendelefFlaskIndexOutOfBounds(lastOwner_, index_);
}
return _lastOwnerMendelefFlaskLists[lastOwner_].at(index_);
}
/// @notice Returns the number of remaining embryo flask tokens available for transmutation in a specific round and rarity.
/// @dev If the value is uninitialized, it returns the maximum possible uint256 value.
/// @param round_ The round number.
/// @param rarity_ The rarity for which the remaining count is queried.
/// @return The number of remaining embryo flask tokens.
function remainingEmbryoFlasksFor(uint256 round_, Rarity rarity_) public view returns (uint256) {
uint256 remainingEmbryoFlasksPlusOne_ = _remainingEmbryoFlasksPlusOne[round_][rarity_];
// If the value is uninitialized, return the maximum possible value
if (remainingEmbryoFlasksPlusOne_ == 0) {
return type(uint256).max;
}
// Return the actual remaining embryo flasks count
return remainingEmbryoFlasksPlusOne_ - 1;
}
/* -------------------------------------------------------------------------- */
/* STATE CHANGING FUNCTIONS */
/* -------------------------------------------------------------------------- */
/// @notice Initiates the transmutation process for a specified flask.
/// @dev Checks ownership and rarity of the flask, transfers non-Legendary flasks to the contract,
/// initializes remaining embryo flask counts if needed, make a VRF request if needed and emits a TransmutationStarted event.
/// For Legendary flasks, transmutation is executed immediately without a VRF request.
/// @param mendelefFlaskId_ The unique identifier of the flask to be transmutated.
function startTransmutation(uint256 mendelefFlaskId_) public whenNotPaused nonReentrant {
IMagicAlchemyMendelefFlask mendelefFlask_ = flask();
address player_ = _msgSender();
address mendelefFlaskOwner_ = mendelefFlask_.ownerOf(mendelefFlaskId_);
if (player_ != mendelefFlaskOwner_) {
revert SenderIsNotOwner(player_, mendelefFlaskOwner_);
}
Rarity rarity_ = mendelefFlask_.rarityOf(mendelefFlaskId_);
if (rarity_ == Rarity.Legendary) {
_transmutate(player_, mendelefFlaskId_, rarity_, true);
} else {
_mendelefFlaskStartTransmutationBlockNumbers[mendelefFlaskId_] = block.number;
_mendelefFlaskLastOwners[mendelefFlaskId_] = player_;
_lastOwnerMendelefFlaskLists[player_].add(mendelefFlaskId_);
mendelefFlask_.transferFrom(player_, address(this), mendelefFlaskId_);
uint256 round_ = mendelefFlask_.roundOf(mendelefFlaskId_);
// Initialize the remaining embryo flask count for the given round and rarity if not already set.
if (_remainingEmbryoFlasksPlusOne[round_][rarity_] == 0) {
_remainingEmbryoFlasksPlusOne[round_][rarity_] = _embryoFlaskQuotas[rarity_] + 1;
}
uint256 requestId_ = _requestRandomWord();
_vrfRequestData[requestId_] = VRFRequestData(player_, round_, mendelefFlaskId_, rarity_);
_statuses[mendelefFlaskId_] = Status.DifferentiationInProgress;
emit TransmutationStarted(player_, mendelefFlaskId_, requestId_);
}
}
/// @notice Completes the transmutation process for a specified Mendelef flask.
/// @dev Checks that the transmutation process has been properly differentiated and has not failed,
/// clears stored transmutation data, and calls _transmutate to burn the Mendelef flask and mint the result.
/// @param mendelefFlaskId_ The unique identifier of the Mendelef flask for which transmutation should be completed.
function completeTransmutation(uint256 mendelefFlaskId_) public whenNotPaused nonReentrant {
// Reverts if the Mendelef flask is not found in the Transmutation Relic (undefined status).
Status status_ = statusOf(mendelefFlaskId_);
if (status_ == Status.DifferentiationInProgress) {
revert TransmutationStillUndifferentiated();
}
if (status_ == Status.Failed) {
revert TransmutationFailed();
}
Rarity mendelefFlaskRarity_ = flask().rarityOf(mendelefFlaskId_);
address mendelefFlaskLastOwner_ = _mendelefFlaskLastOwners[mendelefFlaskId_];
bool becomesEmbryoFlask_ = status_ == Status.BecomesEmbryoFlask;
_clearMendelefFlaskData(mendelefFlaskLastOwner_, mendelefFlaskId_);
_transmutate(mendelefFlaskLastOwner_, mendelefFlaskId_, mendelefFlaskRarity_, becomesEmbryoFlask_);
}
/// @notice Extracts a Mendelef flask from the transmutation relic and returns it to its last owner.
/// @dev Reverts if the caller is not the last owner of the Mendelef flask, if extraction is attempted
/// before the safe block threshold (to protect the VRF Subscription balance), or if the Mendelef flask's
/// transmutation status has already been determined (i.e., it has become a embryo flask or ugly broth).
/// Clears all Mendelef flask related data and transfers the Mendelef flask using safeTransferFrom.
/// @param flaskId_ The flask ID to extract.
function extractMendelefFlask(uint256 flaskId_) public nonReentrant {
address player_ = _msgSender();
// Reverts if the Mendelef flask is not found in the Transmutation Relic
address flaskLastOwner_ = mendelefFlaskLastOwnerOf(flaskId_);
if (player_ != flaskLastOwner_) {
revert PlayerIsNotLastOwner(flaskLastOwner_);
}
// Protection against draining the VRF Subscription balance by ensuring extraction only occurs after the safe block threshold.
uint256 minAllowedBlockNumber_ =
_mendelefFlaskStartTransmutationBlockNumbers[flaskId_] + _vrfRequestConfirmations * 4 + 1;
if (block.number < minAllowedBlockNumber_) {
revert EarlyExtraction(minAllowedBlockNumber_, block.number);
}
Status status_ = _statuses[flaskId_];
if (status_ == Status.BecomesEmbryoFlask || status_ == Status.BecomesUglyBroth) {
revert TransmutationAlreadyDifferentiated(status_);
}
_clearMendelefFlaskData(flaskLastOwner_, flaskId_);
flask().safeTransferFrom(address(this), player_, flaskId_);
emit MendelefFlaskExtracted(player_, flaskId_);
}
/* -------------------------------------------------------------------------- */
/* OWNER-RESTRICTED FUNCTIONS */
/* -------------------------------------------------------------------------- */
/// @notice Pauses the contract, disabling startTransmutation and completeTransmutation functions.
/// @dev Can only be called by the contract owner.
function pause() public onlyOwner {
_pause();
}
/// @notice Unpauses the contract, re-enabling startTransmutation and completeTransmutation functions.
/// @dev Can only be called by the contract owner.
function unpause() public onlyOwner {
_unpause();
}
/// @notice Sets a new Chainlink VRF subscription ID.
/// @dev Can only be called by the contract owner.
/// @param vrfSubscriptionId_ The new VRF subscription ID.
function setVrfSubscriptionId(uint256 vrfSubscriptionId_) public onlyOwner {
_setVrfSubscriptionId(vrfSubscriptionId_);
}
/// @notice Sets a new Chainlink VRF key hash.
/// @dev Can only be called by the contract owner.
/// @param vrfKeyHash_ The new VRF key hash.
function setVrfKeyHash(bytes32 vrfKeyHash_) public onlyOwner {
_setVrfKeyHash(vrfKeyHash_);
}
/// @notice Sets a new VRF callback gas limit.
/// @dev Can only be called by the contract owner.
/// @param vrfCallbackGasLimit_ The new callback gas limit.
function setVrfCallbackGasLimit(uint32 vrfCallbackGasLimit_) public onlyOwner {
_setVrfCallbackGasLimit(vrfCallbackGasLimit_);
}
/// @notice Sets a new number of confirmations required for VRF requests.
/// @dev Can only be called by the contract owner.
/// @param vrfRequestConfirmations_ The new number of VRF request confirmations.
function setVrfRequestConfirmations(uint16 vrfRequestConfirmations_) public onlyOwner {
_setVrfRequestConfirmations(vrfRequestConfirmations_);
}
/// @notice Sets a new Magic Alchemy Mendelef Flask contract instance.
/// @dev Can only be called by the contract owner.
/// @param flask_ The new Magic Alchemy Mendelef Flask contract.
function setMendelefFlask(IMagicAlchemyMendelefFlask flask_) public onlyOwner {
_setFlask(flask_);
}
/// @notice Sets a new embryo flask contract.
/// @dev Can only be called by the contract owner.
/// @param embryoFlask_ The new embryo flask contract.
function setEmbryoFlask(IMagicAlchemyTransmutationResult embryoFlask_) public onlyOwner {
_setEmbryoFlask(embryoFlask_);
}
/// @notice Sets a new ugly broth contract.
/// @dev Can only be called by the contract owner.
/// @param uglyBroth_ The new ugly broth contract.
function setUglyBroth(IMagicAlchemyTransmutationResult uglyBroth_) public onlyOwner {
_setUglyBroth(uglyBroth_);
}
/* -------------------------------------------------------------------------- */
/* INTERNAL FUNCTIONS */
/* -------------------------------------------------------------------------- */
/// @notice Callback function used by Chainlink VRF to deliver random words.
/// @dev Processes the random words received from VRF to determine the outcome of the transmutation process.
/// Updates internal mappings accordingly and emits a TransmutationDifferentiated event. Cannot revert.
/// @param requestId_ The unique identifier of the VRF request.
/// @param randomWords_ An array containing the random words returned by the VRF coordinator.
function fulfillRandomWords(uint256 requestId_, uint256[] calldata randomWords_) internal override {
VRFRequestData memory request_ = _vrfRequestData[requestId_];
// Cannot revert.
Status status_ = _statusBy(request_, randomWords_);
if (status_ == Status.Undefined) {
// Skip undefined status. When extracted before fulfillRandomWords called.
return;
}
_statuses[request_.mendelefFlaskId] = status_;
if (status_ == Status.BecomesEmbryoFlask) {
_remainingEmbryoFlasksPlusOne[request_.round][request_.rarity]--;
}
delete _vrfRequestData[requestId_];
emit TransmutationDifferentiated(request_.player, request_.mendelefFlaskId, request_.round, request_.rarity, status_);
}
/// @notice Internal function that burns a Mendelef flask and mints the corresponding transmutation result.
/// @dev Determines which transmutation result to mint based on the boolean flag becomesEmbryoFlask_.
/// Emits a TransmutationCompleted event upon successful minting.
/// @param to_ The address to receive the minted transmutation result.
/// @param mendelefFlaskId_ The unique identifier of the Mendelef flask being transmutated.
/// @param rarity_ The rarity level of the Mendelef flask.
/// @param becomesEmbryoFlask_ If true, mints a embryo flask token; otherwise, mints an ugly broth token.
function _transmutate(address to_, uint256 mendelefFlaskId_, Rarity rarity_, bool becomesEmbryoFlask_) private {
flask().burn(mendelefFlaskId_);
(IMagicAlchemyTransmutationResult resultContract_, uint256 resultTokenId_) =
becomesEmbryoFlask_ ? (_embryoFlask, _embryoFlask.mint(to_, rarity_, mendelefFlaskId_)) : (_uglyBroth, _uglyBroth.mint(to_, rarity_, mendelefFlaskId_));
emit TransmutationCompleted(to_, mendelefFlaskId_, resultContract_, resultTokenId_, rarity_);
}
/// @notice Sets the VRF subscription ID.
/// @dev This internal function updates the subscription ID used for VRF requests and emits a VrfSubscriptionIdSetup event.
/// @param vrfSubscriptionId_ The new VRF subscription identifier.
function _setVrfSubscriptionId(uint256 vrfSubscriptionId_) private {
_vrfSubscriptionId = vrfSubscriptionId_;
emit VrfSubscriptionIdSetup(vrfSubscriptionId_);
}
/// @notice Sets the VRF key hash.
/// @dev This internal function updates the key hash used to generate VRF requests and emits a VrfKeyHashSetup event.
/// @param vrfKeyHash_ The new VRF key hash.
function _setVrfKeyHash(bytes32 vrfKeyHash_) private {
_vrfKeyHash = vrfKeyHash_;
emit VrfKeyHashSetup(vrfKeyHash_);
}
/// @notice Sets the VRF callback gas limit.
/// @dev This internal function updates the gas limit for VRF callback functions and emits a VrfCallbackGasLimitSetup event.
/// @param vrfCallbackGasLimit_ The new gas limit for VRF callbacks.
function _setVrfCallbackGasLimit(uint32 vrfCallbackGasLimit_) private {
_vrfCallbackGasLimit = vrfCallbackGasLimit_;
emit VrfCallbackGasLimitSetup(vrfCallbackGasLimit_);
}
/// @notice Sets the number of confirmations required for VRF requests.
/// @dev This internal function updates the confirmation requirement for VRF requests and emits a VrfRequestConfirmationsSetup event.
/// @param vrfRequestConfirmations_ The new number of required confirmations for VRF requests.
function _setVrfRequestConfirmations(uint16 vrfRequestConfirmations_) private {
_vrfRequestConfirmations = vrfRequestConfirmations_;
emit VrfRequestConfirmationsSetup(vrfRequestConfirmations_);
}
/// @notice Internal function to set the embryo flask transmutation result contract.
/// @dev Checks if the provided embryo flask contract supports the required interface. If so, it sets the contract and emits a EmbryoFlaskSetup event.
/// @param embryoFlask_ The embryo flask transmutation result contract to be set.
function _setEmbryoFlask(IMagicAlchemyTransmutationResult embryoFlask_) private {
if (!embryoFlask_.supportsInterface(type(IMagicAlchemyTransmutationResult).interfaceId)) {
revert InvalidTransmutationResultInterface();
}
_embryoFlask = embryoFlask_;
emit EmbryoFlaskSetup(embryoFlask_);
}
/// @notice Internal function to set the ugly broth transmutation result contract.
/// @dev Checks if the provided ugly broth contract supports the required interface. If so, it sets the contract and emits an UglyBrothSetup event.
/// @param uglyBroth_ The ugly broth transmutation result contract to be set.
function _setUglyBroth(IMagicAlchemyTransmutationResult uglyBroth_) private {
if (!uglyBroth_.supportsInterface(type(IMagicAlchemyTransmutationResult).interfaceId)) {
revert InvalidTransmutationResultInterface();
}
_uglyBroth = uglyBroth_;
emit UglyBrothSetup(uglyBroth_);
}
/// @notice Internal function that determines the transmutation status based on VRF random words and request data.
/// @dev Evaluates several conditions (e.g., proper registration, valid Mendelef flask supply, remaining embryo flask count) to decide the outcome.
/// If conditions are not met, returns Status.Failed; otherwise, computes the outcome based on a probability.
/// The probability is determined by the remaining potential EmbryoFlask supply for the Mendelef flask’s rarity versus the total Mendelef flasks minted in that round.
/// Cannot revert.
/// @param request_ The VRF request data associated with the transmutation.
/// @param randomWords_ The array containing exactly one random word received from the VRF coordinator.
/// @return The determined transmutation Status (either BecomesEmbryoFlask, BecomesUglyBroth, or Failed).
function _statusBy(VRFRequestData memory request_, uint256[] calldata randomWords_) private view returns (Status) {
Status status_ = _statuses[request_.mendelefFlaskId];
if (status_ == Status.Undefined) {
// When extracted before fulfillRandomWords called.
return Status.Undefined;
}
if (status_ != Status.DifferentiationInProgress) {
// The process is not ongoing, meaning it's too late to determine status.
return Status.Failed;
}
if (randomWords_.length == 0) {
return Status.Failed;
}
if (request_.player == address(0)) {
// Request is not properly registered.
return Status.Failed;
}
uint256 mendelefFlaskMaxSupply_ = flask().maxSupplyFor(request_.round, request_.rarity); // Cannot revert.
if (mendelefFlaskMaxSupply_ == type(uint256).max || mendelefFlaskMaxSupply_ == 0) {
// Flask max supply is either undefined or set to zero, indicating an invalid state.
return Status.Failed;
}
uint256 remainingEmbryoFlasksPlusOne_ = _remainingEmbryoFlasksPlusOne[request_.round][request_.rarity];
if (remainingEmbryoFlasksPlusOne_ == 0) {
// Remaining embryo flasks count is not initialized.
return Status.Failed;
}
return (randomWords_[0] % mendelefFlaskMaxSupply_) + 1 < remainingEmbryoFlasksPlusOne_
? Status.BecomesEmbryoFlask
: Status.BecomesUglyBroth;
}
/// @notice Internal function that clears stored transmutation data for a given Mendelef flask.
/// @dev This function deletes both the last recorded owner and the transmutation status for the Mendelef flask.
/// @param flaskId_ The unique identifier of the Mendelef flask whose data is to be cleared.
function _clearMendelefFlaskData(address flaskLastOwner_, uint256 flaskId_) private {
_lastOwnerMendelefFlaskLists[flaskLastOwner_].remove(flaskId_);
delete _mendelefFlaskLastOwners[flaskId_];
delete _statuses[flaskId_];
delete _mendelefFlaskStartTransmutationBlockNumbers[flaskId_];
}
/// @notice Internal function that sends a request to Chainlink VRF for a random word.
/// @dev Uses the VRFCoordinator stored in _vrfCoordinator with the provided parameters.
/// @return The request ID generated by the VRF coordinator.
function _requestRandomWord() private returns (uint256) {
return s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: _vrfKeyHash,
subId: _vrfSubscriptionId,
requestConfirmations: _vrfRequestConfirmations,
callbackGasLimit: _vrfCallbackGasLimit,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: true}))
})
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuardTransient.sol)
pragma solidity ^0.8.24;
import {TransientSlot} from "./TransientSlot.sol";
/**
* @dev Variant of {ReentrancyGuard} that uses transient storage.
*
* NOTE: This variant only works on networks where EIP-1153 is available.
*
* _Available since v5.1._
*/
abstract contract ReentrancyGuardTransient {
using TransientSlot for *;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
}
function _nonReentrantAfter() private {
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {IVRFCoordinatorV2Plus} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {IVRFMigratableConsumerV2Plus} from "./interfaces/IVRFMigratableConsumerV2Plus.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
/** ****************************************************************************
* @notice Interface for contracts using VRF randomness
* *****************************************************************************
* @dev PURPOSE
*
* @dev Reggie the Random Oracle (not his real job) wants to provide randomness
* @dev to Vera the verifier in such a way that Vera can be sure he's not
* @dev making his output up to suit himself. Reggie provides Vera a public key
* @dev to which he knows the secret key. Each time Vera provides a seed to
* @dev Reggie, he gives back a value which is computed completely
* @dev deterministically from the seed and the secret key.
*
* @dev Reggie provides a proof by which Vera can verify that the output was
* @dev correctly computed once Reggie tells it to her, but without that proof,
* @dev the output is indistinguishable to her from a uniform random sample
* @dev from the output space.
*
* @dev The purpose of this contract is to make it easy for unrelated contracts
* @dev to talk to Vera the verifier about the work Reggie is doing, to provide
* @dev simple access to a verifiable source of randomness. It ensures 2 things:
* @dev 1. The fulfillment came from the VRFCoordinatorV2Plus.
* @dev 2. The consumer contract implements fulfillRandomWords.
* *****************************************************************************
* @dev USAGE
*
* @dev Calling contracts must inherit from VRFConsumerBaseV2Plus, and can
* @dev initialize VRFConsumerBaseV2Plus's attributes in their constructor as
* @dev shown:
*
* @dev contract VRFConsumerV2Plus is VRFConsumerBaseV2Plus {
* @dev constructor(<other arguments>, address _vrfCoordinator, address _subOwner)
* @dev VRFConsumerBaseV2Plus(_vrfCoordinator, _subOwner) public {
* @dev <initialization with other arguments goes here>
* @dev }
* @dev }
*
* @dev The oracle will have given you an ID for the VRF keypair they have
* @dev committed to (let's call it keyHash). Create a subscription, fund it
* @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
* @dev subscription management functions).
* @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
* @dev callbackGasLimit, numWords, extraArgs),
* @dev see (IVRFCoordinatorV2Plus for a description of the arguments).
*
* @dev Once the VRFCoordinatorV2Plus has received and validated the oracle's response
* @dev to your request, it will call your contract's fulfillRandomWords method.
*
* @dev The randomness argument to fulfillRandomWords is a set of random words
* @dev generated from your requestId and the blockHash of the request.
*
* @dev If your contract could have concurrent requests open, you can use the
* @dev requestId returned from requestRandomWords to track which response is associated
* @dev with which randomness request.
* @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
* @dev if your contract could have multiple requests in flight simultaneously.
*
* @dev Colliding `requestId`s are cryptographically impossible as long as seeds
* @dev differ.
*
* *****************************************************************************
* @dev SECURITY CONSIDERATIONS
*
* @dev A method with the ability to call your fulfillRandomness method directly
* @dev could spoof a VRF response with any random value, so it's critical that
* @dev it cannot be directly called by anything other than this base contract
* @dev (specifically, by the VRFConsumerBaseV2Plus.rawFulfillRandomness method).
*
* @dev For your users to trust that your contract's random behavior is free
* @dev from malicious interference, it's best if you can write it so that all
* @dev behaviors implied by a VRF response are executed *during* your
* @dev fulfillRandomness method. If your contract must store the response (or
* @dev anything derived from it) and use it later, you must ensure that any
* @dev user-significant behavior which depends on that stored value cannot be
* @dev manipulated by a subsequent VRF request.
*
* @dev Similarly, both miners and the VRF oracle itself have some influence
* @dev over the order in which VRF responses appear on the blockchain, so if
* @dev your contract could have multiple VRF requests in flight simultaneously,
* @dev you must ensure that the order in which the VRF responses arrive cannot
* @dev be used to manipulate your contract's user-significant behavior.
*
* @dev Since the block hash of the block which contains the requestRandomness
* @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
* @dev miner could, in principle, fork the blockchain to evict the block
* @dev containing the request, forcing the request to be included in a
* @dev different block with a different hash, and therefore a different input
* @dev to the VRF. However, such an attack would incur a substantial economic
* @dev cost. This cost scales with the number of blocks the VRF oracle waits
* @dev until it calls responds to a request. It is for this reason that
* @dev that you can signal to an oracle you'd like them to wait longer before
* @dev responding to the request (however this is not enforced in the contract
* @dev and so remains effective only in the case of unmodified oracle software).
*/
abstract contract VRFConsumerBaseV2Plus is IVRFMigratableConsumerV2Plus, ConfirmedOwner {
error OnlyCoordinatorCanFulfill(address have, address want);
error OnlyOwnerOrCoordinator(address have, address owner, address coordinator);
error ZeroAddress();
// s_vrfCoordinator should be used by consumers to make requests to vrfCoordinator
// so that coordinator reference is updated after migration
IVRFCoordinatorV2Plus public s_vrfCoordinator;
/**
* @param _vrfCoordinator address of VRFCoordinator contract
*/
constructor(address _vrfCoordinator) ConfirmedOwner(msg.sender) {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
}
/**
* @notice fulfillRandomness handles the VRF response. Your contract must
* @notice implement it. See "SECURITY CONSIDERATIONS" above for important
* @notice principles to keep in mind when implementing your fulfillRandomness
* @notice method.
*
* @dev VRFConsumerBaseV2Plus expects its subcontracts to have a method with this
* @dev signature, and will call it once it has verified the proof
* @dev associated with the randomness. (It is triggered via a call to
* @dev rawFulfillRandomness, below.)
*
* @param requestId The Id initially returned by requestRandomness
* @param randomWords the VRF output expanded to the requested number of words
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;
// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
// proof. rawFulfillRandomness then calls fulfillRandomness, after validating
// the origin of the call
function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
if (msg.sender != address(s_vrfCoordinator)) {
revert OnlyCoordinatorCanFulfill(msg.sender, address(s_vrfCoordinator));
}
fulfillRandomWords(requestId, randomWords);
}
/**
* @inheritdoc IVRFMigratableConsumerV2Plus
*/
function setCoordinator(address _vrfCoordinator) external override onlyOwnerOrCoordinator {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
emit CoordinatorSet(_vrfCoordinator);
}
modifier onlyOwnerOrCoordinator() {
if (msg.sender != owner() && msg.sender != address(s_vrfCoordinator)) {
revert OnlyOwnerOrCoordinator(msg.sender, owner(), address(s_vrfCoordinator));
}
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// End consumer library.
library VRFV2PlusClient {
// extraArgs will evolve to support new features
bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1"));
struct ExtraArgsV1 {
bool nativePayment;
}
struct RandomWordsRequest {
bytes32 keyHash;
uint256 subId;
uint16 requestConfirmations;
uint32 callbackGasLimit;
uint32 numWords;
bytes extraArgs;
}
function _argsToBytes(ExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IERC721Enumerable} from "openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {IMagicAlchemyMarathonNft} from "./IMagicAlchemyMarathonNft.sol";
import {MagicAlchemyRarity as Rarity} from "../utils/MagicAlchemyRarity.sol";
interface IMagicAlchemyMendelefFlask is IERC721Enumerable, IMagicAlchemyMarathonNft {
function roundOf(uint256 tokenId) external view returns (uint256);
function maxSupplyFor(uint256 round, Rarity rarity) external view returns (uint256);
function mint(address to, uint256 round, Rarity rarity, uint256 initialMaxRoundRaritySupply)
external
returns (uint256);
function burn(uint256 tokenId) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IMagicAlchemyMarathonNft} from "./IMagicAlchemyMarathonNft.sol";
import {MagicAlchemyRarity as Rarity} from "../utils/MagicAlchemyRarity.sol";
interface IMagicAlchemyTransmutationResult is IMagicAlchemyMarathonNft {
function mint(address to, Rarity rarity, uint256 mendelefFlaskId) external returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IMagicAlchemyMendelefFlask} from "../interfaces/IMagicAlchemyMendelefFlask.sol";
abstract contract MagicAlchemyFlaskAggregator {
event FlaskSetup(IMagicAlchemyMendelefFlask flask);
error InvalidFlaskInterface();
IMagicAlchemyMendelefFlask private _flask;
constructor(IMagicAlchemyMendelefFlask flask_) {
_setFlask(flask_);
}
function flask() public view returns (IMagicAlchemyMendelefFlask) {
return _flask;
}
function _setFlask(IMagicAlchemyMendelefFlask flask_) internal {
if (!flask_.supportsInterface(type(IMagicAlchemyMendelefFlask).interfaceId)) {
revert InvalidFlaskInterface();
}
_flask = flask_;
emit FlaskSetup(flask_);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @dev Enumeration of rarity levels used throughout the Magic Alchemy contracts.
* It defines the four distinct tiers: Common, Rare, Epic, and Legendary.
*/
enum MagicAlchemyRarity {
Common,
Rare,
Epic,
Legendary
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/TransientSlot.sol)
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.
pragma solidity ^0.8.24;
/**
* @dev Library for reading and writing value-types to specific transient storage slots.
*
* Transient slots are often used to store temporary values that are removed after the current transaction.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* * Example reading and writing values using transient storage:
* ```solidity
* contract Lock {
* using TransientSlot for *;
*
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
*
* modifier locked() {
* require(!_LOCK_SLOT.asBoolean().tload());
*
* _LOCK_SLOT.asBoolean().tstore(true);
* _;
* _LOCK_SLOT.asBoolean().tstore(false);
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library TransientSlot {
/**
* @dev UDVT that represent a slot holding a address.
*/
type AddressSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a AddressSlot.
*/
function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
return AddressSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bool.
*/
type BooleanSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a BooleanSlot.
*/
function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
return BooleanSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bytes32.
*/
type Bytes32Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Bytes32Slot.
*/
function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
return Bytes32Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a uint256.
*/
type Uint256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Uint256Slot.
*/
function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
return Uint256Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a int256.
*/
type Int256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Int256Slot.
*/
function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
return Int256Slot.wrap(slot);
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(AddressSlot slot) internal view returns (address value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlot slot, address value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlot slot) internal view returns (bool value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlot slot, bool value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32Slot slot, bytes32 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256Slot slot) internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256Slot slot, uint256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256Slot slot) internal view returns (int256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256Slot slot, int256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
import {IVRFSubscriptionV2Plus} from "./IVRFSubscriptionV2Plus.sol";
// Interface that enables consumers of VRFCoordinatorV2Plus to be future-proof for upgrades
// This interface is supported by subsequent versions of VRFCoordinatorV2Plus
interface IVRFCoordinatorV2Plus is IVRFSubscriptionV2Plus {
/**
* @notice Request a set of random words.
* @param req - a struct containing following fields for randomness request:
* keyHash - Corresponds to a particular oracle job which uses
* that key for generating the VRF proof. Different keyHash's have different gas price
* ceilings, so you can select a specific one to bound your maximum per request cost.
* subId - The ID of the VRF subscription. Must be funded
* with the minimum subscription balance required for the selected keyHash.
* requestConfirmations - How many blocks you'd like the
* oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
* for why you may want to request more. The acceptable range is
* [minimumRequestBlockConfirmations, 200].
* callbackGasLimit - How much gas you'd like to receive in your
* fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
* may be slightly less than this amount because of gas used calling the function
* (argument decoding etc.), so you may need to request slightly more than you expect
* to have inside fulfillRandomWords. The acceptable range is
* [0, maxGasLimit]
* numWords - The number of uint256 random values you'd like to receive
* in your fulfillRandomWords callback. Note these numbers are expanded in a
* secure way by the VRFCoordinator from a single random value supplied by the oracle.
* extraArgs - abi-encoded extra args
* @return requestId - A unique identifier of the request. Can be used to match
* a request to a response in fulfillRandomWords.
*/
function requestRandomWords(VRFV2PlusClient.RandomWordsRequest calldata req) external returns (uint256 requestId);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The IVRFMigratableConsumerV2Plus interface defines the
/// @notice method required to be implemented by all V2Plus consumers.
/// @dev This interface is designed to be used in VRFConsumerBaseV2Plus.
interface IVRFMigratableConsumerV2Plus {
event CoordinatorSet(address vrfCoordinator);
/// @notice Sets the VRF Coordinator address
/// @notice This method should only be callable by the coordinator or contract owner
function setCoordinator(address vrfCoordinator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IERC165} from "openzeppelin-contracts/contracts/interfaces/IERC165.sol";
import {MagicAlchemyRarity as Rarity} from "../utils/MagicAlchemyRarity.sol";
interface IMagicAlchemyMarathonNft is IERC165 {
function rarityOf(uint256 tokenId) external view returns (Rarity);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The IVRFSubscriptionV2Plus interface defines the subscription
/// @notice related methods implemented by the V2Plus coordinator.
interface IVRFSubscriptionV2Plus {
/**
* @notice Add a consumer to a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - New consumer which can use the subscription
*/
function addConsumer(uint256 subId, address consumer) external;
/**
* @notice Remove a consumer from a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - Consumer to remove from the subscription
*/
function removeConsumer(uint256 subId, address consumer) external;
/**
* @notice Cancel a subscription
* @param subId - ID of the subscription
* @param to - Where to send the remaining LINK to
*/
function cancelSubscription(uint256 subId, address to) external;
/**
* @notice Accept subscription owner transfer.
* @param subId - ID of the subscription
* @dev will revert if original owner of subId has
* not requested that msg.sender become the new owner.
*/
function acceptSubscriptionOwnerTransfer(uint256 subId) external;
/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @param newOwner - proposed new owner of the subscription
*/
function requestSubscriptionOwnerTransfer(uint256 subId, address newOwner) external;
/**
* @notice Create a VRF subscription.
* @return subId - A unique subscription id.
* @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
* @dev Note to fund the subscription with LINK, use transferAndCall. For example
* @dev LINKTOKEN.transferAndCall(
* @dev address(COORDINATOR),
* @dev amount,
* @dev abi.encode(subId));
* @dev Note to fund the subscription with Native, use fundSubscriptionWithNative. Be sure
* @dev to send Native with the call, for example:
* @dev COORDINATOR.fundSubscriptionWithNative{value: amount}(subId);
*/
function createSubscription() external returns (uint256 subId);
/**
* @notice Get a VRF subscription.
* @param subId - ID of the subscription
* @return balance - LINK balance of the subscription in juels.
* @return nativeBalance - native balance of the subscription in wei.
* @return reqCount - Requests count of subscription.
* @return owner - owner of the subscription.
* @return consumers - list of consumer address which are able to use this subscription.
*/
function getSubscription(
uint256 subId
)
external
view
returns (uint96 balance, uint96 nativeBalance, uint64 reqCount, address owner, address[] memory consumers);
/*
* @notice Check to see if there exists a request commitment consumers
* for all consumers and keyhashes for a given sub.
* @param subId - ID of the subscription
* @return true if there exists at least one unfulfilled request for the subscription, false
* otherwise.
*/
function pendingRequestExists(uint256 subId) external view returns (bool);
/**
* @notice Paginate through all active VRF subscriptions.
* @param startIndex index of the subscription to start from
* @param maxCount maximum number of subscriptions to return, 0 to return all
* @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one
* @dev should consider keeping the blockheight constant to ensure a holistic picture of the contract state
*/
function getActiveSubscriptionIds(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory);
/**
* @notice Fund a subscription with native.
* @param subId - ID of the subscription
* @notice This method expects msg.value to be greater than or equal to 0.
*/
function fundSubscriptionWithNative(uint256 subId) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(address to) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"chainlink/=lib/chainlink/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"commonEmbryoFlaskQuota_","type":"uint256"},{"internalType":"uint256","name":"rareEmbryoFlaskQuota_","type":"uint256"},{"internalType":"uint256","name":"epicEmbryoFlaskQuota_","type":"uint256"},{"internalType":"contract IMagicAlchemyMendelefFlask","name":"mendelefFlask_","type":"address"},{"internalType":"contract IMagicAlchemyTransmutationResult","name":"uglyBroth_","type":"address"},{"internalType":"contract IMagicAlchemyTransmutationResult","name":"embryoFlask_","type":"address"},{"internalType":"address","name":"vrfCoordinator_","type":"address"},{"internalType":"uint256","name":"vrfSubscriptionId_","type":"uint256"},{"internalType":"bytes32","name":"vrfKeyHash_","type":"bytes32"},{"internalType":"uint32","name":"vrfCallbackGasLimit_","type":"uint32"},{"internalType":"uint16","name":"vrfRequestConfirmations_","type":"uint16"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"minAllowedBlockNumber","type":"uint256"},{"internalType":"uint256","name":"actualBlockNumber","type":"uint256"}],"name":"EarlyExtraction","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"InvalidFlaskInterface","type":"error"},{"inputs":[],"name":"InvalidTransmutationResultInterface","type":"error"},{"inputs":[{"internalType":"address","name":"lastOwner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"LastOwnerMendelefFlaskIndexOutOfBounds","type":"error"},{"inputs":[],"name":"MendelefFlaskNotFound","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"want","type":"address"}],"name":"OnlyCoordinatorCanFulfill","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"coordinator","type":"address"}],"name":"OnlyOwnerOrCoordinator","type":"error"},{"inputs":[{"internalType":"address","name":"lastOwner","type":"address"}],"name":"PlayerIsNotLastOwner","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"player","type":"address"}],"name":"SenderIsNotOwner","type":"error"},{"inputs":[{"internalType":"enum MagicAlchemyTransmutationRelic.Status","name":"currentStatus","type":"uint8"}],"name":"TransmutationAlreadyDifferentiated","type":"error"},{"inputs":[],"name":"TransmutationAlreadyStarted","type":"error"},{"inputs":[],"name":"TransmutationFailed","type":"error"},{"inputs":[],"name":"TransmutationStillUndifferentiated","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vrfCoordinator","type":"address"}],"name":"CoordinatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IMagicAlchemyTransmutationResult","name":"embryo","type":"address"}],"name":"EmbryoFlaskSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IMagicAlchemyMendelefFlask","name":"flask","type":"address"}],"name":"FlaskSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"flaskId","type":"uint256"}],"name":"MendelefFlaskExtracted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":true,"internalType":"uint256","name":"mendelefFlaskId","type":"uint256"},{"indexed":true,"internalType":"contract IMagicAlchemyTransmutationResult","name":"resultTokenContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"resultTokenId","type":"uint256"},{"indexed":false,"internalType":"enum MagicAlchemyRarity","name":"rarity","type":"uint8"}],"name":"TransmutationCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":true,"internalType":"uint256","name":"mendelefFlaskId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"enum MagicAlchemyRarity","name":"rarity","type":"uint8"},{"indexed":false,"internalType":"enum MagicAlchemyTransmutationRelic.Status","name":"status","type":"uint8"}],"name":"TransmutationDifferentiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":true,"internalType":"uint256","name":"mendelefFlaskId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vrfRequestId","type":"uint256"}],"name":"TransmutationStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IMagicAlchemyTransmutationResult","name":"uglyBroth","type":"address"}],"name":"UglyBrothSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"vrfCallbackGasLimit","type":"uint32"}],"name":"VrfCallbackGasLimitSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vrfKeyHash","type":"bytes32"}],"name":"VrfKeyHashSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"vrfRequestConfirmations","type":"uint16"}],"name":"VrfRequestConfirmationsSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vrfSubscriptionId","type":"uint256"}],"name":"VrfSubscriptionIdSetup","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mendelefFlaskId_","type":"uint256"}],"name":"completeTransmutation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"embryoFlask","outputs":[{"internalType":"contract IMagicAlchemyTransmutationResult","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"flaskId_","type":"uint256"}],"name":"extractMendelefFlask","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flask","outputs":[{"internalType":"contract IMagicAlchemyMendelefFlask","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lastOwner_","type":"address"}],"name":"lastOwnerMendelefFlaskCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mendelefFlaskId_","type":"uint256"}],"name":"mendelefFlaskLastOwnerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lastOwner_","type":"address"},{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"mendelefFlaskOfLastOwnerByIndex","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":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"uint256[]","name":"randomWords","type":"uint256[]"}],"name":"rawFulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"round_","type":"uint256"},{"internalType":"enum MagicAlchemyRarity","name":"rarity_","type":"uint8"}],"name":"remainingEmbryoFlasksFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"s_vrfCoordinator","outputs":[{"internalType":"contract IVRFCoordinatorV2Plus","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vrfCoordinator","type":"address"}],"name":"setCoordinator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMagicAlchemyTransmutationResult","name":"embryoFlask_","type":"address"}],"name":"setEmbryoFlask","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMagicAlchemyMendelefFlask","name":"flask_","type":"address"}],"name":"setMendelefFlask","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMagicAlchemyTransmutationResult","name":"uglyBroth_","type":"address"}],"name":"setUglyBroth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"vrfCallbackGasLimit_","type":"uint32"}],"name":"setVrfCallbackGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"vrfKeyHash_","type":"bytes32"}],"name":"setVrfKeyHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"vrfRequestConfirmations_","type":"uint16"}],"name":"setVrfRequestConfirmations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vrfSubscriptionId_","type":"uint256"}],"name":"setVrfSubscriptionId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mendelefFlaskId_","type":"uint256"}],"name":"startTransmutation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mendelefFlaskId_","type":"uint256"}],"name":"statusOf","outputs":[{"internalType":"enum MagicAlchemyTransmutationRelic.Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uglyBroth","outputs":[{"internalType":"contract IMagicAlchemyTransmutationResult","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vrfCallbackGasLimit","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vrfKeyHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vrfRequestConfirmations","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vrfSubscriptionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
0x608080604052346104ce5761016081612309803803809161002082856104d2565b8339810103126104ce578051602082015190604083015192606081015160018060a01b0381168091036104ce5761005960808301610509565b9161006660a08201610509565b60c08201516001600160a01b03811694908590036104ce5760e083015190610100840151926101208501519463ffffffff86168096036104ce5761014001519561ffff8716978888036104ce573315610489575f80546001600160a01b03191633179055801561047a57600280546001600160a81b03191691909117908190556040516301ffc9a760e01b8152635d0a165d60e11b6004820152909190602081602481855afa908115610413575f9161045b575b501561044c57600380546001600160a01b031916821790556040519081527fec23afdbcd86322300b97874ea136efeb1ee4820516bec8bb837205b93c9a87690602090a160ff8160a01c1661043d57600160a01b176002557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a16040516301ffc9a760e01b8152632769c3d960e11b60048201526001600160a01b039190911690602081602481855afa908115610413575f9161041e575b50156103d557600b80546001600160a01b031916821790556040519081527ffdf24252aba5b6859ae3b5bb59bc098152760debec253064163c1a00f47f66ce90602090a16040516301ffc9a760e01b8152632769c3d960e11b60048201526001600160a01b039190911690602081602481855afa908115610413575f916103e4575b50156103d557600c80546001600160a01b031916821790556040519081527f027226b2f8497438c514139ace63e9b9612ef31f0f0b01338458118937873c3c90602090a180600d557f5a48e3bcf1fa980d54acd33041749188e47c12e900814aa9477301808ac723515f80a280600e557f20a0fbc0ee4de1a1cffa4583f49c511e0b01c5c5188dddcaadd9f7929bc981945f80a265ffff00000000600f5492827fd36c67e14aef21a2a06b18e3e20946690ed142971da6f7760bb0e336e4f405915f80a260201b169165ffffffffffff19161717600f557f06c2d6a3a00a473e93b39165178ac616db46635f99f8022674ed0b0a4aee23895f80a260046020527f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec557fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe055560025f527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a755604051611dd390816105368239f35b63233b87f160e01b5f5260045ffd5b610406915060203d60201161040c575b6103fe81836104d2565b81019061051d565b5f61025c565b503d6103f4565b6040513d5f823e3d90fd5b610437915060203d60201161040c576103fe81836104d2565b5f6101da565b63d93c066560e01b5f5260045ffd5b630364d2cf60e01b5f5260045ffd5b610474915060203d60201161040c576103fe81836104d2565b5f61011a565b63d92e233d60e01b5f5260045ffd5b60405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f00000000000000006044820152606490fd5b5f80fd5b601f909101601f19168101906001600160401b038211908210176104f557604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036104ce57565b908160209103126104ce575180151581036104ce579056fe60806040526004361015610011575f80fd5b5f5f3560e01c80624c65aa146115d4578063041d443e146115b75780630771c0571461158f5780631fe543e31461150257806322b232191461142e5780633b7ed7341461140b5780633f198daa146113d35780633f4ba83a14611365578063598bc486146113425780635a405c87146112e35780635c975abb146112be578063607acbd214610ca657806379ba509714610bfb5780638456cb5914610b985780638da5cb5b14610b715780638ea9811714610aa05780639eccacf614610a77578063a092bbee14610a42578063a4653b33146109fa578063ab14953314610988578063ad35efd41461095a578063bc898d8f14610776578063c22ee31814610685578063c36df2d61461063d578063c5be201414610344578063d31756121461031b578063dcd09139146102f2578063e8c46fbd1461027a578063ec000ab614610249578063ed647d211461022b5763f2fde38b1461016e575f80fd5b34610228576020366003190112610228576101876116a8565b61018f611810565b6001600160a01b03163381146101e357600180546001600160a01b0319168217905581546001600160a01b03167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788380a380f35b60405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606490fd5b80fd5b50346102285780600319360112610228576020600d54604051908152f35b50346102285760203660031901126102285760206102686004356117da565b6040516001600160a01b039091168152f35b5034610228576040366003190112610228576102946116a8565b6001600160a01b0316808252600760205260408220546024359291908310156102dc57916040826020946102cd94526007855220611c08565b90549060031b1c604051908152f35b916044926383b5a3b560e01b8352600452602452fd5b5034610228578060031936011261022857600b546040516001600160a01b039091168152602090f35b5034610228578060031936011261022857600c546040516001600160a01b039091168152602090f35b5034610228576020366003190112610228576004356103616119cf565b6103696119ed565b6103728161179b565b6005811015610629576001811461061a576004811461060b57600354604051636e9dafcd60e11b815260048101849052929190602090849060249082906001600160a01b03165afa9283156106005784936105cf575b50818452600660205260408420546001600160a01b0316906103ea8383611a4c565b60035485906001600160a01b0316803b156105cb57818091602460405180948193630852cd8d60e31b83528a60048401525af180156105c0576105a7575b505060020361050857600b54604051632769c3d960e11b81526001600160a01b03909116906020818061046087898860048501611a22565b038189865af19081156104fd5786916104ba575b5060409061049c5f516020611d7e5f395f51905f529391965b83519788526020880190611879565b6001600160a01b031694a4805f516020611d5e5f395f51905f525d80f35b90506020813d6020116104f5575b816104d5602093836116fb565b810103126104f157515f516020611d7e5f395f51905f52610474565b5f80fd5b3d91506104c8565b6040513d88823e3d90fd5b600c54604051632769c3d960e11b81526001600160a01b03909116906020818061053787898860048501611a22565b038189865af19081156104fd578691610568575b5060409061049c5f516020611d7e5f395f51905f5293919661048d565b90506020813d60201161059f575b81610583602093836116fb565b810103126104f157515f516020611d7e5f395f51905f5261054b565b3d9150610576565b816105b1916116fb565b6105bc57845f610428565b8480fd5b6040513d84823e3d90fd5b5080fd5b6105f291935060203d6020116105f9575b6105ea81836116fb565b81019061171d565b915f6103c8565b503d6105e0565b6040513d86823e3d90fd5b6328f3628d60e11b8352600483fd5b63165f280160e31b8352600483fd5b634e487b7160e01b83526021600452602483fd5b50346102285760203660031901126102285760043561065a611810565b80600e557f20a0fbc0ee4de1a1cffa4583f49c511e0b01c5c5188dddcaadd9f7929bc981948280a280f35b5034610228576020366003190112610228576004356001600160a01b038116908190036105cb576106b4611810565b6040516301ffc9a760e01b8152632769c3d960e11b6004820152602081602481855afa90811561076b57839161073c575b501561072d576020817f027226b2f8497438c514139ace63e9b9612ef31f0f0b01338458118937873c3c926001600160601b0360a01b600c541617600c55604051908152a180f35b63233b87f160e01b8252600482fd5b61075e915060203d602011610764575b61075681836116fb565b810190611861565b5f6106e5565b503d61074c565b6040513d85823e3d90fd5b5034610228576020366003190112610228576004356107936119ed565b61079c816117da565b6001600160a01b038116338190036109485750818352600a6020526040832054600f54601e1c6203fffc61fffc82169116810361093457810180911161092057600181018091116109205780431061090a5750818352600560205260ff6040842054166005811080156108f6576002821480156108ec575b6108c15750508161082491611a4c565b60035482906001600160a01b0316803b156105cb57604051632142170760e11b8152306004820152336024820152604481018490529082908290606490829084905af180156105c0576108ac575b5050337fa5b864cfff4e834711fabe4617d4cc25810f0005a6980851a0cee1ca88643cfb8380a3805f516020611d5e5f395f51905f525d80f35b816108b6916116fb565b6105cb57815f610872565b6329e4093f60e11b85526024918591156108da57600452fd5b50634e487b7160e01b81526021600452fd5b5060038214610814565b634e487b7160e01b85526021600452602485fd5b636770c4f760e11b845260045243602452604483fd5b634e487b7160e01b84526011600452602484fd5b634e487b7160e01b85526011600452602485fd5b631306f3e960e21b8452600452602483fd5b503461022857602036600319011261022857602061097960043561179b565b61098660405180926116be565bf35b50346102285760203660031901126102285760043561ffff8116908181036109f6576109b2611810565b65ffff00000000600f549160201b169065ffff00000000191617600f557f06c2d6a3a00a473e93b39165178ac616db46635f99f8022674ed0b0a4aee23898280a280f35b8280fd5b503461022857602036600319011261022857600435610a17611810565b80600d557f5a48e3bcf1fa980d54acd33041749188e47c12e900814aa9477301808ac723518280a280f35b503461022857604036600319011261022857602435906004821015610228576020610a6f83600435611757565b604051908152f35b50346102285780600319360112610228576002546040516001600160a01b039091168152602090f35b503461022857602036600319011261022857610aba6116a8565b81546001600160a01b03163381141580610b5c575b610b3457506001600160a01b03168015610b25576020817fd1a6a14209a385a964d036e404cb5cfb71f4000cdb03c9366292430787261be6926001600160601b0360a01b6002541617600255604051908152a180f35b63d92e233d60e01b8252600482fd5b60025463061db9c160e01b8452336004526024919091526001600160a01b0316604452606482fd5b506002546001600160a01b0316331415610acf565b5034610228578060031936011261022857546040516001600160a01b039091168152602090f35b5034610228578060031936011261022857610bb1611810565b610bb96119cf565b6002805460ff60a01b1916600160a01b1790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602090a180f35b50346102285780600319360112610228576001546001600160a01b0381163303610c685781546001600160a01b031980821633908117855592166001556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606490fd5b50346104f15760203660031901126104f157600435610cc36119cf565b610ccb6119ed565b6003546040516331a9108f60e11b8152600481018390526001600160a01b0390911690602081602481855afa908115611226575f9161127c575b506001600160a01b0316338190036112665750604051636e9dafcd60e11b81526004810183905290602082602481845afa918215611226575f92611245575b5060048210156112315760038203610e6f575060035490919083906001600160a01b0316803b156105cb57818091602460405180948193630852cd8d60e31b83528860048401525af180156105c057610e5a575b5050600b54604051632769c3d960e11b81526001600160a01b039091169260208280610dc986853360048501611a22565b038188885af1918215610e4f578592610e19575b50610df2906040519283526020830190611879565b5f516020611d7e5f395f51905f5260403392a45b805f516020611d5e5f395f51905f525d80f35b9091506020813d602011610e47575b81610e35602093836116fb565b810103126104f1575190610df2610ddd565b3d9150610e28565b6040513d87823e3d90fd5b81610e64916116fb565b6109f657825f610d98565b5f838152600a602090815260408083204390556006825280832080546001600160a01b03191633908117909155835260079091529020610eb0908490611c31565b50803b156104f1576040516323b872dd60e01b8152336004820152306024820152604481018490525f8160648183865af180156112265761120a575b509060206024926040519384809263c7c3809160e01b82528760048301525afa9182156106005784926111d6575b508184526008602052610f308160408620611735565b54156111a3575b600254600e54600d54600f546040519095936001600160a01b031692906020810167ffffffffffffffff81118282101761118f57604052600181526040519063125fa26760e31b6020830152511515602482015260248152610f9a6044826116fb565b6040519160c0830183811067ffffffffffffffff82111761117b5760405282526020820192835260408201968060201c61ffff168852606083019063ffffffff16815260808301906001825260a084019283526040519889958695634d8e1c2f60e11b875260048701602090525160248701525160448601525161ffff1660648501525163ffffffff1660848401525163ffffffff1660a48301525160c4820160c09052805180918160e485015260200161010484015e888183016101040152601f1990601f01168101036101040181885a94602095f1928315610e4f578593611146575b5060405160039161108f826116cb565b3382526020820190815260408201908682526110af60608401958661174b565b858852600960205260408820925183546001600160a01b0319166001600160a01b0391909116178355516001830155516002820155915191019060048110156108f65760ff80198354169116179055818352600560205260408320600160ff198254161790556040519081527fe4113aff1b3b99ecd74e468f6c0bdd30f06df1f1bd15c7e99656d9426554ced760203392a3610e06565b9092506020813d602011611173575b81611162602093836116fb565b810103126104f1575191600361107f565b3d9150611155565b634e487b7160e01b8b52604160045260248bfd5b634e487b7160e01b8a52604160045260248afd5b80845260046020526040842054600181018091116109345782855260086020526111d08260408720611735565b55610f37565b9091506020813d602011611202575b816111f2602093836116fb565b810103126104f15751905f610f1a565b3d91506111e5565b6024929194505f61121a916116fb565b60205f94919250610eec565b6040513d5f823e3d90fd5b634e487b7160e01b5f52602160045260245ffd5b61125f91925060203d6020116105f9576105ea81836116fb565b905f610d44565b632c3bd91560e21b5f523360045260245260445ffd5b90506020813d6020116112b6575b81611297602093836116fb565b810103126104f157516001600160a01b03811681036104f1575f610d05565b3d915061128a565b346104f1575f3660031901126104f157602060ff60025460a01c166040519015158152f35b346104f15760203660031901126104f15760043563ffffffff81168091036104f15761130d611810565b8063ffffffff19600f541617600f557fd36c67e14aef21a2a06b18e3e20946690ed142971da6f7760bb0e336e4f405915f80a2005b346104f1575f3660031901126104f157602061ffff600f54821c16604051908152f35b346104f1575f3660031901126104f15761137d611810565b60025460ff8160a01c16156113c45760ff60a01b19166002556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a1005b638dfc202b60e01b5f5260045ffd5b346104f15760203660031901126104f1576001600160a01b036113f46116a8565b165f526007602052602060405f2054604051908152f35b346104f1575f3660031901126104f157602063ffffffff600f5416604051908152f35b346104f15760203660031901126104f1576004356001600160a01b038116908190036104f15761145c611810565b6040516301ffc9a760e01b8152635d0a165d60e11b6004820152602081602481855afa908115611226575f916114e3575b50156114d4576020817fec23afdbcd86322300b97874ea136efeb1ee4820516bec8bb837205b93c9a876926001600160601b0360a01b6003541617600355604051908152a1005b630364d2cf60e01b5f5260045ffd5b6114fc915060203d6020116107645761075681836116fb565b8261148d565b346104f15760403660031901126104f15760243567ffffffffffffffff81116104f157366023820112156104f157806004013567ffffffffffffffff81116104f1573660248260051b840101116104f1576002546001600160a01b031633819003611579575060246115779201600435611886565b005b63073e64fd60e21b5f523360045260245260445ffd5b346104f1575f3660031901126104f1576003546040516001600160a01b039091168152602090f35b346104f1575f3660031901126104f1576020600e54604051908152f35b346104f15760203660031901126104f1576004356001600160a01b038116908190036104f157611602611810565b6040516301ffc9a760e01b8152632769c3d960e11b6004820152602081602481855afa908115611226575f91611689575b501561167a576020817ffdf24252aba5b6859ae3b5bb59bc098152760debec253064163c1a00f47f66ce926001600160601b0360a01b600b541617600b55604051908152a1005b63233b87f160e01b5f5260045ffd5b6116a2915060203d6020116107645761075681836116fb565b82611633565b600435906001600160a01b03821682036104f157565b9060058210156112315752565b6080810190811067ffffffffffffffff8211176116e757604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176116e757604052565b908160209103126104f1575160048110156104f15790565b906004811015611231575f5260205260405f2090565b60048210156112315752565b9061176c915f52600860205260405f20611735565b548015611795575f1981019081116117815790565b634e487b7160e01b5f52601160045260245ffd5b505f1990565b805f52600560205260ff60405f205416600581101561123157156117cb575f52600560205260ff60405f20541690565b630f8d5de760e41b5f5260045ffd5b5f818152600660205260409020546001600160a01b0316156117cb575f908152600660205260409020546001600160a01b031690565b5f546001600160a01b0316330361182357565b60405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606490fd5b908160209103126104f1575180151581036104f15790565b9060048210156112315752565b919091805f52600960205260405f20906040516118a2816116cb565b60018060a01b0383541681526118e76001840154946020830195865260ff600360028701549660408601978852015416966118e160608501988961174b565b83611aa0565b9160058310156112315782156119c75783515f52600560205260405f2060ff1981541660ff851617905560028314611994575b5f5260096020525f600360408220828155826001820155826002820155015560018060a01b03905116915192519351906004821015611231577f074f8b12e76193b40d7423f88dd96efd74359cb59bc05f28c4635aa44394b3699161199160409261198784518094611879565b60208301906116be565ba4565b84515f52600860205260405f2086516004811015611231576119b591611735565b80548015611781575f1901905561191a565b505050505050565b60ff60025460a01c166119de57565b63d93c066560e01b5f5260045ffd5b5f516020611d5e5f395f51905f525c611a135760015f516020611d5e5f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b6001600160a01b0390911681526060810193929160409190611a48906020830190611879565b0152565b60018060a01b03165f526007602052611a688160405f20611ca0565b505f90815260066020908152604080832080546001600160a01b031916905560058252808320805460ff19169055600a909152812055565b909160408201515f52600560205260ff60405f2054166005811015611231578015611c00575f1901611bac5715611bf95780516001600160a01b031615611bf95760018060a01b036003541690602081016060815192019182516004811015611231576044602092611b2d9660405197889485936398e77ed560e01b855260048501526024840190611879565b5afa928315611226575f93611bc5575b505f1983148015611bbd575b611bb457515f52600860205260405f209051600481101561123157611b6d91611735565b54918215611bac578115611b9857350660018101809111611781571015611b9357600290565b600390565b634e487b7160e01b5f52601260045260245ffd5b505050600490565b50505050600490565b508215611b49565b9092506020813d602011611bf1575b81611be1602093836116fb565b810103126104f15751915f611b3d565b3d9150611bd4565b5050600490565b505050505f90565b8054821015611c1d575f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b6001810190825f528160205260405f2054155f14611c99578054680100000000000000008110156116e757611c86611c70826001879401855584611c08565b819391549060031b91821b915f19901b19161790565b905554915f5260205260405f2055600190565b5050505f90565b906001820191815f528260205260405f20548015155f14611c00575f1981018181116117815782545f1981019190821161178157818103611d28575b50505080548015611d14575f190190611cf58282611c08565b8154905f199060031b1b19169055555f526020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b611d48611d38611c709386611c08565b90549060031b1c92839286611c08565b90555f528360205260405f20555f8080611cdc56fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f007168a528bf5efafe5f1780914af9400c1dbed518e4c12acad6f4e3a6bb470c77a264697066735822122055c1c54429b121af1e757813a503ea7e8fe15104b91a1dee30319eb006aa742964736f6c634300081c00330000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000b83b484260a868656985071d74936595f6a3fc5b0000000000000000000000004d111ccfe0e93c5da951c191bf1a3b8f73425ca4000000000000000000000000903cc78fdd424431ff74bee1f68579243a6132a3000000000000000000000000ec0ed46f36576541c75739e915adbcb3de24bd773a95947940542d4cb3f2ef64626a659a721d72aff10f7647a964626430c7fba0719ed7d7664abc3001c18aac8130a2265e1e70b7e036ae20f3ca8b92b3154d86000000000000000000000000000000000000000000000000000000000007a120000000000000000000000000000000000000000000000000000000000000000c
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f5f3560e01c80624c65aa146115d4578063041d443e146115b75780630771c0571461158f5780631fe543e31461150257806322b232191461142e5780633b7ed7341461140b5780633f198daa146113d35780633f4ba83a14611365578063598bc486146113425780635a405c87146112e35780635c975abb146112be578063607acbd214610ca657806379ba509714610bfb5780638456cb5914610b985780638da5cb5b14610b715780638ea9811714610aa05780639eccacf614610a77578063a092bbee14610a42578063a4653b33146109fa578063ab14953314610988578063ad35efd41461095a578063bc898d8f14610776578063c22ee31814610685578063c36df2d61461063d578063c5be201414610344578063d31756121461031b578063dcd09139146102f2578063e8c46fbd1461027a578063ec000ab614610249578063ed647d211461022b5763f2fde38b1461016e575f80fd5b34610228576020366003190112610228576101876116a8565b61018f611810565b6001600160a01b03163381146101e357600180546001600160a01b0319168217905581546001600160a01b03167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788380a380f35b60405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606490fd5b80fd5b50346102285780600319360112610228576020600d54604051908152f35b50346102285760203660031901126102285760206102686004356117da565b6040516001600160a01b039091168152f35b5034610228576040366003190112610228576102946116a8565b6001600160a01b0316808252600760205260408220546024359291908310156102dc57916040826020946102cd94526007855220611c08565b90549060031b1c604051908152f35b916044926383b5a3b560e01b8352600452602452fd5b5034610228578060031936011261022857600b546040516001600160a01b039091168152602090f35b5034610228578060031936011261022857600c546040516001600160a01b039091168152602090f35b5034610228576020366003190112610228576004356103616119cf565b6103696119ed565b6103728161179b565b6005811015610629576001811461061a576004811461060b57600354604051636e9dafcd60e11b815260048101849052929190602090849060249082906001600160a01b03165afa9283156106005784936105cf575b50818452600660205260408420546001600160a01b0316906103ea8383611a4c565b60035485906001600160a01b0316803b156105cb57818091602460405180948193630852cd8d60e31b83528a60048401525af180156105c0576105a7575b505060020361050857600b54604051632769c3d960e11b81526001600160a01b03909116906020818061046087898860048501611a22565b038189865af19081156104fd5786916104ba575b5060409061049c5f516020611d7e5f395f51905f529391965b83519788526020880190611879565b6001600160a01b031694a4805f516020611d5e5f395f51905f525d80f35b90506020813d6020116104f5575b816104d5602093836116fb565b810103126104f157515f516020611d7e5f395f51905f52610474565b5f80fd5b3d91506104c8565b6040513d88823e3d90fd5b600c54604051632769c3d960e11b81526001600160a01b03909116906020818061053787898860048501611a22565b038189865af19081156104fd578691610568575b5060409061049c5f516020611d7e5f395f51905f5293919661048d565b90506020813d60201161059f575b81610583602093836116fb565b810103126104f157515f516020611d7e5f395f51905f5261054b565b3d9150610576565b816105b1916116fb565b6105bc57845f610428565b8480fd5b6040513d84823e3d90fd5b5080fd5b6105f291935060203d6020116105f9575b6105ea81836116fb565b81019061171d565b915f6103c8565b503d6105e0565b6040513d86823e3d90fd5b6328f3628d60e11b8352600483fd5b63165f280160e31b8352600483fd5b634e487b7160e01b83526021600452602483fd5b50346102285760203660031901126102285760043561065a611810565b80600e557f20a0fbc0ee4de1a1cffa4583f49c511e0b01c5c5188dddcaadd9f7929bc981948280a280f35b5034610228576020366003190112610228576004356001600160a01b038116908190036105cb576106b4611810565b6040516301ffc9a760e01b8152632769c3d960e11b6004820152602081602481855afa90811561076b57839161073c575b501561072d576020817f027226b2f8497438c514139ace63e9b9612ef31f0f0b01338458118937873c3c926001600160601b0360a01b600c541617600c55604051908152a180f35b63233b87f160e01b8252600482fd5b61075e915060203d602011610764575b61075681836116fb565b810190611861565b5f6106e5565b503d61074c565b6040513d85823e3d90fd5b5034610228576020366003190112610228576004356107936119ed565b61079c816117da565b6001600160a01b038116338190036109485750818352600a6020526040832054600f54601e1c6203fffc61fffc82169116810361093457810180911161092057600181018091116109205780431061090a5750818352600560205260ff6040842054166005811080156108f6576002821480156108ec575b6108c15750508161082491611a4c565b60035482906001600160a01b0316803b156105cb57604051632142170760e11b8152306004820152336024820152604481018490529082908290606490829084905af180156105c0576108ac575b5050337fa5b864cfff4e834711fabe4617d4cc25810f0005a6980851a0cee1ca88643cfb8380a3805f516020611d5e5f395f51905f525d80f35b816108b6916116fb565b6105cb57815f610872565b6329e4093f60e11b85526024918591156108da57600452fd5b50634e487b7160e01b81526021600452fd5b5060038214610814565b634e487b7160e01b85526021600452602485fd5b636770c4f760e11b845260045243602452604483fd5b634e487b7160e01b84526011600452602484fd5b634e487b7160e01b85526011600452602485fd5b631306f3e960e21b8452600452602483fd5b503461022857602036600319011261022857602061097960043561179b565b61098660405180926116be565bf35b50346102285760203660031901126102285760043561ffff8116908181036109f6576109b2611810565b65ffff00000000600f549160201b169065ffff00000000191617600f557f06c2d6a3a00a473e93b39165178ac616db46635f99f8022674ed0b0a4aee23898280a280f35b8280fd5b503461022857602036600319011261022857600435610a17611810565b80600d557f5a48e3bcf1fa980d54acd33041749188e47c12e900814aa9477301808ac723518280a280f35b503461022857604036600319011261022857602435906004821015610228576020610a6f83600435611757565b604051908152f35b50346102285780600319360112610228576002546040516001600160a01b039091168152602090f35b503461022857602036600319011261022857610aba6116a8565b81546001600160a01b03163381141580610b5c575b610b3457506001600160a01b03168015610b25576020817fd1a6a14209a385a964d036e404cb5cfb71f4000cdb03c9366292430787261be6926001600160601b0360a01b6002541617600255604051908152a180f35b63d92e233d60e01b8252600482fd5b60025463061db9c160e01b8452336004526024919091526001600160a01b0316604452606482fd5b506002546001600160a01b0316331415610acf565b5034610228578060031936011261022857546040516001600160a01b039091168152602090f35b5034610228578060031936011261022857610bb1611810565b610bb96119cf565b6002805460ff60a01b1916600160a01b1790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602090a180f35b50346102285780600319360112610228576001546001600160a01b0381163303610c685781546001600160a01b031980821633908117855592166001556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606490fd5b50346104f15760203660031901126104f157600435610cc36119cf565b610ccb6119ed565b6003546040516331a9108f60e11b8152600481018390526001600160a01b0390911690602081602481855afa908115611226575f9161127c575b506001600160a01b0316338190036112665750604051636e9dafcd60e11b81526004810183905290602082602481845afa918215611226575f92611245575b5060048210156112315760038203610e6f575060035490919083906001600160a01b0316803b156105cb57818091602460405180948193630852cd8d60e31b83528860048401525af180156105c057610e5a575b5050600b54604051632769c3d960e11b81526001600160a01b039091169260208280610dc986853360048501611a22565b038188885af1918215610e4f578592610e19575b50610df2906040519283526020830190611879565b5f516020611d7e5f395f51905f5260403392a45b805f516020611d5e5f395f51905f525d80f35b9091506020813d602011610e47575b81610e35602093836116fb565b810103126104f1575190610df2610ddd565b3d9150610e28565b6040513d87823e3d90fd5b81610e64916116fb565b6109f657825f610d98565b5f838152600a602090815260408083204390556006825280832080546001600160a01b03191633908117909155835260079091529020610eb0908490611c31565b50803b156104f1576040516323b872dd60e01b8152336004820152306024820152604481018490525f8160648183865af180156112265761120a575b509060206024926040519384809263c7c3809160e01b82528760048301525afa9182156106005784926111d6575b508184526008602052610f308160408620611735565b54156111a3575b600254600e54600d54600f546040519095936001600160a01b031692906020810167ffffffffffffffff81118282101761118f57604052600181526040519063125fa26760e31b6020830152511515602482015260248152610f9a6044826116fb565b6040519160c0830183811067ffffffffffffffff82111761117b5760405282526020820192835260408201968060201c61ffff168852606083019063ffffffff16815260808301906001825260a084019283526040519889958695634d8e1c2f60e11b875260048701602090525160248701525160448601525161ffff1660648501525163ffffffff1660848401525163ffffffff1660a48301525160c4820160c09052805180918160e485015260200161010484015e888183016101040152601f1990601f01168101036101040181885a94602095f1928315610e4f578593611146575b5060405160039161108f826116cb565b3382526020820190815260408201908682526110af60608401958661174b565b858852600960205260408820925183546001600160a01b0319166001600160a01b0391909116178355516001830155516002820155915191019060048110156108f65760ff80198354169116179055818352600560205260408320600160ff198254161790556040519081527fe4113aff1b3b99ecd74e468f6c0bdd30f06df1f1bd15c7e99656d9426554ced760203392a3610e06565b9092506020813d602011611173575b81611162602093836116fb565b810103126104f1575191600361107f565b3d9150611155565b634e487b7160e01b8b52604160045260248bfd5b634e487b7160e01b8a52604160045260248afd5b80845260046020526040842054600181018091116109345782855260086020526111d08260408720611735565b55610f37565b9091506020813d602011611202575b816111f2602093836116fb565b810103126104f15751905f610f1a565b3d91506111e5565b6024929194505f61121a916116fb565b60205f94919250610eec565b6040513d5f823e3d90fd5b634e487b7160e01b5f52602160045260245ffd5b61125f91925060203d6020116105f9576105ea81836116fb565b905f610d44565b632c3bd91560e21b5f523360045260245260445ffd5b90506020813d6020116112b6575b81611297602093836116fb565b810103126104f157516001600160a01b03811681036104f1575f610d05565b3d915061128a565b346104f1575f3660031901126104f157602060ff60025460a01c166040519015158152f35b346104f15760203660031901126104f15760043563ffffffff81168091036104f15761130d611810565b8063ffffffff19600f541617600f557fd36c67e14aef21a2a06b18e3e20946690ed142971da6f7760bb0e336e4f405915f80a2005b346104f1575f3660031901126104f157602061ffff600f54821c16604051908152f35b346104f1575f3660031901126104f15761137d611810565b60025460ff8160a01c16156113c45760ff60a01b19166002556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a1005b638dfc202b60e01b5f5260045ffd5b346104f15760203660031901126104f1576001600160a01b036113f46116a8565b165f526007602052602060405f2054604051908152f35b346104f1575f3660031901126104f157602063ffffffff600f5416604051908152f35b346104f15760203660031901126104f1576004356001600160a01b038116908190036104f15761145c611810565b6040516301ffc9a760e01b8152635d0a165d60e11b6004820152602081602481855afa908115611226575f916114e3575b50156114d4576020817fec23afdbcd86322300b97874ea136efeb1ee4820516bec8bb837205b93c9a876926001600160601b0360a01b6003541617600355604051908152a1005b630364d2cf60e01b5f5260045ffd5b6114fc915060203d6020116107645761075681836116fb565b8261148d565b346104f15760403660031901126104f15760243567ffffffffffffffff81116104f157366023820112156104f157806004013567ffffffffffffffff81116104f1573660248260051b840101116104f1576002546001600160a01b031633819003611579575060246115779201600435611886565b005b63073e64fd60e21b5f523360045260245260445ffd5b346104f1575f3660031901126104f1576003546040516001600160a01b039091168152602090f35b346104f1575f3660031901126104f1576020600e54604051908152f35b346104f15760203660031901126104f1576004356001600160a01b038116908190036104f157611602611810565b6040516301ffc9a760e01b8152632769c3d960e11b6004820152602081602481855afa908115611226575f91611689575b501561167a576020817ffdf24252aba5b6859ae3b5bb59bc098152760debec253064163c1a00f47f66ce926001600160601b0360a01b600b541617600b55604051908152a1005b63233b87f160e01b5f5260045ffd5b6116a2915060203d6020116107645761075681836116fb565b82611633565b600435906001600160a01b03821682036104f157565b9060058210156112315752565b6080810190811067ffffffffffffffff8211176116e757604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176116e757604052565b908160209103126104f1575160048110156104f15790565b906004811015611231575f5260205260405f2090565b60048210156112315752565b9061176c915f52600860205260405f20611735565b548015611795575f1981019081116117815790565b634e487b7160e01b5f52601160045260245ffd5b505f1990565b805f52600560205260ff60405f205416600581101561123157156117cb575f52600560205260ff60405f20541690565b630f8d5de760e41b5f5260045ffd5b5f818152600660205260409020546001600160a01b0316156117cb575f908152600660205260409020546001600160a01b031690565b5f546001600160a01b0316330361182357565b60405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606490fd5b908160209103126104f1575180151581036104f15790565b9060048210156112315752565b919091805f52600960205260405f20906040516118a2816116cb565b60018060a01b0383541681526118e76001840154946020830195865260ff600360028701549660408601978852015416966118e160608501988961174b565b83611aa0565b9160058310156112315782156119c75783515f52600560205260405f2060ff1981541660ff851617905560028314611994575b5f5260096020525f600360408220828155826001820155826002820155015560018060a01b03905116915192519351906004821015611231577f074f8b12e76193b40d7423f88dd96efd74359cb59bc05f28c4635aa44394b3699161199160409261198784518094611879565b60208301906116be565ba4565b84515f52600860205260405f2086516004811015611231576119b591611735565b80548015611781575f1901905561191a565b505050505050565b60ff60025460a01c166119de57565b63d93c066560e01b5f5260045ffd5b5f516020611d5e5f395f51905f525c611a135760015f516020611d5e5f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b6001600160a01b0390911681526060810193929160409190611a48906020830190611879565b0152565b60018060a01b03165f526007602052611a688160405f20611ca0565b505f90815260066020908152604080832080546001600160a01b031916905560058252808320805460ff19169055600a909152812055565b909160408201515f52600560205260ff60405f2054166005811015611231578015611c00575f1901611bac5715611bf95780516001600160a01b031615611bf95760018060a01b036003541690602081016060815192019182516004811015611231576044602092611b2d9660405197889485936398e77ed560e01b855260048501526024840190611879565b5afa928315611226575f93611bc5575b505f1983148015611bbd575b611bb457515f52600860205260405f209051600481101561123157611b6d91611735565b54918215611bac578115611b9857350660018101809111611781571015611b9357600290565b600390565b634e487b7160e01b5f52601260045260245ffd5b505050600490565b50505050600490565b508215611b49565b9092506020813d602011611bf1575b81611be1602093836116fb565b810103126104f15751915f611b3d565b3d9150611bd4565b5050600490565b505050505f90565b8054821015611c1d575f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b6001810190825f528160205260405f2054155f14611c99578054680100000000000000008110156116e757611c86611c70826001879401855584611c08565b819391549060031b91821b915f19901b19161790565b905554915f5260205260405f2055600190565b5050505f90565b906001820191815f528260205260405f20548015155f14611c00575f1981018181116117815782545f1981019190821161178157818103611d28575b50505080548015611d14575f190190611cf58282611c08565b8154905f199060031b1b19169055555f526020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b611d48611d38611c709386611c08565b90549060031b1c92839286611c08565b90555f528360205260405f20555f8080611cdc56fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f007168a528bf5efafe5f1780914af9400c1dbed518e4c12acad6f4e3a6bb470c77a264697066735822122055c1c54429b121af1e757813a503ea7e8fe15104b91a1dee30319eb006aa742964736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in POL
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.