今日の世界では、領収書は取引を検証し、購入の証拠を保管するために不可欠です。大手銀行でも小さな路面店でも、領収書は企業や個人が支出を整理し、追跡するのに役立ちます。 しかし、問題は次のとおりです。ほとんどの 領収書を提供しておらず、トランザクションの詳細を確認するためにエクスプローラーに依存しています。それに依存する必要がなかったらどうなるでしょうか? ユーザーがウォレットを確認することなく領収書を直接生成できれば、どれほど便利になるか想像してみてください。 dApp は Rootstock で支払いベースの dApp を構築している場合、この記事では、Rootstock API と 1 つの RPC (リモート プロシージャ コール) メソッドを使用して、シンプルでありながら効果的な領収書ジェネレーターを作成する方法を説明します。このアプローチにより、プロセスが簡単になり、トランザクション レコードが正確で簡単にアクセスできるようになります。 スムーズな領収書生成エクスペリエンスを実現するために必要な手順とツールを学びましょう。 前提条件 デバイスにノードがインストールされている必要があります Javascriptの知識 選択した Js フレームワークをインストールします コードエディタ(例:VScode) スタイリングにはReact TypescriptとTailwindCSSを使用します ツールとテクノロジー ルートストック API キー : RPCと対話する Web3js : ユーザーがスキャンして領収書を取得するためのQRコードを生成します QRCode React : 領収書をPDFで生成する Jspdf パッケージをインストールしてインポートし、機能コンポーネントを作成する 次のコマンドを使用してすべての依存関係をインストールします。 npm i web3js jspdf qrcode.react 新しいファイルを作成するか、 を使用します App.jsx 次のようにしてパッケージをファイルにインポートします。 import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react"; 機能コンポーネントを初期化する const TransactionReceipt = () => { /......./ } export default TransactionReceipt; 状態管理とWeb3の実装 ここでのコード スニペットは、useState フック、Web3js、Rootstock RPC、および API を使用してトランザクションの詳細を取得および表示するための状態と機能を管理します。 const [transactionId, setTransactionId] = useState(""); interface TransactionDetails { transactionHash: string; from: string; to: string; cumulativeGasUsed: number; blockNumber: number; contractAddress?: string; } const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); const [error, setError] = useState(""); const web3 = new Web3( `https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}` ); : 状態管理 : この行は、状態変数 初期化します。この状態変数は、他の変数や関数が領収書を生成するために活用する入力されたトランザクション ハッシュを担当します。 const [transactionId, setTransactionId] = useState(""); transactionId : この行は、状態変数 値で初期化し、その値を更新するための関数 を提供します。状態は、 オブジェクトまたは いずれかを保持できます。 const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); transactionDetails null setTransactionDetails TransactionDetails null : この行は、状態変数 空の文字列で初期化し、その値を更新するための関数 を提供します。 const [error, setError] = useState(""); error setError : TypeScript インターフェース : これは、トランザクション詳細オブジェクトの構造の TypeScript インターフェイスを定義します。これには、 、 、 、 、 、およびオプションの などのプロパティが含まれます。 interface TransactionDetails transactionHash from to cumulativeGasUsed blockNumber contractAddress : Web3 初期化 : この行は Web3js を初期化し、Rootstock テストネットの RPC エンドポイントに接続します。エンドポイント URL には、 を使用して環境変数から取得される API キーが含まれます。 const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); import.meta.env.VITE_API_KEY 取引の詳細を取得する関数 ここでのコードは、web3.js メソッドを使用して Rootstock から非同期関数を使用してトランザクションの詳細を取得します。 const fetchTransactionDetails = async () => { try { setError(""); setTransactionDetails(null); const receipt = await web3.eth.getTransactionReceipt(transactionId); if (!receipt) { throw new Error("Transaction not found!"); } setTransactionDetails(receipt); } catch (err) { if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } } }; : エラー処理の設定 : この構造は、関数の実行中に発生する可能性のあるエラーを処理するために使用されます。 try { ... } catch (err) { ... } : リセット状態 setError("");: 状態を空の文字列に設定することで、以前のエラー メッセージをすべてクリアします。 error setTransactionDetails(null);: 状態を に設定して、以前のトランザクションの詳細をすべてクリアします。 transactionDetails null : 取引レシートを取得 ;: この行は、web3js メソッドを使用して、入力された transactionId のトランザクション レシートを取得します。 const receipt = await web3.eth.getTransactionReceipt(transactionId) : 受領確認 : レシートが見つからない場合 (つまり、レシートが または 場合)、「Transaction not found!」というメッセージとともにエラーがスローされます。 if (!receipt) { throw new Error("Transaction not found!"); } null undefined : 取引の詳細を設定する : レシートが見つかった場合、取得したレシートで 状態を更新します。 setTransactionDetails(receipt) transactionDetails : エラー処理 : このブロックは、 ブロックの実行中に発生したエラーをキャッチします。 catch (err) { ... } try : キャッチされたエラーが Error クラスのインスタンスである場合は、 状態をエラーのメッセージに設定します。それ以外の場合は、 状態を一般的なエラー メッセージ "不明なエラーが発生しました" に設定します。 if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } error error 領収書PDFを生成する機能 ここでは、Jspdf パッケージを使用して、取引の詳細を含む PDF を生成します。 const generatePDF = () => { if (!transactionDetails) return; const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress, } = transactionDetails; const pdf = new jsPDF(); pdf.setFontSize(16); pdf.text("Transaction Receipt", 10, 10); pdf.setFontSize(12); pdf.text(`Transaction Hash: ${transactionHash}`, 10, 20); pdf.text(`From: ${from}`, 10, 30); pdf.text(`Contract Address: ${contractAddress}`, 10, 40); pdf.text(`To: ${to}`, 10, 40); pdf.text(`Cumulative Gas Used: ${cumulativeGasUsed}`, 10, 50); pdf.text(`Block Number: ${blockNumber}`, 10, 60); pdf.save("Transaction_Receipt.pdf"); }; : 取引の詳細を確認してください : これは、 が または かどうかを確認します。そうである場合、関数は早期に返され、何も実行しません。 if (!transactionDetails) return; transactionDetails null undefined : 解体取引の詳細 : これは、 オブジェクトを分解して個々のプロパティを抽出し、アクセスを容易にします。 const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; transactionDetails : PDFドキュメントを作成 : これは、PDF ドキュメントを表す jsPDF クラスの新しいインスタンスを作成します。 const pdf = new jsPDF() 。 フォントサイズを設定し、タイトルを追加します : 見出しのフォントサイズを16に設定します。 pdf.setFontSize(16) : これにより、PDF ドキュメントの座標 (10, 10) にタイトル "Transaction Receipt" が追加されます。 pdf.text("Transaction Receipt", 10, 10); : 取引の詳細をPDFに追加 : 残りのテキストのフォントサイズを 12 に設定します。 pdf.setFontSize(12); : ${transactionHash} : これにより、座標 (10, 20) にトランザクション ハッシュが追加されます。 pdf.text(Transaction Hash , 10, 20); : 送信者のアドレスを座標 (10, 30) に追加します。 pdf.text(From: ${from}, 10, 30); : これにより、座標 (10, 40) に契約アドレスが追加されます。注: テキストが重ならないように、この行を修正する必要があります。 pdf.text(Contract Address: ${contractAddress}, 10, 40); : 座標 (10, 50) に受信者のアドレスを追加します。 pdf.text(To: ${to}, 10, 50); pdf.text(累積ガス使用量: ${cumulativeGasUsed} : 座標 (10, 60) で使用された累積ガスを追加します。 , 10, 60); : 座標 (10, 70) にブロック番号を追加します。 pdf.text(Block Number: ${blockNumber}, 10, 70); : PDFドキュメントを保存 pdf.save("Transaction_Receipt.pdf");: これにより、PDF ドキュメントが "Transaction_Receipt.pdf" というファイル名で保存されます。 ユーザーインターフェース ここでは、これらの機能コンポーネントをユーザー向けの UI としてレンダリングします。 このコードにはすでにTailwindcssを使用したスタイルが含まれています return ( <div className="p-8 font-sans bg-gray-100 min-h-screen"> <div className="max-w-3xl m-auto bg-white p-6 rounded-lg shadow-lg"> <h1 className="text-3xl font-bold mb-6 text-center text-blue-600"> Transaction Receipt Generator </h1> <div className="mb-6"> <div className="flex"> <input type="text" id="transactionId" value={transactionId} onChange={(e) => setTransactionId(e.target.value)} placeholder="Enter transaction hash" className="border p-2 w-full rounded-l-lg" /> <button onClick={fetchTransactionDetails} className="p-2 bg-blue-500 text-white rounded-r-lg" > Fetch Details </button> </div> </div> {error && ( <p className="text-red-500 mt-4 text-center">Error: {error}</p> )} {transactionDetails && ( <div className="mt-6 flex flex-row gap-8"> <div className="w-2/3"> <h2 className="text-2xl font-semibold mb-4 text-center"> Transaction Details </h2> <div className="bg-gray-50 p-4 rounded-lg shadow-inner w-[460px]"> <p> <strong>Transaction Hash:</strong>{" "} {`${transactionDetails.transactionHash.slice( 0, 6 )}...${transactionDetails.transactionHash.slice(-6)}`} </p> <p> <strong>From:</strong> {transactionDetails.from} </p> <p> <strong>Contract Address:</strong>{" "} {transactionDetails.contractAddress} </p> <p> <strong>To:</strong> {transactionDetails.to} </p> <p> <strong>Cumulative Gas Used:</strong>{" "} {transactionDetails.cumulativeGasUsed.toString()} </p> <p> <strong>Block Number:</strong>{" "} {transactionDetails.blockNumber.toString()} </p> </div> <button onClick={generatePDF} className="mt-6 w-full p-3 bg-green-500 text-white rounded-lg" > Download PDF Receipt </button> </div> <div className="w-1/2 text-center"> <h3 className="text-xl font-semibold mb-4">QR Code</h3> <QRCodeSVG value={`Transaction Hash: ${ transactionDetails.transactionHash }, From: ${transactionDetails.from}, To: ${transactionDetails.to}, Contract Address: ${transactionDetails.contractAddress}, Cumulative Gas Used: ${transactionDetails.cumulativeGasUsed.toString()}, Block Number: ${transactionDetails.blockNumber.toString()}`} size={200} className="mx-auto" /> </div> </div> )} </div> </div> QR コード ジェネレーターには qrcode.react ライブラリが使用され、トランザクションの詳細は QR コード SVG に暗号化されました。 最終的なコードベースと出力 手順に従うと、コードベースは次のようになります。 import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react"; const TransactionReceipt = () => { const [transactionId, setTransactionId] = useState(""); interface TransactionDetails { transactionHash: string; from: string; to: string; cumulativeGasUsed: number; blockNumber: number; contractAddress?: string; } const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); const [error, setError] = useState(""); const web3 = new Web3( `https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}` ); const fetchTransactionDetails = async () => { try { setError(""); setTransactionDetails(null); const receipt = await web3.eth.getTransactionReceipt(transactionId); if (!receipt) { throw new Error("Transaction not found!"); } setTransactionDetails(receipt); } catch (err) { if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } } }; const generatePDF = () => { if (!transactionDetails) return; const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress, } = transactionDetails; const pdf = new jsPDF(); pdf.setFontSize(16); pdf.text("Transaction Receipt", 10, 10); pdf.setFontSize(12); pdf.text(`Transaction Hash: ${transactionHash}`, 10, 20); pdf.text(`From: ${from}`, 10, 30); pdf.text(`Contract Address: ${contractAddress}`, 10, 40); pdf.text(`To: ${to}`, 10, 40); pdf.text(`Cumulative Gas Used: ${cumulativeGasUsed}`, 10, 50); pdf.text(`Block Number: ${blockNumber}`, 10, 60); pdf.save("Transaction_Receipt.pdf"); }; return ( <div className="p-8 font-sans bg-gray-100 min-h-screen"> <div className="max-w-3xl m-auto bg-white p-6 rounded-lg shadow-lg"> <h1 className="text-3xl font-bold mb-6 text-center text-blue-600"> Transaction Receipt Generator </h1> <div className="mb-6"> <div className="flex"> <input type="text" id="transactionId" value={transactionId} onChange={(e) => setTransactionId(e.target.value)} placeholder="Enter transaction hash" className="border p-2 w-full rounded-l-lg" /> <button onClick={fetchTransactionDetails} className="p-2 bg-blue-500 text-white rounded-r-lg" > Fetch Details </button> </div> </div> {error && ( <p className="text-red-500 mt-4 text-center">Error: {error}</p> )} {transactionDetails && ( <div className="mt-6 flex flex-row gap-8"> <div className="w-2/3"> <h2 className="text-2xl font-semibold mb-4 text-center"> Transaction Details </h2> <div className="bg-gray-50 p-4 rounded-lg shadow-inner w-[460px]"> <p> <strong>Transaction Hash:</strong>{" "} {`${transactionDetails.transactionHash.slice( 0, 6 )}...${transactionDetails.transactionHash.slice(-6)}`} </p> <p> <strong>From:</strong> {transactionDetails.from} </p> <p> <strong>Contract Address:</strong>{" "} {transactionDetails.contractAddress} </p> <p> <strong>To:</strong> {transactionDetails.to} </p> <p> <strong>Cumulative Gas Used:</strong>{" "} {transactionDetails.cumulativeGasUsed.toString()} </p> <p> <strong>Block Number:</strong>{" "} {transactionDetails.blockNumber.toString()} </p> </div> <button onClick={generatePDF} className="mt-6 w-full p-3 bg-green-500 text-white rounded-lg" > Download PDF Receipt </button> </div> <div className="w-1/2 text-center"> <h3 className="text-xl font-semibold mb-4">QR Code</h3> <QRCodeSVG value={`Transaction Hash: ${ transactionDetails.transactionHash }, From: ${transactionDetails.from}, To: ${transactionDetails.to}, Contract Address: ${transactionDetails.contractAddress}, Cumulative Gas Used: ${transactionDetails.cumulativeGasUsed.toString()}, Block Number: ${transactionDetails.blockNumber.toString()}`} size={200} className="mx-auto" /> </div> </div> )} </div> </div> ); }; export default TransactionReceipt; 次に、 をインポートし、 ファイルにレンダリングします。 TransactionReceipt App.tsx デモ https://youtu.be/Xwkl9pu8UiM?embedable=true 結論 この記事では、Rootstock API キーと RPC メソッドを使用して、PDF または QR コードにレシート ジェネレーターを構築することができました。次の dApp プロジェクトでは、この機能が採用されることを期待しています。