본 문서는 PLAN.md (v8) 및 DEV_README.md (v8)에서 정의한 NFT DApp 서버의 핵심 기능을 지원하기 위해 contracts/ 워크스페이스에 구현되어야 하는 스마트 컨트랙트의 요구사항을 정의합니다.
서버는 C-130 API [cite: 160, 186]를 통해 여기에 정의된 컨트랙트 템플릿(ABI/Bytecode)을 동적으로 배포합니다.
contracts/contracts/ 디렉토리에는 최소한 다음 템플릿 컨트랙트가 존재해야 합니다.
MijiERC721.sol (standard)MijiERC1155.sol (standard)MijiCertificate721.sol (certificate: 전송 불가, 메타데이터 불변)MijiCertificate1155.sol (certificate: 전송 불가, 메타데이터 불변)아래 표는 MijiERC721.sol과 MijiERC1155.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 권장(메타트랜잭션/대납 지원). |
contractType = "standard": 전송 가능, 메타데이터 수정 가능(기존 템플릿).contractType = "certificate": 전송 불가(민팅/소각만), 메타데이터 불변(tokenURI + metadataHash), ERC2771 지원 유지.
mintCertificate(to, tokenId, tokenUri, metadataHash)mintCertificate(to, id, amount, tokenUri, metadataHash)metadataHash: SHA-256 bytes32, 0x00..00 금지. tokenUri 필수/불변. approve/transfer 전부 revert. burn 허용. getMetadataHash(tokenId) 제공.contractType = "standard": 전송 가능, 메타데이터 수정 가능(기존 템플릿).contractType = "certificate": 전송 불가(민팅/소각만), 메타데이터 불변(tokenURI + metadataHash), ERC2771 지원 유지.
mintCertificate(to, tokenId, tokenUri, metadataHash)mintCertificate(to, id, amount, tokenUri, metadataHash)metadataHash는 SHA-256 bytes32, 0x00..00 금지. tokenUri 필수/불변. approve/transfer 전부 revert. burn 허용. getMetadataHash(tokenId) 제공.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();
}
}
ERC-1155도 위 MijiERC721.sol과 동일한 원칙(Ownable, ERC2981, mint 함수의 onlyOwner 보호)을 따라야 합니다.