paint-brush
Costruisci un generatore di ricevute solo con l'API Rootstock e il metodo RPCdi@ileolami
212 letture

Costruisci un generatore di ricevute solo con l'API Rootstock e il metodo RPC

di Ileolami19m2024/12/01
Read on Terminal Reader

Troppo lungo; Leggere

Le ricevute sono importanti per convalidare le transazioni e fornire la prova di acquisto. Questo articolo ti mostrerà come creare un generatore di ricevute semplice ma efficace utilizzando la Rootstock API e un singolo metodo RPC (Remote Procedure Call).
featured image - Costruisci un generatore di ricevute solo con l'API Rootstock e il metodo RPC
Ileolami HackerNoon profile picture
0-item
1-item

Nel mondo odierno, le ricevute sono essenziali per convalidare le transazioni e conservare la prova degli acquisti. Che si tratti di una grande banca o di un piccolo negozio di alimentari, le ricevute aiutano le aziende e gli individui a rimanere organizzati e a tenere traccia delle proprie spese.


Ma ecco il punto: la maggior parte delle dApp non fornisce ricevute e si affida agli explorer per verificare i dettagli delle transazioni. E se non dovessi affidarti a questo? Immagina quanto sarebbe più comodo per i tuoi utenti generare ricevute direttamente, senza dover controllare i loro wallet.


Se stai creando una dApp basata sui pagamenti su Rootstock, questo articolo ti mostrerà come creare un generatore di ricevute semplice ma efficace utilizzando la Rootstock API e un solo metodo RPC (Remote Procedure Call). Questo approccio semplifica il processo e garantisce che i tuoi record di transazione siano accurati e facili da accedere.


Scopriamo i passaggi e gli strumenti necessari per creare un'esperienza di generazione di ricevute fluida.


Prerequisiti

  1. Devi avere il nodo installato sul tuo dispositivo
  2. conoscenza di Javascript
  3. Framework Js installato di tua scelta
  4. Editor di codice, ad esempio VScode

Per lo styling userò React Typescript e TailwindCSS

Strumenti e tecnologie

  1. Chiave API del portainnesto
  2. Web3js : per interagire con RPC
  3. QRCode React : per generare un codice QR che gli utenti possono scansionare e ottenere la ricevuta
  4. Jspdf : per generare la ricevuta in PDF

Installa, importa i pacchetti e crea il componente funzionale

  1. Installa tutte le dipendenze usando questo comando:

     npm i web3js jspdf qrcode.react
  2. Crea un nuovo file o usa App.jsx

  3. Importare i pacchetti nel file con quanto segue:

     import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react";
  4. Inizializzare il componente funzionale

     const TransactionReceipt = () => { /......./ } export default TransactionReceipt;


Gestione dello Stato e integrazione Web3

Il frammento di codice qui presente gestirà lo stato e la funzionalità per il recupero e la visualizzazione dei dettagli delle transazioni utilizzando l'hook useState, Web3js, Rootstock RPC e 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. Gestione dello Stato :

    • const [transactionId, setTransactionId] = useState(""); : Questa riga inizializza una variabile di stato transactionId . Questa variabile di stato sarà responsabile dell'hash della transazione immesso che altre variabili e funzioni sfrutteranno per generare la ricevuta.
    • const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); : Questa riga inizializza una variabile di stato transactionDetails con un valore null e fornisce una funzione setTransactionDetails per aggiornarne il valore. Lo stato può contenere un oggetto TransactionDetails o null .
    • const [error, setError] = useState(""); : Questa riga inizializza una variabile di stato error con una stringa vuota e fornisce una funzione setError per aggiornarne il valore.
  2. Interfaccia TypeScript :

    • interface TransactionDetails : definisce un'interfaccia TypeScript per la struttura dell'oggetto transaction details. Include proprietà come transactionHash , from , to , cumulativeGasUsed , blockNumber e un contractAddress facoltativo.
  3. Inizializzazione Web3 :

    • const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); : Questa riga inizializza Web3js, connettendosi all'endpoint RPC alla testnet Rootstock. L'URL dell'endpoint include una chiave API che viene recuperata dalle variabili di ambiente tramite import.meta.env.VITE_API_KEY .


Funzione per recuperare i dettagli della transazione

Il codice qui recupererà i dettagli della transazione utilizzando una funzione asincrona da Rootstock con il metodo 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"); } } };
  1. Impostazione della gestione degli errori :
    • try { ... } catch (err) { ... } : Questa struttura viene utilizzata per gestire eventuali errori che potrebbero verificarsi durante l'esecuzione della funzione.
  2. Ripristina stato :
    • setError("");: cancella tutti i messaggi di errore precedenti impostando lo stato error su una stringa vuota.
    • setTransactionDetails(null);: cancella tutti i dettagli delle transazioni precedenti impostando lo stato transactionDetails su null .
  3. Ottieni la ricevuta della transazione :
    • const receipt = await web3.eth.getTransactionReceipt(transactionId) ;: Questa riga utilizza il metodo web3js per recuperare la ricevuta della transazione per il transactionId immesso.
  4. Controllare la ricevuta :
    • if (!receipt) { throw new Error("Transaction not found!"); } : Se la ricevuta non viene trovata (ad esempio, la ricevuta è null o undefined ), viene generato un errore con il messaggio "Transazione non trovata!".
  5. Imposta i dettagli della transazione :
    • setTransactionDetails(receipt) : se la ricevuta viene trovata, aggiorna lo stato transactionDetails con la ricevuta recuperata.
  6. Gestione degli errori :
    • catch (err) { ... } : Questo blocco cattura tutti gli errori che si verificano durante l'esecuzione del blocco try .
    • if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } : Se l'errore rilevato è un'istanza della classe Error, imposta lo stato error sul messaggio di errore. Altrimenti, imposta lo stato error su un messaggio di errore generico "Si è verificato un errore sconosciuto".


Funzioni per generare la ricevuta PDF

Qui verrà utilizzato il pacchetto Jspdf per generare il PDF contenente i dettagli della transazione.

 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. Controlla i dettagli della transazione :
    • if (!transactionDetails) return; : Questo controlla se transactionDetails è null o undefined . In caso affermativo, la funzione restituisce in anticipo e non fa nulla.


  2. Dettagli della transazione di destrutturazione :
    • const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; : Questo destruttura l'oggetto transactionDetails per estrarre singole proprietà per un accesso più semplice.


  3. Crea documento PDF :
    • const pdf = new jsPDF() : Crea una nuova istanza della classe jsPDF, che rappresenta un documento PDF.


  4. Imposta la dimensione del carattere e aggiungi il titolo :
    • pdf.setFontSize(16) : imposta la dimensione del carattere dell'intestazione a 16.

    • pdf.text("Transaction Receipt", 10, 10); : Aggiunge il titolo "Ricevuta di transazione" alle coordinate (10, 10) nel documento PDF.


  5. Aggiungi i dettagli della transazione al PDF :
    • pdf.setFontSize(12); : Imposta la dimensione del carattere a 12 per il resto del testo.

    • pdf.text(Transaction Hash : ${transactionHash} , 10, 20); : Aggiunge l'hash della transazione alle coordinate (10, 20).

    • pdf.text(From: ${from}, 10, 30); : Aggiunge l'indirizzo del mittente alle coordinate (10, 30).

    • pdf.text(Contract Address: ${contractAddress}, 10, 40); : Aggiunge l'indirizzo del contratto alle coordinate (10, 40). Nota: questa riga dovrebbe essere corretta per evitare sovrapposizioni di testo.

    • pdf.text(To: ${to}, 10, 50); : Aggiunge l'indirizzo del destinatario alle coordinate (10, 50).

    • pdf.text(Gas cumulativo utilizzato: ${cumulativeGasUsed} , 10, 60); : Aggiunge il gas cumulativo utilizzato alle coordinate (10, 60).

    • pdf.text(Block Number: ${blockNumber}, 10, 70); : Aggiunge il numero del blocco alle coordinate (10, 70).


  6. Salva documento PDF :
    • pdf.save("Transaction_Receipt.pdf");: Questo salverà il documento PDF con il nome file "Transaction_Receipt.pdf".

L'interfaccia utente

Qui renderizzerai quei componenti funzionali come interfaccia utente per gli utenti.

Questo codice ha già incluso lo stile utilizzando 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>

Per il generatore di codici QR è stata utilizzata la libreria qrcode.react e i dettagli della transazione sono stati crittografati al suo interno nel codice QR SVG.

Codice di base finale e output

Se segui i passaggi, la tua base di codice dovrebbe apparire così:

 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;


Quindi, importa TransactionReceipt e visualizzalo nel tuo file App.tsx

Dimostrazione

Conclusione

In questo articolo, sei riuscito a creare un generatore di ricevute in un PDF o codice QR usando la chiave API Rootstock e il metodo RPC. Quindi, nel tuo prossimo progetto dApp, spero di vedere questa funzionalità.