솔리디티 문법 요약

citron03·2022년 3월 22일
0

블록체인

목록 보기
10/19

SPDX License Identifier

  • 스마트 컨트랙트에 대한 신뢰를 높이고 저작권 문제를 해소하기 위해서 솔리디티 코드 최상단주석으로 SPDX 라이센스를 명시한다.
// SPDX-License-Identifier: MIT

pragma 키워드

  • 특정 컴파일러의 버전을 표기하기 위해 사용한다.

  • pragma 키워드는 모든 소스코드 파일에 있어야한다.

  • 다른 파일을 import하더라도 pragma는 자동으로 불러와지지 않기에 꼭 따로 작성해주어여 한다.

  • 버전 규칙은 npm 문법과 동일하다.

  • 특정 버전 이상의 pragma를 사용할 땐 ^를 붙인다.

pragma solidity 0.8.7; 
pragma solidity ^0.8.7; // 0.8.7 버전 이상

import

  • 자바스크립트에서의 방식과 동일하다.
import 'file' as 'good' from 'file';
import {func1, func2} from 'file'

컨트랙트 작성

contract name {
	...
}

변수 데이터 타입

  • 기본(값)형 데이터 타입으로 bool, int, uint, 고정 바이트 배열, address 객체가 있다.

  • 부호가 있는 경우(+ / -)에는 int, 부호가 없는 0 이상의 값에 uint를 사용한다.

  • int와 uint 뒤에 uint8과 같이 8의 배수인 숫자를 붙여 변수의 크기를 비트 단위로 지정할 수 있다.
    🍇 int16은 -32768 ~ 32767 사이의 정수를 표현할 수 있다.
    🍇 uint16은 0~65535 사이의 정수를 표현할 수 있다.

  • 사용되는 비트의 크기가 적을수록 가스비가 적게 든다.

  • 고정 바이트 배열은 bytes1 에서 bytes32 까지 사용할 수 있으며, 고정된 크기의 배열을 선언한다.

bytes2 isOn = 'on';
isOn[0]; // 'o'
isOn[1]; // 'n'
  • 주소 객체는 Ox로 시작하며 최대 40자리의 16진수로 구성되는 문자열을 값으로 가진다.

  • 주소 객체의 크기는 20바이트이다.

  • 주소는 주로 계정의 잔액을 반환하는 함수인 balance() 함수와 이더를 계정으로 전송하는 transfere() 함수에서 사용된다.

  • 솔리디티 0.8 버전 이상에서 address는 송금이 불가능한 주소값이다.
    🥩 스마트 컨트랙트에서 특정 주소 값으로 송금을 하기 위해서는 payable이 필요하다.

uint160 num = '임의의 값';
address addr = address(num);
address payable send_address = payable(addr); // 송금 가능 주소
  • 컨트랙트를 address payable로 변환할 수도 있다.
contract A  {  
	// 이더를 받을 수 있는 컨트랙트
	constructor () payable { }
} 

address payable addr = address(A);  
// address(A)는 adress payable 형식의 주소값을 반환한다
contract B { 
	// 이더를 받지 않는 컨트랙트
	constructor () { }
}

address addr = address(B); 
// address(B)는 adress 형식의 주소값을 반환한다

address payable addr_payable = payable(addr); 
// payable()을 사용해 address payable 형식의 주소값을 만들 수 있다.
  • function에도 payable을 붙일 수 있다.

  • 솔리디티 데이터의 영역에는 두 종류가 있다.

🍔 memory는 프로그램이 동작하는 동안에만 값을 기억하고 프로그램이 종료되면, 값을 잃는다.
ex. 함수 내부의 상태 변수

🍔 storage는 블록체인에 기록되어 영구적으로 값이 유지된다.
ex. 컨트랙트 전역 변수

uint memory count = 100;
uint storage age = 32;
  • 참조형 데이터 타입으로 배열, 문자열, 구조체, 매핑이 있다.

  • 동적 배열과 정적 배열 선언

uint[10] arrayName1; // 정적
uint[] arrayName2; // 동적
  • 솔리디티의 동적 배열에서는 push를 통해서 데이터를 추가할 수 있다.

  • 배열은 mapping 과는 다르게 길이(length)도 구할 수 있다.

  • pop으로 마지막 원소를 지울 수도 있고, delete로 원하는 인덱스의 값을 0이나 null로 만들 수 있다.
    🍦 delete는 엄밀히 말하면, 삭제하는 것이 아니기에 length의 크기는 변함이 없다.

arrayName2.push("new data 1");
arrayName2.push("new data 2");

uint len = arrayName2.length; 

arrayName2.pop();
delete arrayName2[0];
  • public으로 배열을 선언하면, 자동으로 getter 메소드가 생성된다.
uint[] public userAge;
  • string은 bytes와 동일하지만, 길이(length)와 push()가 없다.
string name = 'Hi man';
  • 구조체는 다음과 같이 선언한다.
struct Person {
    address account;
	string name;
    uint8 age;
}

// 사용하기
Person kim = Person("0x주소", "kim", 35);
  • mapping은 스토리지 데이터 영역에서 key-value 형식으로 데이터를 저장한다.
// 키 데이터 타입 => 값 데이터 타입
mapping(uint => address) public userAddress;

userAddress[키] = 값;
  • 글로벌 변수로 block, msg, tx, This가 있다.

🍙 block은 블록에 대한 정보를 지닌다.
ex. blockhash, coinbase, gaslimit, number, timestamp

🍙 msg는 컨트랙트를 시작한 트랜잭션 콜이나 메세지 콜에 대한 정보를 가진다.
ex. gasleft(), data, sender, gas, value

🍙 tx는 트랜잭션 데이터를 가진다.
ex. gasprice, origin

🍙 This로 현재 컨트랙트를 참조할 수 있다.
(현재 컨트랙트 주소로 암시적으로 변환된다)

상태(state) 변수

  • 값이 컨트랙트 스토리지에 영구적으로 저장되는 변수이다.
contract C {
  uint storedData;
  uint storedData = 1;
}

상태 변수 접근 수준

  • internal : 디폴드값이며, 컨트랙트와 해당 컨트랙트를 상속받은 컨트랙트만 사용할 수 있는 변수이다.

  • public : 컴파일러가 public 상태 변수에 getter 함수를 만든다. getter 함수로 외부 컨트랙트나 클라이언트 코드에서 public 변수에 접근할 수 있다.
    (당연히 컨트랙트 내에서도 직접 public 상태 변수를 사용할 수 있다)

  • private : 동일한 컨트랙트 맴버만 접근할 수 있다.

constant를 통해서 상태 변수를 상수로 선언할 수 있다.

uint constant age = 5;

함수 선언

function func1 (int _a, uint8 _b, string _name){
	~~~
}

// 반환이 있을 때
function func1 (int _a) returns (uint c){
	~~~
    uint c = 100;
}

function func1 (int _a) returns (uint){
	~~~
    uint c = 100;
    return c;
}
  • 함수 인자명을 _로 시작하여 전역변수와 구별하는 것이 관례이다.

함수의 접근 수준

  • public : 디폴트값이며, 컨트랙트 내부, 외부 컨트랙트, 클라이언트 코드에서 모두 호출이 가능하다.

  • external : 외부 컨트랙트나 클라이언트 코드에서 호출할 수 있으며, 컨트랙트 내부에서의 호출은 불가능하다.

  • internal : 컨트랙트 맴버와 상속된 컨트랙트에서만 호출이 가능하다.

  • private : 컨트랙트 맴버만 호출이 가능하다.
    🍉 private 함수의 이름은 _로 시작하는 것이 관례이다.

function _getName () private view returns (string){
	return name;
}

view로 표시된 함수는 상태를 변경하지 않는 읽기 전용 함수이다.

pure로 표시된 함수는 스토리지에서 변수를 읽거나 쓰지 않는 함수이다.

payable이 표시된 함수는 이더를 받을 수 있다.

함수 형태 요약

function (<parameter types>) {internal | external | public | private} [pure | constant | view | payable] [(modifiers)] [returns (<return types>)]

생성자 함수로 컨트랙트가 생성되었을 때, 컨트랙트의 상태를 초기화할 수 있다.

contract C {

    address public account;
    
    constructor(address _account) init {
        account = _account
    }
}

selfdestruct(컨트랙트 생성자 주소); 로 컨트랙트를 소멸시킬 수 있다.

함수 변경자, modifier

  • modifier를 통해서 함수에 변경자를 적용할 수 있다.
    🍡 변경자는 함수 실행 전, 요구 조건을 만족하는지 확인하는 작업니다.
// 변경자는 _;로 작성한다.

uint public count;

modifier counting {
	count++;  // 함수가 실행되기 전 실행된다.
	_;  // 함수 실행
	count--; // 함수가 실행된 후 실행된다
}

function func1() public counting {
	if(count < 10){
    	~~~
    }
}

is를 통해서 컨트랙트를 상속할 수 있다.

  • 다중 상속도 가능하다.
contract Child is Parent1, Parent2, Parent3 {
	~~~
}

revert와 require, assert로 에러 헨들링을 할 수 있다.

  • revert는 해당 함수를 종료하고 에러를 리턴한다.
revert("에러 발생")
  • require와 assert는 설정한 조건이 참인지 확인한 뒤에 조건이 거짓일 경우 에러를 리턴한다.

  • require는 if revert 처럼 게이트 키퍼의 역할을 한다.
    🍚 게이트 키퍼는 조건을 만족했을 경우에만 실행되게 하는 역할을 의미한다.

  • assert와 require의 사용법은 같지만, assert는 사용하지 않은 가스를 호출자에게 반환하지 않으며 공급된 가스를 모두 소모하여 상태를 원래대로 되돌린다.

require(count <= 10, "10회 초과 에러 발생");
// 조건이 참이면 진행, 거짓이면 에러를 리턴한다.

enum

// 다음과 같이 선언할 수 있다.
enum feeling {
	good, bad, soso
}

// 변수로 할당
feeling now = feeling.soso;
  • enum의 데이터는 순서대로 0부터 각 값(index)이 할당된다.

event

  • event는 어떤 결과가 발생했을 때, 해당 컨트랙트에서 dApp 클라이언트나 또 다른 컨트랙트에 전달한다.

  • event 키워드로 이벤트를 설정하고, 경우에 따라서 emit 키워드로 이벤트를 실행한다.

  • 이벤트가 실행되면, 트랜잭션에 기록된다.

contract C {
	event Transfer(address from, address to, unit256 value);
    
    function act(address to, address amount){
    	emit Transfer(msg.sender, to, amount);
    }
}

Keccak256

  • 솔리디티는 내장 해시 함수로 Keccak256을 가진다.

  • 입력 string을 랜덤 256비트 16진수로 매핑한다.

  • Keccak256을 통해서 난수를 생성할 수도 있다.

keccak256("hi");

uint rand = uint(keccak256("hi")); // uint로 형변환

형변환

uint coke = 500;
uint16 cake = 6000;

uint16 totalPrice1 = coke + cake; // 에러가 발생
uint16 totalPrice2 = uint16(coke) + cake; // 에러가 발생하지 않는다

솔리디티에서 Interface는 사용할 함수의 형태를 선언한다. 실제 함수의 내용은 Contract에서 사용한다.

참고 자료 출처 : 코드 스테이츠

profile
🙌🙌🙌🙌

0개의 댓글