체인의정석

Solidity 0.5.0 버젼 오류해결 ) TypeError: Variables cannot be declared in interfaces. 본문

블록체인/Solidity

Solidity 0.5.0 버젼 오류해결 ) TypeError: Variables cannot be declared in interfaces.

체인의정석 2022. 1. 24. 18:40
728x90
반응형

상속관계를 최대한 가볍게 하기 위하여 인터페이스 부분에 mapping을 넣어 놨었는데 메핑처럼 변수들은 인터페이스에서 정의가 되면 안된다. 인터페이스에서 정의되는 것은 함수 또는 이벤트여야 한다.

 

이를 지키지 않을 경우

TypeError: Variables cannot be declared in interfaces.

와 같은 컴파일 에러가 나게 된다.

 

따라서 메핑을 불러와서 조회에 쓰는 경우에는 조회를 하는 함수를 external로 인터페이스에서 만들어서 보여주는 것이 맞다.

메핑 자체를 public으로 보여주지 않고 내부적으로만 접근 가능하게 해놓고 external로 해두는 이유가 이렇게 인터페이스화를 하여 상속을 하여 상속 관계를 깔끔하게 정리해주기 위함이 아닌가 하는 생각이든다.

 

아무튼 오류 해결을 위해 매핑을 정의하고 이를 인터페이스로 풀어내는 법을 역시 오픈제플린에서 확인해 보았다.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.3.0/contracts/token/ERC721/IERC721.sol

 

GitHub - OpenZeppelin/openzeppelin-contracts: OpenZeppelin Contracts is a library for secure smart contract development.

OpenZeppelin Contracts is a library for secure smart contract development. - GitHub - OpenZeppelin/openzeppelin-contracts: OpenZeppelin Contracts is a library for secure smart contract development.

github.com

0.5.0 버전의 ERC721 코드인데 일반적인 view함수와 같이 작성했음을 확인했다. 이에 대한 원본은

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.3.0/contracts/token/ERC721/ERC721.sol

 

GitHub - OpenZeppelin/openzeppelin-contracts: OpenZeppelin Contracts is a library for secure smart contract development.

OpenZeppelin Contracts is a library for secure smart contract development. - GitHub - OpenZeppelin/openzeppelin-contracts: OpenZeppelin Contracts is a library for secure smart contract development.

github.com

여기서

    // Mapping from token ID to owner
    mapping (uint256 => address) private _tokenOwner;

    // Mapping from token ID to approved address
    mapping (uint256 => address) private _tokenApprovals;

    // Mapping from owner to number of owned token
    mapping (address => Counters.Counter) private _ownedTokensCount;

    // Mapping from owner to operator approvals
    mapping (address => mapping (address => bool)) private _operatorApprovals;

다음과 같이 프라이빗으로 선언해둔 후

    /**
     * @dev Gets the balance of the specified address.
     * @param owner address to query the balance of
     * @return uint256 representing the amount owned by the passed address
     */
    function balanceOf(address owner) public view returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");

        return _ownedTokensCount[owner].current();
    }

    /**
     * @dev Gets the owner of the specified token ID.
     * @param tokenId uint256 ID of the token to query the owner of
     * @return address currently marked as the owner of the given token ID
     */
    function ownerOf(uint256 tokenId) public view returns (address) {
        address owner = _tokenOwner[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");

        return owner;
    }

다음과 같이 선언한 변수를 퍼블릭 함수로 넣어둠을 볼 수 있었다.

퍼블릭으로 선언해둔 함수는 인터페이스에서는 external로 바꿔야 하는걸로 알고 있는데 여기서는 퍼블릭으로 나와있었다. 찾아보니 이에 대한 규정은 external이 더 안정적인것 같다는 생각이 들었으며, 최신버전의 동일 소스코드에서는 external로 되어 있는것을 보고 external로 작성하였다.

    /**
     * @dev Returns the number of NFTs in `owner`'s account.
     */
    function balanceOf(address owner) public view returns (uint256 balance);

    /**
     * @dev Returns the owner of the NFT specified by `tokenId`.
     */
    function ownerOf(uint256 tokenId) public view returns (address owner);

또한 순서의 경우 공식 문서에는 순서가 명시되어 있었지만 오픈제플린을 보니 함수의 쓰는 부분과 읽는 부분이 묶여서 나와있었다. 따라서 오픈제플린 최신 규격에 맞추어서 순서를 읽는 함수 밑에 쓰는 함수의 인터페이스를 작성하고 구현 또한 동일한 순서로 하기로 정하였다.

 

 

결과적으로 작성한 코드는 다음과 같다.

 

1. mapping은 private으로 구현

2. view로 조회해오는 함수 구현 후 인터페이스에 external로 넣기

3. 외부호출을 위한 트랜잭션으로 가져와서 사용하는 컨트렉트에서는 아래와 같은 형태로 가져와서 조회를 하며, 상속 또한 원본 컨트렉트가 아닌 인터페이스로 하면 된다.

address 사용하는변수 = 인터페이스컨트렉트.조회함수명(입력값);
728x90
반응형
Comments