Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
15846009 | 636 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0x67d0384db3893058bb67fef81a29957c91536f62
Contract Name:
AdventureV1
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
No with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import "./interfaces/IAdventureEventProxyV1.sol"; import "./interfaces/IAdventureV1.sol"; import "../interfaces/IWithdrawableV1.sol"; import "../interfaces/IRewardsReceiver.sol"; /// @dev Not super efficient yet because implementation is redeployed every time, could be /// improved by using a proxy contract. Ok on Nova for now. contract AdventureV1 is IAdventureV1, IERC721Receiver, IWithdrawableV1 { struct Participant { address owner; uint256 enteredAt; } /**************************** * VARIABLES * ***************************/ IAdventureV1.AdventureConfig public config; IERC721 public monsters; IAdventureEventProxyV1 public eventProxy; // Contract that emits events (makes processing from outside easier) IRewardsReceiver public rewardsReceiver; // Contract that receives the rewards (or splits to more contracts) IERC721 public monsterCollection; // Operator that is allowed to transfer monsters from this contract address public configurator; // Address that can update the config of the adventure uint256 public finalizationRandomness; // Will decide on adventure rewards uint256[] public participants; // Participants of the adventure (monster tokenIds) uint256 public lastMonsterJoined; // Flag to indicate if the last monster has joined the adventure uint256 public firstMonsterWithdrawn; // Flag to indicate if the first monster has withdrawn /**************************** * MAPPINGS * ***************************/ mapping(uint256 => Participant) public participantsByTokenId; // Mapping of tokenId to participant /**************************** * ERRORS * ***************************/ error AdventureAlreadyStarted(); error AdventureInTheFuture(); error AdventureNotOver(); error AlreadyInitialized(); error AlreadyParticipated(); error InvalidPmonAmount(); error Unauthorized(); error Unexpected(); /**************************** * MODIFIERS * ***************************/ modifier onlyConfigurator() { if (msg.sender != configurator) { revert Unauthorized(); } _; } function initialize( address _pmon, address _monsters, address _rewardsReceiver, address _eventProxy, address _configurator, IAdventureV1.AdventureConfig memory _config ) external override { if (address(monsterCollection) != address(0)) { revert AlreadyInitialized(); } monsters = IERC721(_monsters); rewardsReceiver = IRewardsReceiver(_rewardsReceiver); eventProxy = IAdventureEventProxyV1(_eventProxy); configurator = _configurator; config = _config; IERC20(_pmon).approve(address(_rewardsReceiver), type(uint256).max); } // Function to receive ERC721 tokens function onERC721Received( address, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4) { // we only accept ERC721 tokens from the monster collection if (msg.sender != address(monsters)) { revert Unauthorized(); } // check if we really have the token if (monsters.ownerOf(tokenId) != address(this)) { revert Unexpected(); } if (block.number >= config.startBlock) { if (lastMonsterJoined != 0) { revert AdventureAlreadyStarted(); } uint256 pseudoRandomness = uint256( keccak256( abi.encodePacked(blockhash(block.number - 1), address(this)) ) ); uint256 adventureDuration = (pseudoRandomness % (config.durationUpperBound - config.durationLowerBound)) + config.durationLowerBound; uint256 endBlock = config.startBlock + adventureDuration; config.endBlock = endBlock; eventProxy.emitEvent( IAdventureEventProxyV1.AdventureEventType.AdventureEndDateSet, abi.encode(endBlock), "", "", "" ); lastMonsterJoined = tokenId; } /// @dev we could also verify the exact pmon balance in this contract but we rely on the /// functionality of the operator for now uint256 pmonAmount = abi.decode(data, (uint256)); if (pmonAmount != config.pmonFee) { revert InvalidPmonAmount(); } /// @dev The reward receiver will potentially also crash if no actual pmon are sent rewardsReceiver.addRewards(pmonAmount); if (participantsByTokenId[tokenId].owner != address(0)) { revert AlreadyParticipated(); } participantsByTokenId[tokenId] = Participant(from, block.timestamp); participants.push(tokenId); if (participants.length != monsters.balanceOf(address(this))) { revert Unexpected(); } return this.onERC721Received.selector; } function withdrawMonster(address to, uint256 tokenId) external override { if (config.endBlock == 0) { revert AdventureInTheFuture(); } if (block.number < config.endBlock) { revert AdventureNotOver(); } // check the NFT actually belongs to the user if (participantsByTokenId[tokenId].owner != to) { revert Unauthorized(); } if (firstMonsterWithdrawn == 0) { firstMonsterWithdrawn = tokenId; eventProxy.emitEvent( IAdventureEventProxyV1 .AdventureEventType .AdventureFinalizationRequested, abi.encode(tokenId), "", "", "" ); } else { monsters.transferFrom(address(this), to, tokenId); } } /**************************** * CONFIGURATOR FUNCTIONS * ***************************/ function finalize(uint256 randomness) external override onlyConfigurator { if (config.endBlock == 0 || firstMonsterWithdrawn == 0) { /// @dev endBlock should be set first revert Unexpected(); } finalizationRandomness = randomness; monsters.transferFrom( address(this), participantsByTokenId[firstMonsterWithdrawn].owner, firstMonsterWithdrawn ); } function setEndBlock(uint256 endBlock) external override onlyConfigurator { config.endBlock = endBlock; } /**************************** * PUBLIC VIEW FUNCTIONS * ***************************/ function getConfig() external view override returns (IAdventureV1.AdventureConfig memory) { return config; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 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 ERC721 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 ERC721 * 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 caller. * * 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 v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; interface IAdventureEventProxyV1 { enum AdventureEventType { AdventureRevealRequested, AdventureFinalizationRequested, AdventureEndDateSet, AdventureRevealed } function emitEvent( AdventureEventType eventType, bytes calldata arg2, bytes calldata arg3, bytes calldata arg4, bytes calldata arg5 ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; interface IAdventureV1 { struct AdventureConfig { uint256 gatheringBlock; uint256 startBlock; uint256 endBlock; uint256 pmonFee; uint256 durationLowerBound; uint256 durationUpperBound; } event MonsterReceived(uint256 indexed adventureId, uint256 indexed tokenId); function getConfig() external view returns (AdventureConfig memory); function initialize( address _monsters, address _pmon, address _rewardsReceiver, address _eventProxy, address _configurator, IAdventureV1.AdventureConfig memory _config ) external; function setEndBlock(uint256 endBlock) external; function finalize(uint256 randomness) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; interface IRewardsReceiver { function addRewards(uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; /// @dev Interface mainly for adventures, battles and other monster gamification contracts. interface IWithdrawableV1 { function withdrawMonster(address to, uint256 tokenId) external; }
{ "optimizer": { "enabled": false, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract ABI
API[{"inputs":[],"name":"AdventureAlreadyStarted","type":"error"},{"inputs":[],"name":"AdventureInTheFuture","type":"error"},{"inputs":[],"name":"AdventureNotOver","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"AlreadyParticipated","type":"error"},{"inputs":[],"name":"InvalidPmonAmount","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Unexpected","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"adventureId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"MonsterReceived","type":"event"},{"inputs":[],"name":"config","outputs":[{"internalType":"uint256","name":"gatheringBlock","type":"uint256"},{"internalType":"uint256","name":"startBlock","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"},{"internalType":"uint256","name":"pmonFee","type":"uint256"},{"internalType":"uint256","name":"durationLowerBound","type":"uint256"},{"internalType":"uint256","name":"durationUpperBound","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configurator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eventProxy","outputs":[{"internalType":"contract IAdventureEventProxyV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalizationRandomness","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"randomness","type":"uint256"}],"name":"finalize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"firstMonsterWithdrawn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConfig","outputs":[{"components":[{"internalType":"uint256","name":"gatheringBlock","type":"uint256"},{"internalType":"uint256","name":"startBlock","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"},{"internalType":"uint256","name":"pmonFee","type":"uint256"},{"internalType":"uint256","name":"durationLowerBound","type":"uint256"},{"internalType":"uint256","name":"durationUpperBound","type":"uint256"}],"internalType":"struct IAdventureV1.AdventureConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pmon","type":"address"},{"internalType":"address","name":"_monsters","type":"address"},{"internalType":"address","name":"_rewardsReceiver","type":"address"},{"internalType":"address","name":"_eventProxy","type":"address"},{"internalType":"address","name":"_configurator","type":"address"},{"components":[{"internalType":"uint256","name":"gatheringBlock","type":"uint256"},{"internalType":"uint256","name":"startBlock","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"},{"internalType":"uint256","name":"pmonFee","type":"uint256"},{"internalType":"uint256","name":"durationLowerBound","type":"uint256"},{"internalType":"uint256","name":"durationUpperBound","type":"uint256"}],"internalType":"struct IAdventureV1.AdventureConfig","name":"_config","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastMonsterJoined","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"monsterCollection","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"monsters","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"participants","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"participantsByTokenId","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"enteredAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsReceiver","outputs":[{"internalType":"contract IRewardsReceiver","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"endBlock","type":"uint256"}],"name":"setEndBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawMonster","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.