paint-brush
Rootstock API と RPC メソッドだけで領収書ジェネレーターを構築する@ileolami
118 測定値

Rootstock API と RPC メソッドだけで領収書ジェネレーターを構築する

Ileolami19m2024/12/01
Read on Terminal Reader

長すぎる; 読むには

領収書は、取引を検証し、購入の証明を提供するために重要です。この記事では、Rootstock API と単一の RPC (リモート プロシージャ コール) メソッドを使用して、シンプルでありながら効果的な領収書ジェネレーターを作成する方法を説明します。
featured image - Rootstock API と RPC メソッドだけで領収書ジェネレーターを構築する
Ileolami HackerNoon profile picture
0-item
1-item

今日の世界では、領収書は取引を検証し、購入の証拠を保管するために不可欠です。大手銀行でも小さな路面店でも、領収書は企業や個人が支出を整理し、追跡するのに役立ちます。


しかし、問題は次のとおりです。ほとんどのdApp は領収書を提供しておらず、トランザクションの詳細を確認するためにエクスプローラーに依存しています。それに依存する必要がなかったらどうなるでしょうか? ユーザーがウォレットを確認することなく領収書を直接生成できれば、どれほど便利になるか想像してみてください。


Rootstock で支払いベースの dApp を構築している場合、この記事では、Rootstock API と 1 つの RPC (リモート プロシージャ コール) メソッドを使用して、シンプルでありながら効果的な領収書ジェネレーターを作成する方法を説明します。このアプローチにより、プロセスが簡単になり、トランザクション レコードが正確で簡単にアクセスできるようになります。


スムーズな領収書生成エクスペリエンスを実現するために必要な手順とツールを学びましょう。


前提条件

  1. デバイスにノードがインストールされている必要があります
  2. Javascriptの知識
  3. 選択した Js フレームワークをインストールします
  4. コードエディタ(例:VScode)

スタイリングにはReact TypescriptとTailwindCSSを使用します

ツールとテクノロジー

  1. ルートストック API キー
  2. Web3js : RPCと対話する
  3. QRCode React : ユーザーがスキャンして領収書を取得するためのQRコードを生成します
  4. Jspdf : 領収書をPDFで生成する

パッケージをインストールしてインポートし、機能コンポーネントを作成する

  1. 次のコマンドを使用してすべての依存関係をインストールします。

     npm i web3js jspdf qrcode.react
  2. 新しいファイルを作成するか、 App.jsxを使用します

  3. 次のようにしてパッケージをファイルにインポートします。

     import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react";
  4. 機能コンポーネントを初期化する

    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}` );
  1. 状態管理:

    • const [transactionId, setTransactionId] = useState(""); : この行は、状態変数transactionId初期化します。この状態変数は、他の変数や関数が領収書を生成するために活用する入力されたトランザクション ハッシュを担当します。
    • const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); : この行は、状態変数transactionDetails null値で初期化し、その値を更新するための関数setTransactionDetailsを提供します。状態は、 TransactionDetailsオブジェクトまたはnullいずれかを保持できます。
    • const [error, setError] = useState(""); : この行は、状態変数error空の文字列で初期化し、その値を更新するための関数setErrorを提供します。
  2. TypeScript インターフェース:

    • interface TransactionDetails : これは、トランザクション詳細オブジェクトの構造の TypeScript インターフェイスを定義します。これには、 transactionHashfromtocumulativeGasUsedblockNumber 、およびオプションのcontractAddressなどのプロパティが含まれます。
  3. Web3 初期化:

    • const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); : この行は Web3js を初期化し、Rootstock テストネットの RPC エンドポイントに接続します。エンドポイント URL には、 import.meta.env.VITE_API_KEYを使用して環境変数から取得される API キーが含まれます。


取引の詳細を取得する関数

ここでのコードは、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"); } } };
  1. エラー処理の設定:
    • try { ... } catch (err) { ... } : この構造は、関数の実行中に発生する可能性のあるエラーを処理するために使用されます。
  2. リセット状態:
    • setError("");: error状態を空の文字列に設定することで、以前のエラー メッセージをすべてクリアします。
    • setTransactionDetails(null);: transactionDetails状態をnullに設定して、以前のトランザクションの詳細をすべてクリアします。
  3. 取引レシートを取得:
    • const receipt = await web3.eth.getTransactionReceipt(transactionId) ;: この行は、web3js メソッドを使用して、入力された transactionId のトランザクション レシートを取得します。
  4. 受領確認:
    • if (!receipt) { throw new Error("Transaction not found!"); } : レシートが見つからない場合 (つまり、レシートがnullまたはundefined場合)、「Transaction not found!」というメッセージとともにエラーがスローされます。
  5. 取引の詳細を設定する:
    • setTransactionDetails(receipt) : レシートが見つかった場合、取得したレシートでtransactionDetails状態を更新します。
  6. エラー処理:
    • catch (err) { ... } : このブロックは、 tryブロックの実行中に発生したエラーをキャッチします。
    • if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } : キャッチされたエラーが Error クラスのインスタンスである場合は、 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"); };
  1. 取引の詳細を確認してください:
    • if (!transactionDetails) return; : これは、 transactionDetailsnullまたはundefinedかどうかを確認します。そうである場合、関数は早期に返され、何も実行しません。


  2. 解体取引の詳細:
    • const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; : これは、 transactionDetailsオブジェクトを分解して個々のプロパティを抽出し、アクセスを容易にします。


  3. PDFドキュメントを作成:
    • const pdf = new jsPDF() : これは、PDF ドキュメントを表す jsPDF クラスの新しいインスタンスを作成します。


  4. フォントサイズを設定し、タイトルを追加します
    • pdf.setFontSize(16) : 見出しのフォントサイズを16に設定します。

    • pdf.text("Transaction Receipt", 10, 10); : これにより、PDF ドキュメントの座標 (10, 10) にタイトル "Transaction Receipt" が追加されます。


  5. 取引の詳細をPDFに追加:
    • pdf.setFontSize(12); : 残りのテキストのフォントサイズを 12 に設定します。

    • pdf.text(Transaction Hash : ${transactionHash} , 10, 20); : これにより、座標 (10, 20) にトランザクション ハッシュが追加されます。

    • pdf.text(From: ${from}, 10, 30); : 送信者のアドレスを座標 (10, 30) に追加します。

    • pdf.text(Contract Address: ${contractAddress}, 10, 40); : これにより、座標 (10, 40) に契約アドレスが追加されます。注: テキストが重ならないように、この行を修正する必要があります。

    • pdf.text(To: ${to}, 10, 50); : 座標 (10, 50) に受信者のアドレスを追加します。

    • pdf.text(累積ガス使用量: ${cumulativeGasUsed} , 10, 60); : 座標 (10, 60) で使用された累積ガスを追加します。

    • pdf.text(Block Number: ${blockNumber}, 10, 70); : 座標 (10, 70) にブロック番号を追加します。


  6. 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ファイルにレンダリングします。

デモ

結論

この記事では、Rootstock API キーと RPC メソッドを使用して、PDF または QR コードにレシート ジェネレーターを構築することができました。次の dApp プロジェクトでは、この機能が採用されることを期待しています。