안녕하세요, caverjs 를 통해서 스마트 컨트랙트에 트랜잭션을 보내는 과정에서 오류가 났을 때, 가스비 리밋까지 과도하게 가스비가 발견하는 현상 때문에 글을 남깁니다. 아래의 모든 진행은 baobab 네트워크에서 진행하였으며, 모든 트랜잭션을 보낸 계정의 주소는 “0x8Dcb5c195ef30ed66DE9CedeaA5cEc621b6b99FE” 입니다. 이 계정을 실험계정 이라고 칭하겠습니다.
먼저 평범한 KIP-17 기준에 따른 NFT를 발행합니다. 해당 NFT의 소스코드는 다음과 같습니다. 해당 NFT의 컨트랙트 주소는 “0xcB1026eb511ACaA5F75BFCF167Df01E7593b7620” 입니다. 이를 NFT Contract라고 칭하겠습니다.
pragma solidity ^0.5.6;
import "./token/KIP17/KIP17Full.sol";
import "./token/KIP17/KIP17Mintable.sol";
import "./token/KIP17/KIP17Pausable.sol";
import "./ownership/Ownable.sol";
contract Test is
Ownable,
KIP17Full("ABCD", "aaaa"),
KIP17Mintable,
KIP17Pausable
{
string public baseURI = "https://test.com/";
string public baseURIBack = ".json";
function uint2str(uint256 _i)
internal
pure
returns (string memory _uintAsString)
{
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
k = k - 1;
uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
function tokenURI(uint256 tokenId) public view returns (string memory) {
require(
_exists(tokenId),
"KIP17Metadata: URI query for nonexistent token"
);
string memory _baseURI = baseURI;
string memory idstr;
string memory _baseURIBack = baseURIBack;
uint256 temp = tokenId;
idstr = uint2str(temp);
return
bytes(baseURI).length > 0
? string(abi.encodePacked(_baseURI, idstr, _baseURIBack))
: "";
}
function setBaseURI(string memory _baseURI) public onlyOwner {
baseURI = _baseURI;
}
function setBaseURIBack(string memory _baseURIBack) public onlyOwner {
baseURIBack = _baseURIBack;
}
function massMint(uint256 k) public {
uint256 from = totalSupply();
uint256 to = from + k;
for (uint256 i = from; i < to; i += 1) {
mint(msg.sender, i);
}
}
}
그 뒤에 NFT Contract 에서 대량으로 mint 함수를 호출하기 위한 컨트랙트를 작성하였습니다.
pragma solidity ^0.5.6;
import "./token/KIP17/KIP17Mintable.sol";
import "./math/SafeMath.sol";
import "./ownership/Ownable.sol";
contract MintNFT is Ownable {
using SafeMath for uint256;
KIP17Mintable public nft;
constructor(KIP17Mintable _nft) public {
nft = _nft;
}
function nftMint(uint256 num) external onlyOwner {
for (uint256 i = 1; i < num; i += 1) {
nft.mint(msg.sender, i);
}
}
}
제가 배포한 스마트 컨트랙트의 소스코드는 위와 같습니다. 발행된 KIP-17 NFT를 대량으로 민팅하기 위한 스마트 컨트랙트입니다. 위 컨트랙트의 주소는 “0x3C2b2e68Ac04d2E48A248f1D0E8347322a29802e” 입니다. 이 컨트랙트를 Mint Contract라고 칭하겠습니다. Mint Contract 에서 NFT Contract의 mint 함수를 실행하기 위해 NFT Contract 로부터 Mint Contract 에 addminter 함수를 통해 민팅 권한을 주었습니다.
그리고 caver-js 를 통해 다음과 같은 코드를 실행하였습니다.
mintContract.send({ from: keyring.address, gas: 1000000000 }, "nftMint", 1000)
그랬더니
Error: reached the opcode count limit
라는 오류를 뱉으면서 가스량의 한도치까지 모든 클레이가 가스비로 빠져나가는 현상이 발생하였습니다. 해당 트랜잭션의 해시는 "0x72cb490cfee2b0568f4cc2eec6d873f62612975e41dfb85dcee2917a60638978"이며 해당 블록은 baobab의 “81025655” 블록에 해당합니다. 편의를 위해 스코프 링크 첨부합니다.
추가적으로 실험해본 결과 가스비 사용량 제한을 더 높이면 실험계정의 잔고가 가스비 사용량 보다 높은 경우 가스량의 한도치까지 무조건적으로 빠져나가는 현상을 목격했습니다.
단, 아래와 같이 인자로 전달하는 값을 낮추면 문제없이 코드가 작동하며, 가스비도 정상적으로 사용되었습니다.
mintContract.send({ from: keyring.address, gas: 1000000000 }, "nftMint", 10)
(질문1) Mint Contract 의 코드에 문제가 있습니까?
(질문2) Mint Contract 의 nftMint 함수는 무한반복에 빠지는 함수는 아닌 것으로 이해됩니다. 가스비가 제한량까지 높아지는 이유는 무엇입니까?
(질문3) 위 처럼 가스비가 무제한으로 발생하는 것이 오류인지 아닌지가 궁금합니다.
(질문4) 가스사용량 제한을 늘리면 가스 사용량이 무수히 높아집니까?
(질문5) error 명인 reached the opcode count limit은 KLVM에서 계산하는 opcode count limit에 도달한 것으로 이해됩니다. 그렇다면 KLVM은 opcode count limit 을 넘는 트랜잭션 발생 시 가스를 맥시멈까지 사용하고 error를 뱉습니까?
(질문6) klaytn에서 가스비가 너무 높게 계산될 경우 오류를 뱉는 등의 장치는 존재하지 않습니까?
긴 글 읽어주셔서 감사합니다:)