Solidity by example

코와->코어·2021년 10월 1일
0

SKKRYPTO 개발

목록 보기
8/8

deploy : 블록체인 네트워크에 등록
compile : 블록체인 네트워크에 등록하기 전에 미리 검사?

1. 블록체인

블록체인은 전세계적으로 공유되어 트랜잭션이 일어나는 데이터베이스입니다.

변수

  • local : 함수 내부에 선언, blockchain에 기록 안 됨

  • state : 함수 외부에 선언, blockchain에 기록됨
    => public 키워드: 접근 제어자일 뿐, 변수 종류에 영향 미치는 건 아님 optional

  • global : blockchain관련 정보 (block.timestamp, msg.sender)

  • state 변수를 쓰거나 업데이트하려면 transaction을 보내야 함. 읽는 건 무료로 가능.

Gas

  • transaction은 either로 값을 낸다. gas spent * gas price

  • gas spent는 transaction에서 사용한 총 gas의 양

  • gas price는 gas당 얼마의 either를 지불할 것인지

  • gas price가 높으면 block에 포함될 우선순위 높아짐

  • 남은 gas는 환불됨

  • gas limit: 내가 설정한 값

  • block gas limit: 네트워크에서 설정한 값

  • if/else if/else

  • for/while/do while: while은 gas 다 써서 실패할 가능성 있으므로 비추

2. Mapping

keyType에는 uint, address, bytes가능
valueType에는 제한 없음
멤버 한 번에 하나씩 반환 불가능: 파이썬 dictionary처럼 전체 멤버 순회 불가능하다는 뜻
항상 return값을 가지고 설정 안 하면 default값(맨처음값 반환)

  • 주소에 임의의 값을 넣고 remix에서 실행하려면 어떻게 해야 하나?
    : 임의의 값 넣으면 실행 안 되는 게 맞음
contract NestedMapping {
    // Nested mapping (mapping from address to another mapping)
    mapping(address => mapping(uint => bool)) public nested;

    function get(address _addr1, uint _i) public view returns (bool) {
        // You can get values from a nested mapping
        // even when it is not initialized
        return nested[_addr1][_i];
    }

    function set(
        address _addr1,
        uint _i,
        bool _boo
    ) public {
        nested[_addr1][_i] = _boo;
    }

    function remove(address _addr1, uint _i) public {
        delete nested[_addr1][_i];
    }
}

3. Array

  • array를 리턴할 땐 무조건 memory를 써야 하는가? memory 키워드는 필수가 아닌가? -> gas 소모 안 하기 위해

  • delete arr[idx] : gas소모

  • arr[idx] = arr[arr.length-1] -> swap인가? 왜 이렇게 하면 gas 소모 안 하지? 소모를 안 하는 건지 적게 하는 건지

// Solidity can return the entire array.
    // But this function should be avoided for
    // arrays that can grow indefinitely in length.
    function getArr() public view returns (uint[] memory) {
        return arr;
    }

Enum

첫번째 요소가 default
언제나 index로 요소 지정 가능함

  • delete enumName;
    -> delete 내장 함수 찾아봐라
enum Status {
	pending, skipped, accepted
}

Struct

struct Todo {
	string text;
    bool completed;
}

초기화하기

  • Todo(_text, false)
  • Todo({text: _text, completed:false })
  • Todo memory todo;
    todo.text = _text;
  • bool 자료형은 항상 false가 default인가? ㅇㅇ

업데이트하기

  • Todo storage todo = todos[_index];
  • todo.text = _text;

자료형 위치

  • storage : state variable. blockchain에 저장됨
  • memory : function 호출될 동안만 존재한다.
    => 함수~함수 사이에만 존재하는 개념? 그렇게 하려면 storage로 선언해야 하고 memory키워드 붙이면 값만 반환하고 사라질 듯
  • calldata : external function에서만 가능. 특별한 저장공간임. 함수 인자 포함

함수

  • 여러 개 return 가능

  • public 함수는 map, array input, output 불가능

  • getter 함수는 view나 pure로 선언 가능

  • view : 상태변화 없음

  • pure : 상태변화 없고 읽지도 못함 -> return값을 못읽는다는 거? 매개변수는 읽을 수 있나? 컨트랙트 내부 변수는 안 건드리고 매개변수만 다루는 함수에 사용

에러

  • require(요구조건)
  • revert(에러조건)
  • assert() : 틀리면 안 되는 코드에 사용. fail하면 버그있다는 뜻, internal error 체크용

제어자

함수 호출 전후에 실행

생성자

contract 생성할 때 선택적으로 생성

상속

is 로 상속하고 다중상속 가능
overriden할 거면 부모는 virtual 키워드 꼭 쓰고, 자식은 override 키워드 꼭 써야 함
가장 오른쪽에 있는 부모가 덮어쓴다
부모->자식 순으로 써야 한다.

접근제어

function modifier는 deadlock 막는 용도

접근제어자는 함수의 visibility 제한하는 용도

  • public :
  • private : 내부, 상속받아도 안 됨
  • internal : 내부, 상속받으면 됨
  • external : 외부에서만

4. Interface

다른contract와 상호작용하기 위해
함수구현, 생성자, state변수 안 됨
상속 가능
모든 함수는 external로 선언해야 함

========================

  • interface만 모아 둔 .sol파일을 만들어서 사용해도 되나? 어떻게 import 없이 사용하지?매개변수 주소로 해당 함수와 변수가 선언된 컨트랙트 위치 불러옴

  • MyContract에서 매개 변수로 받는 _counter는 Counter contract 주소만 가능하다 ㅇㅇ

  • MyContract의 함수 내부 말고 state 변수처럼 함수 밖에다 ICounter 선언하고 사용하면 안되나? 그러면 값이 고정되기 때문에 변화하는 값을 불러올 수 없음

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

payable

function이나 address에 사용

이더 보내기/받기

보내기

  • transfer(2300 gas, throws error)
  • send(2300 gas, returns bool)
  • call(forward all gas or set gas, returns bool)

call함수+재입장 방지 제어자 사용 추천

받기

  • receive() external paybale : msg.data가 empty
  • fallback() external payalbe : 이외의 모든 경우

fallback은 매개변수도 없고 return도 안 함
1) 존재하지 않는 함수를 호출하거나
2) 이더를 보내는 데 receive()가 없거나 msg.data가 없으면
자동으로 실행됨

  • emit log(gasleft());
  • event keyword;
    -> emit, event가 뭔가?

5. Call

low level 함수
fallback()한테 이더 보낼 때 추천됨

  • 이미 있는 함수로 보낼 땐 비추?

  • {value: msg.value, gas: 5000} call 함수의 특별한 형식인 듯

// Let's imagine that contract B does not have the source code for
    // contract A, but we do know the address of A and the function to call.
    function testCallFoo(address payable _addr) public payable {
        // You can send ether and specify a custom gas amount
        (bool success, bytes memory data) = _addr.call{value: msg.value, gas: 5000}(
            abi.encodeWithSignature("foo(string,uint256)", "call foo", 123)
        );

        emit Response(success, data);
    }

6. Calling Other Contract

  • caller에서 실제로 세 가지 함수를 호출할 때, Callee 타입의 _callee에는 어떤 값이 들어가야 하나?
    : 초기화된 contract 그 자체
contract Callee {
    uint public x;
    uint public value;

    function setX(uint _x) public returns (uint) {
        x = _x;
        return x;
    }

    function setXandSendEther(uint _x) public payable returns (uint, uint) {
        x = _x;
        value = msg.value;

        return (x, value);
    }
}

contract Caller {

    function setX(Callee _callee, uint _x) public {
        uint x = _callee.setX(_x);
    }

    function setXFromAddress(address _addr, uint _x) public {
        Callee callee = Callee(_addr);
        callee.setX(_x);
    }

    function setXandSendEther(Callee _callee, uint _x) public payable {
        (uint x, uint value) = _callee.setXandSendEther{value: msg.value}(_x);
    }
}

7. DelegateCall

메시지 콜은 다양한 변형이 있는데, 델리게이트 콜 의 경우는 대상 주소의 코드가 호출하는 컨트랙트의 컨텍스트 내에서 실행된다는 것과 msg.sender 와 msg.value 가 값이 바뀌지 않는다는 것 외에는 메시지 콜과 동일합니다.

이것은 컨트랙트가 실행 중 다양한 주소의 코드를 동적으로 불러온다는 것을 뜻합니다. 스토리지, 현재 주소와 잔액은 여전히 호출하는 컨트랙트를 참조하지만 코드는 호출된 주소에서 가져옵니다.
=> 코드만 불러오고 데이터는 내 컨트랙트 내의 데이터 사용한다는 뜻

8. Library

컨트랙트와 비슷하지만, state 변수 선언이나 이더를 보내지는 못함
모든 함수가 internal이어야 함
아니면 deploy한 다음 호출하는 애가 deploy하기 전에 link되어야 함

using 키워드 사용하고 안 하고의 차이는 뭔가?

library Math {
    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
        // else z = 0 (default value)
    }
}

contract TestSafeMath {
    using SafeMath for uint;

    uint public MAX_UINT = 2**256 - 1;

    function testAdd(uint x, uint y) public pure returns (uint) {
        return x.add(y);
    }

    function testSquareRoot(uint x) public pure returns (uint) {
        return Math.sqrt(x);
    }
}

9. 솔리디티 공식 문서

  • mint는 뭔가?

  • 트랜잭션
    한 계정에서 다른 계정(같을수도 있고, 비어있을 수도 있습니다. 아래 참조)으로 보내지는 일종의 메시지입니다. 그리고 바이너리 데이터("페이로드"라고 불림)와 Ether 양을 포함할 수 있습니다.
    대상 계정이 코드를 포함하고 있으면 코드는 실행되고 페이로드는 입력 데이터로 제공됩니다.
    대상 계정이 설정되지 않은 경우(트랜잭션에 받는 사람이 없거나 받는 사람이 null 로 설정된 경우) 일 땐, 트랜잭션은 새로운 컨트랙트 를 생성하며 앞서 말씀드렸던 것처럼 사용자와 "논스"로 불리는 트랜잭션의 수에 의해 주소가 결정됩니다. 각 컨트랙트 생성 트랜잭션 페이로드는 EVM 바이트코드로 실행되기 위해 사용됩니다. 이 실행 데이터는 컨트랙트의 코드로 영구히 저장됩니다. 즉, 컨트랙트를 만들기 위해 실제 코드를 보내는 대신, 실행될 때의 코드를 리턴하는 코드를 보내야 한다는 것을 뜻합니다.

  • 스토리지는 256비트 문자가 키-값 형태로 연결된 저장소입니다. 컨트랙트 내의 스토리지를 탐색하는 건 불가능하며 읽고 수정하는데 비용이 많이 듭니다. 컨트랙트가 소유하지 않은 스토리지는 읽거나 쓸 수 없습니다.

  • 동기/비동기 정의

  • 호출은 1024개의 깊이로 제한되며 이는 복잡한 연산일수록 재귀호출보다 반복문이 선호된다는 것을 뜻합니다

profile
풀스택 웹개발자👩‍💻✨️

0개의 댓글

관련 채용 정보