[멋쟁이 사자처럼 블록체인 스쿨 3기] 23-05-18

임형석·2023년 5월 22일
0

Solidity


gas 비교 1.

솔리디티에서는 가스비를 최소한으로 아껴야 컨트랙트를 잘 짠다고 할 수 있겠다.

가스비는 이더리움. 돈과 연결되는 부분이므로 줄일 수 있을 만큼 최대한 줄여야 좋은 컨트랙트이다.

먼저 가스비 비교를 위해 아래와 같은 코드를 작성한다.

contract POPvsDELETE {
    uint[] public a;

    function pushA(uint _n) public {
        a.push(_n);
    }

    function returnA() public view returns(uint[] memory) {
        return a;
    }

    function popA() public {
        a.pop(); 
    } 

    function deleteA() public {
        delete a; // 81202, 37288, 25546
    } 
    function renewA() public {
        a = new uint[](0);
    } 
}

pop vs delete

array를 초기화 시키기 위해서 delete는 한번만 수행, pop은 여러번 수행해야하는 차이가 있음.

  1. delete : 첫 실행 시 가스비가 많이 듦. 이후엔 같은 양의 cost 를 소모.

  1. pop : 같은 양의 cost를 소모하지만 맨 마지막 번에는 추가적인 gas를 요구함
    (아래 사진)

결과.

pop : 5번 실행 시 213,530 gas 사용
delete : 5번 실행 시 201,268 gas 사용
(string 배열을 이용해서 실험해도 거의 비슷한 결과)

결론.

pop 은 처음 실행부터 마지막 전까지 일정한 cost 유지, 마지막 실행은 많이 듦.
delete 는 처음 실행 시 가스비가 많이 듦. 이후엔 적은 cost 값 유지.

5번 이상 실행 시엔 delete 가 효율적, 4번까지 실행시엔 pop 이 더 효율적.


gas 비교 2.

delete vs renew

요약.

delete와 renew 모두 내부 요소의 개수가 커질수록 cost도 늘어남.

4개의 경우 그리고 10개의 경우 모두 delete가 gas, transaction, execution cost 모두 낮음.

uint[]의 경우에는 delete가 renew 보다 (위 코드에서는) 효율적으로 보임.


Test 1

1) 숫자를 넣었을 때 그 숫자의 자릿수와 각 자릿수의 숫자를 나열한 결과를 반환하세요. 예시) 3902 > 4 / 3,9,0,2

아래와 같이 함수를 만들었다.

function test1(uint _n) public pure returns(uint, uint[] memory) {

        uint num = _n;
        uint num2 = _n;
        uint a;

        for( ; num > 0 ; ) {
            a++;
            num /= 10;
        }

        uint[] memory number = new uint[](a);

        for(; a > 0 ;) {
            number[a-1] = num2 % 10;
            a--;
            num2 /= 10;
        }

        return (number.length,number);
    }

문제를 보자마자, 자릿수를 하나씩 넣어주려면 uint 형식의 배열. 그리고 배열의 길이를 가져오면 쉽게 풀 수 있을거라 생각했다.

먼저, 받아온 값을 uint 형 지역변수 num 을 선언하고 넣어준다.

그리고 for 문을 사용해 a 에 num 의 자릿수를 찾아준다.

for 문 대신 while 문을 사용해도 됨.

그리고 자릿수 만큼의 값을, 새로운 uint[] 에 지정해준다.

자릿수를 지정하지 않으면, new uint[] 문법을 사용할 수 없기 때문이다.

그리고 다시 for 또는 while 문을 이용해서 number 배열에 % 로 나눈 나머지값을 하나하나 넣어준다.

만약, 3579 라는 값이라면 for 문 안에서 이렇게 배열에 하나 하나 값이 담길 것이다.

number[3] = 3579 % 10;		-> 9
number[2] = 357 % 10;		-> 7
number[1] = 35 % 10;		-> 5
number[0] = 3 % 10;			-> 3

이렇게 값을 담은 배열의 길이와 값을 반환해주면 된다.


test 2

2) 문자열을 넣었을 때 그 문자열의 자릿수와 문자열을 한 글자씩 분리한 결과를 반환하세요. 예시) bear > 4 / b,e,a,r

아래와 같이 함수를 만들었다.

function test2(string memory _n) public pure returns(uint, string[] memory, string memory){

        uint length;
        length = bytes(_n).length;

        bytes1[] memory array = new bytes1[](length);

        for(uint i; i < length ; i++){
            array[i] = bytes(_n)[i];
        }

        string[] memory str = new string[](length);
        
        for(uint i; i < length ; i++){
            str[i] = string(abi.encodePacked(array[i]));
        }
        
        return (length, str, string(abi.encodePacked(array)));
    }

먼저, string 형태는 숫자보다 더 쉽다고 생각했다. string => bytes 형태로 바로 바꿀 수 있기에.

먼저, 받아온 string을 bytes으로 바꾸어주었다.

bytes 는 배열이기에, length 로 배열의 길이를 가져올 수 있다.

length 라는 지역변수를 선언해서 배열의 길이 값을 넣어주었다.

그리고 for 문을 이용해서 bytes(_n)[] 을 하나하나 array 에 넣어주었고,

그리고 new 를 이용해 length 만큼의 자릿수를 정의해준 string[] 형태 str 을 선언해주었다.

그리고 for 문을 이용해서 str 배열에 string 으로 변환시킨 bytes 값을 하나하나 넣어주었다.

이 문제에서 가장 막혔던 부분은... 아래다.

string(abi.encodePacked(array[]))

이전까진 keccak256 만 사용했지, abi encodedpacked 을 사용해서 bytes => string 으로 형변환 시키는 방법은 모르고 있었다.

string(array[]) 

이렇게도 만들어보고 여러가지 방법을 다 시도해봤었는데, 결국 저 마지막 부분은 답지를 보고 풀었다.


0개의 댓글