BlockChain

Solidity #2

부산대보금자리 2022. 1. 5. 17:30

- Try/Catch

 

Try/catch 프로그램을 종료시키지 않고 어떠한 대처를 있다.

 

  1. 정상 실행되므로 Try/catch내의 에러를 잡기가 힘들다
  2. 3가지 Catch 존재한다
  • Catch Error(string memory reason){ … } : revert require 통해 생성된 에러 용도
  • Catch panic(uint errorcode) { … } : assert를 통해 생성된 에러

0.8버전에는 없고 0.8.1버전 부터 존재

  • Catch(bytesmemoryLowlevelData) { … } : low level error를 잡는다

3. 언제 사용되는지 ?

 

1) 외부 스마트 컨트랙트 함수를 부를 때 : 다른 스마트 컨트랙을 인스턴스화 해서, try/catch문이 있는 스마트 컨트랙의 함수를 불러와서 사용

 

// SPDX-License-Identifier:GPL-30

pragma solidity >=0.7.0 <0.9.0;

 

contract math{

    function division(uint256 _num1, uint256 _num2) public pure returns(uint256){

        require(_num<10,"num1 shoud not be more than 10");

        return _num1/_num2;

    }

}

contract runner{

   

    event catcherr(string _name, string _err);

    event catchpanic(string _name,string _err);

    event catchlowlevelerr(string _name,string _err);

    math public mathinstance = new math();

    function playtrycatch(uint256 _num1, uint256 _num2) public returns(uint256,bool){

        try mathinstance.division(_num1,_num2) returns(uint256 value){

            returns(value,true);

        }

        catch Error(string memory _err){

            emit catcherr("revert/require",_err);

            return(0,false)

        }

        catch Panic(uint256 _errorcode){

            emit catchpanic("assert/panic",_errorcode);

            return(0,false);

        }

        catch (bytes memory _errorcode){

            emit catchlowlevelerr("lowlevelerror",_errorcode)

            return(0,false);

        }

    }

 

}

 

2) 외부 스마트 컨트랙을 생성 할 때 : 다른 스마트컨트랙을 인스턴스화 할 때 쓴다.

 

contract character{

    string private name;

    uint private power;

    constructor(string memory _name,uint256 _power){

        name = _name;

        power = _power;

    }

}

contract runner{

    event catchonly(string _name,string _err);

    function playtrycatch(string memory _name,uint256 _power) public returns(bool successorfail){

        try new character(_name,_power){

            return(true);

        }

        catch{

            emit catchonly("catch","error");

            return(false)

        }

    }

Try 내에서의 revert 같은 에러들은 잡히지 않는다.

 

3) 스마트컨트랙 내에서 함수를 부를 때

 

contract runner2{

   

    function simple() public returns(uint256){

        return 4;

    }

    function playtrycatch() public return(uint256,bool){

        try this.simple() returns(uint256 value) {

            return(value,true)

        }

        catch{

            emit catchonly("catch","error");

            return(0,false);

        }

    }

}

 

- Error handler

  • Require

특정한 조건에 부합하지 않으면(false ) 에러를 발생시키고, gas를 환불 시켜준다.

function requirenow(uint256 _age) public pure{

     Require(false,"occurred");

     Require(_age>19,"you are not allowd to pay for the cigarette");

}

 

  • Assert

Gas를 소비한 , 특정한 조건에 부합하지 않으면 에러를 발생시킨다.

Gas 환불이 안된다.

But 0.8버전부터는 가스환불 된다.(revert 동일)

Panic(uint256)이라는 에러타입의 에러 발생

=> 에러 타입에 대해 정의되어 있다.

 

function assertnow() public pure{

     // false 이므로 에러 발생

     Assert(false);

     // 주로부적 테스트 용도,

}

 

  • Revert

조건없이 에러를 발생시키고, gas를 환불시켜준다.

 

function assertnow(uint256 _age) public pure{

     //무조건 에러 발생하며 메세지남길수 있다.

     revert("error ! ");

     // 조건도 없이 에러가 나오므로 실용적이지 못하다 따라서 if 함께 or require사용

     If(_age<19)}

     Revert("you are not allowed to pay for the cigarette");

}

}

이때는 revert에 대한 gas비용만 나간다.

 

  • Function 변수 명시

 

function add(uint256 _num1, uint256 _num2) public pure returns(uint256){

    uint256 total = _num1 + _num2;

    return total;

}

function add(uint256 _num1, uint256 _num2) public pure returns(uint256 total){

    total = _num1 + _num2;

    return total;

}

명시할 시에 함수 내에서 따로 선언해줄 필요 없으며 여러 값을 리턴하는 경우에 편하다.

 

 

  • Function Modifier

Revert나 require사용 시에 편하게 가능하다.

함수는 다르지만 require 조건이 같은 경우

 

contract modi{

   

    modifier onlyAdults{

        require(_age>18,"You are not allowd to pay for the cigarette");

        //revert("You are not allowd to pay for the cigarette");

        _; // 이 위치에 함수가 들어간다.

    }

    // return 되기 전에 revert문이 먼저 실행

    function buyCigarette() public onlyAdults returns(string memory){

        return "Your payment is succeeded";

    }

   

}

 

  • 이더 관련 함수
  • Payable : 이더/토큰과 상호작용시 필요한 키워드

Send, transfer, call 이용하여 이더를 보낼때 payable이라는 키워드가 필요하며 payable 주로 함수, 주소, 생성자에 붙여서 사용된다.

+ msg.value = 송금보낸 코인의

 

 

 

1. Send

2300gas 소비, 성공여부를 True 또는 False 리턴, 에러를 보지 못한다는게 단점

 

function sendnow(address payable _to) public payable{

    // 스마트컨트랙트 주소로도 이더를 받을수 있다.

    //msg.value는 창에 입력하는 송금 금액

    bool sent = _to.send(msg.value);

    require(sent,"Failed to send ether");

    emit howmuch(msg.value);

}

 

2. Call

가변적인 gas 소비(gas값 지정 가능), 성공여부를 true 또는 false 리턴

재진입 공격 위험성 있음

 

function callnow(address payable _to) public payable{

    // ~ 0.7

    (bool sent, ) = _to.call.gas(1000).value(msg,value)("");

    require(sent,"Failed to send either");

    //0.7 ~

    (bool sent, ) = _to.call{value:msg.value, gas:1000}("");

    require(sent,"Failed to send either");

    emit howmuch(msg.value);

}

 

 

3. Transfer

2300gas를 소비, 실패시 에러를 발생

 

function transfernow(address payable _to)public payable{

    _to.transfer(msg.value);

    emit howmuch(msg.value);

 

 

 

2019 이스탄불 하드포크 가스 가격이 올랐고 2300가스로는 스마트컨트랙트 이용이 부족할수 있음 그래서 다시 call을 사용하게

 

4. Balance

해당 특정 주소의 현재 갖고 있는 이더의 잔액을 나타낸다.

주소.balance 같은 형태로 사용한다.

 

 

5. Msg.sender

 

contract MobileBanking{

    event sendInfo(address _msgSender, uint256 _currentValue);

    event mycurrentValue(address _msgSender, uint256 _value);

    event currentValueOfSomeone(address _msgSender, address _to,uint256 _value);

 

    function sendEther(address _to payable) 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);

    }

 

 

}

 

msg.sender 활용한 권한 부여

 

 

contract MobileBanking{

   

    address owner;

    //생성자에 payable 키워드를 통해 스마트컨트랙이 처음 배포될떄 이더를 받을수 있다

    constructor () payable {

        // 배포할 때 그 주소를 owner에 넣는다

        owner = msg.sender;

    }

    event sendInfo(address _msgSender, address _msgReceiver,uint256 _currentValue);

    event mycurrentValue(address _msgSender, uint256 _value);

    event currentValueOfSomeone(address _msgSender, address _to,uint256 _value);

 

    modifier onlyOwner{

        require(msg.sender ==owner,"Only owner");

        require(msg.sender.balance>msg.value, "Your balance is not enough");

        _;

    }

    function sendEther(address payable _to) onlyOwner public payable{

        _to.transfer(msg.value);

        emit sendInfo(msg.sender,_to,(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);

    }

 

 

}

 

 

 

 

 

'BlockChain' 카테고리의 다른 글

Solidity 취약점 카테고리화  (0) 2022.01.11
Solidity #5  (0) 2022.01.10
Solidity #4  (0) 2022.01.07
Solidity #3  (0) 2022.01.06
Solidity #1  (0) 2022.01.04