paint-brush
Создайте генератор квитанций, используя только API Rootstock и метод RPCк@ileolami
194 чтения

Создайте генератор квитанций, используя только API Rootstock и метод RPC

к Ileolami19m2024/12/01
Read on Terminal Reader

Слишком долго; Читать

Чеки важны для проверки транзакций и предоставления доказательства покупки. В этой статье вы узнаете, как создать простой, но эффективный генератор чеков с использованием API Rootstock и одного метода RPC (удаленный вызов процедуры).
featured image - Создайте генератор квитанций, используя только API Rootstock и метод RPC
Ileolami HackerNoon profile picture
0-item
1-item

В современном мире чеки имеют решающее значение для подтверждения транзакций и сохранения доказательств покупок. Будь то крупный банк или небольшой придорожный магазин, чеки помогают компаниям и частным лицам оставаться организованными и отслеживать свои расходы.


Но вот в чем дело: большинство dApps не предоставляют квитанции и полагаются на explorers для проверки деталей транзакции. А что, если бы вам не пришлось полагаться на это? Представьте, насколько удобнее было бы вашим пользователям генерировать квитанции напрямую, без необходимости проверять свои кошельки.


Если вы создаете платежное dApp на Rootstock, эта статья покажет вам, как создать простой, но эффективный генератор квитанций с использованием API Rootstock и всего одного метода RPC (Remote Procedure Call). Такой подход упрощает процесс и гарантирует точность и простоту доступа к записям транзакций.


Давайте изучим шаги и инструменты, необходимые для создания бесперебойного процесса генерации чеков.


Предпосылки

  1. На вашем устройстве должен быть установлен Node.
  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 для структуры объекта деталей транзакции. Он включает такие свойства, как transactionHash , from , to , cumulativeGasUsed , blockNumber и необязательный contractAddress .
  3. Инициализация Web3 :

    • const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); : эта строка инициализирует Web3js, подключаясь к конечной точке RPC в тестовой сети Rootstock. URL конечной точки включает ключ API, который извлекается из переменных среды с помощью import.meta.env.VITE_API_KEY .


Функция получения сведений о транзакции

Приведенный здесь код извлечет данные транзакции, используя асинхронную функцию из Rootstock с методом 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. Настройка обработки ошибок :
    • 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 ), выдается ошибка с сообщением "Транзакция не найдена!".
  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; : Проверяет, является ли transactionDetails null или undefined . Если это так, функция возвращается раньше времени и ничего не делает.


  2. Подробности деструктуризации транзакции :
    • const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; : Это деструктурирует объект transactionDetails для извлечения отдельных свойств для более легкого доступа.


  3. Создать PDF-документ :
    • const pdf = new jsPDF() : создает новый экземпляр класса jsPDF, представляющий PDF-документ.


  4. Установите размер шрифта и добавьте заголовок :
    • pdf.setFontSize(16) : устанавливает размер шрифта заголовка равным 16.

    • pdf.text("Transaction Receipt", 10, 10); : добавляет заголовок "Квитанция о транзакции" в точке с координатами (10, 10) в документе PDF.


  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".

Пользовательский интерфейс

Здесь вы будете отображать эти функциональные компоненты в виде пользовательского интерфейса для пользователей.

В этом коде уже включен стиль с использованием 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

Демо

Заключение

В этой статье вы смогли встроить генератор квитанций в PDF или QR-код с помощью ключа API Rootstock и метода RPC. Поэтому в вашем следующем проекте dApp я надеюсь увидеть эту функцию.