[EIP-1559] viem におけるトランザクションの挙動

以下の記事でweb3.jsとethers.jsしか記載がなかったため、viemについても同様の検証を行ってみる
viem のバージョンは ^2.22.11 を使用する

まずは、EIP1559トランザクションを投げてみる

コードはこんな感じ
EIP-1559のトランザクションを送信する関数
import {
createWalletClient,
formatEther,
http,
parseEther,
publicActions,
type Address,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
/**
* EIP-1559のトランザクションを送信する関数
*/
const main = async function () {
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Address);
try {
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http(),
}).extend(publicActions);
const balance = await walletClient.getBalance(account);
console.log("Balance:", formatEther(balance));
const feeData = await walletClient.estimateFeesPerGas();
console.log("Fee Data:", feeData);
const tx = {
account: account,
chain: sepolia,
type: "eip1559" as const,
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
to: process.env.TO_ADDRESS as Address,
value: parseEther("0.01"),
};
console.log("Transaction Data:", tx);
const txHash = await walletClient.sendTransaction(tx);
console.log(`https://sepolia.etherscan.io/tx/${txHash}`);
} catch (error) {
console.error(error);
}
};
main();

実行結果
etherscan:
ログ(抜粋):
Fee Data: {
maxFeePerGas: 60537035506n,
maxPriorityFeePerGas: 16054747n,
}

指定した値が、実際に作成されたトランザクションのmaxFeePerGas、maxPriorityFeePerGasに設定されていることがわかる

実際に支払ったGas Priceは、 BaseFeePerGas と MaxPriorityFeePerGas を加算した値になっていることも確認できる
Gas Price: 50.209795374 Gwei (0.000000050209795374 ETH)
Base: 50.193740627 Gwei
Max Priority: 0.016054747 Gwei

次に、レガシートランザクションを投げてみる

レガシートランザクションについてはviem のドキュメントにも記載あり
Legacy Transaction
A Legacy Transaction in Ethereum refers to a transaction that was created using an older version of Ethereum's transaction format, known as "transaction type 0". This transaction format was used prior to the introduction of the EIP-1559 upgrade, which was implemented in August 2021.

トランザクションの作り方は、type: "legacy" と指定するだけでよい

maxFeePerGas、maxPriorityFeePerGasを指定したままだともちろん怒られる

コードはこんな感じ
レガシートランザクションを送信する関数
import {
createWalletClient,
formatEther,
http,
parseEther,
publicActions,
type Address,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
/**
* レガシートランザクションを送信する関数
*/
const main = async function () {
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Address);
try {
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http(),
}).extend(publicActions);
const balance = await walletClient.getBalance(account);
console.log("Balance:", formatEther(balance));
const feeData = await walletClient.estimateFeesPerGas({
type: "legacy",
});
console.log("Fee Data:", feeData);
const tx = {
account: account,
chain: sepolia,
type: "legacy" as const,
to: process.env.TO_ADDRESS as Address,
value: parseEther("0.01"),
gasPrice: feeData.gasPrice,
};
console.log("Transaction Data:", tx);
const txHash = await walletClient.sendTransaction(tx);
console.log(`https://sepolia.etherscan.io/tx/${txHash}`);
} catch (error) {
console.error(error);
}
};
main();
estimateFeesPerGas でレガシートランザクションのGasPriceも見積もりできるらしい

実行結果
etherscan:
ログ(抜粋):
Fee Data: {
gasPrice: 41216599554n,
}

estimateFeesPerGas で見積もりしたGasPriceがちゃんと設定されていることを確認できたけど、Baseが安かったので少し過払いになった。
Gas Price: 41.216599554 Gwei (0.000000041216599554 ETH)
Base: 39.231596798 Gwei

etherscan のOther Attributes:
の箇所を見るとTx Typeが0(Legacy) になってることを確認できる

何も指定しない場合はどの扱いになるのか

コードはこんな感じ
Tx Typeを指定せずにトランザクションを送信する関数
import {
createWalletClient,
formatEther,
http,
parseEther,
publicActions,
type Address,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
/**
* Tx Typeを指定せずにトランザクションを送信する関数
*/
const main = async function () {
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Address);
try {
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http(),
}).extend(publicActions);
const balance = await walletClient.getBalance(account);
console.log("Balance:", formatEther(balance));
const tx = {
account: account,
chain: sepolia,
to: process.env.TO_ADDRESS as Address,
value: parseEther("0.01"),
};
console.log("Transaction Data:", tx);
const txHash = await walletClient.sendTransaction(tx);
console.log(`https://sepolia.etherscan.io/tx/${txHash}`);
} catch (error) {
console.error(error);
}
};
main();

実行結果
etherscan:

何も指定しない場合でも、EIP1559トランザクションになっている

EIP-1559のトランザクションを送信する関数
のときと、MaxPriorityFeePerGasの値が全く同じだった
estimateFeesPerGas
で見積もりした値を指定してたけど、トランザクションを作成する際に見積もりで得たMaxPriorityFeePerGasを指定するのはあまり意味はなさそう 🤔

MaxPriorityFeePerGasは、うまく指定することで優先的にブロックに格納してもらうための手数料を節約できる
さっき作成したトランザクションはMaxPriorityが0.016054747 Gwei
となっていたため、それより低い値を指定したトランザクションを投げてみる
const tx = {
account: account,
chain: sepolia,
to: process.env.TO_ADDRESS as Address,
value: parseEther("0.01"),
maxPriorityFeePerGas: parseGwei("0.005"), ← 追加
};

MaxPriorityFeePerGas
を指定して優先手数料を節約した実行結果
etherscan:

Max Priority: 0.005 Gwei
となって優先手数料が節約できていることがわかる
あまり低すぎるとブロックに取り込まれるのに時間がかかってしまう可能性があるため、ユースケースに合わせて設定するのが良さそう

MaxFeePerGasは、BaseFee + MaxPriorityFee の上限を決めることができる
トランザクションのmaxFeePerGasのみを指定した場合、Base Fee + Max Priorityがその値を超えないように決定されることになる
const tx = {
account: account,
chain: sepolia,
to: process.env.TO_ADDRESS as Address,
value: parseEther("0.01"),
maxFeePerGas: parseGwei("35"), ← 追加
};

↑
このように指定することで、ネットワークが混雑していたとしてもGas Priceは35 Gweiを絶対に超えない

viemにおけるトランザクションの挙動 :kanzen_rikai: