paint-brush
Cree un generador de recibos con solo la API de Rootstock y el método RPCpor@ileolami
204 lecturas

Cree un generador de recibos con solo la API de Rootstock y el método RPC

por Ileolami19m2024/12/01
Read on Terminal Reader

Demasiado Largo; Para Leer

Los recibos son importantes para validar transacciones y proporcionar pruebas de compra. Este artículo le mostrará cómo crear un generador de recibos simple pero eficaz utilizando la API Rootstock y un único método RPC (llamada a procedimiento remoto).
featured image - Cree un generador de recibos con solo la API de Rootstock y el método RPC
Ileolami HackerNoon profile picture
0-item
1-item

En el mundo actual, los recibos son fundamentales para validar las transacciones y conservar las pruebas de las compras. Ya sea que se trate de un gran banco o de una pequeña tienda al borde de la carretera, los recibos ayudan a las empresas y a las personas a mantenerse organizadas y a realizar un seguimiento de sus gastos.


Pero la cuestión es la siguiente: la mayoría de las aplicaciones descentralizadas no proporcionan recibos y dependen de exploradores para verificar los detalles de las transacciones. ¿Qué pasaría si no tuviera que depender de eso? Imagine cuánto más conveniente sería para sus usuarios generar recibos directamente, sin necesidad de revisar sus billeteras.


Si está creando una aplicación descentralizada basada en pagos en Rootstock, este artículo le mostrará cómo crear un generador de recibos simple pero efectivo utilizando la API de Rootstock y solo un método RPC (llamada a procedimiento remoto). Este enfoque facilita el proceso y garantiza que sus registros de transacciones sean precisos y de fácil acceso.


Aprenda los pasos y las herramientas necesarias para crear una experiencia de generación de recibos fluida.


Prerrequisitos

  1. Debes tener el nodo instalado en tu dispositivo
  2. conocimiento en Javascript
  3. Instala el framework Js de tu elección
  4. Editor de código, por ejemplo, VScode

Usaré React Typescript y TailwindCSS para diseñar.

Herramientas y tecnologías

  1. Clave API de portainjerto
  2. Web3js : para interactuar con RPC
  3. QRCode React : para generar un código QR para que los usuarios lo escaneen y obtengan su recibo
  4. Jspdf : para generar el recibo en archivos PDF

Instalar, importar los paquetes y crear el componente funcional

  1. Instale todas las dependencias usando este comando:

     npm i web3js jspdf qrcode.react
  2. Crea un nuevo archivo o utiliza App.jsx

  3. Importe los paquetes al archivo con lo siguiente:

     import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react";
  4. Inicializar el componente funcional

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


Gestión del Estado e instalación de Web3

El fragmento de código aquí administrará el estado y la funcionalidad para obtener y mostrar detalles de la transacción usando el gancho useState, Web3js, Rootstock RPC y 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. Gestión estatal :

    • const [transactionId, setTransactionId] = useState(""); : Esta línea inicializa una variable de estado transactionId . Esta variable de estado será responsable del hash de la transacción ingresada que otras variables y funciones aprovecharán para generar el recibo.
    • const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); : Esta línea inicializa una variable de estado transactionDetails con un valor null y proporciona una función setTransactionDetails para actualizar su valor. El estado puede contener un objeto TransactionDetails o null .
    • const [error, setError] = useState(""); : Esta línea inicializa una variable de estado error con una cadena vacía y proporciona una función setError para actualizar su valor.
  2. Interfaz TypeScript :

    • interface TransactionDetails : define una interfaz TypeScript para la estructura del objeto de detalles de transacción. Incluye propiedades como transactionHash , from , to , cumulativeGasUsed , blockNumber y una contractAddress opcional.
  3. Inicialización de Web3 :

    • const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); : Esta línea inicializa Web3js y conecta el punto final de RPC a la red de prueba Rootstock. La URL del punto final incluye una clave API que se recupera de las variables de entorno mediante import.meta.env.VITE_API_KEY .


Función para obtener los detalles de la transacción

El código aquí obtendrá los detalles de la transacción utilizando una función asincrónica de Rootstock con el método 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. Configuración de manejo de errores :
    • try { ... } catch (err) { ... } : Esta estructura se utiliza para manejar cualquier error que pueda ocurrir durante la ejecución de la función.
  2. Estado de reinicio :
    • setError("");: Esto borra cualquier mensaje de error anterior estableciendo el estado error en una cadena vacía.
    • setTransactionDetails(null);: Esto borra todos los detalles de la transacción anterior estableciendo el estado transactionDetails en null .
  3. Obtener recibo de transacción :
    • const receipt = await web3.eth.getTransactionReceipt(transactionId) ;: Esta línea utiliza el método web3js para obtener el recibo de transacción para el transactionId ingresado.
  4. Comprobar recibo :
    • if (!receipt) { throw new Error("Transaction not found!"); } : Si no se encuentra el recibo (es decir, el recibo es null o undefined ), se genera un error con el mensaje "¡Transacción no encontrada!".
  5. Establecer detalles de la transacción :
    • setTransactionDetails(receipt) : si se encuentra el recibo, actualiza el estado transactionDetails con el recibo obtenido.
  6. Manejo de errores :
    • catch (err) { ... } : Este bloque captura cualquier error que ocurra durante la ejecución del bloque try .
    • if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } : Si el error detectado es una instancia de la clase Error, establece el estado error en el mensaje del error. De lo contrario, establece el estado error en un mensaje de error genérico "Ocurrió un error desconocido".


Funciones para generar recibos en formato PDF

Aquí se utilizará el paquete Jspdf para generar el PDF que contiene los detalles de la transacción.

 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. Verificar detalles de la transacción :
    • if (!transactionDetails) return; : Esto verifica si transactionDetails es null o undefined . Si lo es, la función retorna antes y no hace nada.


  2. Detalles de la transacción de desestructuración :
    • const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; : Esto desestructura el objeto transactionDetails para extraer propiedades individuales para un acceso más fácil.


  3. Crear documento PDF :
    • const pdf = new jsPDF() : Esto crea una nueva instancia de la clase jsPDF, que representa un documento PDF.


  4. Establecer tamaño de fuente y agregar título :
    • pdf.setFontSize(16) : Esto establece el tamaño de fuente del encabezado a 16.

    • pdf.text("Transaction Receipt", 10, 10); : Esto agrega el título "Recibo de transacción" en las coordenadas (10, 10) en el documento PDF.


  5. Agregar detalles de transacción a PDF :
    • pdf.setFontSize(12); : Esto establece el tamaño de fuente a 12 para el resto del texto.

    • pdf.text(Transaction Hash : ${transactionHash} , 10, 20); : Esto agrega el hash de la transacción en las coordenadas (10, 20).

    • pdf.text(From: ${from}, 10, 30); : Esto agrega la dirección del remitente en las coordenadas (10, 30).

    • pdf.text(Contract Address: ${contractAddress}, 10, 40); : Esto agrega la dirección del contrato en las coordenadas (10, 40). Nota: Esta línea debe corregirse para evitar superponer texto.

    • pdf.text(To: ${to}, 10, 50); : Esto agrega la dirección del destinatario en las coordenadas (10, 50).

    • pdf.text(Gas acumulado utilizado: ${cumulativeGasUsed} , 10, 60); : Esto agrega el gas acumulado utilizado en las coordenadas (10, 60).

    • pdf.text(Block Number: ${blockNumber}, 10, 70); : Esto agrega el número de bloque en las coordenadas (10, 70).


  6. Guardar documento PDF :
    • pdf.save("Transaction_Receipt.pdf");: Esto guardará el documento PDF con el nombre de archivo "Transaction_Receipt.pdf".

La interfaz de usuario

Aquí representarás esos componentes funcionales como una interfaz de usuario para los usuarios.

Este código ya ha incluido el estilo usando 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>

Para el generador de código QR, se utilizó la biblioteca qrcode.react y los detalles de la transacción se cifraron en el código QR SVG.

Código base final y salida

Si sigues los pasos, tu código base debería verse así:

 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;


Luego, importe el TransactionReceipt y preséntelo en su archivo App.tsx

Manifestación

Conclusión

En este artículo, ha podido crear un generador de recibos en formato PDF o código QR utilizando la clave API de Rootstock y el método RPC. Por lo tanto, espero ver esta función en su próximo proyecto de dApp.