[Solidity] Visibility, Interface

jhcha·2023년 7월 30일
0

Solidity

목록 보기
7/17
post-thumbnail

Visibility

url: https://solidity-by-example.org/visibility/

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

contract Base {
    // Private function can only be called
    // - inside this contract
    // Contracts that inherit this contract cannot call this function.
    function privateFunc() private pure returns (string memory) {
        return "private function called";
    }

    function testPrivateFunc() public pure returns (string memory) {
        return privateFunc();
    }

    // Internal function can be called
    // - inside this contract
    // - inside contracts that inherit this contract
    function internalFunc() internal pure returns (string memory) {
        return "internal function called";
    }

    function testInternalFunc() public pure virtual returns (string memory) {
        return internalFunc();
    }

    // Public functions can be called
    // - inside this contract
    // - inside contracts that inherit this contract
    // - by other contracts and accounts
    function publicFunc() public pure returns (string memory) {
        return "public function called";
    }

    // External functions can only be called
    // - by other contracts and accounts
    function externalFunc() external pure returns (string memory) {
        return "external function called";
    }

    // This function will not compile since we're trying to call
    // an external function here.
    // function testExternalFunc() public pure returns (string memory) {
    //     return externalFunc();
    // }

    // State variables
    string private privateVar = "my private variable";
    string internal internalVar = "my internal variable";
    string public publicVar = "my public variable";
    // State variables cannot be external so this code won't compile.
    // string external externalVar = "my external variable";
}

contract Child is Base {
    // Inherited contracts do not have access to private functions
    // and state variables.
    // function testPrivateFunc() public pure returns (string memory) {
    //     return privateFunc();
    // }

    // Internal function call be called inside child contracts.
    function testInternalFunc() public pure override returns (string memory) {
        return internalFunc();
    }
}

가시성은 앞서 나온 포스트에서 가시성 지정자, 접근 제어자 라는 용어로 다루었다.

public, private, internal, external 4가지로 나누어진다.

  • public: 어느 곳에서든 접근 가능
  • private: private가 선언된 내부에서만 접근 가능
  • internal: private 처럼 internal 정의된 내부에서 접근 가능, 상속받은 자식들도 접근 가능
  • external: private와 반대로 외부에서만 접근 가능, 내부에서 접근 불가능
    function privateFunc() private pure returns (string memory) {
        return "private function called";
    }

    function testPrivateFunc() public pure returns (string memory) {
        return privateFunc();
    }

privateFunc()는 private 가시성으로 선언되어 내부에서만 접근할 수 있다.
따라서, testPrivateFunc()를 외부에서 호출함으로써 내부적으로 동작하도록 작성됐다.

contract Base {
    function internalFunc() internal pure returns (string memory) {
        return "internal function called";
    }

    function testInternalFunc() public pure virtual returns (string memory) {
        return internalFunc();
    }
}
contract Child is Base {
    function testInternalFunc() public pure override returns (string memory) {
        return internalFunc();
    }
}

internalFunc()는 내부 혹은 상속한 하위 컨트랙트에서 접근할 수 있다.
상속한 Child 컨트랙트에서 testInternalFunc()를 오버라이딩하고, internalFunc()에 접근할 수 있다.

contract Base {
    function externalFunc() external pure returns (string memory) {
        return "external function called";
    }

    // function testExternalFunc() public pure returns (string memory) {
    //     return externalFunc();
    // }
}
constract Child {
	Base private base;
    function testExternalFunc() public view returns (string memory) {
    	return externalFunc();
    }
}

external 접근 제어자는 내부에서 접근 불가능하고, 외부에서만 접근이 가능하다.
상속하지 않은 Child 컨트랙트에서 Base 컨트랙트 객체를 생성하고, base 객체의 externalFunc()를 호출할 수 있다.

    // State variables
    string private privateVar = "my private variable";
    string internal internalVar = "my internal variable";
    string public publicVar = "my public variable";
    // State variables cannot be external so this code won't compile.
    // string external externalVar = "my external variable";

상태 변수는 함수에서 사용하는 접근 제어자와 동일한 기능을 하지만, 예외적으로 상태변수는 external 접근 제어자로 선언할 수 없다.

Interface

url: https://solidity-by-example.org/interface/

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

contract Counter {
    uint public count;

    function increment() external {
        count += 1;
    }
}

interface ICounter {
    function count() external view returns (uint);

    function increment() external;
}

contract MyContract {
    function incrementCounter(address _counter) external {
        ICounter(_counter).increment();
    }

    function getCount(address _counter) external view returns (uint) {
        return ICounter(_counter).count();
    }
}

// Uniswap example
interface UniswapV2Factory {
    function getPair(
        address tokenA,
        address tokenB
    ) external view returns (address pair);
}

interface UniswapV2Pair {
    function getReserves()
        external
        view
        returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

contract UniswapExample {
    address private factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
    address private dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address private weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    function getTokenReserves() external view returns (uint, uint) {
        address pair = UniswapV2Factory(factory).getPair(dai, weth);
        (uint reserve0, uint reserve1, ) = UniswapV2Pair(pair).getReserves();
        return (reserve0, reserve1);
    }
}

Solidity는 인터페이스를 통해 다른 컨트랙트와 상호작용할 수 있다.

  • 인터페이스는 어느 기능도 구현할 수 없다.
  • 다른 인터페이스에서 상속할 수 있다.
  • 선언된 모든 함수는 외부 함수여야 한다.
  • 생성자를 선언할 수 없다.
  • 상태변수를 선언할 수 없다.
contract Counter {
    uint public count;

    function increment() external {
        count += 1;
    }
}

예제 실행을 위해 Counter 컨트랙트를 배포한다.

배포된 스마트 컨트랙트의 주소는 0xaE036c65C649172b43ef7156b009c6221B596B8b 이다.

interface ICounter {
    function count() external view returns (uint);

    function increment() external;
}

contract MyContract {
    function incrementCounter(address _counter) external {
        ICounter(_counter).increment();
    }

    function getCount(address _counter) external view returns (uint) {
        return ICounter(_counter).count();
    }
}

다음 MyContract를 배포한 후 incrementCounter 함수에 Counter 컨트랙트 주소를 입력하여 호출한다.

MyContract 컨트랙트에서 interfaceICounter를 통해, Counter 컨트랙트의 상태 변수를 변경할 수 있다.
아래 그림은 Counter 컨트랙트의 상태 변수 조회 화면

혹은, MyContract에서 Counter 컨트랙트 상태 변수를 조회해서 확인할 수도 있다.

인터페이스는 스마트 컨트랙트 간의 상호작용을 추상화 하는 개념이다. 따라서, 인터페이스는 위의 예시와 같이 서로 다른 컨트랙트 간에 상호운용성을 위해 사용할 수 있다.
만약 Counter 컨트랙트 상태 변수, 함수의 이름이 interface와 다르면 interface를 통해 호출하는 것이 불가능하다.

contract Counter {
    uint public count;

    function increment() external {
        count += 1;
    }
}

interface ICounter {
    function count() external view returns (uint);

    function increment2() external;
}

contract MyContract {
    function incrementCounter(address _counter) external {
        ICounter(_counter).increment2();
    }

    function getCount(address _counter) external view returns (uint) {
        return ICounter(_counter).count();
    }
}

incrementCounter()는 오류, getCount()는 정상 동작한다.

Uniswap

암호화폐를 거래하기 위한 중앙화된 거래소는 FTX 거래소 파산과 같이, 중앙집중화된 시스템의 문제 및 다양한 리스크를 가지고 있다. 이를 보완하기 위해 DEX (Decentralized Exchange), 탈중앙화 거래소가 등장했다. 대표적인 DEX, 유니스왑은 이더리움 블록체인 네트워크에서 ERC20 토큰을 교환할 수 있는 자동화된 탈중앙화 암호화폐 거래 플랫폼이다. 유니스왑은 탈중앙화 거래소 단점이었던 높은 수수료와 유동성 문제를 해결하고 이더리움 네트워크 상에서 ERC-20토큰과 이더리움 간의 교환을 지원한다. 유니스왑은 유동성 제공자 (LP)로부터 스마트 컨트랙트 거래 풀에 토큰을 예치하여 유동성 문제를 해결하고, 유동성 제공자는 제공한 유동성을 회수하는 시점까지 수수료에 대한 보상을 지급받는다.

0개의 댓글