[Solidity]Libraries

끼리·2022년 3월 22일
1

솔리디티 공부

목록 보기
3/4

Solidity Contracts 간에 코드를 공유해야 할 때, Libraries를 활용해야 한다.

라이브러리를 사용하면. 많은 스마트 컨트랙트에 공통적인 기능을 저장할 수 있다. 라이브러리 함수는 재사용 가능한 함수에서 계약 패턴 및 권한에 이르기까지 다양하다.

라이브러리를 사용해야 하는 이유는 다음과 같다.

오류 및 버그 발생 가능성 제한하는 새 코드 감소

새로 코드를 짜지 않아도 되는 개발 시간 절약

잘 짜여진 코드 및 기존 모범 사례를 통해 계약 보호

이미 구축된 코드를 사용하여 가스 절약

가스비를 절약하는 것은 라이브러리 사용 방법에 따라 다르다. 라이브러리는 계약처럼 자체적으로 배치할 수 있으며 계약 바이트코드로 직접 컴파일할 수도 있다.

UINT LIBRARY

라이브러리는 컨트랙트와 상당히 비슷하다. 둘다 동일한 구문(contract C {} 및 library C {})으로 정의된다. 둘 다 동일한 속성, 유형, 구문을 가진 함수들을 포함하고 있다. 또한 동일한 전역 변수와 연산 코드에 접근할 수 있다.

그렇다면 차이점은 무엇일까?

라이브러리와 컨트랙트의 한 가지 주요한 차이점은 라이브러리는 state가 없다. 라이브러리에서는 state 변수를 선언하려고 하면 컴파일되지 않습니다.

또한 라이브러리는 ethers를 받거나 상속을 해주거나 받거나 폐기할 수 없다.

라이브러리의 목적코드를 공유하는 것이다.

라이브러리는 기본적으로 재사용 가능한 알고리즘이 있는 함수를 포함하고 있다. 이것은 개발자들이 바퀴를 재발명하는 것을 멈추도록 도울 것입니다!

좋은 라이브러리는 엄격한 테스트와 검증을 받는다.따라서 이러한 라이브러리를 사용하면 버그가 발생할 가능성이 줄어들기 때문에 계약에 도움이 된다!

Open Zeppelin은 이 분야의 선도적인 보안 회사들에 의해 많은 검증을 받은 훌륭한 Solidity 라이브러리를 제공한다.

Library Functions

라이브러리 기능은 주로 컨트랙트에 의해 사용되도록 되어 있다. 라이브러리는 그 자체의 state가 없다.
이러한 이유로 라이브러리 함수는 대부분 pure하며 읽기/쓰기 상태를 나타내지 않는다.
라이브러리 함수는 pure 또는 view로 표시된 경우에만 직접 호출할 수 있다. 테스트에서 자바스크립트에서 직접 라이브러리 함수를 호출하는 것을 볼 수 있다. 이것은 함수가 상태를 수정하지 않을 것으로 표시된 경우에만 작동한다.
이 함수가 상태를 수정하지 않는다는 것을 보장하지 않으면 라이브러리의 ABI로 컴파일되지 않는다. 이는 테스트나 ABI 검증에서 액세스할 수 없음을 의미한다.
그렇다면, 라이브러리에 직접 호출하는 것에 대한 대안은 무엇일까?

라이브러리 함수는 컨트랙트/메시지 데이터를 사용하고 컨트랙트상의 상태 변수에 접근할 수 있는 컨트랙트의 context에서 실행될 수 있다. 이 시점에서 라이브러리는 컨트랙트 state을 변경할 수 있다.

pragma solidity ^0.8.4;

library UIntFunctions {
    
function isEven(uint x) public pure returns(bool) {
		return x % 2 == 0;
	} 
    
}

이때 함수는 무조건 pure 혹은 view 형태여야 한다.

USING LIBRARY

라이브러리를 사용해보자
라이브러리는 방금 위에서의 테스트 코드와 마찬가지로 직접 호출할 수 있다. 그러나 그것들은 일반적으로 컨트랙트로 import된다.

Library Compilation & Communication

라이브러리를 import 해오는 것은 두가지의 경우가 있다.
1. 계약서에 internal로 표시된 라이브러리 기능을 사용할 경우 함수 내부의 코드가 컨트랙트 자체에 복사돼 컨트랙트와 함께 컴파일된다.

2.계약이 external 또는 public 으로 표시된 라이브러리 기능을 사용하는 경우 라이브러리는 자체 주소로 배포되어야 한다. 그러면 컨트랙트는 도서라이브러리 주소로 연결된다. 런타임에서, 컨트랙트는 라이브러리 함수에 접근하기 위해 DELEGATECALL (an EVM opcode)을 사용하여 메시지를 생성할 것이다.

라이브러리를 import 해올때, 해당 코드는 계약 코드와 동일한 context에서 실행된다. 라이브러리가 바이트코드로 컴파일되면 코드는 실제로 컨트랙트 내부에서 실행된다. 라이브러리가 연결되면 DELEGATECALL opcode는 컨트랙트 context 를 유지하면서 라이브러리를 호출한다.

라이브러리가 자체적으로 배포되어야 할 때, 솔리디티 컴파일러는 컨트랙트 바이트코드 안에 placeholder을 삽입한다:

608060405234801561001057600080fd5b5073__$ba528da1e2dc9d528a3d6faf88239359ae$__633ef7df506040518163ffffffff1660e01b815260040160206040518083038186803b15801561005557600080fd5b505af4158015610069573d6000803e3d6000fd5b505050506040513d602081101561007f57600080fd5b81019080805190602001909291905050506000819055506085806100a46000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80630c55699c14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea264697066735822122086180a06d1da4b2b704e3d89f6c825310e78b0304df1d268790893e24b9f206164736f6c63430006020033

위에서의 ba528da1e2dc9d528a3d6faf88239359aeba528da1e2dc9d528a3d6faf88239359ae 가 link placeholder 다.
컴파일러는 또한 바이트코드 내에 placeholder가 존재하는 위치에 대한 참조를 제공한다:

"linkReferences": {
    "contracts/Library.sol": {
        "Library": [
            {
                "length": 20,
                "start": 19
            }
        ]
    }
}

이는 Library.sol에 대한 기준이 16진수 문자 38(19바이트)에서 시작하여 40자 길이임을 나타냅니다.

라이브러리가 배포되면 placeholder는 블록체인의 라이브러리 주소로 바뀔 수 있다.

placeholder가 배포되면 컨트랙트 바이트코드가 배포될 준비가 된다!

EVM에서 라이브러리 함수가 호출되어야 할 때, 컨트랙트는 라이브러리의 주소에서 함수를 실행하기 위해 DEARGCALL로 메시지를 생성할 것이다.

마지막에서 짠 코드 라이브러리를 컨트랙트 예제로 사용하면,
사용하는 두 가지 방법이 있다. 첫번째는

import "./UIntFunctions.sol";
contract Example {
    function isEven(uint x) public pure returns(bool) {
        return UIntFunctions.isEven(x);
    }
}

여기서 UIntFunctions 라이브러리를 import하고 함수 isEven을 호출할 수 있다.

또 다른 방법은 다음과 같다.

import "./UIntFunctions.sol";
contract Example {
    using UIntFunctions for uint;
    function isEven(uint x) public pure returns(bool) {
        return x.isEven();
    }
}

이 예제에서 UIntFunctions 라이브러리를 uint 데이터 유형에 적용한다. 이것은 컨트랙트에 있는 모든 기능에 라이브러리의 모든 기능이 추가될 것이다.

USING LIBRARY 관련 예제 코드

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

import "./UIntFunctions.sol";

contract Game {
    uint public participants;
    bool public allowTeams;

    constructor (uint a){
        participants = a;
        if(UIntFunctions.isEven(a))
        {
              allowTeams = true;
        }
       
    }
}

CONSOLE LOG

자바스크립트의 console.log와 마찬가지로 솔리디티에도 존재한다.

// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import "hardhat/console.sol";

contract Contract {
    constructor(uint x, string y, bool z) {
        console.log(x); // 1
        console.log(y); // Hello World!
        console.log(z); // true
    }
}

console.log는 하드햇의 라이브러리를 사용할수있다.

EVENLY DIVIDES

Base Functions

또한 기본적인 목적을 가진 기능을 만들어 다른 라이브러리 기능에 재사용할 수 있다.

이러한 방식으로 코드를 더 쉽게 dry(Don't repeat yourself)시킬 수 있다.

pragma solidity ^0.8.4;

library Prime {
    function dividesEvenly(uint a,uint b) external pure returns(bool){
        if(a%b==0)
        {return true;}
    }
}

IS PRIME

library UIntFunctions {
    function isEven(uint x) public pure returns(bool) {
        return x % 2 == 0;
    }
    function isOdd(uint x) public pure returns(bool) {
        return !isEven(x);
    } 
}

해당 코드 처럼 isOdd 함수는 isEven함수를 NOT operator만 사용할 수 있다.

이를 활용한 ISPRIME의 간단한 코드는 다음과 같다.

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

library Prime {
	function dividesEvenly(uint x, uint y) public pure returns(bool) {
		return (x % y == 0);
	}

	function isPrime(uint x) public pure returns(bool) {
		for(uint i = 2; i <= x / 2; i++) {
			if(dividesEvenly(x, i)) {
				return false;
			}
		}
		return true;
	}
}

NEXT PRIME

Block Global

Solidity 내부에서 우리가 접근할 수 있는 많은 전역 속성 중에는 블록이 있다. 블록은 이 트랜잭션이 마이닝되고 있는 현재 블록에 대한 정보를 알려준다.

block.coinbase - 이 블록 주소의 마이너
block.난이도 - 현재 블록의 난이도
block.gaslimt - 블럭의 총 가스 제한
block.number - 현재 블록 번호
block.timestamp - 블록의 현재 타임스탬프(unix epoch 이후 초)

계약 및 라이브러리에 다음과 같이 매우 간단하게 사용할 수 있다.

import "hardhat/console.sol";
contract MyExample {
    constructor() {
        console.log( block.timestamp ); // 1583271154
        console.log( block.number ); // 9600665
    }
}
pragma solidity ^0.8.4;

import "./Prime.sol";

contract PrimeGame {
    using Prime for uint;

    function isWinner() public view returns (bool) {
        return block.number.isPrime();
    }
}

0개의 댓글