일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- NFT
- Programming
- 스마트 컨트랙트
- ethers
- web3
- tcp
- truffle
- git
- Python
- 제어의역전
- 블록체인
- JavaScript
- Ethereum
- erc721
- 솔리디티
- server
- 네트워크
- erc
- 트랜잭션
- 이더리움
- ERC20
- Docker
- MySQL
- web3.js
- geth
- ERC165
- github
- solidity
- blockchain
- web
- Today
- Total
멍개의 연구소
[ethereum] gas와 관련된 에러 본문
안녕하세요 멍개입니다. 이번글에서는 이더리움에서 트랜잭션 실행할 때 발생할 수 있는 gas 에러를 살펴보겠습니다.
▶ out of gas
▶ exceeds block gas limit
▶ gasLimit is too low
▶ base fee exceeds gas limit
· 네트워크 실행
$ ganache-cli
· out of gas
트랜잭션을 실행하여 상태를 변경할 때 실행된 코드에 따라 가스를 소모합니다. 이때 트랜잭션엔 어느정도 코드 실행을 할지 결정하는 gas를 설정할 수 있는데 설정된 값보다 더 많은 코드를 실행하려고 할 때 발생하는 에러입니다.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
contract Mung {
string public text;
constructor(string memory _text) {
text = _text;
}
function setText(string memory _text) public{
text = _text;
}
function say() public view returns(string memory){
return text;
}
function errorOccur(uint a) public pure returns (uint) {
require(a == 0, "hello world error");
return a;
}
}
해당 컨트랙트 코드를 배포하는 트랜잭션을 만들어보겠습니다.
const Web3 = require('web3');
let web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
async function main() {
const from = '0x42aBb1C1b24fC723634547AcC6fa2478fD0EBe44'; // ganache-cli에서 생성한 주소
var mungContract = new web3.eth.Contract([
{
"inputs": [
{
"internalType": "string",
"name": "_text",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "a",
"type": "uint256"
}
],
"name": "errorOccur",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "say",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_text",
"type": "string"
}
],
"name": "setText",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "text",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
]); // remix에서 ABI 복사한 값을 인자로 전달
var mung = mungContract.deploy({
data: '', // remix에서 bytecode.object 복사한 값
arguments: ['test'] // 생성자에 전달할 값
}).send({
from: from,
gas: '100000'
}, function (err, contract){
console.log(err, contract);
})
}
main();
여기서 data엔 컴파일 결과인 바이트 코드를 넣어줍니다. gas가 100,000으로 설정되어 있습니다. 해당 코드를 실행하면 다음과 같은 에러 메시지를 출력합니다.
Error: Returned error: VM Exception while processing transaction: out of gas
실행될 코드가 가스를 초과하서 발생한 에러입니다.
해당 컨트랙트 코드는 4,700,000 정도면 충분히 배포 가능합니다.
· Exceeds block gas limit
실행할 트랜잭션은 블록에 포함되어집니다. 이때 블록이 포함하고 있는 트랜잭션의 모든 가스합을 가지고 있는데 이 값이 특정값을 넘어가면 발생하는 에러가 Exceeds block gas limit 입니다. 앞의 코드에서 gas를 매우 높여서 실행하면 다음과 같은 에러가 발생합니다.
Error: Returned error: Exceeds block gas limit
· base fee exceeds gas limit
해당 에러는 스마트 컨트랙트를 배포하거나 실행하는 트랜잭션을 실행할 떄 최소 기준 가스보다 gas의 한도치를 너무 낮게 설정할 경우 발생하는 에러입니다.(이더리움을 전송하는 트랜잭션은 해당 에러가 발생하지 않습니다.)
Returned error: base fee exceeds gas limit
· gasLimit is too low
이더리움 전송 트랜잭션은 gasLimit이 21,000으로 고정되어 있습니다. 만약 해당 수치보다 낮은 수치로 트랜잭션을 발생하면 gasLimit is too low 에러가 발생합니다. 기준치는 초과하지만 21000보다 작을 때 해당 에러가 발생합니다.
const Web3 = require('web3');
let web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
async function main() {
const from = '0x42aBb1C1b24fC723634547AcC6fa2478fD0EBe44';
const pk = '0xa960a13e39802345fb5c0e4bce81aed3e94ca4fccf3f17e148fd68beece8fe08' // from에 대응하는 privateKey를 넣어준다
const to = '0x6c6117196b7c4D4986a1DA1234C003DaE54F44C9'
const tx = {
from,
to,
gas: 21000, // gasLimit
gasPrice: '21000000000', // 해당 값은 그대로 입력 합니다.
value: '1000000000000000' // 원하는 이더전송 수량을 입력합니다.
}
const account = web3.eth.accounts.privateKeyToAccount(pk) // 개인키를 account 객체로 복구
const signedTx = await account.signTransaction(tx) // 개인키로 트랜잭션 서명
const sentTx = await web3.eth.sendSignedTransaction(
signedTx.raw || signedTx.rawTransaction
); // 서명된 트랜잭션 이더리움 노드로 전송
console.log(sentTx);
}
main();
from, pk, to는 적절히 넣어줍니다. 해당 코드를 실행하면 정상적으로 동작합니다. gas를 다음과 같이 수정한 후 실행하면 에러가 발생합니다.
const tx = {
from,
to,
gas: 2100, // 해당 값은 그대로 입력 합니다.
gasPrice: '21000000000', // 해당 값은 그대로 입력 합니다.
value: '1000000000000000' // 원하는 이더전송 수량을 입력합니다.
}
Error: Signer Error: Signer Error: gasLimit is too low. given 2100, need at least 21000.
두 번째로 스마트 컨트랙트를 호출 할 때 21,000보다 낮게 설정해도 해당 에러가 발생합니다.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
contract Mung {
string public text;
constructor(string memory _text) {
text = _text;
}
function setText(string memory _text) public{
text = _text;
}
function say() public view returns(string memory){
return text;
}
function errorOccur(uint a) public pure returns (uint) {
require(a == 0, "hello world error");
return a;
}
}
const Web3 = require('web3');
let web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
async function main() {
const CA = "0xaF3DE8794C26363c04A313Bc0cf8A01915017B21" // 배포한 스마트 컨트랙트 주소
const from = '0x42aBb1C1b24fC723634547AcC6fa2478fD0EBe44';
const pk = '0xa960a13e39802345fb5c0e4bce81aed3e94ca4fccf3f17e148fd68beece8fe08' // from에 대응하는 privateKey를 넣어준다어준다
const ABI = [
{
"inputs": [
{
"internalType": "string",
"name": "_text",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "a",
"type": "uint256"
}
],
"name": "errorOccur",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "say",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_text",
"type": "string"
}
],
"name": "setText",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "text",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
]; // remix 또는 트러플 컴파일 결과에서 ABI 복사하여 넣어준다.
// 스마트 컨트랙트 객체 생성
let Contract = new web3.eth.Contract(ABI, CA);
// 스마트 컨트랙트에 정의한 함수 실행
let bytedata = await Contract.methods.setText("test").encodeABI();
console.log(bytedata);
const tx = {
from,
to: CA,
gas: 1000, // 해당 수치는 그대로 입력합니다.
gasPrice: '21000000000', // 해당 수치는 그대로 입력합니다.
data: bytedata
}
const account = web3.eth.accounts.privateKeyToAccount(pk)
const signedTx = await account.signTransaction(tx)
const sentTx = await web3.eth.sendSignedTransaction(signedTx.raw || signedTx.rawTransaction);
console.log(sentTx);
}
main();
gas를 1000으로 매우 낮게 주었습니다.
Error: Signer Error: Signer Error: gasLimit is too low. given 1000, need at least 21520.
이더리움 전송할때와 유사한 에러를 출력합니다. 여시거 21520이 필요하다고 하는데 제가 찾은 최소값은 26788입니다.
'블록체인' 카테고리의 다른 글
[ethereum] web3.js와 같은 ethers 알아보기 (0) | 2022.08.28 |
---|---|
[ethereum] ERC20 만들 때 주의할 점 - symbol, decimals (0) | 2022.08.28 |
[ethereum] EIP, ERC 그것을 파악해보자 (0) | 2022.08.28 |
[ethereum] hardhat을 이용하여 스마트 컨트랙트 개발하기 (0) | 2022.08.28 |
[ethereum] solidity withdrawals pattern(출금패턴) (0) | 2022.08.28 |