4 분 소요

솔리디티


솔리디티란 객체 지향 프로그램이 언어로써, 이더리움 블록체인 플랫폼에서 다양한 스마트 컨트랙트(계약 로직)를 작성할 때 사용한다.

특징

  • 정적 타입의 중괄호 언어 : 솔리디티는 정적 타입의 중괄호 프로그래밍 언어, JS와 다르게 컴파일 시에 변수에 타입이 결정되기 때문에 소스코드에 타입을 명시해줘야 한다.
  • Ethereum Virtual Machine(EVM) : 솔리디티는 EVM 위에서 동작한다.
  • 세미콜론(;) : 솔리디티는 문장 끝에 반드시 세미콜론을 붙여야 한다.

Base

SPDX License Identifier


스마트 컨트랙트에 대한 신뢰를 높이고 저작권과 같은 문제를 해소하기 위해 솔리디티 코드의 최상단에 SPDX 라이센스를 명시해야 한다.

// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: GPL-3.0

SPDX 라이센스 리스트

Pragma


pragma는 특정 컴파일러의 버전을 표기할 때 사용한다. 모든 소스 코드 파일에 있어야 하며, 다른 파일을 임포트 하더라도 pragma는 자동으로 임포트되지 않는다.

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.14 //0.8.14 버전 사용
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.14 //0.8.14 버전 이상 사용
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0 //0.4.0 이상 0.9.0 미만의 버전을 사용

Contract


솔리디티 계약은 이더리움의 특정 주소를 가진 기능(코드)와 상태(데이터)의 모음이다.

특정 기능은 함수, 상태는 변수로 표현한다.

contract SimpleStorage {
	uint storedData; //상태 변수
	
	//함수
	function set(uint x) public {
		storedData = x;
	}
	
	//함수
	function get() public view returns (uint) {
		return storedData;
	}
}

Import


파일을 임포트하는 방식은 JS와 동일하다.

import "파일명";

//임포트하는 파일을 symbolName이라는 이름으로 사용
import * as symbolName from "파일명";
import "파일명" as symbolName;

//파일의 일부분만 임포트 하는 경우
import {symbol1 as alias, symbol2} from "파일명";

주석


// 단일 라인 주석
/*
 여러 라인 주석
*/ 

변수

데이터 저장 위치


  • 솔리디티의 데이터 저장 위치
    • 메모리
    • 스토리지
    • Calldata(메모리처럼 동작, 수정 불가능하고 비영구적)
  • 강제 데이터 저장 위치
    • 외부 함수의 매개 변수(반환 값 미포함) : calldata
    • 상태 변수 : 스토리지
  • 기본 데이터 위치
    • 함수의 매개변수(반환 값 포함) : 메모리
    • 모든 지역 변수 : 스토리지

변수의 종류


상태 변수

  • 컨트랙트 저장소(이더리움 블록체인)에 영구적으로 저장되는 변수
  • 항상 스토리지에 저장된다.
pragma solidity ^0.8.14;

contract SimpleStorage {
	uint storedData; // 상태변수 선언
	uint storedData2 = 20; // 상태변수 선언 및 초기화
}
  • 접근 수준
    • internal(default)
      • 상태 변수에 기본적 사용
      • 컨트랙트 및 해당 컨트랙트를 상속받은 컨트랙트만 접근 가능
      • 외부에서 엑세스 불가능
    • public
      • 컴파일러가 자동으로 getter 함수를 생성해줌
      • 컨트랙트 내에서 직접 퍼블릭 상태 변수를 사용 가능
      • 외부 컨트랙트나 클라이언트 코드에서도 getter 함수를 통해 퍼블릭 상태 변수에 접근 가능
    • private
      • 동일한 컨트랙트 멤버만 프라이빗 상태 변수에 접근 가능
    • constant / immutable
      • 선언될 때 값을 할당해야 함
      • 상수화 => 변경 불가능

지역변수

  • 함수가 실행될 때까지만 존재하는 변수
  • 기본값으로는 스토리지에 저장(레퍼런스 타입의 경우 재정의 가능)
pragma solidity ^0.8.14;

contract SimpleStorage {
	
	function simpleFunction() public pure returns(uint) {
		uint a; // 지역 변수 선언
		uint b = 1; // 지역 변수 선언 및 초기화
		a = 1;
		uint result = a + b;
		return result;
		
	}
}

전역변수

  • 글로벌한 블록체인 안에 있는 특수 변수
  • 블록체인 및 트랜잭션에 대한 속성을 가져올 수 있음
function f(uint start, uint daysAfter) public {
	if(block.timestamp >= start + dayAfter * 1days) {
		// 여기서 block.timestamp가 전역변수
	}
}
  • 속성
    • block : 블록에 대한 정보
    • msg : 컨트랙트를 시작한 트랜잭션 콜이나 메시지 콜에 대한 정보
    • tx : 트랜잭션 데이터
    • This : 현재 컨트랙트 참조, 현재 컨트랙트 주소로 암시적 변환

전역변수

자료형

값 형 데이터 타입(Value Types)


Bool

bool isOpen = true;
bool isSold = false;

정수(int, uint)

  • 부호가 있는 경우 int, 부호가 없는 0 이상의 값 uint
  • int, uint 뒤에 8의 배수를 붙여 변수의 크기를 비트 단위로 지정 가능
    • ex. int8, uint64
int8 seoulTemp= -20;
uint16 myAge = 30;

주소(address)

  • 유형
    • address : 20바이트의 이더리움 주소 값
    • address payable : address와 동일한 값을 가지지만 추가 멤버인 transfer, send를 가짐
address yourAddress = 0x10abb5efEcdc01234f7b2384912398798E089Ab2;
  • 0.8 버전부터 address 형식은 송금이 불가능한 주소값
    • 스마트 컨트랙트에서 특정 주소 값으로 송금하기 위해서는 address payable 형식을 사용
// address -> address payable
address addr1;
address payable p_addr1 = payable(addr1); 

// uint160 or bytes20 -> address payable
uint160 num;
address addr = address(num)
address payable p_addr = payable(addr)

// contract -> address payable
contract C { // 이더를 받을 수 있는 컨트랙트
	constructor () payable {}
}
address payable addr = address(C);

// contract -> address payable
contract D { // 이더를 받을 수 없는 컨트랙트
	constructor () {}
}
address addr = address(D);
address payable addr_p = payable(addr);

바이트 배열(고정 크기)

  • byte1 ~ byte32까지의 고정된 크기의 배열
  • 데이터를 바이너리 형태로 저장
byte3 alphabets = 'abc'

alphabets[0] // 'a'

열거형(enum)

  • 특정 값들로 집합을 지정하고 집합에 있는 데이터만을 값으로 가진다.
  • 각 집합의 데이터는 내부적으로 순서에 따라 0부터 1씩 증가값
enum EvalLevel {Bad, Soso, Great} // 열거형 집합 지정
EvalLevel kimblock = EvalLevel.Bad // 열거형 변수 선언
int16 kimblockValue = int16(kimblock) // kimblock 열거형 값 0을 정수형으로

참조형 데이터 타입(Reference Types)


  • 마치 배열과 같이, 연속되어 저장되어 있는 값의 첫번째 메모리의 주소를 값으로 가지는 변수 타입
  • 메모리에 저장할지 스토리지에 저장할지 명시해야 한다.
    • 상태변수는 무조건 스토리지에 저장
function f() {
	// 5개의 int32 형태의 데이터를 메모리에 저장하는 변수 선언
	int32[5] memory fixedSlots;
	fixedSlots[0] = 13;
}

배열(Array)

  • 정적 배열 : uint[4] {배열이름}과 같은 형식으로 사용할 배열 크기 지정하여 선언
  • 동적 배열 : uint[] {배열이름}과 같은 형식으로 배열의 크기를 지정하지 않고 선언
    • new 키워드를 통해 동적 배열을 메모리에 할당할 수 있다.
    • 바이트 배열(가변 크기), 문자열(String)
bytes alphabets = 'abc'; // 가변 크기의 배열 선언
string name = 'kimblock'; // bytes와 동일하지만 index, push, length, concat 등을 지원하지 않음, 리터럴로 초기화

구조체(struct)

  • 서로 다른 유형의 항목을 포함하는 집합
  • 사용자 정의 형식
  • 배열과 매핑 내부에서 사용 가능, 구조체에 배열과 매핑을 포함 가능
  • 구조체가 동일한 구조체 타입의 멤버를 포함할 수 없음
contract exampleC {
	struct User {
		address account;
		string name;
		mapping (uint => Funder) funders;
	}
	
	mapping(uint => User) users;
}
  • 추가는 객체 형식으로
contract exampleC {
	struct User {
		address account;
		string name;
	}
	
	function newUser (address newAddress, string newName)  {
		User memory newOne = User({account: newAddress, name: newName});
	}
}

매핑(mapping)

  • 스토리지 데이터 영역에서 key-value 구조로 데이터를 저장할 때 사용하는 참조형
    • 스토리지 참조 타입, 라이브러리 함수의 매개 변수에만 허용
  • mapping({키 형식} => {값 형식}) {변수명}
    • 키 형식은 매핑, 구조체, 배열 제외한 유형
    • 값 형식은 모든 유형
  • 키 자체가 실제로 저장되지 않고, 키의 keccak256 해시를 이용해 값 접근
mapping(address => int) public userAddress;

연산자, 조건문, 반복문

JS와 동일

본 포스팅은 코드스테이츠 BEB 과정을 수강하며 작성한 글입니다.