MIJI DApp Server Docs

Smart Contract Requirements (CONTRACT.md)

1. 개요

본 문서는 PLAN.md (v8) 및 DEV_README.md (v8)에서 정의한 NFT DApp 서버의 핵심 기능을 지원하기 위해 contracts/ 워크스페이스에 구현되어야 하는 스마트 컨트랙트의 요구사항을 정의합니다.

서버는 C-130 API [cite: 160, 186]를 통해 여기에 정의된 컨트랙트 템플릿(ABI/Bytecode)을 동적으로 배포합니다.

2. 대상 컨트랙트 파일

contracts/contracts/ 디렉토리에는 최소한 다음 템플릿 컨트랙트가 존재해야 합니다.

  1. MijiERC721.sol (standard)
  2. MijiERC1155.sol (standard)
  3. MijiCertificate721.sol (certificate: 전송 불가, 메타데이터 불변)
  4. MijiCertificate1155.sol (certificate: 전송 불가, 메타데이터 불변)

3. 핵심 요구사항 및 검증 (Checklist)

아래 표는 MijiERC721.solMijiERC1155.sol 양쪽 모두에 공통으로 적용되어야 하는 기능 요구사항입니다.

기능 요구사항 PLAN.md 연관 ID 기술적 구현 스펙 (OpenZeppelin) 검증 완료 (Y/N)
1. 표준 준수 C-100 ERC721.sol 또는 ERC1155.sol 표준을 import하고 상속받아야 합니다.  
2. 소유권 이전 기능 C-150 [cite: 160, 186] Ownable.sol (또는 Ownable2Step.sol)을 import하고 상속(is Ownable)받아야 합니다.  
    constructor()에서 _transferOwnership(msg.sender)을 호출하여 배포자(서버 지갑)를 초기 소유자로 설정해야 합니다.  
    transferOwnership() 함수가 외부에 노출되어야 합니다. (C-150 API가 향후 호출할 대상)  
3. 가스 대납 민팅 (Airdrop) T-110 [cite: 164, 186] standard: safeMint(721) / mint(1155); certificate: mintCertificate 전용.  
    (핵심) 모든 민팅 함수는 onlyOwner 보호.  
4. 로열티 (2차 수수료) C-100 [cite: 160, 186] standard: ERC2981 적용, 기본 로열티 설정 가능. certificate: 로열티 미사용.  
5. 메타데이터 URI M-130 [cite: 162, 186] standard: setBaseURI/setURI 제공(onlyOwner). certificate: baseURI setter 제거/비활성, 민팅 시 tokenURI 고정.  
6. (선택) 가스리스 확장성 (T-110) [cite: 164, 186] ERC2771Context 권장(메타트랜잭션/대납 지원).  

타입 구분 (배포 시 선택)

타입 구분 (배포 시 선택)

4. 구현 상세 및 검증 방법

4-1. MijiERC721.sol (예시 템플릿)

AI(Cursor) 또는 개발자는 아래 구조를 참조하여 컨트랙트를 작성해야 합니다.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// 1. 표준, 2. 소유권, 4. 로열티, 6. 가스리스(선택)
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/royalty/ERC2981.sol";
import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; // (6번)

// (ERC1155의 경우 ERC1155.sol을 상속)
contract MijiERC721 is ERC721, Ownable, ERC2981, ERC2771Context {
    
    // (5번) Base URI 관리
    string private _baseTokenURI;

    // (Constructor) C-130 API가 DTO로 전달할 값들
    constructor(
        string memory name,         // DTO.name
        string memory symbol,       // DTO.symbol
        address initialOwner,      // 서버 지갑 주소
        address royaltyReceiver,   // 로열티 수신자
        uint96 feeNumerator,       // 로열티 (e.g., 500 = 5%)
        address trustedForwarder   // (6번) EIP-2771용
    ) 
    ERC721(name, symbol)
    Ownable(initialOwner) // (2번) 소유권 이전
    ERC2771Context(trustedForwarder) // (6번)
    {
        // (4번) 로열티 설정
        _setDefaultRoyalty(royaltyReceiver, feeNumerator);
    }

    // (3번) 가스 대납 민팅 함수 (T-110)
    // (서버 지갑만 이 함수를 호출할 수 있음)
    function safeMint(address to, uint256 tokenId) public onlyOwner {
        _safeMint(to, tokenId);
    }

    // (5번) Base URI 설정 함수 (M-130 서빙 주소 설정용)
    function setBaseURI(string memory newBaseURI) public onlyOwner {
        _baseTokenURI = newBaseURI;
    }

    // --- OpenZeppelin Overrides ---

    function _baseURI() internal view override returns (string memory) {
        return _baseTokenURI;
    }

    // (4번) 로열티 표준 지원
    function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC2981) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    // (6번) EIP-2771 적용 (권장)
    function _msgSender() internal view override(Context, ERC2771Context) returns (address) {
        return ERC2771Context._msgSender();
    }
}

4-2. MijiERC1155.sol

ERC-1155도 위 MijiERC721.sol과 동일한 원칙(Ownable, ERC2981, mint 함수의 onlyOwner 보호)을 따라야 합니다.