U današnjem svijetu, računi su ključni za potvrđivanje transakcija i čuvanje dokaza o kupnji. Bilo da se radi o velikoj banci ili maloj trgovini uz cestu, računi pomažu tvrtkama i pojedincima da ostanu organizirani i prate svoju potrošnju.
Ali evo u čemu je stvar: većina dApp-ova ne daje potvrde i oslanja se na istraživače za provjeru pojedinosti o transakciji. Što ako se ne morate oslanjati na to? Zamislite koliko bi praktičnije bilo za vaše korisnike da generiraju račune izravno, bez potrebe da provjeravaju svoje novčanike.
Ako na Rootstocku gradite dApp temeljen na plaćanju, ovaj će vam članak pokazati kako stvoriti jednostavan, ali učinkovit generator računa pomoću Rootstock API-ja i samo jedne RPC (Remote Procedure Call) metode. Ovaj pristup olakšava proces i osigurava da su vaši zapisi o transakcijama točni i laki za pristup.
Naučimo korake i alate potrebne za stvaranje glatkog iskustva generiranja računa.
Koristit ću React Typescript i TailwindCSS za stiliziranje
Instalirajte sve ovisnosti pomoću ove naredbe:
npm i web3js jspdf qrcode.react
Napravite novu datoteku ili upotrijebite App.jsx
Uvezite pakete u datoteku sa sljedećim:
import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react";
Inicijalizirajte funkcionalnu komponentu
const TransactionReceipt = () => { /......./ } export default TransactionReceipt;
Isječak koda koji se nalazi ovdje upravljat će stanjem i funkcionalnošću za dohvaćanje i prikazivanje detalja transakcije pomoću useState kuke, Web3js, Rootstock RPC i API-ja.
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}` );
Upravljanje državom :
const [transactionId, setTransactionId] = useState("");
: Ovaj redak inicijalizira varijablu stanja transactionId
. Ova varijabla stanja bit će odgovorna za uneseni hash transakcije koji će druge varijable i funkcije iskoristiti za generiranje računa.const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null);
: Ovaj redak inicijalizira varijablu stanja transactionDetails
s null
vrijednošću i pruža funkciju setTransactionDetails
za ažuriranje njezine vrijednosti. Stanje može sadržavati objekt TransactionDetails
ili null
.const [error, setError] = useState("");
: Ovaj redak inicijalizira error
varijable stanja s praznim nizom i pruža funkciju setError
za ažuriranje njezine vrijednosti.TypeScript sučelje :
interface TransactionDetails
: Ovo definira TypeScript sučelje za strukturu objekta detalja transakcije. Uključuje svojstva kao što su transactionHash
, from
, to
, cumulativeGasUsed
, blockNumber
i izbornu contractAddress
.Web3 inicijalizacija :
const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY});
: Ovaj redak inicijalizira Web3js, povezujući se s RPC krajnjom točkom na Rootstock testnet. URL krajnje točke uključuje API ključ koji se dohvaća iz varijabli okruženja pomoću import.meta.env.VITE_API_KEY
.
Kod ovdje će dohvatiti detalje transakcije pomoću asinkrone funkcije iz Rootstocka s metodom 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) { ... }
: Ova se struktura koristi za rješavanje bilo kakvih pogrešaka koje se mogu pojaviti tijekom izvođenja funkcije.error
na prazan niz.transactionDetails
na null
.const receipt = await web3.eth.getTransactionReceipt(transactionId)
;: Ovaj redak koristi metodu web3js za dohvaćanje potvrde o transakciji za uneseni transakcijskiId.if (!receipt) { throw new Error("Transaction not found!"); }
: Ako potvrda nije pronađena (tj. potvrda je null
ili undefined
), pojavljuje se pogreška s porukom "Transakcija nije pronađena!".setTransactionDetails(receipt)
: Ako je potvrda pronađena, ažurira stanje transactionDetails
s dohvaćenom potvrdom.catch (err) { ... }
: Ovaj blok hvata sve pogreške koje se dogode tijekom izvođenja bloka try
.if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); }
: Ako je uhvaćena pogreška instanca klase Pogreška, ona postavlja stanje error
na poruku o pogrešci. U suprotnom, postavlja stanje error
na generičku poruku o pogrešci "Došlo je do nepoznate pogreške".
Ovdje će se paket Jspdf koristiti za generiranje PDF-a koji sadrži detalje transakcije.
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;
: Ovim se provjerava je li transactionDetails
null
ili undefined
. Ako jest, funkcija se rano vraća i ne radi ništa.
const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails;
: Ovo destrukturira objekt transactionDetails
kako bi se izdvojila pojedinačna svojstva radi lakšeg pristupa.
const pdf = new jsPDF()
: Ovo stvara novu instancu klase jsPDF, koja predstavlja PDF dokument.
pdf.setFontSize(16)
: Ovo postavlja veličinu fonta naslova na 16.
pdf.text("Transaction Receipt", 10, 10);
: Ovo dodaje naslov "Potvrda o transakciji" na koordinatama (10, 10) u PDF dokumentu.
pdf.setFontSize(12);
: Ovo postavlja veličinu fonta na 12 za ostatak teksta.
pdf.text(Transaction Hash
: ${transactionHash} , 10, 20);
: Ovo dodaje hash transakcije na koordinate (10, 20).
pdf.text(From: ${from}, 10, 30);
: Ovo dodaje adresu pošiljatelja na koordinate (10, 30).
pdf.text(Contract Address: ${contractAddress}, 10, 40);
: Ovo dodaje adresu ugovora na koordinate (10, 40). Napomena: ovaj redak treba ispraviti kako bi se izbjeglo preklapanje teksta.
pdf.text(To: ${to}, 10, 50);
: Ovo dodaje adresu primatelja na koordinate (10, 50).
pdf.text(Kumulativno iskorišteni plin: ${cumulativeGasUsed} , 10, 60);
: Ovo dodaje kumulativni plin korišten na koordinatama (10, 60).
pdf.text(Block Number: ${blockNumber}, 10, 70);
: Ovo dodaje broj bloka na koordinate (10, 70).
Ovdje ćete korisnicima prikazati te funkcionalne komponente kao korisničko sučelje.
Ovaj kod već je uključio stil korištenjem Tailwindcss-a
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>
Za generator QR koda korištena je biblioteka qrcode.react, a detalji transakcije šifrirani su u QR kod SVG.
Ako slijedite korak, vaša bi baza koda trebala izgledati ovako:
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;
Zatim uvezite TransactionReceipt
i renderirajte ga u svojoj datoteci App.tsx
U ovom ste članku uspjeli izgraditi generator računa u PDF ili QR kod pomoću Rootstock API ključa i RPC metode. Nadam se da ću u vašem sljedećem dApp projektu vidjeti ovu značajku.