// SPDX-License-Identifier: MIT
특정 컴파일러의 버전을 표기하기 위해 사용한다.
pragma 키워드는 모든 소스코드 파일에 있어야한다.
다른 파일을 import하더라도 pragma는 자동으로 불러와지지 않기에 꼭 따로 작성해주어여 한다.
버전 규칙은 npm 문법과 동일하다.
특정 버전 이상의 pragma를 사용할 땐 ^를 붙인다.
pragma solidity 0.8.7;
pragma solidity ^0.8.7; // 0.8.7 버전 이상
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); // 송금 가능 주소
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];
uint[] public userAge;
string name = 'Hi man';
struct Person {
address account;
string name;
uint8 age;
}
// 사용하기
Person kim = Person("0x주소", "kim", 35);
// 키 데이터 타입 => 값 데이터 타입
mapping(uint => address) public userAddress;
userAddress[키] = 값;
🍙 block은 블록에 대한 정보를 지닌다.
ex. blockhash, coinbase, gaslimit, number, timestamp
🍙 msg는 컨트랙트를 시작한 트랜잭션 콜이나 메세지 콜에 대한 정보를 가진다.
ex. gasleft(), data, sender, gas, value
🍙 tx는 트랜잭션 데이터를 가진다.
ex. gasprice, origin
🍙 This로 현재 컨트랙트를 참조할 수 있다.
(현재 컨트랙트 주소로 암시적으로 변환된다)
contract C {
uint storedData;
uint storedData = 1;
}
internal : 디폴드값이며, 컨트랙트와 해당 컨트랙트를 상속받은 컨트랙트만 사용할 수 있는 변수이다.
public : 컴파일러가 public 상태 변수에 getter 함수를 만든다. getter 함수로 외부 컨트랙트나 클라이언트 코드에서 public 변수에 접근할 수 있다.
(당연히 컨트랙트 내에서도 직접 public 상태 변수를 사용할 수 있다)
private : 동일한 컨트랙트 맴버만 접근할 수 있다.
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;
}
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
}
}
// 변경자는 _;로 작성한다.
uint public count;
modifier counting {
count++; // 함수가 실행되기 전 실행된다.
_; // 함수 실행
count--; // 함수가 실행된 후 실행된다
}
function func1() public counting {
if(count < 10){
~~~
}
}
contract Child is Parent1, Parent2, Parent3 {
~~~
}
revert("에러 발생")
require와 assert는 설정한 조건이 참인지 확인한 뒤에 조건이 거짓일 경우 에러를 리턴한다.
require는 if revert 처럼 게이트 키퍼의 역할을 한다.
🍚 게이트 키퍼는 조건을 만족했을 경우에만 실행되게 하는 역할을 의미한다.
assert와 require의 사용법은 같지만, assert는 사용하지 않은 가스를 호출자에게 반환하지 않으며 공급된 가스를 모두 소모하여 상태를 원래대로 되돌린다.
require(count <= 10, "10회 초과 에러 발생");
// 조건이 참이면 진행, 거짓이면 에러를 리턴한다.
// 다음과 같이 선언할 수 있다.
enum feeling {
good, bad, soso
}
// 변수로 할당
feeling now = feeling.soso;
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을 가진다.
입력 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; // 에러가 발생하지 않는다
참고 자료 출처 : 코드 스테이츠