EIP1559
概要
block ごとに固定の fee per gas(block.base_fee_per_gas)を導入
ある block に含まれる tx は全て base_fee = block.base_fee_per_gas * gas_used を手数料として支払う必要がある
base_fee は burn される
ある block の block.base_fee_per_gas の値は以下に応じて上下する
A. 親 block で消費された gas の量
B. 親 block の gas target
block gas limit / elasticity multiplier
A > B の場合、block.base_fee_per_gas の値は大きくなる
A < B の場合、block.base_fee_per_gas の値は小さくなる
code:tx
0x02 || rlp([
chain_id,
nonce,
max_priority_fee_per_gas,
max_fee_per_gas,
gas_limit,
destination,
amount,
data,
access_list,
signature_y_parity,
signature_r,
signature_s
])
新しく導入されたパラメータは以下
max_priority_fee_per_gas
max_fee_per_gas
m0t0k1ch1.icon signer は tx が取り込まれる block の block.base_fee_per_gas を予測することが困難であるため、この値を利用して想定外の手数料支払いを予防する
各 tx に関する手数料の流れの整理
miner が受け取る手数料(priority_fee):priority_fee_per_gas * gas_used
priority_fee_per_gas = min(tx.max_priority_fee_per_gas, tx.max_fee_per_gas - block.base_fee_per_gas)
signer が支払う手数料(base_fee + priority_fee):effective_gas_price * gas_used
effective_gas_price = block.base_fee_per_gas + priority_fee_per_gas
m0t0k1ch1.icon effective_gas_price が tx type:0 の tx.gas_price に相当する
burn される手数料(base_fee):block.base_fee_per_gas * gas_used
tx type:2 な tx の作成方法
Go だと以下のような感じになる
以下のコードで実際に送信してみた tx が こちら code:main.go
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"
"github.com/pkg/errors"
)
func main() {
if err := run(context.Background()); err != nil {
log.Fatal(err)
}
}
func run(ctx context.Context) error {
client, err := ethclient.Dial("<RPC_URL>")
if err != nil {
return errors.Wrap(err, "failed to initialze client")
}
privkey, err := crypto.HexToECDSA("<PRIVATE_KEY>")
if err != nil {
return errors.Wrap(err, "failed to parse private key")
}
pubkey := privkey.Public()
pubkeyECDSA, ok := pubkey.(*ecdsa.PublicKey)
if !ok {
return errors.New("unexpected pubkey format")
}
fromAddress := crypto.PubkeyToAddress(*pubkeyECDSA)
chainID, err := client.ChainID(ctx)
if err != nil {
return errors.Wrap(err, "failed to get chain ID")
}
nonce, err := client.PendingNonceAt(ctx, fromAddress)
if err != nil {
return errors.Wrap(err, "failed to get pending nonce")
}
gasTipCap, err := client.SuggestGasTipCap(ctx)
if err != nil {
return errors.Wrap(err, "failed to suggest gas tip cap")
}
fmt.Println("gasTipCap:", gasTipCap.String())
gasFeeCap, err := client.SuggestGasPrice(ctx)
if err != nil {
return errors.Wrap(err, "failed to suggest gas fee cap")
}
fmt.Println("gasFeeCap:", gasFeeCap.String())
toAddress := common.HexToAddress("<TO_ADDRESS>")
signedTx, err := types.SignTx(types.NewTx(
&types.DynamicFeeTx{
ChainID: chainID,
Nonce: nonce,
GasTipCap: gasTipCap,
GasFeeCap: gasFeeCap,
Gas: 21000,
To: &toAddress,
Value: new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)),
Data: nil,
},
), types.NewLondonSigner(chainID), privkey)
if err != nil {
return errors.Wrap(err, "failed to sign tx")
}
fmt.Println("txHash:", signedTx.Hash().String())
if err := client.SendTransaction(ctx, signedTx); err != nil {
return errors.Wrap(err, "failed to send tx")
}
return nil
}