Katika ulimwengu wa sasa, risiti ni muhimu ili kuthibitisha miamala na kuhifadhi uthibitisho wa ununuzi. Iwe ni benki kubwa au duka dogo la kando ya barabara, risiti husaidia biashara na watu binafsi kujipanga na kufuatilia matumizi yao.
Lakini jambo kuu ni hili: dApps nyingi hazitoi risiti na hutegemea wagunduzi kuthibitisha maelezo ya muamala. Je, ikiwa haukuhitaji kutegemea hilo? Hebu fikiria jinsi ingekuwa rahisi zaidi kwa watumiaji wako kutoa risiti moja kwa moja, bila kuhitaji kuangalia pochi zao.
Iwapo unaunda dApp kulingana na malipo kwenye Rootstock, makala haya yatakuonyesha jinsi ya kuunda jenereta rahisi lakini yenye ufanisi ya risiti kwa kutumia API ya Rootstock na mbinu moja tu ya RPC (Simu ya Utaratibu wa Mbali). Mbinu hii hurahisisha mchakato na kuhakikisha rekodi zako za miamala ni sahihi na ni rahisi kufikia.
Hebu tujifunze hatua na zana zinazohitajika ili kuunda uzoefu mzuri wa kutengeneza risiti.
Nitakuwa nikitumia React Typescript na TailwindCSS kwa kupiga maridadi
Sakinisha utegemezi wote kwa kutumia amri hii:
npm i web3js jspdf qrcode.react
Unda faili mpya au utumie App.jsx
Ingiza vifurushi kwenye faili na yafuatayo:
import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react";
Anzisha kipengele cha utendaji
const TransactionReceipt = () => { /......./ } export default TransactionReceipt;
Kijisehemu cha msimbo hapa kitasimamia hali na utendakazi wa kuleta na kuonyesha maelezo ya muamala kwa kutumia useState hook, Web3js, Rootstock RPC na 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}` );
Usimamizi wa Jimbo :
const [transactionId, setTransactionId] = useState("");
: Mstari huu huanzisha transactionId
ya hali tofauti. Kigezo hiki cha hali kitawajibika kwa heshi ya muamala iliyoingizwa ambayo vigeu vingine na vitendaji vingine vitajiinua ili kutoa risiti.const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null);
: Laini hii inaanzisha transactionDetails
hali tofauti yenye thamani null
na hutoa chaguo la kukokotoa setTransactionDetails
ili kusasisha thamani yake. Jimbo linaweza kushikilia kitu cha TransactionDetails
au null
.const [error, setError] = useState("");
: Mstari huu huanzisha error
ya hali tofauti kwa mfuatano tupu na hutoa chaguo la kukokotoa setError
kusasisha thamani yake.Kiolesura cha TypeScript :
interface TransactionDetails
: Hii inafafanua kiolesura cha TypeScript kwa muundo wa kitu cha maelezo ya muamala. Inajumuisha sifa kama vile transactionHash
, from
, to
, cumulativeGasUsed
, blockNumber
, na contractAddress
.Uanzishaji wa Web3 :
const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY});
: Laini hii inaanzisha Web3js, ikiunganisha kwenye sehemu ya mwisho ya RPC kwenye testnet ya Rootstock. URL ya mwisho inajumuisha ufunguo wa API ambao hutolewa kutoka kwa vigezo vya mazingira kwa kutumia import.meta.env.VITE_API_KEY
.
Nambari hapa itachukua maelezo ya ununuzi kwa kutumia chaguo la kukokotoa kutoka kwa Rootstock kwa kutumia mbinu ya web3.js.
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) { ... }
: Muundo huu unatumika kushughulikia hitilafu zozote zinazoweza kutokea wakati wa utekelezaji wa chaguo la kukokotoa.error
kuwa mfuatano tupu.transactionDetails
null
.const receipt = await web3.eth.getTransactionReceipt(transactionId)
;: Laini hii hutumia mbinu ya web3js kuleta risiti ya muamala kwa Kitambulisho cha muamala kilichowekwa.if (!receipt) { throw new Error("Transaction not found!"); }
: Ikiwa risiti haijapatikana (yaani, risiti ni null
au undefined
), hitilafu hutupwa na ujumbe "Muamala haujapatikana!".setTransactionDetails(receipt)
: Iwapo risiti itapatikana, inasasisha hali ya transactionDetails
kwa risiti iliyoletwa.catch (err) { ... }
: Kizuizi hiki kinanasa hitilafu zozote zinazotokea wakati wa utekelezaji wa try
.if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); }
: Ikiwa kosa lililopatikana ni mfano wa darasa la Hitilafu, huweka hali error
kwa ujumbe wa kosa. Vinginevyo, huweka hali ya error
kwa ujumbe wa hitilafu ya jumla "Hitilafu isiyojulikana imetokea".
Hapa kifurushi cha Jspdf kitatumika kutengeneza PDF iliyo na maelezo ya muamala.
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;
: Hii hukagua kama transactionDetails
ni null
au undefined
. Ikiwa ni, kazi inarudi mapema na haifanyi chochote.
const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails;
: Hii inaharibu kitu cha transactionDetails
ili kutoa mali binafsi kwa ufikiaji rahisi.
const pdf = new jsPDF()
: Hii inaunda mfano mpya wa darasa la jsPDF, ambalo linawakilisha hati ya PDF.
pdf.setFontSize(16)
: Hii huweka saizi ya fonti ya kichwa kuwa 16.
pdf.text("Transaction Receipt", 10, 10);
: Hii inaongeza kichwa "Risiti ya Muamala" kwenye viwianishi (10, 10) katika hati ya PDF.
pdf.setFontSize(12);
: Hii huweka ukubwa wa fonti hadi 12 kwa maandishi mengine.
pdf.text(Transaction Hash
: ${transactionHash} , 10, 20);
: Hii inaongeza heshi ya muamala kwenye viwianishi (10, 20).
pdf.text(From: ${from}, 10, 30);
: Hii inaongeza anwani ya mtumaji kwenye viwianishi (10, 30).
pdf.text(Contract Address: ${contractAddress}, 10, 40);
: Hii inaongeza anwani ya mkataba katika kuratibu (10, 40). Kumbuka: Mstari huu unapaswa kusahihishwa ili kuzuia maandishi yanayopishana.
pdf.text(To: ${to}, 10, 50);
: Hii huongeza anwani ya mpokeaji kwenye viwianishi (10, 50).
pdf.text(Cumulative Gesi Imetumika: ${cumulativeGasUsed} , 10, 60);
: Hii inaongeza mkusanyiko wa gesi inayotumika katika kuratibu (10, 60).
pdf.text(Block Number: ${blockNumber}, 10, 70);
: Hii inaongeza nambari ya kuzuia kwenye kuratibu (10, 70).
Hapa utakuwa ukitoa vipengele hivyo vya utendaji kama UI kwa watumiaji.
Msimbo huu tayari umejumuisha mtindo kwa kutumia 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>
Kwa jenereta ya msimbo wa QR, maktaba ya qrcode.react ilitumiwa, na maelezo ya muamala yakasimbwa ndani yake msimbo wa QR SVG.
Ukifuata hatua, codebase yako inapaswa kuonekana kama hii:
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;
Kisha, leta TransactionReceipt
na uiweke katika faili yako ya App.tsx
Katika makala haya, umeweza kuunda jenereta ya risiti katika msimbo wa PDF au QR kwa kutumia Ufunguo wa API ya Mizizi na mbinu ya RPC. Kwa hivyo katika mradi wako unaofuata wa dApp, natumai kuona kipengele hiki ndani yake.