솔리디티 문법 1
솔리디티
솔리디티란 객체 지향 프로그램이 언어로써, 이더리움 블록체인 플랫폼에서 다양한 스마트 컨트랙트(계약 로직)를 작성할 때 사용한다.
특징
- 정적 타입의 중괄호 언어 : 솔리디티는 정적 타입의 중괄호 프로그래밍 언어, JS와 다르게 컴파일 시에 변수에 타입이 결정되기 때문에 소스코드에 타입을 명시해줘야 한다.
- Ethereum Virtual Machine(EVM) : 솔리디티는 EVM 위에서 동작한다.
- 세미콜론(;) : 솔리디티는 문장 끝에 반드시 세미콜론을 붙여야 한다.
Base
SPDX License Identifier
스마트 컨트랙트에 대한 신뢰를 높이고 저작권과 같은 문제를 해소하기 위해 솔리디티 코드의 최상단에 SPDX 라이센스를 명시해야 한다.
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: GPL-3.0
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
- 선언될 때 값을 할당해야 함
- 상수화 => 변경 불가능
- internal(default)
지역변수
- 함수가 실행될 때까지만 존재하는 변수
- 기본값으로는 스토리지에 저장(레퍼런스 타입의 경우 재정의 가능)
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 과정을 수강하며 작성한 글입니다.