단위와 전역 변수
단위와 전역 변수 (Units and Globally Available Variables)
Solidity에는 Ether의 세분 단위(wei, gwei, ether)와 시간 단위(seconds, minutes, hours, days, weeks)를 숫자 리터럴에 붙일 수 있는 접미사가 있어요. 또한 전역 네임스페이스에 항상 존재하는 특별한 변수와 함수들이 블록체인에 대한 정보 제공이나 범용 유틸리티로 쓰여요. 금액과 시간 계산을 정확히 하려면 각 단위의 환산 관계를 잘 알아야 해요.
출처: 문서
본문
Ether 단위 (Ether Units)
숫자 리터럴은 Ether의 세분 단위를 지정하기 위해 wei, gwei 또는 ether 접미사를 가질 수 있고, 접미사가 없는 Ether 숫자는 Wei로 간주돼요.
open in Remix
assert(1 wei == 1);
assert(1 gwei == 1e9);
assert(1 ether == 1e18);
세분 단위 접미사의 유일한 효과는 10의 거듭제곱을 곱하는 것이에요.
참고 (Note)
finney와szabo단위는 버전 0.7.0에서 제거됐어요.
시간 단위 (Time Units)
숫자 리터럴 뒤의 seconds, minutes, hours, days, weeks 같은 접미사는 시간 단위를 지정하는 데 사용할 수 있어요. seconds가 기본 단위이고 단위들은 다음과 같이 단순하게 간주돼요:
1 == 1 seconds1 minutes == 60 seconds1 hours == 60 minutes1 days == 24 hours1 weeks == 7 days
이 단위들로 달력 계산을 수행할 때는 주의해야 해요. 모든 해가 365일인 것도 아니고, 윤초(leap seconds) 때문에 모든 날이 24시간인 것도 아니기 때문이에요. 윤초는 예측할 수 없으므로, 정확한 달력 라이브러리는 외부 오라클로 갱신해야 해요.
참고 (Note)
years접미사는 위의 이유로 버전 0.5.0에서 제거됐어요.이 접미사들은 변수에 적용될 수 없어요. 예를 들어 함수 매개변수를 days로 해석하려면 다음처럼 할 수 있어요:
open in Remix
function f(uint start, uint daysAfter) public {
if (block.timestamp >= start + daysAfter * 1 days) {
// ...
}
}
특별한 변수와 함수 (Special Variables and Functions)
전역 네임스페이스에 항상 존재하며 주로 블록체인에 대한 정보를 제공하거나 범용 유틸리티 함수로 쓰이는 특별한 변수와 함수가 있어요.
블록과 트랜잭션 속성 (Block and Transaction Properties)
blockhash(uint blockNumber)returns(bytes32): 블록 번호가 가장 최근 256개 블록 중 하나일 때 주어진 블록의 해시. 그렇지 않으면 0 반환.blobhash(uint index)returns(bytes32): 현재 트랜잭션과 연관된index번째 blob의 버전 해시. 버전 해시는 버전(현재0x01)을 나타내는 단일 바이트와, 그 뒤의 KZG 커밋먼트의 SHA256 해시 마지막 31바이트로 구성돼요(EIP-4844). 주어진 인덱스의 blob이 없으면 0 반환.block.basefee(uint): 현재 블록의 기본 수수료(EIP-3198과 EIP-1559).block.blobbasefee(uint): 현재 블록의 blob 기본 수수료(EIP-7516과 EIP-4844).block.chainid(uint): 현재 체인 ID.block.coinbase(address payable): 현재 블록 채굴자의 주소.block.difficulty(uint): 현재 블록 난이도(EVM < Paris). 다른 EVM 버전에서는block.prevrandao(EIP-4399)의 비권장 별칭으로 동작.block.gaslimit(uint): 현재 블록의 가스 한도.block.number(uint): 현재 블록 번호.block.prevrandao(uint): 비콘 체인이 제공하는 난수(EVM >= Paris).block.slotnum(uint64): 현재 비콘 체인 슬롯 번호(EIP-7843, EVM >= Amsterdam).block.timestamp(uint): Unix epoch 이후 초 단위의 현재 블록 타임스탬프.gasleft()returns(uint256): 남은 가스.msg.data(bytes calldata): 전체 콜데이터.msg.sender(address): 메시지의 발신자(현재 호출).msg.sig(bytes4): 콜데이터의 처음 4바이트(즉 함수 식별자).msg.value(uint): 메시지와 함께 보내진 wei의 수.tx.gasprice(uint): 트랜잭션의 가스 가격.tx.origin(address): 트랜잭션의 발신자(전체 호출 체인).
참고 (Note)
msg.sender와msg.value를 포함한msg의 모든 멤버 값은 매 외부 함수 호출마다 바뀔 수 있어요. 여기에는 라이브러리 함수 호출도 포함돼요.
참고 (Note)
컨트랙트가 블록에 포함된 트랜잭션의 맥락이 아니라 오프체인에서 평가될 때는,
block.*와tx.*가 특정 블록이나 트랜잭션의 값을 참조한다고 가정하면 안 돼요. 이 값들은 컨트랙트를 실행하는 EVM 구현이 제공하며 임의적일 수 있어요.
참고 (Note)
무슨 일을 하고 있는지 알지 못한다면,
block.timestamp나blockhash를 난수 소스로 의존하지 마세요. 타임스탬프와 블록 해시 둘 다 채굴자가 어느 정도 영향력을 행사할 수 있어요. 채굴 커뮤니티의 나쁜 행위자는 예를 들어 선택한 해시로 카지노 지급 함수를 실행하고, 보상(예: Ether)을 받지 못하면 다른 해시로 다시 시도할 수 있어요. 현재 블록 타임스탬프는 마지막 블록의 타임스탬프보다 엄격히 커야 하지만, 유일한 보장은 캐노니컬 체인의 두 연속 블록 타임스탬프 사이 어딘가에 있을 것이라는 것뿐이에요.
참고 (Note)
확장성 이유로 블록 해시는 모든 블록에 대해 사용할 수 없어요. 가장 최근 256개 블록의 해시에만 접근할 수 있고, 다른 모든 값은 0이에요.
참고 (Note)
blockhash함수는 이전에block.blockhash로 알려졌으며, 버전 0.4.22에서 비권장되고 0.5.0에서 제거됐어요.
참고 (Note)
gasleft함수는 이전에msg.gas로 알려졌으며, 버전 0.4.21에서 비권장되고 0.5.0에서 제거됐어요.
참고 (Note)
버전 0.7.0에서
block.timestamp의 별칭now가 제거됐어요.
ABI 인코딩·디코딩 함수 (ABI Encoding and Decoding Functions)
abi.decode(bytes memory encodedData, (...))returns(...): 주어진 데이터를 ABI-디코딩해요. 타입은 두 번째 인자로 괄호 안에 주어져요. 예:(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))abi.encode(...)returns(bytes memory): 주어진 인자를 ABI-인코딩해요.abi.encodePacked(...)returns(bytes memory): 주어진 인자의 패킹 인코딩을 수행해요. 패킹 인코딩은 모호할 수 있다는 점에 주의해요!abi.encodeWithSelector(bytes4 selector, ...)returns(bytes memory): 두 번째 인자부터 주어진 인자를 ABI-인코딩하고 주어진 4바이트 셀렉터를 앞에 붙여요.abi.encodeWithSignature(string memory signature, ...)returns(bytes memory):abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)와 동일해요.abi.encodeCall(function functionPointer, (...))returns(bytes memory): 튜플에 들어 있는 인자로functionPointer에 대한 호출을 ABI-인코딩해요. 타입이 함수 시그니처와 일치하는지 확인하는 전체 타입 검사를 수행해요. 결과는abi.encodeWithSelector(functionPointer.selector, (...))와 같아요.
참고 (Note)
이 인코딩 함수들은 실제로 외부 함수를 호출하지 않고 외부 함수 호출을 위한 데이터를 만들 때 사용할 수 있어요. 게다가
keccak256(abi.encodePacked(a, b))는 구조화된 데이터의 해시를 계산하는 방법이에요(다만 서로 다른 함수 파라미터 타입으로 "해시 충돌"을 만들 수 있다는 점을 알아야 해요). 인코딩에 대한 자세한 내용은 ABI와 타이트 패킹 인코딩 문서를 참고해요.
bytes의 멤버 (Members of bytes)
bytes.concat(...)returns(bytes memory): 가변 개수의bytes와bytes1, …,bytes32인자를 하나의 바이트 배열로 이어붙여요.
string의 멤버 (Members of string)
string.concat(...)returns(string memory): 가변 개수의string인자를 하나의 문자열 배열로 이어붙여요.
에러 처리 (Error Handling)
에러 처리에 대한 자세한 내용과 어떤 함수를 언제 써야 하는지는 assert와 require에 대한 전용 절을 참고해요.
assert(bool condition): 조건이 충족되지 않으면 Panic 에러를 일으켜 상태 변경을 되돌려요. 내부 에러에 사용.require(bool condition): 조건이 충족되지 않으면 revert해요. 입력이나 외부 컴포넌트의 에러에 사용.require(bool condition, string memory message): 조건이 충족되지 않으면 revert해요. 입력이나 외부 컴포넌트의 에러에 사용. 에러 메시지도 제공.revert(): 실행을 중단하고 상태 변경을 되돌려요.revert(string memory reason): 설명 문자열을 제공하며 실행을 중단하고 상태 변경을 되돌려요.
수학·암호 함수 (Mathematical and Cryptographic Functions)
addmod(uint x, uint y, uint k)returns(uint):(x + y) % k를 계산해요. 덧셈은 임의 정밀도로 수행되고2**256에서 래핑되지 않아요. 버전 0.5.0부터k != 0임을 단언해요.mulmod(uint x, uint y, uint k)returns(uint):(x * y) % k를 계산해요. 곱셈은 임의 정밀도로 수행되고2**256에서 래핑되지 않아요. 버전 0.5.0부터k != 0임을 단언해요.keccak256(bytes memory)returns(bytes32): 입력의 Keccak-256 해시를 계산해요.
참고 (Note)
keccak256의 별칭sha3가 있었고 버전 0.5.0에서 제거됐어요.
sha256(bytes memory)returns(bytes32): 입력의 SHA-256 해시를 계산해요.ripemd160(bytes memory)returns(bytes20): 입력의 RIPEMD-160 해시를 계산해요.ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)returns(address): 타원곡선 서명에서 공개키와 연관된 주소를 복구하거나 오류 시 0을 반환해요. 함수 매개변수는 서명의 ECDSA 값에 대응해요:r= 서명의 첫 32바이트,s= 서명의 두 번째 32바이트,v= 서명의 마지막 1바이트.ecrecover는address payable이 아닌address를 반환해요. 복구된 주소로 자금을 보내야 한다면 변환은address payable을 참고해요. 자세한 내용은 사용 예시를 읽어보세요.
경고 (Warning)
ecrecover를 사용할 때, 유효한 서명이 해당 개인키를 알 필요 없이 다른 유효한 서명으로 바뀔 수 있다는 점을 알아야 해요. Homestead 하드포크에서 이 문제는_transaction_서명에 대해 수정됐지만(EIP-2),ecrecover함수는 그대로 남아 있어요. 서명이 고유해야 하거나 항목 식별에 사용하지 않는 한 보통 문제가 되지 않아요. OpenZeppelin에는 이 문제가 없는ecrecover래퍼로 사용할 수 있는 ECDSA 헬퍼 라이브러리가 있어요.
참고 (Note)
프라이빗 블록체인에서
sha256,ripemd160또는ecrecover를 실행할 때 Out-of-Gas를 만날 수 있어요. 이 함수들은 "precompiled contracts"로 구현되고 첫 메시지를 받은 후에만 실제로 존재하기 때문이에요(컨트랙트 코드는 하드코딩되어 있지만). 존재하지 않는 컨트랙트로의 메시지는 더 비싸므로 실행이 Out-of-Gas 에러에 부딪힐 수 있어요. 이 문제의 해결책은 실제 컨트랙트에서 사용하기 전에 각 컨트랙트에 Wei(예: 1)를 먼저 보내는 것이에요. 메인넷이나 테스트넷에서는 문제가 아니에요.
erc7201(string memory id)returns(uint): ERC-7201이 정의한 erc7201 공식에 따라 주어진 id의 스토리지 네임스페이스 기본 슬롯을 계산해요. 공식은keccak256(keccak256(id) - 1) & ~0xff와 동일해요. 이 내장 함수는 공백을 포함한 임의 문자열을 받아들여요. 컴파일 타임 컨텍스트에서 사용할 수 있어요.
주소 타입의 멤버 (Members of Address Types)
이 멤버들은 주소의 멤버 절에서 더 자세히 설명해요.
<address>.balance(uint256): Wei 단위의 주소 잔액.<address>.code(bytes memory): 주소의 코드(비어 있을 수 있음).<address>.codehash(bytes32): 주소의 코드해시.<address payable>.transfer(uint256 amount): 주소로 주어진 양의 Wei를 보내고, 실패 시 revert하며, 2300 가스 stipend를 전달하고 조정할 수 없어요.<address payable>.send(uint256 amount)returns(bool): 주소로 주어진 양의 Wei를 보내고, 실패 시false를 반환하며, 2300 가스 stipend를 전달하고 조정할 수 없어요.
경고 (Warning)
send()와transfer()는 비권장이며 제거될 예정이에요. 자세한 내용은 send와 transfer 절을 참고해요.
<address>.call(bytes memory)returns(bool, bytes memory): 주어진 페이로드로 저수준 CALL을 실행하고 성공 여부와 반환 데이터를 반환해요. 사용 가능한 모든 가스를 전달(일부 EVM 버전이 부과하는 추가 제한 적용), 조정 가능.<address>.delegatecall(bytes memory)returns(bool, bytes memory): 주어진 페이로드로 저수준 DELEGATECALL을 실행하고 성공 여부와 반환 데이터를 반환해요. 사용 가능한 모든 가스를 전달(일부 EVM 버전이 부과하는 추가 제한 적용), 조정 가능.<address>.staticcall(bytes memory)returns(bool, bytes memory): 주어진 페이로드로 저수준 STATICCALL을 실행하고 성공 여부와 반환 데이터를 반환해요. 사용 가능한 모든 가스를 전달(일부 EVM 버전이 부과하는 추가 제한 적용), 조정 가능.
자세한 내용은 Address 절을 참고해요.
경고 (Warning)
다른 컨트랙트 함수를 실행할 때 가능하면
.call()사용을 피해야 해요. 타입 검사, 함수 존재 검사, 인자 패킹을 우회하기 때문이에요.
경고 (Warning)
send사용에는 몇 가지 위험이 있어요. 호출 스택 깊이가 1024에 있으면 전송이 실패하고(호출자가 항상 강제할 수 있음), 수신자가 가스가 부족해져도 실패해요. 안전한 Ether 전송을 위해 항상send의 반환 값을 확인하고,transfer를 사용하거나, 더 좋게는 수신자가 Ether를 출금하는 패턴을 사용해요.
경고 (Warning)
EVM은 존재하지 않는 컨트랙트에 대한 호출을 항상 성공하는 것으로 간주하기 때문에, Solidity는 외부 호출을 수행할 때
extcodesizeopcode를 사용한 추가 검사를 포함해요. 이는 호출하려는 컨트랙트가 실제로 존재하거나(코드를 포함하거나) 예외가 발생하도록 보장해요. 컨트랙트 인스턴스가 아니라 주소에 대해 동작하는 저수준 호출(즉.call(),.delegatecall(),.staticcall(),.send(),.transfer())은 이 검사를 포함하지 않아 가스 측면에서 더 저렴하지만 덜 안전해요.
참고 (Note)
버전 0.5.0 이전에는 Solidity가 컨트랙트 인스턴스로 address 멤버에 접근하는 것을 허용했어요. 예:
this.balance. 이제 금지되며address(this).balance처럼 address로의 명시적 변환이 필요해요.
참고 (Note)
상태 변수가 저수준
delegatecall을 통해 접근되면, 호출된 컨트랙트가 호출 컨트랙트의 스토리지 변수를 이름으로 올바르게 접근하려면 두 컨트랙트의 스토리지 레이아웃이 일치해야 해요. 고수준 라이브러리의 경우처럼 스토리지 포인터가 함수 인자로 전달되는 경우에는 당연히 그렇지 않아요.
참고 (Note)
버전 0.5.0 이전에는
.call,.delegatecall,.staticcall이 성공 여부만 반환하고 반환 데이터는 반환하지 않았어요.
참고 (Note)
버전 0.5.0 이전에는
delegatecall과 비슷하지만 약간 다른 의미를 가진callcode라는 멤버가 있었어요.
컨트랙트 관련 (Contract-related)
this(현재 컨트랙트의 타입): 현재 컨트랙트, Address로 명시적으로 변환 가능.super: 상속 계층에서 한 단계 위의 컨트랙트.selfdestruct(address payable recipient): 현재 컨트랙트를 파괴하고, 그 자금을 주어진 Address로 보내고 실행을 종료해요.
참고 (Note)
selfdestruct에는 EVM에서 물려받은 몇 가지 특이점이 있어요. 수신 컨트랙트의receive함수는 실행되지 않아요. 컨트랙트는 트랜잭션 끝에서만 실제로 파괴되고revert가 파괴를 "되돌릴(undo)" 수 있어요. 게다가 현재 컨트랙트의 모든 함수가 현재 함수를 포함해 직접 호출 가능해요.
경고 (Warning)
EVM >= Cancun부터
selfdestruct는 계정에 있는 모든 Ether를 주어진 수신자에게 보낼 뿐이고 컨트랙트를 파괴하지는 않아요. 그러나selfdestruct가 그것을 호출하는 컨트랙트를 생성하는 트랜잭션에서 호출되면, Cancun 하드포크 이전(EVM <= Shanghai)의selfdestruct동작이 보존되고 현재 컨트랙트를 파괴해 스토리지 키, 코드, 계정 자체를 포함한 모든 데이터를 삭제해요. 자세한 내용은 EIP-6780을 참고해요. 새 동작은 이더리움 메인넷과 테스트넷에 존재하는 모든 컨트랙트에 영향을 주는 네트워크 전역 변경의 결과예요. 이 변경은 컨트랙트가 배포된 체인의 EVM 버전에 의존한다는 점을 알아두는 게 중요해요. 컨트랙트를 컴파일할 때 사용한--evm-version설정은 이에 영향을 주지 않아요.또한 EIP-6049가 권장한 대로
selfdestructopcode가 Solidity 버전 0.8.18에서 비권장됐다는 점도 알아두세요. 비권장은 여전히 유효하며 컴파일러는 여전히 그 사용에 경고를 내보내요. 새 동작을 고려하더라도 새로 배포하는 컨트랙트에서의 사용은 강력히 권장되지 않아요. EVM의 미래 변경이 opcode의 기능을 더 줄일 수 있어요.
참고 (Note)
버전 0.5.0 이전에는
selfdestruct와 같은 의미를 가진suicide라는 함수가 있었어요.
타입 정보 (Type Information)
표현식 type(X)으로 타입 X에 대한 정보를 검색할 수 있어요. 현재 이 기능의 지원은 제한적이지만(X는 컨트랙트 또는 정수 타입일 수 있음), 미래에 확장될 수 있어요.
컨트랙트 타입 C에 대해 다음 속성을 사용할 수 있어요:
type(C).name: 컨트랙트의 이름.type(C).creationCode: 컨트랙트의 생성 바이트코드를 담은 메모리 바이트 배열. 인라인 어셈블리에서 커스텀 생성 루틴을 만들 때, 특히create2opcode를 사용할 때 사용할 수 있어요. 이 속성은 컨트랙트 자체나 어떤 파생 컨트랙트에서도 접근할 수 없어요. 그 바이트코드를 호출 지점의 바이트코드에 포함시키기 때문에 그런 순환 참조는 불가능해요.type(C).runtimeCode: 컨트랙트의 런타임 바이트코드를 담은 메모리 바이트 배열. 이는 보통C의 생성자가 배포하는 코드예요.C가 인라인 어셈블리를 사용하는 생성자를 가지면 실제 배포된 바이트코드와 다를 수 있어요. 또한 라이브러리는 일반 호출을 막기 위해 배포 시점에 런타임 바이트코드를 수정한다는 점을 알아두세요..creationCode와 같은 제한이 이 속성에도 적용돼요.
위 속성에 더해, 인터페이스 타입 I에 대해 다음 속성을 사용할 수 있어요:
type(I).interfaceId: 주어진 인터페이스I의 EIP-165 인터페이스 식별자를 담은bytes4값. 이 식별자는 상속된 모든 함수를 제외하고 인터페이스 자체에 정의된 모든 함수 셀렉터의 XOR로 정의돼요.
정수 타입 T에 대해 다음 속성을 사용할 수 있어요:
type(T).min: 타입T가 나타낼 수 있는 가장 작은 값.type(T).max: 타입T가 나타낼 수 있는 가장 큰 값.
예약 키워드 (Reserved Keywords)
이 키워드들은 Solidity에서 예약되어 있어요. 미래에 문법의 일부가 될 수 있어요: after, alias, apply, auto, byte, case, copyof, default, define, final, implements, in, inline, let, macro, match, mutable, null, of, partial, promise, reference, relocatable, sealed, sizeof, static, supports, switch, typedef, typeof, var.
참고 (Note)
다음 식별자들은 미래에 키워드가 되어 더 이상 이름으로 사용할 수 없게 됩니다:
at,error,layout,leave,super,transient,this.미래에 Yul 예약 식별자로 간주될 이름도 있어요:
basefee,blobbasefee,blobhash,clz,memoryguard,mcopy,prevrandao,slotnum,tload,tstore.