본문 바로가기
블록체인교육/솔리디티

[솔리디티] 10. 인터페이스(interface), Payable, 이더 전송,받기 관련

by Danny_Kim 2022. 6. 13.

[NEW] 누구나 쉽게 따라하는 솔리디티 강의(솔리디티 버전 0.8.13)

1. Helloworld, 카운터컨트랙트, 데이터타입

2. 변수, 상수, 불변, 상태변수 읽고 쓰기

3. 이더 단위, 가스와 가스가격

4. 조건문, 반복문, 맵핑(mapping)

5. 배열, 열거형(enum), 구조체(calldata,memory) 

6. 데이터 저장공간, 함수(view,pure 속성)

7. 에러(error), 함수수정자(modifier)

8. 이벤트(events), 생성자(constructor), 상속

9. 상속, 섀도잉,super키워드 함수 속성들

10. 인터페이스(interface), payable, 이더전송,받기 관련

11. Fallback, Call, Delegate(솔리디티 업그레이드 기법)

12. 함수 선택자(function selector), 다른 컨트랙트 사용 및 생성기법

13. Try Catch, Import(임포트), Library(라이브러리)

14. ABI 디코드, hash 함수, 서명검증, 가스최적화

* 블록체인 전문가들도 놓치기 쉬운 비트코인, 이더리움의 핵심가치 강의

 

블록체인 전문가들도 놓치기 쉬운 비트코인, 이더리움의 핵심 가치 - 인프런 | 강의

블록체인 기획자,개발자,회사 대표라면 반드시 한번은 봐야 하는 강의입니다. 따로 공부할 시간이 없었다면, 이 요약본 강의를 통해서 비트코인,이더리움 백서의 핵심을 이해할 수 있습니다., -

www.inflearn.com


1. Interface(인터페이스)

 - Interface 선언을 통해서 다른 컨트랙트와 상호작용할 수 있다.

 - 함수를 구현하지 못함.

 - 다른 interface로 부터 상속받지 못함

 - 모든 선언된 함수들은 external이어야 함.

 - 생성자(constructor)를 선언할 수 없음

 - 상태변수(state variables)를 선언할 수 없음

 

- Interface 예제)

- Counter 컨트랙트에서 interface에서 선언한 ICounter의 increment() 함수 사용.

- 코드 없이 이미 배포된 컨트랙트를 interface를 사용하여 호출할 수 있음.

- Deploy후, 카운터 컨트랙트를 Mycontract에서 접근하여 Counter 컨트랙트 활용가능.

 

- Interface Uniswap 예제

- UniswapV2Factory 및 UniswapV2Pari 인터페이스 선언 후, uniswap의 컨트랙트를 사용하여 getTokenReserves() 사용.

 

전체소스

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

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);
    }
}

 

2. Payable (지불)

 - 함수와 주소(address)는 payable 키워드를 사용하여 ether를 받을 수 있음.

 

2.1 주소 타입 변수, 생성자

   - 주소타입변수 및 생성자(constructory)에 payable 키워드를 사용하면 Ether를 받을 수 있음 

2.2 함수에 payable 키워드를 사용하여 ether를 받을 수 있음.

2.3 withdraw()함수는 현재 이 컨트랙트에 있는 모든 balance(이더)를 인출할 수 있음

 - 컨트랙트의 owner에게 모두 인출하는 명령어.

2.4 transfer() 함수는 주소를 지정하여 컨트랙트에 있는 이더를 해당 주소로 보낼 수 있음.

 

전체소스

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

contract Payable {
    // Payable address can receive Ether
    address payable public owner;

    // Payable constructor can receive Ether
    constructor() payable {
        owner = payable(msg.sender);
    }

    // Function to deposit Ether into this contract.
    // Call this function along with some Ether.
    // The balance of this contract will be automatically updated.
    function deposit() public payable {}

    // Call this function along with some Ether.
    // The function will throw an error since this function is not payable.
    function notPayable() public {}

    // Function to withdraw all Ether from this contract.
    function withdraw() public {
        // get the amount of Ether stored in this contract
        uint amount = address(this).balance;

        // send all Ether to owner
        // Owner can receive Ether since the address of owner is payable
        (bool success, ) = owner.call{value: amount}("");
        require(success, "Failed to send Ether");
    }

    // Function to transfer Ether from this contract to address from input
    function transfer(address payable _to, uint _amount) public {
        // Note that "to" is declared as payable
        (bool success, ) = _to.call{value: _amount}("");
        require(success, "Failed to send Ether");
    }
}

 

2.5 과제 : 위 소스에서 Transfer()의 경우 특정인 혹은 owner만 사용할 수 있도록 수정.

 

 

3. transfer, send, call 함수 

 3.1 Ether를 전송하는 방법

 -  Transfer() 함수는 2300 gas가 소모되고, 실패의 경우 error 를 발생시킴

 - Send() 함수는 2300 gas가 소모되고, bool 형으로 return 

 - call() 함수의 경우는 모든 가스 혹은 설정된 가스를 소모하고, bool형으로 return

 

 3.2 Ether를 받는 방법

 - receive() external payable

 - fallback() external payable

 - receive()의 경우는 msg.data, 즉 calldata가 비어있는 경우 사용되고, 다른 경우에는 fallback() 함수를 호출하면 됨.

 

3.3 유용한 팁

 - 재진입(re-entracncy guard)을 고려하여 사용.

 - 다른 컨트랙트를 호출하기 전에 모든 상태를 변경할것.

 - 재진입(re-entrancy) 방지를 위한  수정자를 사용할 것.

 

예제) cadded, fallback(), receive() 어떤걸 사용해야 할까?

      - msg.data가 없다면(콜데이터가 없다면) receive() 사용, 아니라면 fallback() 사용

     * receive()는 콜데이터가 없을때 호출되고, fallback()은 콜데이터가 존재하면 호출됨

 

예제) 이더를 보낼경우,

  - 아래 예시에서 sendViaTransfer(), sendViaSend() 함수는 더이상 추천되지 않음(사용하지 말것을 권고)

  - sendViaCall() 형태로 사용할것을 추천!

전체소스

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

contract ReceiveEther {
    /*
    Which function is called, fallback() or receive()?

           send Ether
               |
         msg.data is empty?
              / \
            yes  no
            /     \
receive() exists?  fallback()
         /   \
        yes   no
        /      \
    receive()   fallback()
    */

    // Function to receive Ether. msg.data must be empty
    receive() external payable {}

    // Fallback function is called when msg.data is not empty
    fallback() external payable {}

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

contract SendEther {
    function sendViaTransfer(address payable _to) public payable {
        // This function is no longer recommended for sending Ether.
        _to.transfer(msg.value);
    }

    function sendViaSend(address payable _to) public payable {
        // Send returns a boolean value indicating success or failure.
        // This function is not recommended for sending Ether.
        bool sent = _to.send(msg.value);
        require(sent, "Failed to send Ether");
    }

    function sendViaCall(address payable _to) public payable {
        // Call returns a boolean value indicating success or failure.
        // This is the current recommended method to use.
        (bool sent, bytes memory data) = _to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
}

3.4 과제) 재진입 공격 및 취약점에 대해서 찾아보고 발표.

 

 

소스참고 https://solidity-by-example.org/

 

블록체인 교육 문의는 아래 링크 참고 바랍니다. 

https://kimsfamily.kr/414

 

블록체인 교육 커리큘럼 및 프로필

안녕하세요 제 프로필을 간략하게 정리하였습니다. 비즈니스 문의는 dannykim@kakao.com 으로 연락주시기 바랍니다. 감사합니다. 프로필) 블록체인 강의 경력) 1. 블록체인 강의 (20년~현재) 2022년  -

kimsfamily.kr

 

반응형

댓글0