이번 포스트 내용은
Turing Complete라고 스마트 컨트랙트 포스트에서 언급한 내용과
Block & Transanction 관련 사용법을 알아보자.
다른 언어들과 같은 형태로 사용된다. (C++ 기반)
contract Ex{
uint public minPrice = 100; // 조건문에 사용할 최소 금액
mapping (int => address) public owner;
function conditional(uint ask_price) public {
if (ask_price > minPrice) {
owner[1] = (주소);
} else {
// fail!!!
// revert(); 뒤에서 다룰 내용임. 조건문 구조에 집중.
}
}
// 혹시 else if 까지 사용한다면 모든 언어처럼 같이 사용하면 된다.
}
이 또한 for, while, do while을 사용한다.
contract Ex{
// for을 이용해 1~10 합 구하기
function forLoop() {
uint256 sum = 0;
for (uint i = 1; i <= 10; i++) {
sum += i;
}
}
function whileLoop() {
uint sum = 0;
uint i = 1;
while (i < 11) {
sum += i;
i++;
}
}
function doWhileLoop() {
uint sum = 0;
uint i = 1;
do {
sum += i;
i++;
} while (i < 11);
}
// 혹시나 while / do - while 차이를 모르는 분을 위해
// 언급을 하면
// 동작은 같으나
// do - while의 경우 우선 실행 후 조건 판단을 거친다.
// break, continue 모두 사용 가능ㅎ라다.
function breakAndContinueLoop() {
uint256 sum = 0;
for (uint i = 1; i <= 10; i++) {
if (i == 9) break;
if (i == 5) continue;
sum += i;
}
}
// break는 어느 시점에서 반복문을 멈추고 탈출한다.
// continue는 그 시점만 멈추고 반복분을 이어 진행한다.
}
우선 블록부터 다뤄보면
블록 속성을 하나씩 담은 상태 변수들은 선언한다.
uint public block_info1 = block.basefee;
uint public block_info2 = block.chainid;
address payable public block_info3 = block.coinbase;
uint public block_info4 = block.difficulty; // !!!지금 안 쓴다
uint public block_info5 = block.gaslimit;
uint public block_info6 = block.number;
uint public block_info7 = block.timestamp;
이 컨트랙트를 Sepolia Testnet에 Deploy를 해보면

여기에서 확인할 수 있다. etherscan.io활용
한 줄씩 보자
uint public block_info1 = block.basefee;
Remix결과 => uint256: 13487643828 라고 나온다.
이더스캔도 보면 13.487643828 Gwei로 나온 것을 볼 수 있다.
1 gwei / (10 ** 9) = 1 ETH를 참고하면 된다.
block.basefee를 사용하게되면 basefee를 알 수 있다.
BaseFee는 가스 단가를 결정하는 요소 중 하나다.
참고로, gas price는 Base + Max Priority와 Max 中 작은 값을 택한다.
위 자료를 보면 알 수 있다.
uint public block_info2 = block.chainid;
Remix결과 => 0:uint256: 11155111
이 컨트랙트를 배포할 때 Sepolia Testnet을 사용했기 때문에
메타마스크에 들어간 후 네트워크를 누르고 이 네트워크 편집을 누르면 정보를 볼 수 있다.

여기 체인ID를 보면 11155111임을 보고 잘 출력된 것을 볼 수 있다.
address payable public block_info3 = block.coinbase;

( 이 자료는 해당 트랜잭션의 블록 정보다)
Remix => 0: address: 0x13CB6AE34A13a0977F4d7101eBc24B87Bb23F0d5
이 결과와 블록 자료의 Fee Recipient(수수료 받은 자)와 같은 것을 볼 수 있다.
coinbase는 금액으로 착각할 수 있는데 Validator's Address다.
만약 Ethereum 1.0이었다면 PoW의 Miner's Address이지만,
Ethereum 2.0이기 때문에 PoS의 Validator's Address다.
그렇기 때문에 address 자료형과 돈과 관련있기 때문에 payable 또한 사용한다.
uint public block_info4 = block.difficulty; // 바뀜 prevrandao로
Remix => 0: uint256: 50518057400793422371435560525982288197416761609966869609190165362393016051425
block.difficulty를사용하면 Remix상에서 오류뜬다.
2.0 업데이트로 완전히 작업증명 방식이 바뀌었기 때문에 난이도가 의미가 없다.
혹시 사용해도 리믹스에서 block.prevrandao를 사용도록 안내해준다.
이더스캔에서는 그래도 역사적 데이터 분석이나 기존 시스템과 호환성을 위해 유지하고있다.
uint public block_info5 = block.gaslimit;
Remix => 0:uint256: 36000000
이는 한 블록이 생성될 때 총 가스양의 한도다.
Remix에서 이 컨트랙트를 배포할 때 Estimated Gas를 선택했기 때문에
36,000,000으로 위 블록자료와 동일하게 나온다.
여담으로, 24.12.19에 가스 한도를 늘리자는 의견이 Validator 사이에 꽤 나왔다.
만약 가스한도가 늘게되면 Tx Fee는 줄게되지만, 이 상승 속도가 빠르게 된다면
네트워크의 보안성과 탈중앙화성이 떨어지게 되는 점이 있다.
아직 현재(25.01.03)기준으로는 30M을 유지하고 있다.
uint public block_info6 = block.number;
Remix Result => 0:uint256: 7411468
간단하게 이 Smart Contract를 Deploy한 Tx이 담긴 블록의 넘버를 나타낸다.
위 Etherscan자료와 비교해보면 같은 것을 볼 수 있다.
uint public block_info7 = block.timestamp;
Remix => 0:uint256: 1735890084
블록의 타임스탬프는 바로 위 Remix와 같이 출력된다.
이것은 자료의 타임스탬프 값이다. (Jan-03-2025 07:41:24 AM +UTC)
하지만 이더스캔은 유저들의 보기 편함을 위해 우리 시간으로 변환한 것이다.
실제로는 리믹스 결과처럼 긴 숫자로 나온다.
1970년 1월 1일 0시 0분 0초부터 블록생성 시점까지 지난 초를 의미한다.
contract BlockExample{
// It is common used for generating random value
function generateRanNum() public view returns(uint8) {
uint8 number = uint8(uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty)))%251);
return number;
}
}
이 방식으로 1.0때 많이 쓰였다.
하지만 이것들이 의미가없어졌다. 난이도가 의미가 없어졌을 뿐더러
랜덤값을 생성하는 prevrandao가 생겼기 때문에
이 하나로 difficulty도 대처하고 랜덤값 생성을 할 수 있다.
function getRandomNumber() public view returns (uint256) {
// block.prevrandao는 256비트 값이므로, 원하는 범위로 나누어 랜덤 숫자 생성
uint256 randomNumber = block.prevrandao % 100; // 0 ~ 99 사이의 랜덤 숫자 생성
return randomNumber;
}
1.0처럼 복잡하게 하지 않고 2.0에서 깔끔하게 구할 수 있다.
트랜잭션 관련 메서드를 살펴보자
uint public msg1 = gasleft(); // 실행하고 남은 가스양
// 하지만 예시라 이렇게 작성했지만 이 메서드는 실행 시점에 남은 가스를 리턴한다.
// 이 상황은 컨트랙트 배포되면서 바로 실행돼서 의미가 없다
// 실제로 사용하기 위해서는 함수 내부에 선언해야 한다.
bytes public msg2 = msg.data; // 함수 호출 할 시 데이터값
address public msg3 = msg.sender; // 메시지를 보낸 사람 = 트랜잭션 요청자 주소
bytes4 public msg4 = msg.sig; // 함수 호출한 데이터값, 함수 호출한 데이터를 바이트코드로 바꾼값이다. == 호출된 함수 시그니처
uint public msg5 = msg.value; // 사용자가 전송한 이더의 크기
function checkValue() external payable {
uint public value = msg.value; // msg를 호출하거나 하면 상관없는데 돈과 관련있는 부분이다 보니까 payable을 사용애햐한다.
}
어떻게 사용되는지 보자.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Ex {
// 사용자가 주문한 내역을 보고싶을 때
mapping(address => uint) private orderList;
function newOrderList() external payable {
orderList[msg.sender] = msg.value;
}
bytes4 private checkFunction;
function newCheckFunction(address sender, uint price) external returns(bool) {
// newOrderList()함수의 시그니처를 생성한다.
bytes4 selector = bytes4(keccak256("newOrderList()"));
// msg.sig 호출된 함수의 시그니처(newCheckFunction())
if(selector == msg.sig) return true;
else return false;
}
}
이렇게 몇 문법과 블록 & 트랜잭션 메서드를 살펴봤다.