Автономні трекери EVM
Трекери @tuwaio/pulsar-evm працюють і без сховища Pulsar. Використовуйте їх, коли ви:
- зберігаєте транзакції у власному стані (Redux, MobX, інше сховище Zustand, база даних);
- відстежуєте транзакції на сервері, де немає ні
localStorage, ні гаманця; - хочете отримувати колбек на кожному етапі життєвого циклу замість оновлень сховища.
Без сховища нічого не зберігається і нічого не відновлюється після перезапуску: ви самі вирішуєте, що зберігати і коли знову почати відстеження. Трекери використовують лише viem, @wagmi/core і HTTP API (трекер Safe з @tuwaio/pulsar-evm/safe — лише viem і HTTP), тому працюють і в браузері, і в Node.js.
Чому evmTracker, а не waitForTransactionReceipt
| Ситуація | waitForTransactionReceipt (viem) | evmTracker (Pulsar) |
|---|---|---|
| Вузол ще не проіндексував транзакцію | Чекає на квитанцію; деталі транзакції не запитуються. | Повторює getTransaction (за замовчуванням 10 разів з інтервалом 3 с) і передає деталі (nonce, комісії, to) у колбек. |
| Транзакцію має лише RPC гаманця | Нічого не знаходить за її хешем. | Запитує гаманець, підключений у цій мережі, через його провайдер і без запиту підтвердження, і далі працює з отриманим nonce: пришвидшення такої транзакції теж видно. |
| Тимчасові помилки RPC | Виклик відхиляється, щойно спливає його тайм-аут або вичерпуються власні повтори. | Будь-яка помилка під час очікування означає, що відповіді ще немає: чекає знову до receiptAttempts разів зі зростаючими паузами (за замовчуванням 6, 435 с), потім викликає onFailure. |
| Пришвидшення або скасування в гаманці | Повідомляється через onReplaced, якщо заміна потрапляє в блок під час одного виклику і цей виклик може впізнати транзакцію. | Після кожної спроби читає nonce відправника, тож про заміну повідомляє в будь-який момент, навіть коли вузол уже не повертає старий хеш. Викликає onReplaced і зупиняється, тож замінену транзакцію ніколи не буде позначено успішною. |
| Кілька підтверджень | Параметр confirmations одного виклику. | Кожні 5 с опитує кількість підтверджень і повідомляє кожне значення через onConfirmationsUpdate. |
evmTracker викликає onSuccess для кожної транзакції, включеної в блок, зокрема й для відхилених (reverted): перевіряйте receipt.status.
Стандартні транзакції: evmTracker
evmTracker відстежує транзакцію за її хешем через клієнт wagmi її мережі й повертає результат, коли відстеження завершено:
import { evmTracker } from '@tuwaio/pulsar-evm';
import { createConfig, http } from '@wagmi/core';
import { sepolia } from 'viem/chains';
const wagmiConfig = createConfig({ chains: [sepolia], transports: { [sepolia.id]: http() } });
export async function trackTransaction(hash: `0x${string}`) {
await evmTracker({
config: wagmiConfig,
tx: { txKey: hash, chainId: sepolia.id, requiredConfirmations: 2 },
onTxDetailsFetched: (details) => console.log('Nonce', details.nonce),
onConfirmationsUpdate: (confirmations) => console.log(`${confirmations}/2 confirmations`),
onSuccess: async (_details, receipt) => {
console.log(receipt.status === 'success' ? 'Confirmed' : 'Reverted', receipt.blockNumber);
},
onReplaced: (replacement) => console.log(`Replaced (${replacement.reason}) by`, replacement.transaction.hash),
onFailure: (error) => console.error('Tracking failed', error),
});
}Мережу має бути налаштовано в конфігурації wagmi. Для нульового хешу або невідомої мережі відстеження одразу завершується помилкою.
UserOperations ERC-4337: erc4337Tracker
erc4337Tracker кожні 2 с опитує eth_getUserOperationReceipt, доки UserOperation не потрапить до бандла, і припиняє спроби після 60 невдалих запитів поспіль. Він викликає onSuccess із хешем транзакції бандла; передайте цей хеш у evmTracker, якщо вам потрібні ще й підтвердження блоків:
import { erc4337Tracker, evmTracker } from '@tuwaio/pulsar-evm';
import { type Config } from '@wagmi/core';
import { sepolia } from 'viem/chains';
declare const wagmiConfig: Config;
export function trackUserOperation(userOpHash: `0x${string}`, pimlicoApiKey?: string) {
erc4337Tracker({
// Uses bundlerUrl if set, else api.pimlico.io with the API key, else the rate-limited public Pimlico endpoint.
tx: { txKey: userOpHash, chainId: sepolia.id, pimlicoApiKey, pending: true },
onSuccess: ({ hash }) => {
if (!hash) return;
void evmTracker({
config: wagmiConfig,
tx: { txKey: hash, chainId: sepolia.id },
onTxDetailsFetched: () => {},
onSuccess: async (_details, receipt) => console.log('Bundle transaction', receipt.status),
onReplaced: () => {},
onFailure: (error) => console.error(error),
});
},
onFailure: (result) => console.error('UserOperation failed:', result?.reason ?? 'too many failed requests'),
});
}erc4337Tracker повертає керування одразу; опитування триває у фоні. Відхилена (reverted) UserOperation викликає onFailure з причиною відхилення.
Батчі викликів EIP-5792: eip5792Tracker
eip5792Tracker кожні 2 с через конектор wagmi запитує статус батчу в гаманця, який надіслав цей батч через wallet_sendCalls, і припиняє спроби після 60 невдалих запитів поспіль (наприклад, поки жоден гаманець не підключено). Він викликає onSuccess із хешем транзакції, яка виконала батч; передайте цей хеш у evmTracker, якщо вам потрібні ще й підтвердження блоків:
import { eip5792Tracker } from '@tuwaio/pulsar-evm';
import { type Config, sendCalls } from '@wagmi/core';
import { type Address, encodeFunctionData, erc20Abi } from 'viem';
declare const wagmiConfig: Config;
export async function payInOneBatch(token: Address, to: Address, amount: bigint) {
const { id } = await sendCalls(wagmiConfig, {
calls: [{ to: token, data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [to, amount] }) }],
});
eip5792Tracker({
tx: { txKey: id, pending: true },
config: wagmiConfig,
onSuccess: ({ hash }) => console.log('Executed in', hash),
onFailure: (result) => console.error('Batch failed:', result?.reason ?? 'too many failed requests'),
});
}У сховищі поверніть ID батчу з actionFunction і передайте tracker: TransactionTracker.EIP5792: трекер сховища записує hash транзакції, що виконала батч, а потім відстежує її в мережі, а після перезавантаження продовжує з того етапу, якого досяг.
Мультипідпис Safe: фетчер з опитуванням
Safe відстежується опитуванням API. Передайте його фетчер у initializePollingTracker з @tuwaio/pulsar-core, який за замовчуванням викликає його кожні 5 с (або за розкладом функції pollingInterval) і припиняє спроби після 10 невдалих запитів поспіль (тоді він викликає onFailure() без аргументів), або, з retryForeverEveryMs, продовжує запитувати з цією затримкою.
Трекер Safe також доступний як @tuwaio/pulsar-evm/safe — точка входу, яка не завантажує ні @wagmi/core, ні @tuwaio/orbit-evm: встановіть @tuwaio/pulsar-evm, @tuwaio/pulsar-core, @tuwaio/orbit-core, viem і dayjs, і застосунок, побудований лише на viem, зможе відстежувати транзакції Safe.
import { initializePollingTracker } from '@tuwaio/pulsar-core';
import { createSafeFetcher, safePollingInterval } from '@tuwaio/pulsar-evm/safe';
// Safe: txKey is the safeTxHash, from is the Safe address. Without a timeout it waits until the owners act.
export function trackSafeTransaction(safeTxHash: `0x${string}`, safeAddress: `0x${string}`, chainId: number) {
initializePollingTracker({
tx: { txKey: safeTxHash, chainId, from: safeAddress, pending: true },
fetcher: createSafeFetcher({ pendingTimeoutMs: null }),
pollingInterval: safePollingInterval, // every 5 s for the first hour, then every 30 s
onIntervalTick: (status) =>
console.log(`Confirmed by ${status.confirmations?.length ?? 0} of ${status.confirmationsRequired} owners`),
onSuccess: (status) => console.log('Executed in', status.transactionHash),
onFailure: (status) => console.error(status ? 'Execution failed' : 'Not found or too many failed requests'),
onReplaced: (executed) =>
console.warn('Another transaction with the same nonce was executed:', executed.safeTxHash),
});
}- Safe: фетчер звертається до Safe Transaction Service мережі (підтримувані мережі перелічено в
SafeTransactionServiceUrls) і передає кожну перевірку вonIntervalTickразом із зібраними підтвердженнями власників (confirmations) і порогом (confirmationsRequired). Відповідь 404 викликаєonFailure()без відповіді й зупиняє відстеження. Транзакція, що все ще очікує черезpendingTimeoutMsпісля того, як її запропонували (один день дляsafeFetcherі за замовчуванням), викликаєonFailureзі своїм статусом і зупиняє відстеження;createSafeFetcher({ pendingTimeoutMs: null })чекає без обмеження.
Ці фетчери зупиняються з withoutRemoving: true, тому removeTxFromPool, переданий у initializePollingTracker, викликається лише тоді, коли опитування припиняється: після maxRetries помилок поспіль або через FinalPollingError.
Помічники
checkTransactionsTracker({ actionTxKey, connectorType, tracker })повертає трекер, який Pulsar використав би для ключа:ERC4337лише на запит,Safeдля конекторів Safe (evm:safe), інакшеEthereum.readNonceSlot(client, { hash, slot: { from, nonce } })за відправником і nonce показує, що сталося з транзакцією, коли вузол уже не повертає її хеш:open(nonce вільний),spender_found(його використала інша транзакція, а її чек ще не читається),ours,replaced(з даними заміни від viem) абоspent. Після перезапуску передайте відправника й nonce вevmTrackerякknownDetails, щоб він теж їх читав.speedUpTxActionіcancelTxActionповторно надсилають EIP-1559-транзакцію, що очікує, з тим самим nonce, на 15% вище за комісії, надіслані для неї востаннє, і не нижче за ринкову оцінку, лише з рахунку, який її надіслав. Їм потрібніnonceі поля комісій, які отримав трекер; після цьогоevmTrackerповідомляє, що вихідну транзакцію замінено.selectEvmTxExplorerLinkбудує посилання на транзакцію в експлорері блоків (або в застосунку Safe).
import { TransactionTracker } from '@tuwaio/pulsar-core';
import { checkTransactionsTracker } from '@tuwaio/pulsar-evm';
const { tracker, txKey } = checkTransactionsTracker({ actionTxKey: '0xabc123', connectorType: 'evm:metamask' });
console.log(tracker === TransactionTracker.Ethereum, txKey); // true '0xabc123'Повний список експортів — у довіднику @tuwaio/pulsar-evm.