Go-ethereum으로 트랜잭션 수수료 구하기

박재훈·2023년 1월 1일
0

GO

목록 보기
9/23
post-custom-banner

트랜잭션 타입에 따라 구하는 방법이 다르다.

먼저 트랜잭션 및 영수증(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 방식으로 처리되긴 한다.
위에서 보여지는 트랜잭션은 레거시 타입이다.

(레거시 트랜잭션을 이더스캔에서 검색하면 가스 정보는 위처럼 나온다.)

레거시 트랜잭션은 gasPricegasUsed라는 필드를 가지고 있으며 이 둘을 곱하면 수수료를 구할 수 있다. 따라서 그 코드는 아래와 같다.

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)
profile
생각대로 되지 않을 때, 비로소 코딩은 재미있는 법.
post-custom-banner

0개의 댓글