ERC-7628 ERC-721 소유 지분 확장 (ERC-721 Ownership Shares Extension)
Review · NFT·멀티토큰 · 2024-02-20 작성
ERC-721 NFT의 각 토큰에 소유·수익 지분 수량을 붙여 그 지분을 조회하고 다른 토큰이나 주소로 옮기고 승인할 수 있게 하는 확장 표준입니다.
왜 필요한가
NFT에서 배당을 나누거나 권리를 지분 비율대로 배정하려면 각 토큰에 소유 지분 수량이라는 속성이 있어야 합니다. 그런데 기존 표준으로는 이것이 어렵습니다. 여러 종류의 토큰을 수량 단위로 다루는 ERC-1155는 지갑 주소가 가진 토큰 잔액으로 지분을 표현할 수 있지만 그 대신 토큰 하나하나의 고유성을 포기해야 합니다. 반대로 고유한 토큰을 표현하는 ERC-721은 각 토큰의 고유성은 지키지만 소유 지분을 나타내는 속성이 없고 메타데이터만으로는 토큰 소유자가 지분 수량을 자유롭게 옮길 수도 없습니다. 이 확장은 두 표준의 특징을 합쳐 각 지분 보유자가 토큰 소유자와 비슷한 성격을 갖게 하고 지분 표현과 토큰 고유성 사이의 간극을 메우려 합니다.
구현해야 하는 것
function shareDecimals() external view returns (uint8);: 지분에 쓰는 소수점 자릿수를 돌려줍니다.function totalShares() external view returns (uint256);: 모든 토큰에 존재하는 지분의 총합을 돌려줍니다.function shareOf(uint256 tokenId) external view returns (uint256);: 지정한 토큰이 가진 지분 수량을 돌려줍니다.function shareAllowance(uint256 tokenId, address spender) external view returns (uint256);: 토큰 소유자가 spender 주소에 허용해 둔 지분 한도를 돌려줍니다.function approveShare(uint256 tokenId, address spender, uint256 shares) external;: 호출자를 대신해 spender가 정해진 수량까지 지분을 쓸 수 있게 승인합니다.function transferShares(uint256 fromTokenId, uint256 toTokenId, uint256 shares) external;: 한 토큰에서 다른 토큰으로 지분을 옮깁니다.function transferSharesToAddress(uint256 fromTokenId, address to, uint256 shares) external;: 토큰의 지분을 주소로 옮깁니다. 받는 주소에 새 토큰이 생기거나 기존 지분이 늘어납니다.function addSharesToToken(uint256 tokenId, uint256 shares) external;: 토큰에 지분을 추가합니다. 컨트랙트 소유자만 호출할 수 있습니다.event SharesTransfered(uint256 indexed fromTokenId, uint256 indexed toTokenId, uint256 amount);: 지분이 한 토큰에서 다른 토큰으로 옮겨질 때 발생합니다.event SharesApproved(uint256 indexed tokenId, address indexed spender, uint256 amount);: 소유자를 대신해 지분을 쓸 수 있는 승인이 이뤄질 때 발생합니다.
심층 설명
지분 조회 인터페이스
지분 조회용 뷰 함수는 네 가지입니다. shareDecimals는 지분에 쓰는 소수점 자릿수를 돌려주고 totalShares는 모든 토큰에 존재하는 지분의 총합을 돌려줍니다. shareOf(tokenId)는 특정 토큰이 가진 지분 수량을, shareAllowance(tokenId, spender)는 토큰 소유자가 spender 주소에 허용해 둔 지분 한도를 돌려줍니다. 이 확장을 구현하는 컨트랙트는 명세에 나온 모든 함수를 반드시(MUST) 갖춰야 합니다. 참조 구현에서 shareDecimals는 18을 돌려줍니다.
지분 승인과 두 가지 이전 방식
approveShare(tokenId, spender, shares)는 호출자를 대신해 spender가 정해진 수량까지 지분을 쓸 수 있게 승인합니다. 이전 함수는 두 가지입니다. transferShares(fromTokenId, toTokenId, shares)는 한 토큰에서 다른 토큰으로 지분을 옮깁니다. transferSharesToAddress(fromTokenId, to, shares)는 토큰의 지분을 주소로 옮기며 받는 주소에 새 토큰이 생기거나 기존 지분이 늘어납니다. 원문은 지분을 주소로 이전하는 것이 부분 소유를 가능하게 해 NFT의 유동성과 접근성을 높이고 부분 판매나 담보화 같은 쓰임새를 지원한다고 설명합니다.
지분 추가 발행 addSharesToToken
addSharesToToken(tokenId, shares)은 특정 토큰에 지분을 더하는 함수로, 컨트랙트 소유자만 호출할 수 있습니다. 원문은 추가 발행이 이해관계자의 필요 변화에 맞춰 소유 구조를 블록체인 위에서 투명하고 안전하게 바꿀 수 있게 하며 수익 분배나 투자 조정 같은 시나리오를 돕는다고 설명합니다. 참조 구현에서는 onlyOwner 제한을 걸고 tokenId가 0이 아닌지 확인한 뒤, 해당 토큰의 지분과 _totalShares를 함께 늘리고 fromTokenId를 0으로 둔 SharesTransfered 이벤트를 냅니다.
이벤트와 참조 구현의 권한 검사
이벤트는 두 가지입니다. SharesTransfered(fromTokenId, toTokenId, amount)는 지분이 토큰 사이에서 옮겨질 때, SharesApproved(tokenId, spender, amount)는 지분 사용 승인이 이뤄질 때 발생합니다. 이벤트 이름 철자는 원문 그대로 SharesTransfered입니다. 참조 구현의 approveShare는 호출자가 토큰 소유자인지 확인하고 현재 소유자 자신에게는 승인하지 못하게 막습니다. 두 이전 함수는 _isApprovedOrOwner로 호출자가 소유자이거나 getApproved 또는 isApprovedForAll로 승인받은 주소인지 확인하고 nonReentrant 수식어로 재진입을 막습니다. transferSharesToAddress는 _nextTokenId를 올린 뒤 _safeMint로 새 토큰을 만들어 지분을 배정합니다.
호환성과 제안 상태
이 표준은 ERC-721을 필요 조건(requires: 721)으로 하며 원문 Backwards Compatibility 절은 완전한 ERC-721 호환이라고 밝힙니다. 기존 ERC-721을 다루는 지갑이나 앱의 동작은 그대로 유지되고 지분 기능은 추가 함수와 이벤트로 얹히는 구조입니다. 한편 이 제안의 상태는 Review로, 아직 검토 중이어서 인터페이스나 세부 내용이 바뀔 수 있는 제안입니다. 구현하거나 연동할 때는 원문의 최신 버전을 확인하는 것이 안전합니다.
쓸 때 주의할 점
- 소유권 이전 시 승인 정리: 토큰 소유권을 옮길 때는 기존 승인을 모두 지워야 합니다. 그래야 이전에 승인받았던 주소가 토큰 주인이 바뀐 뒤에도 접근 권한을 유지하는 일을 막을 수 있습니다.
- 재진입 공격 방지: 구현은 재진입 공격을 막아야 합니다. 특히 지분 이전처럼 잔액이나 소유권을 바꾸는 함수가 이런 취약점에 안전하도록 해야 합니다.
- 토큰 ID와 주소 검증: 모든 동작에서 토큰 ID와 지갑 주소가 올바른지 확인해야 합니다. 그래야 오류를 피하고 토큰과 그에 딸린 지분이 정확하게 처리됩니다.
- 소유권 변경 시 지분 관리: 토큰 소유권이 옮겨질 때 지분 수량을 제대로 관리해야 합니다. 지분이 토큰과 함께 정확히 계산되고 이전되어야 소유 구조가 온전하게 유지됩니다.