트랜잭션 타입에 따라 구하는 방법이 다르다.
먼저 트랜잭션 및 영수증(receipt) 정보를 가져와보겠다.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
client, err := ethclient.DialContext(ctx, "RPC_URL")
assert(err)
hash := common.HexToHash("0x...")
tx, _, err := client.TransactionByHash(ctx, hash)
assert(err)
txAsJson, err := json.MarshalIndent(tx, "", " ")
assert(err)
fmt.Println(string(txAsJson))
receipt, err := client.TransactionReceipt(ctx, tx.Hash())
assert(err)
receiptAsJson, err := json.MarshalIndent(receipt, "", " ")
assert(err)
fmt.Println(string(receiptAsJson))
RPC URL과 트랜잭션 해시값을 채워놓고 돌리면 된다.
여기에 레거시 타입의 트랜잭션 해시값을 넣고 돌려보면 다음과 같은 결과가 나온다.
// tx
{
"type": "0x0",
"nonce": "0x8c",
"gasPrice": "0x2ea88fa73",
"maxPriorityFeePerGas": null,
"maxFeePerGas": null,
"gas": "0xf571",
"value": "0x0",
"input": "0xa9059cbb000000000000000000000000a26f4e9eb0d3780b64018e3b2370136fc0544d2600000000000000000000000000000000000000000000001043561a8829300000",
"v": "0x26",
"r": "0x7b7354c7dadd9f74e01e8c4f435894cd4e5710e8bb14b91a46e9bc458fba7973",
"s": "0x793a09c98ef20efb8806e512d3efe0941d961a09d2c24fb78c78e273fb9c75df",
"to": "0x55af2344088e2fbca58cacc36b4b2815675a3f98",
"hash": "0x7ffd35ee40fc8ad77f203357b99583b3668d72dbd805b97af3b52249957ec2da"
}
// receipt
{
"root": "0x",
"status": "0x1",
"cumulativeGasUsed": "0xc8f57f",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000100000000000000000000000000000000000000000000100000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000100000000000000000000000000002000000000000000000000000000000000100000000000000000000000000000000400000000000000000000000000000000000000000000000004000",
"logs": [
{
"address": "0x55af2344088e2fbca58cacc36b4b2815675a3f98",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000007d86e7379d494469de10070747d248b9f0d7c4f",
"0x000000000000000000000000a26f4e9eb0d3780b64018e3b2370136fc0544d26"
],
"data": "0x00000000000000000000000000000000000000000000001043561a8829300000",
"blockNumber": "0xf86e76",
"transactionHash": "0x7ffd35ee40fc8ad77f203357b99583b3668d72dbd805b97af3b52249957ec2da",
"transactionIndex": "0x93",
"blockHash": "0x5dbc020d61f830fc7b60b37ecc9d4c186ca37c08f878f072259edc64fb874c25",
"logIndex": "0x136",
"removed": false
}
],
"transactionHash": "0x7ffd35ee40fc8ad77f203357b99583b3668d72dbd805b97af3b52249957ec2da",
"contractAddress": "0x0000000000000000000000000000000000000000",
"gasUsed": "0xcc89",
"blockHash": "0x5dbc020d61f830fc7b60b37ecc9d4c186ca37c08f878f072259edc64fb874c25",
"blockNumber": "0xf86e76",
"transactionIndex": "0x93"
}
트랜잭션 타입은 EIP-1559 적용 전(레거시, 0x0)과 적용 후(0x2)로 나뉜다. 어차피 지금은 내부적으로는 다 EIP-1559 방식으로 처리되긴 한다.
위에서 보여지는 트랜잭션은 레거시 타입이다.
(레거시 트랜잭션을 이더스캔에서 검색하면 가스 정보는 위처럼 나온다.)
레거시 트랜잭션은 gasPrice
와 gasUsed
라는 필드를 가지고 있으며 이 둘을 곱하면 수수료를 구할 수 있다. 따라서 그 코드는 아래와 같다.
gasPrice := tx.GasPrice()
gasUsed := big.NewInt(int64(receipt.GasUsed))
txFee := new(big.Int).Mul(gasPrice, gasUsed)
EIP-1559 이후 트랜잭션은 base fee라는 개념이 등장하기에 해당 트랜잭션이 담긴 블록에 대한 정보를 가져와야 한다.
먼저 위처럼 트랜잭션과 영수증 정보를 가져온 결과는 다음과 같다.
// tx
{
"type": "0x2",
"nonce": "0x20e",
"gasPrice": null,
"maxPriorityFeePerGas": "0x1dcd6500",
"maxFeePerGas": "0x29f43ad47",
"gas": "0x20619",
"value": "0x0",
"input": "0x26c858a40000000000000000000000000e3ec4a7fcd9e0b0afe47ae645dc0373aa07b91500000000000000000000000000000000000000000000000000000000702fe0f0000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000082d25f326324d70d35ef6935874698989250e36800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"v": "0x0",
"r": "0x7308b29214a4974babfd4e3b0e9a17905fa53571ad9473837f320774fb91c37e",
"s": "0x3bd0e3fdf3a421d8f9debcdbc1f4b1707cbd3d1ae6d4b7dc2275f8c29f2e53e4",
"to": "0x44e94034afce2dd3cd5eb62528f239686fc8f162",
"chainId": "0x1",
"accessList": [],
"hash": "0x94246922c60ce133f9632ed6f67b0a5949d11f98088fe52690ab009f0ce6f4fe"
}
// receipt
{
"type": "0x2",
"root": "0x",
"status": "0x1",
"cumulativeGasUsed": "0x93fb71",
"logsBloom": "0x00000000001000000000000000000000000000000000000000000000000000000000000400000010000000000800000000000000000000000000000000003400000000000000000280000080000000000000000000000000000000000000000000000000020000000000000000000820000000000000000000000000000000000000000000000004000000000000002000000000000000000020000000000000000000000000000000000040000088000000000000000000000008000000000000000000000000000000000000000000000000001088000000000000000020000000000000000000000000000000900000000000000000000000080000000000",
"logs": [
{
"address": "0x0e3ec4a7fcd9e0b0afe47ae645dc0373aa07b915",
"topics": [
"0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62",
"0x00000000000000000000000044e94034afce2dd3cd5eb62528f239686fc8f162",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x00000000000000000000000082d25f326324d70d35ef6935874698989250e368"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a",
"blockNumber": "0xf86de6",
"transactionHash": "0x94246922c60ce133f9632ed6f67b0a5949d11f98088fe52690ab009f0ce6f4fe",
"transactionIndex": "0x71",
"blockHash": "0x103287379f99329b183cfc84f974dec3baf144f5fef2768a458cdaafed5347a2",
"logIndex": "0xdf",
"removed": false
},
{
"address": "0x44e94034afce2dd3cd5eb62528f239686fc8f162",
"topics": [
"0x74f5d3254dfa39a7b1217a27d5d9b3e061eafe11720eca1cf499da2dc1eb1259",
"0x0000000000000000000000000e3ec4a7fcd9e0b0afe47ae645dc0373aa07b915",
"0x00000000000000000000000000000000000000000000000000000000702fe0f0"
],
"data": "0x000000000000000000000000000000000000000000000000000000000000000a",
"blockNumber": "0xf86de6",
"transactionHash": "0x94246922c60ce133f9632ed6f67b0a5949d11f98088fe52690ab009f0ce6f4fe",
"transactionIndex": "0x71",
"blockHash": "0x103287379f99329b183cfc84f974dec3baf144f5fef2768a458cdaafed5347a2",
"logIndex": "0xe0",
"removed": false
}
],
"transactionHash": "0x94246922c60ce133f9632ed6f67b0a5949d11f98088fe52690ab009f0ce6f4fe",
"contractAddress": "0x0000000000000000000000000000000000000000",
"gasUsed": "0x1e7bd",
"blockHash": "0x103287379f99329b183cfc84f974dec3baf144f5fef2768a458cdaafed5347a2",
"blockNumber": "0xf86de6",
"transactionIndex": "0x71"
}
block, err := client.BlockByNumber(ctx, receipt.BlockNumber)
assert(err)
baseFee := block.BaseFee()
gasPrice := new(big.Int).Add(baseFee, tx.EffectiveGasTipValue(baseFee))
gasUsed := big.NewInt(int64(receipt.GasUsed))
txFee := new(big.Int).Mul(gasPrice, gasUsed)
따라서 EIP-1559에서의 트랜잭션 수수료는 위 코드를 통해 구할 수 있다.
(이더스캔에서 EIP-1559 트랜잭션을 조회하면 가스 정보가 위와 같이 나온다.)
baseFee
에 가스팁을 더한 값이 gasPrice
이다. 따라서 거기에 gasUsed
를 곱하면 수수료가 된다.
이 두가지 방식이 모두 호환되게끔 짜여진 전체적인 코드는 다음과 같다.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
client, err := ethclient.DialContext(ctx, "RPC_URL")
assert(err)
hash := common.HexToHash("0x...")
tx, _, err := client.TransactionByHash(ctx, hash)
assert(err)
txAsJson, err := json.MarshalIndent(tx, "", " ")
assert(err)
fmt.Println(string(txAsJson))
receipt, err := client.TransactionReceipt(ctx, tx.Hash())
assert(err)
receiptAsJson, err := json.MarshalIndent(receipt, "", " ")
assert(err)
fmt.Println(string(receiptAsJson))
block, err := client.BlockByNumber(ctx, receipt.BlockNumber)
assert(err)
baseFee := block.BaseFee()
if baseFee == nil {
baseFee = tx.GasPrice()
}
gasPrice := new(big.Int).Add(baseFee, tx.EffectiveGasTipValue(baseFee))
gasUsed := big.NewInt(int64(receipt.GasUsed))
txFee := new(big.Int).Mul(gasPrice, gasUsed)