Summer & Summer Moments

年
2022
作家
Toshi / wildmouse
媒体
Ethereum mainnet、ERC-998 と ERC-721、Solidity
契約
0x363d730ed06f00d0b1cd014212093eb560befce9 — Etherscan で見る

Summer

2021 年には膨大な数の NFT コレクティブルが生まれ、それは「NFT Summer」と呼ばれる動きにまで広がった。私たちは、その NFT Summer そのものを一つの NFT として表すこの作品をつくった。

この NFT は 576(24 × 24)の層でできていて、2021 年に台頭した NFT コレクティブルで CryptoPunks のモザイクを組み上げている。見る人は NFT を一つ(層を一枚)受け取り、その NFT Summer の一瞬を、個別の NFT として持つことができる。

一枚の絵を組む 576(24 × 24)の層を、奥行きに広げた図

一枚の絵を組む 576(24 × 24)の層を、奥行きに広げた図

この NFT は二つのトークン規格、ERC-998(Summer)と ERC-721(Summer Moments)で組まれている。Summer Moments は Etherscan から受け取れる。

注: Etherscan の getMoment 関数で NFT を受け取れる。

Etherscan の Write Contract で getMoment(引数なし)を呼ぶ画面(2022-04-19)

Etherscan の Write Contract で getMoment(引数なし)を呼ぶ画面(2022-04-19)

NFT Overview

Summer

Network: Ethereum Mainnet
Standard: ERC998
Contract Address: 0x363d730ed06f00d0b1cd014212093eb560befce9
Name: Summer
Symbol: SUMMER
Max Total Supply: 1
Contract Owner: Owner of nft-summer.eth

Summer Moments

Network: Ethereum Mainnet
Standard: ERC721
Contract Address: 0x794a6eC563C4e58D077ECCaAaF5A121bc1baDd1b
Name: Summer Moments
Symbol: MOMENT
Price per token: free
Max Total Supply: 576
Contract Owner: Summer NFT Contract

Contract

Summer

// SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./SummerMoments.sol";
import "../../../interface/ERC998/iERC998ERC721TopDown.sol";

contract Summer is ERC721, iERC998ERC721TopDown {

    using Strings for uint256;

    SummerMoments public moments;
    uint16 public currentMomentId = 1;
    uint256 public constant SUMMER_TOKEN_ID = 0;

    constructor(address moments_) ERC721("Summer", "SUMMER") {
        _mint(owner(), SUMMER_TOKEN_ID);
        moments = SummerMoments(moments_);
    }

    function getMoment() external {
        require(moments.balanceOf(address(this)) > 0, "Summer: No moments is owned");
        Summer(address(this)).safeTransferChild(SUMMER_TOKEN_ID, msg.sender, address(moments), currentMomentId++);
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(moments.totalSupply() > 0, "Summer: Moments are not minted yet");
        require(_exists(tokenId), "Summer: URI query for nonexistent token");

        uint256 id = currentMomentId <= moments.totalSupply() ? currentMomentId : SUMMER_TOKEN_ID;
        return string(abi.encodePacked("https://ipfs.io/ipfs/QmSnsb5vUhhXWbPjgAFyGQUpTrGv2H6HixotzJxTgmBxB7/", id.toString(), ".json"));
    }
    
    function owner() public view virtual returns(address) {
        return IERC721(0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85).ownerOf(46115204191989145517182997463894886476175430296292312167639913498843229671398);
    }

    ///////////////////////////////////
    /// ERC998ERC721TopDown implementations
    ///////////////////////////////////
    bytes32 constant ERC998_MAGIC_VALUE = 0x00000000000000000000000000000000000000000000000000000000cd740db5;

    function rootOwnerOf(uint256 _tokenId) public view override returns (bytes32 rootOwner) {
        return rootOwnerOfChild(address(0), _tokenId);
    }

    function rootOwnerOfChild(address _childContract, uint256 _childTokenId) public view override returns (bytes32 rootOwner) {
        address rootOwnerAddress;
        if (_childContract != address(0)) {
            (rootOwner, _childTokenId) = ownerOfChild(_childContract, _childTokenId);
            rootOwnerAddress = address(uint160(uint256(rootOwner)));
        } else {
            rootOwnerAddress = ownerOf(_childTokenId);
        }
        while (rootOwnerAddress == address(this)) {
            (rootOwner, _childTokenId) = ownerOfChild(rootOwnerAddress, _childTokenId);
            rootOwnerAddress = address(uint160(uint256(rootOwner)));
        }
        (bool success, bytes memory data) = rootOwnerAddress.staticcall(abi.encodeWithSelector(0xed81cdda, address(this), _childTokenId));
        if (data.length != 0) { rootOwner = abi.decode(data, (bytes32)); }
        if(success && rootOwner >> 224 == ERC998_MAGIC_VALUE) { 
            return rootOwner; 
        } else { 
            return ERC998_MAGIC_VALUE << 224 | bytes32(uint256(uint160(rootOwnerAddress))); 
        }
    } 

    function ownerOfChild(address _childContract, uint256 _childTokenId) public view override returns (bytes32 parentTokenOwner, uint256 parentTokenId) {
        require(_childContract == address(moments), "Summer: Non moment address is given");
        address childOwner = moments.ownerOf(_childTokenId);
        require(childOwner == address(this) && _childTokenId >= currentMomentId, "Summer: Child token is not owned by token of this contract");
        return (ERC998_MAGIC_VALUE << 224 | bytes32(uint256(uint160(ownerOf(SUMMER_TOKEN_ID)))), SUMMER_TOKEN_ID);
    }
    
    function onERC721Received(address _operator, address _from, uint256 _childTokenId, bytes memory _data) external override returns(bytes4) { revert("disabled"); }
    function transferChild(uint256 _fromTokenId,address _to, address _childContract, uint256 _childTokenId) external override { revert("disabled"); }

    function safeTransferChild(uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId) external override { 
        require(msg.sender == address(this), "Summer: Only contract itself is able to transfer");
        require(_fromTokenId == SUMMER_TOKEN_ID, "Summer: Invalid token id is given");
        address rootOwner = address(uint160(uint256(rootOwnerOf(SUMMER_TOKEN_ID))));
        require(msg.sender == rootOwner || isApprovedForAll(rootOwner, msg.sender) || getApproved(SUMMER_TOKEN_ID) == msg.sender, "Summer: not an owner nor not approved");
        moments.safeTransferFrom(address(this), _to, _childTokenId, "");
        emit TransferChild(_fromTokenId, _to, _childContract, _childTokenId);
    }
    
    function safeTransferChild(uint256 _fromTokenId,address _to, address _childContract, uint256 _childTokenId, bytes memory _data) external override { revert("disabled"); }
    function transferChildToParent(uint256 _fromTokenId, address _toContract, uint256 _toTokenId, address _childContract, uint256 _childTokenId, bytes memory _data) external override { revert("disabled"); }
    function getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId) external override { revert("disabled"); }

    function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
        return
            interfaceId == type(iERC998ERC721TopDown).interfaceId ||
            interfaceId == type(IERC721Receiver).interfaceId ||
            super.supportsInterface(interfaceId);
    }

}

Summer Moments

//SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

import "@openzeppelin/contracts/utils/Strings.sol";
import "../../../common/ERC721/ERC721A.sol";

contract SummerMoments is ERC721A {

    using Strings for uint256;

    address public owner;

    constructor() ERC721A("Summer Moments", "MOMENT") { owner = msg.sender; } 

    function mintForSummer(address summer_) external {
        require(msg.sender == owner, "SummerMoments: Only owner is able to call");
        require(totalSupply() == 0, "SummerMoments: Minted already");
        owner = summer_;
        _mint(summer_, 576, "", false);
    }

    function _startTokenId() internal pure override returns (uint256) { return 1; }
 
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "SummerMoments: URI query for nonexistent token");
        require(ownerOf(tokenId) != owner, "SummerMoments: Summer holds this token");

        return string(abi.encodePacked("https://ipfs.io/ipfs/QmRcQfiQ3vbPDBdEd8a9oD6KEY14ev71H6JePPpBjH2mKR/", tokenId.toString(), ".json"));
    }

}

Read / Write

Ethereum mainnet の契約を直接読み、自分のウォレットから呼び出せる。読むだけならウォレットは要らない。

Summer0x363d730ed06f00d0b1CD014212093EB560BeFcE9

Etherscan の Read ContractEtherscan の Write Contract

Summer Moments0x794a6eC563C4e58D077ECCaAaF5A121bc1baDd1b

Etherscan の Read ContractEtherscan の Write Contract

Exhibitions

Links

作品一覧へ戻る