가스
가스는 이더리움 네트워크에서 특정 작업을 수행하는 데 필요한 계산량을 측정한 것입니다.
단위
1그웨이 => 0.000000001이더 => 1,000,000,000 웨이
업그레이드 전 런던
총수수료 = GasLimit * GasPrice
런던 업그레이드 후
EIP-1559
TotalFee 계산 방법: 사용한 가스 단위(GasLimit) * (baseFee + priorityFee)
baseFee: 계약서에 설정된 값
priorityFee: 사용자가 채굴자에게 팁으로 설정한 값
기본 비용
• EIP-1559는 네트워크 상태에 따라 결정되는 기본 속도 매개변수를 도입합니다.
• 각 트랜잭션이 블록에 포함되기 위해 지불해야 하는 최소 가스 가격
기본 요금 조정
• 동적 마르코프 프로세스
• 이전 블록에서 사용한 블록 가스에 따라 다름
차단 가스가 목표보다 큰 경우.
• 다음 블록에 대한 기본 수수료가 증가하고 그 반대의 경우도 마찬가지입니다.
기본 수수료는 거래를 찾는 최종 사용자나 거래를 검증하려는 광부가 설정하는 것이 아니라 이더리움 네트워크에 의해 결정됩니다.
기본 수수료는 전체 블록의 50%를 대상으로 하며 가장 최근에 확인된 블록의 내용을 기반으로 합니다.
기본 요금은 새 블록이 얼마나 가득 차 있는지에 따라 자동으로 증가하거나 감소합니다.
예:
마지막 블록이 정확히 50% 차면 기본 요율은 변경되지 않습니다.
마지막 블록이 100% 가득 차면 다음 블록의 기본 수수료율이 최대 12.5%까지 증가합니다.
마지막 블록이 50% 이상 100% 미만이면 기본 수수료가 12.5% 미만으로 증가합니다.
마지막 블록이 0% 차면(즉, 비어 있음) 다음 블록의 기본 수수료가 최대 12.5%까지 감소합니다.
마지막 블록이 0%보다 크고 50% 미만이면 기본 수수료가 12.5% 미만으로 인하됩니다.
이 새로운 메커니즘은 거래 수수료를 원활하게 하고 갑작스러운 급증을 방지하도록 설계되었습니다.
기본 요금에 대해 기억해야 할 가장 중요한 점은 100% 자동이며 웹에서 직접 읽을 수 있다는 것입니다.
위 식을 예로 들면,
사용자가 입찰하는 경우 (MaxFee, MaxPriorityFee) = (60, 2)
(1) BaseFee 60 이상인 경우
• 거래는 이 블록에 포함될 수 없습니다.
• BaseFee가 소진될 때까지 메모리 풀에서 대기
(2) 58 < 기본요금 < 60인 경우
• 채굴자는 이 트랜잭션을 포함할지 여부를 선택할 수 있습니다.
• 사용자는 채굴자에게 60 – BaseFee Gwei를 우선 수수료로 지불합니다.
• 사용자는 가스당 총 60Gwei를 지불합니다.
(3) 기본요금이 58 미만인 경우
• 채굴자는 이 트랜잭션을 포함할지 여부를 선택할 수 있습니다.
• 사용자가 채굴자에게 지불2계화지불하다
• 사용자는 기본 요금 + 가스당 2 Gwei를 지불합니다.
예를 들어 거래를 하면
Ex) Jordan은 Tyler 1 Ether를 지불해야 합니다.
거래의 가스 한도는 21,000이고 기본 수수료는 10gwei입니다.
Jordan에는 2gwei 힌트가 포함되어 있습니다.
이것은 21,000 * ( 10 + 2 ) = 252,000 gwei를 반환합니다.
Jordan이 송금하면 1.000252 ETH가 Jordan의 계정에서 차감됩니다.
Taylor는 1.0000 ETH를 받게 됩니다.
유효성 검사기는 0.000042 ETH의 팁을 받습니다.
0.00021 ETH의 기본 수수료가 소각됩니다.
또한 요르단 maxFeePerGas 트랜잭션에 대한 최대 수수료( )를 설정할 수도 있습니다.
최대 수수료와 실제 수수료의 차액은 요르단에서 환불됩니다.
환불 = 최대 수수료 – (기본 수수료 + 우선 수수료). Jordan은 거래 실행에 대한 최대 수수료를 설정할 수 있으며 거래가 실행될 때 “초과” 기본 수수료를 초과 지불하는 것에 대해 걱정할 필요가 없습니다.
거래
- nonce(트랜잭션 번호) – 이전 트랜잭션에서 생성된 nonce 값을 기반으로 새 nonce 값을 생성합니다.
이 값은 트랜잭션의 고유 식별자로 사용됩니다. - gasLimit – 스마트 계약을 실행하는 데 필요한 최대 가스량입니다.
트랜잭션을 실행할 때 이 값을 사용하십시오. - maxPriorityFeePerGas – 사용자가 트랜잭션에 대해 지불할 수 있는 최대 우선 순위 수수료입니다.
- maxFeePerGas(최대 수수료) – 사용자가 트랜잭션에서 지불할 수 있는 최대 총 수수료입니다.
- to (받는 주소) – 거래 대상 계정의 주소
- value (전송할 이더의 양) – 트랜잭션에서 전송된 이더의 양입니다.
- 데이터(스마트 계약 데이터) – 실행할 스마트 계약의 데이터
- chainId(체인 ID) – 트랜잭션이 속한 이더리움 네트워크의 체인 ID입니다.
maxPriorityFeePerGas
기본요금 + 팁
동일한 블록의 다른 트랜잭션보다 우선해야 하는 트랜잭션의 경우 경쟁 트랜잭션을 능가하기 위해 더 높은 힌트가 사용됩니다.
이 필드는 기본적으로 선택 사항이지만 현재 대부분의 네트워크 참가자는 거래가 성공하려면 최소 2.0gwei의 팁이 필요하다는 데 동의합니다.
일반적인 비혼잡 네트워크 조건에서 커밋된 “일반적인” 트랜잭션의 경우 2.0 gwei에 가까워야 합니다.
그러나 다음 블록에서 트랜잭션의 우선 순위를 지정하거나 중요한 트랜잭션이 발생하거나 네트워크가 매우 혼잡할 때 더 높은 값이 필요할 수 있습니다.
최대 수수료PerGas
거래 실행을 위해 지불한 최대 금액
거래가 실행되기 위해 최대 수수료 > 기본 수수료 + 팁 거래해야 하는 발신자 maxFeeFerGas 사용 maxPriorityFeePerGas 차액 환불
maxFee는 사용자가 설정할 수 있지만 자동화용으로만 설정하는 경우
최대 수수료 = (2 * 기본 수수료) + maxPriorityFeePerGas
예를 들어, baseFee: 100gwei, tip: 2gwei인 경우 최대값은 202gwei입니다.
위의 표를 보면 이 공식은 트랜잭션 급증으로 인해 6개의 블록이 모두 꽉 찬 상황을 커버할 수 있습니다.
EIP-1559는
결과적으로 검증인은 보상을 덜 받지만 네트워크 사용자와 검증인 모두 그 과정에서 가스 수수료가 소각되기 때문에 간접적으로 이익을 얻습니다.
자동 가스 요금 할당
지갑을 사용하지 않는 플랫폼에서 트랜잭션을 효율적으로 처리하기 위해 tip 부분과 gasLimit 부분을 자동화하는 논리에 대해 생각해 봅시다.
내가 지금 더 혼란스러워하는 것은 baseFee와 baseFeePerGas 사이에 차이가 있는지 여부입니다.
baseFeePerGas가 사용되는 것을 확인하고 확인했습니다.
baseFeePerGas는 현재 블록에서 가져옵니다.
Web3j web3j = Web3j.build(new HttpService("main-net Url"));
//baseFeePerGas
EthBlock ethBlock = web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, true).sendAsync().get();;
String baseFeePerGasString = ethBlock.getBlock().getBaseFeePerGas();
byte() baseFeePerGasBytes = Numeric.hexStringToByteArray(baseFeePerGasString);
BigInteger baseFeePerGas = new BigInteger(1, baseFeePerGasBytes);
BigInteger baseFeePerGasToWei = baseFeePerGas.divide(BigInteger.valueOf(1000000000));
System.out.println(" baseFeePerGas : " + baseFeePerGasToWei);
어젯밤 현재 Ethereum 메인넷의 기본 수수료는 약 100입니다.
오늘 아침 현재 baseFee는 약 17입니다.
첫째, 내가 지금 네트워크에 트랜잭션을 전송하면 현재 baseFee를 받는 블록에 저장될 수도 있고, 다음 블록에 저장될 수도 있고, 보류 중인 트랜잭션이 너무 많을 경우 다음 블록에 로드될 수도 있습니다.
.그렇다면 무엇이 문제인가
문제는 baseFee가 변경된다는 것입니다.
내 거래보다 높은 거래 비용을 제공하는 거래가 이전 블록에 로드되어 거래가 지연되었습니다.
baseFee는 청크에 따라 계속 달라지므로 baseFee 3 청크를 더 일찍 제안합니다.
물론 maxFee를 두 배로 설정하면 문제가 없습니다.
6 블록이 최대 100%가 아닌 한.
어려운 어려운
가장 좋은 방법은 머신러닝으로 baseFee의 동적 모델링을 수행하는 가장 좋은 방법인 것 같습니다.
나중에 이에 대한 방법을 개발할 것입니다.
자체 모델을 구축하거나 api를 사용해야 할 것 같습니다.
우선 Etherscan API에 Gas Tracker가 있는데 메인넷에서만 가능한 것 같습니다.
public JSONObject getGasOracle () throws Exception {
String apiEndpoint = "https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey=myApiKey";
URL url = new URL(apiEndpoint);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) !
= null) {
response.append(inputLine);
}
in.close();
org.json.JSONObject json = new JSONObject(response.toString());
JSONObject result = json.getJSONObject("result");
return result;
}
반환 값은
{"SafeGasPrice":"19",
"ProposeGasPrice":"19",
"LastBlock":"16823853",
"suggestBaseFee":"18.245100141",
"FastGasPrice":"21",
"gasUsedRatio":"0.268590633333333,0.424627066666667,0.698782133333333,0.3857806,0.937766166666667"}
maxPiriorityFeePerGas – baseFee에 fastGasPrice fastGasprice를 작성할 수 있는 것 같습니다.
최대 수수료의 두 배
앞에서 만든 baseFee 트레이스의 값을 Etherscan을 사용하여 API와 비교해 봅시다.
@GetMapping("getGasObject")
public String getGasObject () throws Exception {
JSONObject gasOracleData = getGasOracle();
System.out.println(gasOracleData);
String SafeGasPrice = gasOracleData.getString("SafeGasPrice");
String ProposeGasPrice = gasOracleData.getString("ProposeGasPrice");
String LastBlock = gasOracleData.getString("LastBlock");
String suggestBaseFee = gasOracleData.getString("suggestBaseFee");
String FastGasPrice = gasOracleData.getString("FastGasPrice");
String gasUsedRatio = gasOracleData.getString("gasUsedRatio");
System.out.println("SafeGasPrice" + SafeGasPrice);
System.out.println("proposeGasPrice " + ProposeGasPrice);
System.out.println("lastBlock" + LastBlock);
System.out.println("suggestBaseFee" + suggestBaseFee);
System.out.println("FastGasPrice" + FastGasPrice);
System.out.println("gasUsedRatio" + gasUsedRatio);
Web3j web3j = Web3j.build(new HttpService("https://mainnet.infura.io/v3/myApi"));
//baseFeePerGas
EthBlock ethBlock = web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, true).sendAsync().get();;
String baseFeePerGasString = ethBlock.getBlock().getBaseFeePerGas();
byte() baseFeePerGasBytes = Numeric.hexStringToByteArray(baseFeePerGasString);
BigInteger baseFeePerGas = new BigInteger(1, baseFeePerGasBytes);
BigInteger nowBaseFee = baseFeePerGas.divide(BigInteger.valueOf(1000000000));
System.out.println(" baseFeePerGas : " + nowBaseFee);
// JSONArray getGasLimit = getGasLimit();
// System.out.println(getGasLimit);
return "redirect:/";
}
반환 값은
SafeGasPrice16
proposeGasPrice 16
lastBlock16823996
suggestBaseFee15.052132009
FastGasPrice17
gasUsedRatio0.3815551,0.469327966666667,0.6418416,0.448290433333333,0.374924633333333
baseFeePerGas : 15
현재 baseFeePerGas: 안전한 전송을 위한 15 + 1 팁 safeGasPrice 더 빠른 전송을 위한 16 값 17 + 2 팁
이 방법은 메인넷에서 사용할 수 있고, 테스트넷에서는 baseFee plus 2를 받을 수 있을 것 같습니다.
이중 최대 수수료.
오 더 나은 API를 찾았습니다
끝점: https://api.blocknative.com/gasprices/blockprices
{
"system": "ethereum",
"network": "main",
"unit": "gwei",
"maxPrice": 120,
"currentBlockNumber": 14630321,
"msSinceLastBlock": 14653,
"blockPrices": (
{
"blockNumber": 14630322,
"estimatedTransactionCount": 224,
"baseFeePerGas": 77.233467826,
"estimatedPrices": (
{
"confidence": 99,
"price": 79,
"maxPriorityFeePerGas": 2,
"maxFeePerGas": 109.93
},
{
"confidence": 95,
"price": 78,
"maxPriorityFeePerGas": 1.52,
"maxFeePerGas": 109.45
},
{
"confidence": 90,
"price": 78,
"maxPriorityFeePerGas": 1.5,
"maxFeePerGas": 109.43
},
{
"confidence": 80,
"price": 78,
"maxPriorityFeePerGas": 1.27,
"maxFeePerGas": 109.2
},
{
"confidence": 70,
"price": 78,
"maxPriorityFeePerGas": 1.07,
"maxFeePerGas": 109
}
)
}
),
"estimatedBaseFees": (
{
"pending+1": (
{
"confidence": 99,
"baseFee": 86.89
}
)
},
{
"pending+2": (
{
"confidence": 99,
"baseFee": 97.74
}
)
},
{
"pending+3": (
{
"confidence": 99,
"baseFee": 106.19
}
)
},
{
"pending+4": (
{
"confidence": 99,
"baseFee": 105.99
}
)
},
{
"pending+5": (
{
"confidence": 99,
"baseFee": 107.93
}
)
}
)
}
근데 테스트넷 버전은 없는거 같은데…
가스 제한
public JSONArray getGasLimit () throws Exception {
String apiEndPoint = "https://api.etherscan.io/api?module=stats&action=dailyavggaslimit&startdate=2019-02-01&enddate=2019-02-28&sort=asc&apikey=myApi";
URL url = new URL(apiEndPoint);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) !
= null) {
response.append(inputLine);
}
in.close();
JSONObject json = new JSONObject(response.toString());
System.out.println(json);
JSONArray result = json.getJSONArray("result");
System.out.println(result);
return result;
}
이 기능은 False Ending이 있지만 Etherscan에서 제공하는 유료 옵션이므로 사용할 수 없습니다.
일반적으로 어떻게 사용되는지 봅시다.
거래를 스크롤하고 사진을 찍었지만 뭔가 이상했습니다.
baseFee의 반환 값은 7 Wei인 Wei로 반환됩니다.
현재 테스트넷 기반
이것은 0.000000007 구이허
maxPriority의 최소 근사치는 2gwei여야 합니다.
maxFee가 2배가 되었다고 하니 2배로 합시다.
테스트넷 기반 EIP-1559 트랜잭션 샘플
public String testGasLimit () throws Exception {
Web3j sepolia = Web3j.build(new HttpService("url"));
long seploiaChainId = 11155111;
String contractAddress = "contractAddress";
String from = "from";
String fromPrivate = "fromPrivate";
String url = "1";
EthBlock ethBlock = sepolia.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, true).sendAsync().get();;
String baseFeePerGasString = ethBlock.getBlock().getBaseFeePerGas();
byte() baseFeePerGasBytes = Numeric.hexStringToByteArray(baseFeePerGasString);
BigInteger baseFeePerGas = new BigInteger(1, baseFeePerGasBytes);
BigInteger nowBaseFee = baseFeePerGas.divide(BigInteger.valueOf(1000000000));
System.out.println(" baseFeePerGas : " + nowBaseFee);
// testnet gastracker가 없기때문에 하드값설정
BigInteger maxPriorityFeeHardCode = BigInteger.valueOf(2).multiply(BigInteger.valueOf(1000000000));
// maxFee는 baseFee와 maxPriorityFee 값보다 항상 커야하므로 두 값을 더한 값으로 지정하려고했으나
네트워크상황에 따라 팬딩될수 있기때문에 더한값에 2배값을 지정해준다
BigInteger maxPee = (baseFee.add(maxPriorityFeeHardCode)).multiply(BigInteger.valueOf(2));
BigInteger value = BigInteger.ZERO;
//my Contract Function
Function mintFunction = new Function("mint",
Arrays.asList(new Utf8String(url))
, Collections.emptyList()
);
String mintEncodedFuntion = FunctionEncoder.encode(mintFunction);
org.web3j.protocol.core.methods.request.Transaction transaction =
org.web3j.protocol.core.methods.request.Transaction.createFunctionCallTransaction(
from,
checkNonce(sepolia, from),
null,
null,
contractAddress,
mintEncodedFuntion
);
// gas Limit estimate
EthEstimateGas ethEstimateGas = sepolia.ethEstimateGas(transaction).send();
BigInteger gasLimited = ethEstimateGas.getAmountUsed();
System.out.println("gasLimited " + gasLimited);
RawTransaction priviousMintTx = RawTransaction.createTransaction(
seploiaChainId,
nonce,
gasLimited,
contractAddress,
value,
mintEncodedFuntion,
maxPriorityFeeHardCode,
maxPee
);
byte() mintSignedMessage = TransactionEncoder.signMessage(priviousMintTx, seploiaChainId, credentials(fromPrivate));
String mintHexValue = Numeric.toHexString(mintSignedMessage);
System.out.println("hexValue" + mintHexValue);
EthSendTransaction mintEthSendTransaction = sepolia.ethSendRawTransaction(mintHexValue).send();
String mintTransactionHash = mintEthSendTransaction.getTransactionHash();
System.out.println("Tx hash : " + mintTransactionHash);
while (true) {
EthGetTransactionReceipt mintTransactionReceipt = sepolia
.ethGetTransactionReceipt(mintTransactionHash)
.send();
if (mintTransactionReceipt.getResult() !
= null) {
net.sf.json.JSONObject receipt = net.sf.json.JSONObject.fromObject(mintTransactionReceipt);
System.out.println("Mint Receipt" + receipt);
net.sf.json.JSONObject receiptResult = net.sf.json.JSONObject.fromObject(receipt.getJSONObject("result"));
System.out.println("Result :" + receiptResult);
net.sf.json.JSONObject receiptLogs = (net.sf.json.JSONObject) net.sf.json.JSONObject.fromObject(receiptResult).getJSONArray("logs").get(0);
String tokenId = String.valueOf(Long.decode(receiptLogs.getJSONArray("topics").get(3).toString()));
System.out.println("TokenId" + tokenId);
break;
}
Thread.sleep(15000);
}
return "redirect:/";
}
에테르 스캔 확인
테스트넷을 만들었으니 이제 Gas Tracker를 이용하여 메인넷을 만들어 봅시다.
그 전에 코드를 모듈화해야 합니다.