ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Send Ethereum
    Block-Chain/Solidity 2023. 4. 4. 17:12

    코인 이더리움을 송신하는 방법 및 관련 개념에 대해 정리하였습니다.

     

     


    < 이더를 보내는 함수 >

    코인 이더리움을 보내는 3가지 방법은 send, transfer, call이 있습니다.

    키워드 Payable은 코인과 상호작용(송금)시 필요한 키워드입니다. 주로 함수, 주소, 생성자에 붙여 사용됩니다.

    msg.value는 송금보낸 코인의 값입니다.

     

    • send : 2300 gas를 소비. 성공여부를 true 또는 false로 리턴
    • transfer : 2300 gas를 소비. 실패시 에러를 발생
    • call : 가변적인 gas 소비(gas값 지정 가능). 성공여부를 true 또는 false로 리턴

     

    Ex)

    event howMuch(uint256 _value);
    
    function sendNow(address payable _to) public payable {
      bool sent = _to.send(msg.value); // return true or false
      require(sent, "failed to send ethereum");
      emit howMuch(msg.value);
    }
    
    function transferNow(address payable _to) public payable {
      _to.transfer(msg.value);
      emit howMuch(msg.value);
    }
    
    function callNow(address payable _to) public payable {
      // ~0.7
      bool sent = _to.call.gas(1000).value(msg.value)("");
      require(sent, "failed to send ethereum");
      
      // 0.7~
      bool sent = _to.call{value: msg.value, gas: 1000}("");
      require(sent, "failed to send ethereum");  
    }

     

     


    < balance와 msg.sender >

    balance는 해당 지갑 주소의 현재 이더 잔액을 말합니다.

    msg.sender는 스마트컨트랙트와 상호작용하는 지갑주소를 말합니다.

     

    Ex)

    event SendInfo(address _msgSender, uint256 _currentValue);
    event MyCurrentValue(address _msgSender, uint256 _value);
    event CurrentValueOfSomeone(address _msgSender, address _to, uint256 _value);
    
    function sendEther(address payable _to) public payable {
      require(msg.sender.balance >= msg.value, "Your balance is not enough");
      _to.transfer(msg.value);
      emit SendInfo(msg.sender, (msg.sender).balance);
    }
    
    function checkValueNow() public {
      emit MyCurrentValue(msg.sender, msg.sender.balance);
    }
    
    function checkUserMoney(address _to) public {
      emit CurrentValueOfSomeone(msg.sender, _to, _to.balance);
    }

     

     


    < 생성자 payable, 함수 사용 권한 제한 >

    생성자는 스마트컨트랙트 생성(첫 배포)시 작동하는데, 여기에 payable을 붙이면 이더를 스마트컨트랙트 안에 넣어줄 수 있습니다.

    msg.sender를 require와 연계하여 특정 지갑주소에게만 특정 함수를 사용할 수 있도록 권한을 설정할 수 있습니다. 

     

    Ex)

    contract lecture34 {
      address owner;
      constructor() payable {
        owner = msg.sender;
      }
      
      modifier onlyOwner {
        require(msg.sender == owner, "Only Onwer!");
        _;
      }
      
      event SendInfo(address _msgSender, uint256 _currentValue);
      event MyCurrentValue(address _msgSender, uint256 _value);
      event CurrentValueOfSomeone(address _msgSender, address _to,uint256 _value);
      
      function sendEther(address payable _to) public onlyOwner payable {
        require(msg.sender.balance>=msg.value, "Your balance is not enough");
        _to.transfer(msg.value);    
        emit SendInfo(msg.sender,(msg.sender).balance);
      }
        
      function checkValueNow() public onlyOwner {
        emit MyCurrentValue(msg.sender, msg.sender.balance);
      }
        
      function checkUserMoney(address _to) public onlyOwner {
        emit CurrentValueOfSomeone(msg.sender,_to ,_to.balance);
      }
    }

     

     


    < fallback / receive >

    fallback는 말 그대로 대비책 함수입니다. 무기명 함수이고, 다음과 같은 이유로 사용합니다.

     

    fallback의 사용 이유 3가지

    • 스마트 컨트랙이 이더를 받을 수 있게 한다. => payable
    • 이더를 받고난 후 어떠한 행동을 취하게 할 수 있다. => payable
    • call함수로 없는 함수가 불려질 때, 어떠한 행동을 취하게 할 수 있다. => external

     

    < 0.6 이전, 이후 fallback >
    이전 :
    fallback() external payable {
      
    }


    이후 : receive와 fallback 두가지 형태로 나뉨.
    - receive : 순수하게 이더만 받을때 작동
    - fallback : 함수를 실행하며 이더를 보낼때, 또는 불려진 함수가 없을 때 작동합니다.

    receive() external payable {
    
    }
    
    fallback() external payable {
    
    }

     

     


    < call >

    call 함수는 코인 송금 외에도, 외부 스마트컨트랙트의 함수에 접근할 수 있습니다. 

     

    Ex)

    contract add {
      event JustFallback(string _str);
      event JustReceive(string _str);
      
      function addNumber(uint256 _num1, uint256 _num2) public pure returns(uint256) {
        return _num1 + _num2;
      }
      fallback() external {
        emit JustFallback("JustFallback is called");
      }
      receive() external payable {
        emit JustReceive("JustReceive is called");
      }
    }
    
    contract caller {
      event calledFunction(bool _success, bytes _output);
      
      // call을 이용한 송금
      function transferEther(address payable _to) public payable {
        (bool success, ) = _to.call{ value:msg.value }("");
        require(success, "failed to transfer ether");
      }
      
      // call을 이용한 외부 스마트 컨트랙트 함수 부르기
      function  callMethod(address _contractAddr, uint256 _num1, uint256 _num2) public {
        (bool success, bytes memory outputFromCalledFunction) = _contractADdr.call(
          abi.encodeWithSignature("addNumber2(uint256, uint256)", _num1, _num2)
        );
        require(success, "failed to call other function");
        emit callMethod(success, outputFromCalledFunction);
      }
    }

     

     


    < call vs delegate call >

    delegate call은 스마트컨트랙트 B의 함수들을 스마트 컨트랙트 A에 옮겨놓는 것처럼 행동하는 것을 말합니다.
    쉽게 말하자면, 스마트 컨트랙 A는 껍데기이고, 스마트 컨트랙 B는 주요 함수들을 갖고 있는 핵심이라 할 수 있습니다. 

     

    조건 : A스마트컨트랙트와 B스마트컨트랙트는 같은 변수를 갖고 있어야 한다.

     

    Ex: num 변수 값 변경


    < Call >

    1. 스마트컨트랙트 A의 msg.sender는 Alice의 주소

    2. 스마트컨트랙트 B의 msg.sender는 스마트 컨트랙트 A의 주소

    3. 스마트 컨트랙트 B의 num은 5로 변경되고, num=5라는 것은 스마트 컨트랙트 B에 저장된다.

     


    < Delegate Call >

    1. 스마트컨트랙트 B의 msg.sender는 스마트 컨트랙트 A의 주소가 아니라, Alice의 주소

    2. 스마트컨트랙트 B의 함수가 불려서 num 값이 5가 되었지만, 정작 num=5라는 것은 스마트컨트랙트 A에 저장되어있고, 스마트컨트랙트 B의 num값은 그대로 3이다.

     

    contract add {
      uint256 public num = 0;
      event Info(address _addr, uint256 _num);
      
      function plusOne() public {
        num = num + 1;
        emit Info(msg.sender, num);
      }
    }
    
    contract caller {
      uint256 public num = 0;
      
      function callNow(address _contractAddr) public payable {
        (bool success, ) = _contractAddr.call(abi.encodeWithSignature("plusOne()"));
        require(success, "failed to transfer ether");
      }
      function delcateCallNow(address _contractAddr) public payable {
        (bool success, ) = _contractAddr.delegatecall(abi.encodeWithSignature("plusOne()"));
        require(success, "failed to transfer ether");
      }
    }

     


    < delegate call이 필요한 이유 >
    num을 5로 변경하는 함수가, 10으로 변경하는 로직으로 수정해야 할 때

    =>
    스마트컨트랙트 A는 껍데기니까, 스마트컨트랙트 B의 주요 로직을 변경해서 새로운 스마트컨트랙트 B'를 배포한다.
    그리고 스마트컨트랙트 A의 delegate call 주소를 B -> B'로 변경하면 완료!

    이를 upgradable smart contract framework 라고 합니다.

     

     

     

    'Block-Chain > Solidity' 카테고리의 다른 글

    Enum & Interface & Library & Import  (0) 2023.04.24
    Modifier & SPDX  (0) 2023.03.22
    에러 핸들러  (0) 2023.03.07
    조건문 & 반복문  (0) 2023.02.15
    Mapping & Array & struct(구조체)  (0) 2023.01.16

    댓글

Designed by Tistory.