viem blobTransaction
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useCallback, useRef, useState } from 'react';
import { createWalletClient, Hex, http, parseEther, stringToHex, toBlobs, TransactionReceipt } from 'viem';
import { sepolia } from 'viem/chains';
import { useNotification } from './components/notification-context';
import RequiredAddress from './components/required-address';
import { blobGasUtil, gasUtil } from './util';
import { privateKeyToAccount } from 'viem/accounts';
import { hexToBigInt } from 'viem';
import { loadKZG } from 'kzg-wasm';
// brower can not call nodejs api, copy from viem
interface Receipt extends Omit<TransactionReceipt, 'gasUsed' | 'blockNumber' | 'cumulativeGasUsed' | 'effectiveGasPrice'> {
gasUsed: string;
blockNumber: string;
cumulativeGasUsed: string;
effectiveGasPrice: string;
}
export const defaultParameters = ['blobVersionedHashes', 'chainId', 'fees', 'gas', 'nonce', 'type'] as const;
const chain = sepolia;
const BlobTransaction: React.FC<any> = ({ walletProviderInstance, address }) => {
const notify = useNotification();
const [blobLoading, setBlobLoading] = useState(false);
const [blobTxHash, setBlobTxHash] = useState<Hex>();
const [blobTxReceipt, setBlobTxReceipt] = useState<Receipt>();
// useEffect(() => {
// const init = async () => {
// console.log('kzg-');
// const kzg = await createKZG();
// console.log('kzg', kzg);
// initKZG(kzg, '');
// kzgRef.current = kzg;
// };
// init();
// }, []);
const handleEthSendBlobTransaction = useCallback(async () => {
const blobGas = await blobGasUtil(sepolia);
const { maxFeePerGas, maxPriorityFeePerGas } = await gasUtil(sepolia);
try {
setBlobLoading(true);
const account = privateKeyToAccount('0xF7578FAbCF6dcBE50674841ee0961C602E27E82a 的 私钥');
// https://sepolia.etherscan.io/tx/0xc8836b8371b4a0ebfa01bbe890ecbe1fda4c8e4342943ff08c5a69fb48669239
const client = createWalletClient({
account,
chain,
transport: http(),
});
const tx = {
maxFeePerGas: hexToBigInt(maxFeePerGas), // 10 Gwei in Wei
maxPriorityFeePerGas: hexToBigInt(maxPriorityFeePerGas), // 2 Gwei in Wei
to: '0x88E4CaB47eDfE9BaB5dB525c0F3598Cad73436CD',
value: parseEther('0'), // 发送0 ETH
maxFeePerBlobGas: blobGas,
kzg: await loadKZG(),
blobs: toBlobs({ data: stringToHex('hello!') }),
gas: 21000,
parameters: [...defaultParameters, 'sidecars'],
};
const request = await client.prepareTransactionRequest(tx);
console.log('request', request);
const hash = await client.signTransaction(request);
const txhash = await client.sendRawTransaction({ serializedTransaction: hash });
setBlobTxHash(txhash);
const receipt = await walletProviderInstance.current?.waitForTransactionReceipt({
hash: txhash!,
});
setBlobLoading(false);
if (receipt) {
console.log('receipt', receipt);
setBlobTxReceipt({
...receipt,
blockNumber: receipt.blockNumber.toString(),
cumulativeGasUsed: receipt.cumulativeGasUsed.toString(),
gasUsed: receipt.gasUsed.toString(),
effectiveGasPrice: receipt.effectiveGasPrice.toString(),
} as Receipt);
setBlobLoading(false);
}
} catch (error) {
console.log('error', error);
setBlobLoading(false);
notify.error((error as Error).message);
}
}, [notify, walletProviderInstance]);
return (
<div>
<p className="space-x-3">
<RequiredAddress disabled={!address}>
<button disabled={!address} className="btn btn-active btn-info btn-xs min-w-52" onClick={handleEthSendBlobTransaction}>
eth_sendTransaction blob
</button>
</RequiredAddress>
{blobTxHash && (
<a className="link link-secondary" target="_blank" href={`https://sepolia.etherscan.io/tx/${blobTxHash}`}>
View: {blobTxHash}
</a>
)}
</p>
</div>
);
};
export default BlobTransaction;
测试记录
https://sepolia.etherscan.io/address/0xf7578fabcf6dcbe50674841ee0961c602e27e82a

