KIP17Full, KIP17Metadata, KIP17, KIP17Token의 차이점?

안녕하세요, KIP17 컨트랙트를 이용해 NFT 관련 프로젝트를 진행하는 도중 이슈가 있어서 질문 드립니다.

현재 제가 작성한 스마트 컨트랙트 두 개의 주요 내용은 다음과 같습니다.

contract NFTManager{

    NFTToken public minter;
    NFTToken [] public minters;
    address owner;  
    mapping (address => NFTToken ) public nftManager;

    constructor() public {
        owner = msg.sender;
    }
    function mintNewToken(string memory _name, string memory _symbol) public returns (address){
        minter = new NFTToken(owner, nameNFT, symbolNFT);
        minters.push(minter);
        nftManager[msg.sender] = minter;
    }

    function mint(uint _tokenNum, address payable minterAddress) public payable {
        nftManager[minterAddress].mint.value(msg.value)(_tokenNum, minterAddress);
    }

위 컨트랙트에서 KIP17을 상속받는 NFT 발행용 컨트랙트(NFTToken)를 아래와 같이 생성합니다.

pragma solidity ^0.5.6;

import "@klaytn/contracts/token/KIP17/KIP17Metadata.sol";
import "@klaytn/contracts/token/KIP17/KIP17.sol";

contract NFTToken is KIP17, KIP17Metadata{  
  address owner;  
  string public name;
  string public symbol;
//생성자가 KIP17Metadata를 생성하는 부분은 KIP17Full을 참고하여 넣었습니다.
  constructor(address _owner, string memory _name, string memory _symbol) KIP17Metadata(_name, _symbol) public { 
    owner = _owner;
  }

  function mint(uint _tokenNum, address payable minteAddress) public payable {
    _mint(msg.sender, _tokenNum);
    _setTokenURI(_tokenNum, tokenUriStore[_tokenNum]);
  }

위와 같이 작성하면 deploy까지 문제없이 되지만, 웹에서 kaikas를 통해 mint함수를 호출하면 revert가 납니다.

캡처

원래 KIP17Full을 상속받아 구현하려고 했습니다만, KIP17Full이나 KIP17Token으로 아래와 같이 스마트컨트랙트를 작성하면 아래와 같이 에러가 뜹니다.

pragma solidity ^0.5.6;

import "@klaytn/contracts/token/KIP17/KIP17Full.sol";

contract NFTToken is KIP17Full{  
  address owner;  
  string public name;
  string public symbol;
  constructor(address _owner, string memory _name, string memory _symbol) KIP17Full(_name, _symbol) public { 
    owner = _owner;
  }

  function mint(uint _tokenNum, address payable minteAddress) public payable {
    _mint(msg.sender, _tokenNum);
    _setTokenURI(_tokenNum, tokenUriStore[_tokenNum]);
  }

_setTokenURI()가 KIP17Metadata에, _mint()가 KIP17에 존재하고, KIP17Full이 두 컨트랙트를 상속받아 만들어져있기 때문에 그대로 사용하면 되겠다싶어서 사용했습니다만, 저 에러가 도저히 해결이 안되서 KIP17과 KIP17Metadata를 KIP17Full처럼 상속받아 만들었습니다만, _mint()만 호출이 되지 않는 상황입니다.

지난 번에 비슷한 질문을 올렸습니다만, 당시에 올려주신 예제는 KIP17Token을 사용하는 방법이었는데 KIP17Token의 경우에도 같은 revert가 발생합니다.

해결 방법이 있을까요?

상기 코드중에서 minteAddress는 어디에 사용되는지요?
또한 상단의 import 부분에서 KIP17Full.sol 이 누락된것 같은데, 이부분은 실제코드에서는 반영되어있는 부분인지요?

참고로 차이점은
KIP17Token 은 Full 보다 상위의 함수입니다. 차이점으로는 MetadataMintable, Mintable, Pausable, Burnable 을 추가로 상속받고있고요.

KIP17Full은 KIP17에 Enumerable 과 metadata를 추가로 상속받은 것입니다.

KIP17Metadata는 KIP17에 name과 symbol을 추가해준 부분이고요.

KIP17은 가장 기본적인 base 메소드들을 제공하는 소스코드입니다.

1 Like

앗 네 실제 코드에서는 KIP17Full.sol이 import되어있습니다 옮기는 과정에서 실수가 있었네요. minteAddress는 실제 구현에서는 컨트랙트에 전송된 value를 transfer하도록 되어있습니다.

답변 감사합니다! 말씀하신 것과 같이 서로 상하위의 컨트랙트로 보입니다.

조금 더 세부적으로 궁금한 것이, KIP17Full을 상속한 맨 아래 컨트랙트의 경우에는 알 수 없는 revert가 발생하고, KIP17Full과 비슷하게 KIP17과 KIP17Metadata를 KIP17Full을 거치지 않고 상속받아 만들게 되면 deploy가 되는 차이점이 궁금합니다.

또한, KIP17과 KIP17Metadata로 deploy를 성공한 경우에도 KIP17의 _mint()와 KIP17Metadata의 _setTokenURI()를 호출하는 경우에 revert됩니다.(_mint()와 _setTokenURI()를 지우면 정상 작동되는 것으로 보아 해당 함수 호출에 문제가 발생하는 것으로 보입니다.)

상속을 동일하게 진행하시면 특별한 차이가 없다고 생각합니다.
한가지 가능성은 NFTmanager의 데이터 값이, contract의 root에 NFTToken 객체를 정의해서,
Contract 최대 크기를 초과한 경우가 될 수 있는데요, 이는 ide.klaytn.com 을 이용해 디플로이 해보시면 발생되는지 확인이 가능하십니다.

NFTManager 에서 mint함수를 보면

nftManager[minterAddress].mint 형태로 함수를 호출하는데, nftManager 프로퍼티에는 mintNewToken 함수에서 msg.sender 에 minter를 등록하도록 되어있는것 같은데.

mint 함수에서 사용하는 minterAddress 는 mintNewToken을 과거 실행한 지갑주소가 들어가는 부분인가요?

네 맞습니다. 해당 minterAddress로 mapping 내에서 저장하고 불러오고, transfer를 받게 됩니다. Contract의 최대크기는 전혀 고려해보지 못했네요; 답변 정말 감사드립니다. 클레이튼 IDE를 이용해 확인해보겠습니다. 관련한 문서가 따로 있을까요?

위 문서를 확인해보시면 될것같아요.
혹시 해당 관련문제시면, Contract interface를 array로.매핑하시지않고, 주소만 매핑하셔서, 함수단에서 interface를 이용해 인스턴스 객체 생성하셔서 실행하는 방식이 효율적일것으로 보입니다.

감사합니다. 방금 클레이튼 IDE에서 배포하니 말씀해주신 에러는 뜨지 않고 잘 배포가 됩니다;; 헌데 역시나 _mint()부분에서는 revert가 나고있습니다. 아마도 배포환경에서 뭔가 다른 문제가 또 있긴 한 것 같습니다만, _mint()호출의 revert는 컨트랙트의 크기와는 다른 문제가 있는 것 같습니다ㅠ

그렇다면 정의하신 mint 함수에 원인이 있을 것으로 생각됩니다.

minterAddress라는 주소가 NFTToken 에서도 사용되는 parameter인가요?

function mint(uint _tokenNum) public payable {
    _mint(msg.sender, _tokenNum);
    _setTokenURI(_tokenNum, tokenUriStore[_tokenNum]);
}

형태로 가셔도 괜찮지 않을까 생각됩니다.

아니면 실제 _mint(msg.sender, _tokenNum; 에서 에러가 발생하는 부분인지 확인차
_setTokenURI 행을 커맨트 처리하시고 새로 디블로이 하셔서 테스트해보시는것도 좋은 방법일것 같습니다.
특별히 _mint 함수 실행에는 문제가 없을 것이라고 생각되고, tokenUriStore[] 등의 데이터타입만 새로 정의된 부분인것 같아, 현재 전체 소스코드가 보여지진 않는 것 같이 제공하신 소스만으로는 원인을 확인하기에는 조금 힘들 것 같습니다.

1 Like

늦은 밤까지 정말 감사드립니다. 일단 요점을 정리하자면 다음과 같았습니다.

  1. 로컬 IDE에서 'KIP17Full을 상속받는 컨트랙’을 생성하는 컨트랙트를 배포하자 revert가 났습니다만, 클레이튼 IDE에서 배포했더니 정상적으로 배포되었습니다. 다만, KIP17Token으로 배포하자 말씀하셨던 사이징 문제가 발생했습니다.

  2. 클레이튼 IDE에서 배포 성공 후, 여전히 mint()함수 호출에서 revert가 났습니다만, 계속 확인해본 결과, _mint()나 _setTokenURI()의 문제는 아닌 것으로 확인되었습니다. 나머지 코드를 전부 지우고 _mint()와 _setTokenURI()만 남겨서 호출해본 결과 성공적으로 민트되었습니다.

감사합니다!

1 Like

1번 문제의 경우에는

컨트랙트 소스에 다른 컨트랙트를 mapping 하거나 array로 형태로 정의하시면

현재 컨트랙트 + 오브젝트로 정의된 컨트랙트 의.바이트코드가 같이 디플로이 되기때문에 사이즈가 커집니다.

이부분은 앞서 말씀드렸다 싶이 컨트랙트 주소만 저장하시고, 함수단에서 인스턴스 생성해서 메소드 호출하시는 방법 추천드립니다.

2번의 경우에는, 별도로 공유하지 않으신 추가소스가 있으셨던 것인가요? 로직 짜실때 require 문으로 에러 발생할수인는 케이스 미리 잡아두시면 디버깅하는데 좋지않을까 싶습니다. IDE 가 상대적으로 에러메시지를 더 구체적으로 제공해줘서 유용하더라고요.

1 Like

네 말씀하신대로 구현해보는 중입니다. 2번에서 추가 소스가 있고, 주소의 payable 선언 문제가 있었네요. 앞으로 스마트 컨트랙트는 클레이튼 IDE에서 작업하는 것이 확실히 좋을 것 같군요.

도움 정말 감사드립니다^^

1 Like