ETH Price: $3,469.94 (+2.80%)

Transaction Decoder

Block:
16074899 at Nov-29-2022 09:57:59 AM +UTC
Transaction Fee:
0.00106756296506026 ETH $3.70
Gas Used:
90,305 Gas / 11.821748132 Gwei

Emitted Events:

435 Proxy.0x5a3358a3d27a5373c0df2604662088d37894d56b7cfd27f315770440f4e0d919( 0x5a3358a3d27a5373c0df2604662088d37894d56b7cfd27f315770440f4e0d919, 0x0000000000000000000000005ae31d9e1668b17b8f5861893ddfefda1a38a212, 0x0000000000000000000000000000000000000000000000000000000000f54893, 0000000000000000000000000000000000000000000000000000000000008c0e, 0000000000000000000000000000000000000000000000000000000000000049 )
436 Proxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000005ae31d9e1668b17b8f5861893ddfefda1a38a212, 0x0000000000000000000000000000000000000000000000000000000000008c0e )

Account State Difference:

  Address   Before After State Difference Code
0x5AE31d9E...a1A38a212
0.002331400045537959 Eth
Nonce: 5
0.001263837080477699 Eth
Nonce: 6
0.00106756296506026
(beaverbuild)
35.850643299444732288 Eth35.850778756944732288 Eth0.0001354575
0xc4a5025c...44DAd58Eb

Execution Trace

Proxy.CALL( )
  • Prometheans.DELEGATECALL( )
    File 1 of 2: Proxy
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.7;
    /// @dev Proxy for NFT Factory
    contract Proxy {
        // Storage for this proxy
        bytes32 private constant IMPLEMENTATION_SLOT = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
        bytes32 private constant ADMIN_SLOT          = bytes32(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103);
        constructor(address impl) {
            require(impl != address(0));
            _setSlotValue(IMPLEMENTATION_SLOT, bytes32(uint256(uint160(impl))));
            _setSlotValue(ADMIN_SLOT, bytes32(uint256(uint160(msg.sender))));
        }
        function setImplementation(address newImpl) public {
            require(msg.sender == _getAddress(ADMIN_SLOT));
            _setSlotValue(IMPLEMENTATION_SLOT, bytes32(uint256(uint160(newImpl))));
        }
        
        function setAdmin(address newAdmin) public {
            require(msg.sender == _getAddress(ADMIN_SLOT));
            _setSlotValue(ADMIN_SLOT, bytes32(uint256(uint160(newAdmin))));
        }
        
        function implementation() public view returns (address impl) {
            impl = address(uint160(uint256(_getSlotValue(IMPLEMENTATION_SLOT))));
        }
        function _getAddress(bytes32 key) internal view returns (address add) {
            add = address(uint160(uint256(_getSlotValue(key))));
        }
        function _getSlotValue(bytes32 slot_) internal view returns (bytes32 value_) {
            assembly {
                value_ := sload(slot_)
            }
        }
        function _setSlotValue(bytes32 slot_, bytes32 value_) internal {
            assembly {
                sstore(slot_, value_)
            }
        }
        /**
         * @dev Delegates the current call to `implementation`.
         *
         * This function does not return to its internall call site, it will return directly to the external caller.
         */
        function _delegate(address implementation__) internal virtual {
            assembly {
                // Copy msg.data. We take full control of memory in this inline assembly
                // block because it will not return to Solidity code. We overwrite the
                // Solidity scratch pad at memory position 0.
                calldatacopy(0, 0, calldatasize())
                // Call the implementation.
                // out and outsize are 0 because we don't know the size yet.
                let result := delegatecall(gas(), implementation__, 0, calldatasize(), 0, 0)
                // Copy the returned data.
                returndatacopy(0, 0, returndatasize())
                switch result
                // delegatecall returns 0 on error.
                case 0 {
                    revert(0, returndatasize())
                }
                default {
                    return(0, returndatasize())
                }
            }
        }
        receive() external payable {}
        /**
         * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
         * function in the contract matches the call data.
         */
        fallback() external payable virtual {
            _delegate(_getAddress(IMPLEMENTATION_SLOT));
        }
    }
    

    File 2 of 2: Prometheans
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity 0.8.7;
    contract Prometheans {
        bytes32 private constant ADMIN_SLOT = bytes32(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103);
        uint256 public DURATION; // = 75;
        uint256 public MATURITY; // = 21600; // 72 hours in block time (12 seconds)
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event Transfer(address indexed from, address indexed to, uint256 indexed id);
        event Approval(address indexed owner, address indexed spender, uint256 indexed id);
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        event Minted(address indexed owner, uint256 id, uint256 indexed blockNumber, uint256 index);
        /*//////////////////////////////////////////////////////////////
                          ERC721 BALANCE/OWNER STORAGE
        //////////////////////////////////////////////////////////////*/
        
        string public name;
        string public symbol;
        
        bool public open;
        uint256 public currentId;
        uint256 public lowestEmber;
        mapping(uint256 => TokenData) internal tokens;
        mapping(address => UserData)  internal users;
        mapping(uint256 => address)                  public getApproved;
        mapping(address => mapping(address => bool)) public isApprovedForAll;
        struct TokenData {
            address owner;
            uint48  mintingBlock;
            uint48  ember;
        }
        struct UserData {
            uint128 balance;
            uint128 minted;
        }
        /*//////////////////////////////////////////////////////////////
                        CONSTRUCTOR & MODIFIERS
        //////////////////////////////////////////////////////////////*/
        function initialize(string memory _name, string memory _symbol, uint256 duration_, uint256 maturity_) external {
            require(msg.sender == owner(), "NOT AUTHORIZED");
            name = _name;
            symbol = _symbol;
            DURATION = duration_;
            MATURITY = maturity_;
            lowestEmber = duration_;
        }
        modifier onlyMature(uint256 id) {
            require(lastMinted() >= tokens[id].mintingBlock + MATURITY, "NOT MATURED");
            _;
        }
        /*//////////////////////////////////////////////////////////////
                             VIEW FUNCTIONS
        //////////////////////////////////////////////////////////////*/
        function balanceOf(address owner_) public view virtual returns (uint256) {
            require(owner_ != address(0), "ZERO_ADDRESS");
            return users[owner_].balance;
        }
        function currentEmber() public view virtual returns (uint256) {
            uint256 elapsed = block.number - lastMinted();
            require(DURATION > elapsed, "GAME OVER");
            return DURATION - elapsed;
        }
        function emberOf(uint256 id) public view returns (uint256) {
            return tokens[id].ember;
        }
        function lastMinted() public view returns (uint256) {
            return currentId == 0 ? block.number : tokens[currentId].mintingBlock;
        }
        function minted(address owner_) public view returns (uint256) {
            return users[owner_].minted;
        }
        function owner() public view returns (address owner_) {
            return _getAddress(ADMIN_SLOT);
        }
        function ownerOf(uint256 id) public view returns (address owner_) {
            require((owner_ = tokens[id].owner) != address(0), "NOT_MINTED");
        }
        function tokenURI(uint256 id) public view virtual returns (string memory) {
            TokenData memory token = tokens[id];
            return string(abi.encodePacked("https://monks.prometheans.xyz/nft/", _toString(id), "/", _toString(token.ember), "/", _toString(token.mintingBlock + MATURITY)));
        }
        /*//////////////////////////////////////////////////////////////
                                  MINTING LOGIC
        //////////////////////////////////////////////////////////////*/
        function mint() external payable {  
            require(open,                                    "NOT OPEN");
            require(block.number <= lastMinted() + DURATION, "GAME OVER");
            uint256 index = currentEmber(); 
            if (index < lowestEmber) lowestEmber = index;
            emit Minted(msg.sender, ++currentId, block.number, index);
            _safeMint(msg.sender, currentId, index);
        }
        function mintTo(address destination_) external payable {    
            require(open,                                    "NOT OPEN");
            require(block.number <= lastMinted() + DURATION, "GAME OVER");
            uint256 index = currentEmber(); 
            if (index < lowestEmber) lowestEmber = index;
            emit Minted(destination_, ++currentId, block.number, index);
            _safeMint(destination_, currentId, index);
        }
        /*//////////////////////////////////////////////////////////////
                                  ADMIN LOGIC
        //////////////////////////////////////////////////////////////*/
        function withdraw(address destination, uint256 amount) external {
            require(msg.sender == owner(), "NOT_ADMIN");
            (bool success, ) = destination.call{value: amount}("");
            require(success, "TRANSFER FAILED");
        }
        function setOpen(bool open_) external {
            require(msg.sender == owner(), "NOT_ADMIN");
            open = open_;
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC721 LOGIC
        //////////////////////////////////////////////////////////////*/
        function approve(address spender, uint256 id) public virtual {
            address owner_ = tokens[id].owner;
            require(msg.sender == owner_ || isApprovedForAll[owner_][msg.sender], "NOT_AUTHORIZED");
            getApproved[id] = spender;
            emit Approval(owner_, spender, id);
        }
        function setApprovalForAll(address operator, bool approved) public virtual {
            isApprovedForAll[msg.sender][operator] = approved;
            emit ApprovalForAll(msg.sender, operator, approved);
        }
        function transferFrom(
            address from,
            address to,
            uint256 id
        ) public virtual onlyMature(id) {
            require(from == tokens[id].owner, "WRONG_FROM");
            require(to != address(0), "INVALID_RECIPIENT");
            require(
                msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
                "NOT_AUTHORIZED"
            );
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            unchecked {
                users[from].balance--;
                users[to].balance++;
            }
            tokens[id].owner = to;
            delete getApproved[id];
            emit Transfer(from, to, id);
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id
        ) public virtual onlyMature(id) {
            transferFrom(from, to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            bytes calldata data
        ) public virtual onlyMature(id) {
            transferFrom(from, to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC165 LOGIC
        //////////////////////////////////////////////////////////////*/
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return
                interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(address to, uint256 id, uint256 ember) internal virtual {
            require(to != address(0), "INVALID_RECIPIENT");
            require(tokens[id].owner == address(0), "ALREADY_MINTED");
            // Counter overflow is incredibly unrealistic.
            unchecked {
                users[to].minted++;
                users[to].balance++;
            }
            tokens[id].owner = to;
            tokens[id].mintingBlock = uint48(block.number);
            tokens[id].ember        = uint48(ember);
            emit Transfer(address(0), to, id);
        }
        function _burn(uint256 id) internal virtual {
            address owner_ = tokens[id].owner;
            require(owner_ != address(0), "NOT_MINTED");
            // Ownership check above ensures no underflow.
            unchecked {
                users[owner_].balance--;
            }
            tokens[id].owner = address(0);
            delete getApproved[id];
            emit Transfer(owner_, address(0), id);
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL SAFE MINT LOGIC
        //////////////////////////////////////////////////////////////*/
        function _safeMint(address to, uint256 id, uint256 index) internal virtual {
            _mint(to, id, index);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function _safeMint(
            address to,
            uint256 id,
            uint256 index,
            bytes memory data
        ) internal virtual {
            _mint(to, id, index);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL UTILITIES
        //////////////////////////////////////////////////////////////*/
        function _toString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        function _getAddress(bytes32 key) internal view returns (address add) {
            add = address(uint160(uint256(_getSlotValue(key))));
        }
        function _getSlotValue(bytes32 slot_) internal view returns (bytes32 value_) {
            assembly {
                value_ := sload(slot_)
            }
        }
    }
    /// @notice A generic interface for a contract which properly accepts ERC721 tokens.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
    abstract contract ERC721TokenReceiver {
        function onERC721Received(
            address,
            address,
            uint256,
            bytes calldata
        ) external virtual returns (bytes4) {
            return ERC721TokenReceiver.onERC721Received.selector;
        }
    }