@tuwaio/pulsar-core 1.0.0-fix-callbacks-alpha.3.e3b3acb → 1.0.0-fix-callbacks-alpha.4.795d3ad

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -68,7 +68,10 @@ type GelatoTxKey = {
68
68
  * - A structured object from a relay service like Gelato (`GelatoTxKey`).
69
69
  */
70
70
  type ActionTxKey = `0x${string}` | GelatoTxKey | string;
71
- type OnSuccessCallback<T> = (tx: T) => Promise<void> | void;
71
+ type OnSuccessCallback<T> = {
72
+ /** Callback to execute when the transaction is successfully submitted. */
73
+ onSuccessCallback?: (tx: T) => Promise<void> | void;
74
+ };
72
75
  /**
73
76
  * The fundamental structure for any transaction being tracked by Pulsar.
74
77
  * This serves as the base upon which chain-specific transaction types are built.
@@ -234,8 +237,7 @@ type TxAdapter<T extends Transaction> = {
234
237
  /** Selects and initializes the correct background tracker for a given transaction. */
235
238
  checkAndInitializeTrackerInStore: (params: {
236
239
  tx: T;
237
- onSucceedCallback?: OnSuccessCallback<T>;
238
- } & Pick<ITxTrackingStore<T>, 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'>) => Promise<void>;
240
+ } & OnSuccessCallback<T> & Pick<ITxTrackingStore<T>, 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'>) => Promise<void>;
239
241
  /** Returns the base URL for the blockchain explorer for the current network. */
240
242
  getExplorerUrl: (url?: string) => string | undefined;
241
243
  /** Optional: Fetches a name from a chain-specific name service (e.g., ENS). */
@@ -275,9 +277,7 @@ type ITxTrackingStore<T extends Transaction> = IInitializeTxTrackingStore<T> & {
275
277
  params: Omit<InitialTransactionParams, 'actionFunction'>;
276
278
  /** The default tracker to use if it cannot be determined automatically. */
277
279
  defaultTracker?: TransactionTracker;
278
- /** Callback to execute when the transaction is successfully submitted. */
279
- onSucceedCallback?: OnSuccessCallback<T>;
280
- }) => Promise<void>;
280
+ } & OnSuccessCallback<T>) => Promise<void>;
281
281
  /**
282
282
  * Initializes trackers for all pending transactions in the pool.
283
283
  * This is essential for resuming tracking after a page reload.
package/dist/index.d.ts CHANGED
@@ -68,7 +68,10 @@ type GelatoTxKey = {
68
68
  * - A structured object from a relay service like Gelato (`GelatoTxKey`).
69
69
  */
70
70
  type ActionTxKey = `0x${string}` | GelatoTxKey | string;
71
- type OnSuccessCallback<T> = (tx: T) => Promise<void> | void;
71
+ type OnSuccessCallback<T> = {
72
+ /** Callback to execute when the transaction is successfully submitted. */
73
+ onSuccessCallback?: (tx: T) => Promise<void> | void;
74
+ };
72
75
  /**
73
76
  * The fundamental structure for any transaction being tracked by Pulsar.
74
77
  * This serves as the base upon which chain-specific transaction types are built.
@@ -234,8 +237,7 @@ type TxAdapter<T extends Transaction> = {
234
237
  /** Selects and initializes the correct background tracker for a given transaction. */
235
238
  checkAndInitializeTrackerInStore: (params: {
236
239
  tx: T;
237
- onSucceedCallback?: OnSuccessCallback<T>;
238
- } & Pick<ITxTrackingStore<T>, 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'>) => Promise<void>;
240
+ } & OnSuccessCallback<T> & Pick<ITxTrackingStore<T>, 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'>) => Promise<void>;
239
241
  /** Returns the base URL for the blockchain explorer for the current network. */
240
242
  getExplorerUrl: (url?: string) => string | undefined;
241
243
  /** Optional: Fetches a name from a chain-specific name service (e.g., ENS). */
@@ -275,9 +277,7 @@ type ITxTrackingStore<T extends Transaction> = IInitializeTxTrackingStore<T> & {
275
277
  params: Omit<InitialTransactionParams, 'actionFunction'>;
276
278
  /** The default tracker to use if it cannot be determined automatically. */
277
279
  defaultTracker?: TransactionTracker;
278
- /** Callback to execute when the transaction is successfully submitted. */
279
- onSucceedCallback?: OnSuccessCallback<T>;
280
- }) => Promise<void>;
280
+ } & OnSuccessCallback<T>) => Promise<void>;
281
281
  /**
282
282
  * Initializes trackers for all pending transactions in the pool.
283
283
  * This is essential for resuming tracking after a page reload.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var immer=require('immer'),w=require('dayjs'),middleware=require('zustand/middleware'),vanilla=require('zustand/vanilla'),zustand=require('zustand');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var w__default=/*#__PURE__*/_interopDefault(w);function k(){return (r,t)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:e=>{r(n=>immer.produce(n,i=>{i.lastAddedTxKey=e.txKey,e.txKey&&(i.transactionsPool[e.txKey]={...e,pending:true});}));},updateTxParams:(e,n)=>{r(i=>immer.produce(i,a=>{let l=a.transactionsPool[e];l&&Object.assign(l,n);}));},removeTxFromPool:e=>{r(n=>immer.produce(n,i=>{delete i.transactionsPool[e];}));},closeTxTrackedModal:e=>{r(n=>immer.produce(n,i=>{e&&i.transactionsPool[e]&&(i.transactionsPool[e].isTrackedModalOpen=false),i.initialTx=void 0;}));},getLastTxKey:()=>t().lastAddedTxKey})}var h=r=>Object.values(r).sort((t,e)=>Number(t.localTimestamp)-Number(e.localTimestamp)),N=r=>h(r).filter(t=>t.pending),j=(r,t)=>r[t],b=(r,t)=>h(r).filter(e=>e.from.toLowerCase()===t.toLowerCase()),G=(r,t)=>b(r,t).filter(e=>e.pending);var S=({adapterKey:r,adapter:t})=>{if(Array.isArray(t)){if(t.length===0){console.error("Adapter selection failed: The provided adapters array is empty.");return}let e=t.find(n=>n.key===r);return e||(console.warn(`No adapter found for key: "${r}". Falling back to the first available adapter: "${t[0].key}".`),t[0])}return t};function Y({adapter:r,...t}){return vanilla.createStore()(middleware.persist((e,n)=>({...k()(e,n),initializeTransactionsPool:async()=>{let i=Object.values(n().transactionsPool).filter(a=>a.pending);await Promise.all(i.map(a=>S({adapterKey:a.adapter,adapter:r})?.checkAndInitializeTrackerInStore({tx:a,...n()})));},handleTransaction:async({defaultTracker:i,actionFunction:a,onSucceedCallback:l,params:p})=>{let{desiredChainID:x,...m}=p,g=w__default.default().unix();e({initialTx:{...p,actionFunction:a,localTimestamp:g,isInitializing:true}});let c=S({adapterKey:m.adapter,adapter:r}),T=o=>{let u=o instanceof Error?o.message:String(o);e(s=>immer.produce(s,d=>{d.initialTx&&(d.initialTx.isInitializing=false,d.initialTx.errorMessage=u);}));};if(!c){let o=new Error("No adapter found for this transaction.");throw T(o),o}try{let{walletType:o,walletAddress:u}=c.getWalletInfo();await c.checkChainForTx(x);let s=await a();if(!s){e({initialTx:void 0});return}let{tracker:d,txKey:y}=c.checkTransactionsTracker(s,o),v={...m,walletType:o,from:u,tracker:d||i,chainId:x,localTimestamp:g,txKey:y,hash:d==="ethereum"?s:void 0,pending:!1,isTrackedModalOpen:p.withTrackedModal};n().addTxToPool(v),e(K=>immer.produce(K,P=>{P.initialTx&&(P.initialTx.isInitializing=!1,P.initialTx.lastTxKey=y);}));let I=n().transactionsPool[y];await c.checkAndInitializeTrackerInStore({tx:I,onSucceedCallback:l,...n()});}catch(o){throw T(o),o}}}),{...t}))}var E=(n=>(n.EVM="evm",n.SOLANA="solana",n.Starknet="starknet",n))(E||{}),z=(i=>(i.Ethereum="ethereum",i.Safe="safe",i.Gelato="gelato",i.Solana="solana",i))(z||{}),M=(n=>(n.Failed="Failed",n.Success="Success",n.Replaced="Replaced",n))(M||{});var re=(r=>t=>zustand.useStore(r,t));var C=5e3,L=10;function oe(r){let{tx:t,fetcher:e,onInitialize:n,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p,removeTxFromPool:x,pollingInterval:m=C,maxRetries:g=L}=r;if(!t.pending)return;n?.();let c=g,T=true,o=s=>{T&&(T=false,x&&!s?.withoutRemoving&&x(t.txKey));};(async()=>{for(;T&&c>0;)try{if(await new Promise(s=>setTimeout(s,m)),!T)break;await e({tx:t,stopPolling:o,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p});}catch(s){console.error(`Polling fetcher for txKey ${t.txKey} threw an error:`,s),c--;}c<=0&&(console.warn(`Polling for txKey ${t.txKey} stopped after reaching the maximum number of retries.`),a(),o());})();}exports.TransactionAdapter=E;exports.TransactionStatus=M;exports.TransactionTracker=z;exports.createBoundedUseStore=re;exports.createPulsarStore=Y;exports.initializePollingTracker=oe;exports.initializeTxTrackingStore=k;exports.selectAdapterByKey=S;exports.selectAllTransactions=h;exports.selectAllTransactionsByActiveWallet=b;exports.selectPendingTransactions=N;exports.selectPendingTransactionsByActiveWallet=G;exports.selectTxByKey=j;//# sourceMappingURL=index.js.map
1
+ 'use strict';var immer=require('immer'),w=require('dayjs'),middleware=require('zustand/middleware'),vanilla=require('zustand/vanilla'),zustand=require('zustand');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var w__default=/*#__PURE__*/_interopDefault(w);function k(){return (r,t)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:e=>{r(n=>immer.produce(n,i=>{i.lastAddedTxKey=e.txKey,e.txKey&&(i.transactionsPool[e.txKey]={...e,pending:true});}));},updateTxParams:(e,n)=>{r(i=>immer.produce(i,a=>{let l=a.transactionsPool[e];l&&Object.assign(l,n);}));},removeTxFromPool:e=>{r(n=>immer.produce(n,i=>{delete i.transactionsPool[e];}));},closeTxTrackedModal:e=>{r(n=>immer.produce(n,i=>{e&&i.transactionsPool[e]&&(i.transactionsPool[e].isTrackedModalOpen=false),i.initialTx=void 0;}));},getLastTxKey:()=>t().lastAddedTxKey})}var h=r=>Object.values(r).sort((t,e)=>Number(t.localTimestamp)-Number(e.localTimestamp)),N=r=>h(r).filter(t=>t.pending),j=(r,t)=>r[t],b=(r,t)=>h(r).filter(e=>e.from.toLowerCase()===t.toLowerCase()),G=(r,t)=>b(r,t).filter(e=>e.pending);var S=({adapterKey:r,adapter:t})=>{if(Array.isArray(t)){if(t.length===0){console.error("Adapter selection failed: The provided adapters array is empty.");return}let e=t.find(n=>n.key===r);return e||(console.warn(`No adapter found for key: "${r}". Falling back to the first available adapter: "${t[0].key}".`),t[0])}return t};function Y({adapter:r,...t}){return vanilla.createStore()(middleware.persist((e,n)=>({...k()(e,n),initializeTransactionsPool:async()=>{let i=Object.values(n().transactionsPool).filter(a=>a.pending);await Promise.all(i.map(a=>S({adapterKey:a.adapter,adapter:r})?.checkAndInitializeTrackerInStore({tx:a,...n()})));},handleTransaction:async({defaultTracker:i,actionFunction:a,onSuccessCallback:l,params:p})=>{let{desiredChainID:x,...m}=p,g=w__default.default().unix();e({initialTx:{...p,actionFunction:a,localTimestamp:g,isInitializing:true}});let c=S({adapterKey:m.adapter,adapter:r}),T=o=>{let u=o instanceof Error?o.message:String(o);e(s=>immer.produce(s,d=>{d.initialTx&&(d.initialTx.isInitializing=false,d.initialTx.errorMessage=u);}));};if(!c){let o=new Error("No adapter found for this transaction.");throw T(o),o}try{let{walletType:o,walletAddress:u}=c.getWalletInfo();await c.checkChainForTx(x);let s=await a();if(!s){e({initialTx:void 0});return}let{tracker:d,txKey:y}=c.checkTransactionsTracker(s,o),v={...m,walletType:o,from:u,tracker:d||i,chainId:x,localTimestamp:g,txKey:y,hash:d==="ethereum"?s:void 0,pending:!1,isTrackedModalOpen:p.withTrackedModal};n().addTxToPool(v),e(K=>immer.produce(K,P=>{P.initialTx&&(P.initialTx.isInitializing=!1,P.initialTx.lastTxKey=y);}));let I=n().transactionsPool[y];await c.checkAndInitializeTrackerInStore({tx:I,onSuccessCallback:l,...n()});}catch(o){throw T(o),o}}}),{...t}))}var E=(n=>(n.EVM="evm",n.SOLANA="solana",n.Starknet="starknet",n))(E||{}),z=(i=>(i.Ethereum="ethereum",i.Safe="safe",i.Gelato="gelato",i.Solana="solana",i))(z||{}),M=(n=>(n.Failed="Failed",n.Success="Success",n.Replaced="Replaced",n))(M||{});var re=(r=>t=>zustand.useStore(r,t));var L=5e3,C=10;function oe(r){let{tx:t,fetcher:e,onInitialize:n,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p,removeTxFromPool:x,pollingInterval:m=L,maxRetries:g=C}=r;if(!t.pending)return;n?.();let c=g,T=true,o=s=>{T&&(T=false,x&&!s?.withoutRemoving&&x(t.txKey));};(async()=>{for(;T&&c>0;)try{if(await new Promise(s=>setTimeout(s,m)),!T)break;await e({tx:t,stopPolling:o,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p});}catch(s){console.error(`Polling fetcher for txKey ${t.txKey} threw an error:`,s),c--;}c<=0&&(console.warn(`Polling for txKey ${t.txKey} stopped after reaching the maximum number of retries.`),a(),o());})();}exports.TransactionAdapter=E;exports.TransactionStatus=M;exports.TransactionTracker=z;exports.createBoundedUseStore=re;exports.createPulsarStore=Y;exports.initializePollingTracker=oe;exports.initializeTxTrackingStore=k;exports.selectAdapterByKey=S;exports.selectAllTransactions=h;exports.selectAllTransactionsByActiveWallet=b;exports.selectPendingTransactions=N;exports.selectPendingTransactionsByActiveWallet=G;exports.selectTxByKey=j;//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store/initializeTxTrackingStore.ts","../src/store/transactionsSelectors.ts","../src/utils/selectAdapterByKey.ts","../src/store/txTrackingStore.ts","../src/types.ts","../src/utils/createBoundedUseStore.ts","../src/utils/initializePollingTracker.ts"],"names":["initializeTxTrackingStore","set","get","tx","state","produce","draft","txKey","fields","selectAllTransactions","transactionsPool","a","b","selectPendingTransactions","selectTxByKey","key","selectAllTransactionsByActiveWallet","from","selectPendingTransactionsByActiveWallet","selectAdapterByKey","adapterKey","adapter","foundAdapter","createPulsarStore","options","createStore","persist","pendingTxs","defaultTracker","actionFunction","onSucceedCallback","params","desiredChainID","restParams","localTimestamp","dayjs","handleTxError","e","errorMessage","error","walletType","walletAddress","txKeyFromAction","updatedTracker","finalTxKey","newTx","TransactionAdapter","TransactionTracker","TransactionStatus","createBoundedUseStore","store","selector","useStore","DEFAULT_POLLING_INTERVAL","DEFAULT_MAX_RETRIES","initializePollingTracker","config","fetcher","onInitialize","onSuccess","onFailure","onIntervalTick","onReplaced","removeTxFromPool","pollingInterval","maxRetries","retriesLeft","isPolling","stopPolling","resolve"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file This file defines the core Zustand slice for managing the state of transactions. It includes the state,\n * actions, and types necessary for initializing the store and performing CRUD operations on the transaction pool.\n */\n\nimport { Draft, produce } from 'immer';\n\nimport { EvmTransaction, InitialTransaction, SolanaTransaction, StoreSlice, Transaction } from '../types';\n\n/**\n * Defines the structure of the transaction pool, a key-value store of transactions indexed by their unique keys.\n * @template T The type of the transaction object being tracked.\n */\nexport type TransactionPool<T extends Transaction> = Record<string, T>;\n\n/**\n * A utility type that creates a union of all fields that can be safely updated\n * on a transaction object via the `updateTxParams` action. This ensures type safety\n * and prevents accidental modification of immutable properties.\n */\ntype UpdatableTransactionFields = Partial<\n Pick<\n EvmTransaction,\n | 'to'\n | 'nonce'\n | 'txKey'\n | 'pending'\n | 'hash'\n | 'status'\n | 'replacedTxHash'\n | 'errorMessage'\n | 'finishedTimestamp'\n | 'isTrackedModalOpen'\n | 'isError'\n | 'maxPriorityFeePerGas'\n | 'maxFeePerGas'\n | 'input'\n | 'value'\n >\n> &\n Partial<Pick<SolanaTransaction, 'slot' | 'confirmations' | 'fee' | 'instructions' | 'recentBlockhash' | 'rpcUrl'>>;\n\n/**\n * The interface for the base transaction tracking store slice.\n * It includes the state and actions for managing the transaction lifecycle.\n * @template T The specific transaction type.\n */\nexport interface IInitializeTxTrackingStore<T extends Transaction> {\n /** A pool of all transactions currently being tracked, indexed by `txKey`. */\n transactionsPool: TransactionPool<T>;\n /** The `txKey` of the most recently added transaction. */\n lastAddedTxKey?: string;\n /** The state for a transaction being initiated, used for UI feedback before it's submitted to the chain. */\n initialTx?: InitialTransaction;\n\n /** Adds a new transaction to the tracking pool and marks it as pending. */\n addTxToPool: (tx: T) => void;\n /** Updates one or more properties of an existing transaction in the pool. */\n updateTxParams: (txKey: string, fields: UpdatableTransactionFields) => void;\n /** Removes a transaction from the tracking pool by its key. */\n removeTxFromPool: (txKey: string) => void;\n /** Closes the tracking modal for a transaction and clears any initial transaction state. */\n closeTxTrackedModal: (txKey?: string) => void;\n /** A selector function to retrieve the key of the last transaction added to the pool. */\n getLastTxKey: () => string | undefined;\n}\n\n/**\n * Creates a Zustand store slice with the core logic for transaction state management.\n * This function is a slice creator intended for use with Zustand's `create` function.\n *\n * @template T The specific transaction type.\n * @param options Configuration for the store slice.\n * @returns A Zustand store slice implementing `IInitializeTxTrackingStore`.\n */\nexport function initializeTxTrackingStore<T extends Transaction>(): StoreSlice<IInitializeTxTrackingStore<T>> {\n return (set, get) => ({\n transactionsPool: {},\n lastAddedTxKey: undefined,\n initialTx: undefined,\n\n addTxToPool: (tx) => {\n set((state) =>\n produce(state, (draft) => {\n draft.lastAddedTxKey = tx.txKey;\n if (tx.txKey) {\n draft.transactionsPool[tx.txKey] = {\n ...tx,\n pending: true, // Ensure all new transactions start as pending.\n } as Draft<T>;\n }\n }),\n );\n },\n\n updateTxParams: (txKey, fields) => {\n set((state) =>\n produce(state, (draft) => {\n const tx = draft.transactionsPool[txKey];\n // Ensure the transaction exists before attempting to update.\n if (tx) {\n Object.assign(tx, fields);\n }\n }),\n );\n },\n\n removeTxFromPool: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n delete draft.transactionsPool[txKey];\n }),\n );\n },\n\n closeTxTrackedModal: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n if (txKey && draft.transactionsPool[txKey]) {\n draft.transactionsPool[txKey].isTrackedModalOpen = false;\n }\n // Always clear the initial transaction state when a modal is closed\n draft.initialTx = undefined;\n }),\n );\n },\n\n getLastTxKey: () => get().lastAddedTxKey,\n });\n}\n","/**\n * @file This file contains selector functions for deriving state from the transaction tracking store.\n * Selectors help abstract the state's shape and provide efficient, memoized access to computed data.\n */\n\nimport { Transaction } from '../types';\nimport { TransactionPool } from './initializeTxTrackingStore';\n\n/**\n * Selects all transactions from the pool and sorts them by their creation timestamp in ascending order.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of all transactions, sorted chronologically.\n */\nexport const selectAllTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return Object.values(transactionsPool).sort((a, b) => Number(a.localTimestamp) - Number(b.localTimestamp));\n};\n\n/**\n * Selects all transactions that are currently in a pending state, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of pending transactions.\n */\nexport const selectPendingTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return selectAllTransactions(transactionsPool).filter((tx) => tx.pending);\n};\n\n/**\n * Selects a single transaction from the pool by its unique key (`txKey`).\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} key - The `txKey` of the transaction to retrieve.\n * @returns {T | undefined} The transaction object if found, otherwise undefined.\n */\nexport const selectTxByKey = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n key: string,\n): T | undefined => {\n return transactionsPool[key];\n};\n\n/**\n * Selects all transactions initiated by a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of transactions associated with the given wallet.\n */\nexport const selectAllTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Filters all transactions to find those matching the provided `from` address.\n return selectAllTransactions(transactionsPool).filter((tx) => tx.from.toLowerCase() === from.toLowerCase());\n};\n\n/**\n * Selects all pending transactions for a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of pending transactions for the given wallet.\n */\nexport const selectPendingTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Reuses the `selectAllTransactionsByActiveWallet` selector for efficiency\n // and then filters for pending transactions.\n return selectAllTransactionsByActiveWallet(transactionsPool, from).filter((tx) => tx.pending);\n};\n","/**\n * @file This file contains a utility function for selecting a specific transaction adapter from a list.\n */\n\nimport { Transaction, TransactionAdapter, TxAdapter } from '../types';\n\n/**\n * Selects a transaction adapter from a list based on a provided key.\n *\n * This function searches through an array of `TxAdapter` instances and returns the one\n * that matches the given `adapterKey`. If no specific adapter is found, it logs a warning\n * and returns the first adapter in the array as a fallback. This fallback mechanism\n * ensures that the system can still function, but it highlights a potential configuration issue.\n *\n * @template T - The transaction type, extending the base `Transaction`.\n *\n * @param {object} params - The parameters for the selection.\n * @param {TransactionAdapter} params.adapterKey - The key of the desired adapter.\n * @param {TxAdapter<T> | TxAdapter<T>[]} params.adapter - Adapter or an array of adapters for different chains or transaction types.\n *\n * @returns {TxAdapter<T> | undefined} The found transaction adapter, the fallback adapter, or undefined if the adapters array is empty.\n */\nexport const selectAdapterByKey = <T extends Transaction>({\n adapterKey,\n adapter,\n}: {\n adapterKey: TransactionAdapter;\n adapter: TxAdapter<T> | TxAdapter<T>[];\n}): TxAdapter<T> | undefined => {\n if (Array.isArray(adapter)) {\n if (adapter.length === 0) {\n console.error('Adapter selection failed: The provided adapters array is empty.');\n return undefined;\n }\n\n const foundAdapter = adapter.find((a) => a.key === adapterKey);\n\n if (foundAdapter) {\n return foundAdapter;\n } else {\n console.warn(\n `No adapter found for key: \"${adapterKey}\". Falling back to the first available adapter: \"${adapter[0].key}\".`,\n );\n return adapter[0];\n }\n }\n return adapter;\n};\n","/**\n * @file This file is the nucleus of the Pulsar store, orchestrating transaction handling, state management,\n * and communication with blockchain adapters. It utilizes Zustand for state management, Immer for safe,\n * immutable updates, and a persistence middleware to maintain state across user sessions.\n */\n\nimport dayjs from 'dayjs';\nimport { produce } from 'immer';\nimport { persist, PersistOptions } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\n\nimport { ITxTrackingStore, Transaction, TxAdapter } from '../types';\nimport { selectAdapterByKey } from '../utils/selectAdapterByKey';\nimport { initializeTxTrackingStore } from './initializeTxTrackingStore';\n\n/**\n * Creates the main Pulsar store for transaction tracking.\n *\n * This function configures a Zustand store enhanced with persistence. It combines the core transaction management\n * slice with a powerful orchestration logic that leverages chain-specific adapters to handle the entire\n * lifecycle of a transaction—from initiation and chain validation to execution and background status tracking.\n *\n * @template T The specific transaction type, extending the base `Transaction`.\n *\n * @param config Configuration object for creating the store.\n * @param config.adapter Adapter or an array of adapters for different chains or transaction types.\n * @param options Configuration for the Zustand `persist` middleware.\n * @returns A fully configured Zustand store instance.\n */\nexport function createPulsarStore<T extends Transaction>({\n adapter,\n ...options\n}: {\n adapter: TxAdapter<T> | TxAdapter<T>[];\n} & PersistOptions<ITxTrackingStore<T>>) {\n return createStore<ITxTrackingStore<T>>()(\n persist(\n (set, get) => ({\n // Initialize the base store slice with core state and actions\n ...initializeTxTrackingStore<T>()(set, get),\n\n /**\n * Initializes trackers for all pending transactions upon store creation.\n * This is crucial for resuming tracking after a page refresh or session restoration.\n */\n initializeTransactionsPool: async () => {\n const pendingTxs = Object.values(get().transactionsPool).filter((tx) => tx.pending);\n\n // Concurrently initialize trackers for all pending transactions\n await Promise.all(\n pendingTxs.map((tx) => {\n const foundAdapter = selectAdapterByKey({\n adapterKey: tx.adapter,\n adapter,\n });\n // Delegate tracker initialization to the appropriate adapter\n return foundAdapter?.checkAndInitializeTrackerInStore({\n tx,\n ...get(),\n });\n }),\n );\n },\n\n /**\n * The primary function to orchestrate sending and tracking a new transaction.\n * It manages the entire lifecycle, from UI state updates and chain switching to\n * signing, submission, and background tracker initialization.\n */\n handleTransaction: async ({ defaultTracker, actionFunction, onSucceedCallback, params }) => {\n const { desiredChainID, ...restParams } = params;\n const localTimestamp = dayjs().unix();\n\n // Step 1: Set initial state for immediate UI feedback (e.g., loading spinner).\n set({\n initialTx: {\n ...params,\n actionFunction,\n localTimestamp,\n isInitializing: true,\n },\n });\n\n const foundAdapter = selectAdapterByKey({\n adapterKey: restParams.adapter,\n adapter,\n });\n\n // Centralized error handler for this transaction flow\n const handleTxError = (e: unknown) => {\n const errorMessage = e instanceof Error ? e.message : String(e);\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.errorMessage = errorMessage;\n }\n }),\n );\n };\n\n if (!foundAdapter) {\n const error = new Error('No adapter found for this transaction.');\n handleTxError(error);\n throw error; // Re-throw to allow the caller to handle it.\n }\n\n try {\n const { walletType, walletAddress } = foundAdapter.getWalletInfo();\n\n // Step 2: Ensure the wallet is connected to the correct chain.\n await foundAdapter.checkChainForTx(desiredChainID);\n\n // Step 3: Execute the provided action (e.g., signing and sending the transaction).\n const txKeyFromAction = await actionFunction();\n\n // If `txKeyFromAction` is undefined, it indicates the user cancelled the action.\n if (!txKeyFromAction) {\n set({ initialTx: undefined });\n return;\n }\n\n // Step 4: Determine the final tracker and txKey from the action's result.\n const { tracker: updatedTracker, txKey: finalTxKey } = foundAdapter.checkTransactionsTracker(\n txKeyFromAction,\n walletType,\n );\n\n // Step 5: Construct the full transaction object for the pool.\n const newTx = {\n ...restParams,\n walletType,\n from: walletAddress,\n tracker: updatedTracker || defaultTracker,\n chainId: desiredChainID,\n localTimestamp,\n txKey: finalTxKey,\n // For EVM, the hash is often the preliminary key from the action.\n hash: updatedTracker === 'ethereum' ? (txKeyFromAction as `0x${string}`) : undefined,\n pending: false, // will be set to true by addTxToPool\n isTrackedModalOpen: params.withTrackedModal,\n };\n\n // Step 6: Add the transaction to the pool.\n get().addTxToPool(newTx as T);\n\n // Step 7: Update the initial state to link it with the newly created transaction.\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.lastTxKey = finalTxKey;\n }\n }),\n );\n\n // Step 8: Initialize the background tracker for the transaction.\n const tx = get().transactionsPool[finalTxKey];\n await foundAdapter.checkAndInitializeTrackerInStore({ tx, onSucceedCallback, ...get() });\n } catch (e) {\n handleTxError(e);\n throw e; // Re-throw for external handling if needed.\n }\n },\n }),\n {\n ...options, // Merges user-provided persistence options.\n },\n ),\n );\n}\n","/**\n * @file This file defines the core data structures and TypeScript types for the Pulsar transaction tracking engine.\n * It specifies the framework-agnostic models for transactions, their lifecycle statuses, and the interfaces for\n * the Zustand-based store and chain-specific adapters.\n */\n\nimport { StoreApi } from 'zustand';\n\nimport { IInitializeTxTrackingStore } from './store/initializeTxTrackingStore';\n\n// =================================================================================================\n// 1. ZUSTAND UTILITY TYPES\n// =================================================================================================\n\n/**\n * A utility type for creating modular Zustand store slices, enabling composable state management.\n * @template T The state slice being defined.\n * @template S The full store state that includes the slice `T`.\n */\nexport type StoreSlice<T extends object, S extends object = T> = (\n set: StoreApi<S extends T ? S : S & T>['setState'],\n get: StoreApi<S extends T ? S : S & T>['getState'],\n) => T;\n\n// =================================================================================================\n// 2. ENUMS AND CORE TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Defines the supported blockchain adapters. Each adapter corresponds to a specific chain architecture.\n */\nexport enum TransactionAdapter {\n /** For Ethereum Virtual Machine (EVM) compatible chains like Ethereum, Polygon, etc. */\n EVM = 'evm',\n /** For the Solana blockchain. */\n SOLANA = 'solana',\n /** For the Starknet L2 network. */\n Starknet = 'starknet',\n}\n\n/**\n * Enum representing the different tracking strategies available for EVM transactions.\n * Each tracker corresponds to a specific method of monitoring a transaction's lifecycle.\n */\nexport enum TransactionTracker {\n /** For standard on-chain EVM transactions tracked by their hash. */\n Ethereum = 'ethereum',\n /** For multi-signature transactions managed and executed via a Safe contract. */\n Safe = 'safe',\n /** For meta-transactions relayed and executed by the Gelato Network. */\n Gelato = 'gelato',\n /** The tracker for monitoring standard Solana transaction signatures. */\n Solana = 'solana',\n}\n\n/**\n * Represents the terminal status of a transaction after it has been processed.\n */\nexport enum TransactionStatus {\n /** The transaction failed to execute due to an on-chain error or rejection. */\n Failed = 'Failed',\n /** The transaction was successfully mined and included in a block. */\n Success = 'Success',\n /** The transaction was replaced by another with the same nonce (e.g., a speed-up or cancel). */\n Replaced = 'Replaced',\n}\n\n/**\n * Defines the shape of the identifier for a Gelato transaction task.\n */\nexport type GelatoTxKey = {\n taskId: string;\n};\n\n/**\n * A union type representing the unique identifier returned by an `actionFunction`\n * after a transaction is submitted to the network or a relay service.\n *\n * This key is crucial for the EVM adapter to determine which tracker should\n * monitor the transaction.\n *\n * It can be one of the following:\n * - A standard `0x...` transaction hash (`Hex`).\n * - A structured object from a relay service like Gelato (`GelatoTxKey`).\n */\nexport type ActionTxKey = `0x${string}` | GelatoTxKey | string;\n\nexport type OnSuccessCallback<T> = (tx: T) => Promise<void> | void;\n\n/**\n * The fundamental structure for any transaction being tracked by Pulsar.\n * This serves as the base upon which chain-specific transaction types are built.\n */\nexport type BaseTransaction = {\n /** The chain identifier (e.g., 1 for Ethereum Mainnet, 'SN_MAIN' for Starknet). */\n chainId: number | string;\n /**\n * User-facing description. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single description for all states\n * description: 'Swap 1 ETH for 1,500 USDC'\n * // Specific descriptions for each state in order: [pending, success, error, replaced]\n * description: ['Swapping...', 'Swapped Successfully', 'Swap Failed', 'Swap Replaced']\n */\n description?: string | [string, string, string, string];\n /** The error message if the transaction failed. */\n errorMessage?: string;\n /** The on-chain timestamp (in seconds) when the transaction was finalized. */\n finishedTimestamp?: number;\n /** The sender's wallet address. */\n from: string;\n /** A flag indicating if the transaction is in a failed state. */\n isError?: boolean;\n /** A UI flag to control the visibility of a detailed tracking modal for this transaction. */\n isTrackedModalOpen?: boolean;\n /** The local timestamp (in seconds) when the transaction was initiated by the user. */\n localTimestamp: number;\n /** Any additional, custom data associated with the transaction. */\n payload?: object;\n /** A flag indicating if the transaction is still awaiting on-chain confirmation. */\n pending: boolean;\n /** The final on-chain status of the transaction. */\n status?: TransactionStatus;\n /**\n * User-facing title. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single title for all states\n * title: 'ETH/USDC Swap'\n * // Specific titles for each state in order: [pending, success, error, replaced]\n * title: ['Processing Swap', 'Swap Complete', 'Swap Error', 'Swap Replaced']\n */\n title?: string | [string, string, string, string];\n /** The specific tracker responsible for monitoring this transaction's status. */\n tracker: TransactionTracker;\n /** The unique identifier for the transaction (e.g., EVM hash, Gelato task ID). */\n txKey: string;\n /** The application-specific type or category of the transaction (e.g., 'SWAP', 'APPROVE'). */\n type: string;\n /** The type of wallet used to sign the transaction (e.g., 'injected', 'walletConnect'). */\n walletType: string;\n};\n\n// =================================================================================================\n// 3. CHAIN-SPECIFIC TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents an EVM-specific transaction, extending the base properties with EVM fields.\n */\nexport type EvmTransaction = BaseTransaction & {\n adapter: TransactionAdapter.EVM;\n /** The on-chain transaction hash, available after submission. */\n hash?: `0x${string}`;\n /** The data payload for the transaction, typically for smart contract interactions. */\n input?: `0x${string}`;\n /** The maximum fee per gas for an EIP-1559 transaction (in wei). */\n maxFeePerGas?: string;\n /** The maximum priority fee per gas for an EIP-1559 transaction (in wei). */\n maxPriorityFeePerGas?: string;\n /** The transaction nonce, a sequential number for the sender's account. */\n nonce?: number;\n /** The hash of a transaction that this one replaced. */\n replacedTxHash?: `0x${string}`;\n /** The recipient's address or contract address. */\n to?: `0x${string}`;\n /** The amount of native currency (in wei) being sent. */\n value?: string;\n};\n\n/**\n * Represents a Solana-specific transaction, extending the base properties.\n */\nexport type SolanaTransaction = BaseTransaction & {\n adapter: TransactionAdapter.SOLANA;\n /** The transaction fee in lamports. */\n fee?: number;\n /** The instructions included in the transaction. */\n instructions?: unknown[];\n /** The recent blockhash used for the transaction. */\n recentBlockhash?: string;\n /** The slot in which the transaction was processed. */\n slot?: number;\n /** The number of confirmations received. `string` when tx successed. `null` if the transaction is pending or unconfirmed. */\n confirmations?: number | string | null;\n /** The RPC URL used to submit and track this transaction. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a Starknet-specific transaction, extending the base properties.\n */\nexport type StarknetTransaction = BaseTransaction & {\n adapter: TransactionAdapter.Starknet;\n /** The actual fee paid for the transaction. */\n actualFee?: { amount: string; unit: string };\n /** The address of the contract being interacted with. */\n contractAddress?: string;\n};\n\n/** A union type representing any possible transaction structure that Pulsar can handle. */\nexport type Transaction = EvmTransaction | SolanaTransaction | StarknetTransaction;\n\n// =================================================================================================\n// 4. INITIAL TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents the parameters required to initiate a new transaction tracking flow.\n */\nexport type InitialTransactionParams = {\n adapter: TransactionAdapter;\n /** The function that executes the on-chain action (e.g., sending a transaction) and returns a preliminary identifier like a hash. */\n actionFunction: (...args: any[]) => Promise<ActionTxKey | undefined>;\n /** A user-facing description for the transaction. Supports state-specific descriptions. */\n description?: string | [string, string, string, string];\n /** The target chain ID for the transaction. */\n desiredChainID: number | string;\n /** Any custom data to associate with the transaction. */\n payload?: object;\n /** A user-facing title for the transaction. Supports state-specific titles. */\n title?: string | [string, string, string, string];\n /** The application-specific type of the transaction. */\n type: string;\n /** If true, the detailed tracking modal will open automatically upon initiation. */\n withTrackedModal?: boolean;\n /** The RPC URL to use for the transaction. Required for Solana transactions. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a transaction in its temporary, pre-submission state.\n * This is used for UI feedback while the transaction is being signed and sent.\n */\nexport type InitialTransaction = InitialTransactionParams & {\n /** An error message if the initialization fails (e.g., user rejects signature). */\n errorMessage?: string;\n /** A flag indicating if the transaction is being processed (e.g., waiting for signature). */\n isInitializing: boolean;\n /** The `txKey` of the on-chain transaction that this action produced, used for linking the states. */\n lastTxKey?: string;\n /** The local timestamp when the user initiated the action. */\n localTimestamp: number;\n};\n\n// =================================================================================================\n// 5. ADAPTER AND STORE INTERFACES\n// =================================================================================================\n\n/**\n * Defines the interface for a transaction adapter, which provides chain-specific logic and utilities.\n * @template T The specific transaction type, extending `Transaction`.\n */\nexport type TxAdapter<T extends Transaction> = {\n /** The unique key identifying this adapter. */\n key: TransactionAdapter;\n /** Returns information about the currently connected wallet. */\n getWalletInfo: () => {\n walletAddress: string;\n walletType: string;\n };\n /** Ensures the connected wallet is on the correct network for the transaction. Throws an error if the chain is mismatched. */\n checkChainForTx: (chainId: string | number) => Promise<void>;\n /** Determines the appropriate tracker and final `txKey` from the result of an action. */\n checkTransactionsTracker: (\n actionTxKey: ActionTxKey,\n walletType: string,\n ) => { txKey: string; tracker: TransactionTracker };\n /** Selects and initializes the correct background tracker for a given transaction. */\n checkAndInitializeTrackerInStore: (\n params: { tx: T; onSucceedCallback?: OnSuccessCallback<T> } & Pick<\n ITxTrackingStore<T>,\n 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'\n >,\n ) => Promise<void>;\n /** Returns the base URL for the blockchain explorer for the current network. */\n getExplorerUrl: (url?: string) => string | undefined;\n /** Optional: Fetches a name from a chain-specific name service (e.g., ENS). */\n getName?: (address: string) => Promise<string | null>;\n /** Optional: Fetches an avatar URL from a chain-specific name service. */\n getAvatar?: (name: string) => Promise<string | null>;\n /** Optional: Logic to cancel a pending EVM transaction. */\n cancelTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to speed up a pending EVM transaction. */\n speedUpTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to retry a failed transaction. */\n retryTxAction?: (\n params: {\n txKey: string;\n tx: InitialTransactionParams;\n onClose: (txKey?: string) => void;\n } & Partial<Pick<ITxTrackingStore<T>, 'handleTransaction'>>,\n ) => Promise<void>;\n /**\n * Optional: Constructs a full explorer URL for a specific transaction.\n * May require the full transaction pool to resolve details for replaced transactions.\n */\n getExplorerTxUrl?: (tx: T) => string;\n};\n\n/**\n * The complete interface for the Pulsar transaction tracking store.\n * @template T The transaction type.\n */\nexport type ITxTrackingStore<T extends Transaction> = IInitializeTxTrackingStore<T> & {\n /**\n * The primary method for initiating and tracking a new transaction from start to finish.\n * It manages UI state, executes the on-chain action, and initiates background tracking.\n * @param params The parameters for handling the transaction.\n */\n handleTransaction: (params: {\n /** The async function to execute (e.g., a smart contract write call). Must return a unique key or undefined. */\n actionFunction: () => Promise<ActionTxKey | undefined>;\n /** The metadata for the transaction. */\n params: Omit<InitialTransactionParams, 'actionFunction'>;\n /** The default tracker to use if it cannot be determined automatically. */\n defaultTracker?: TransactionTracker;\n /** Callback to execute when the transaction is successfully submitted. */\n onSucceedCallback?: OnSuccessCallback<T>;\n }) => Promise<void>;\n\n /**\n * Initializes trackers for all pending transactions in the pool.\n * This is essential for resuming tracking after a page reload.\n */\n initializeTransactionsPool: () => Promise<void>;\n};\n","/**\n * @file This file provides a utility for creating a type-safe, bounded Zustand hook from a vanilla store.\n * This pattern is recommended by the official Zustand documentation to ensure full type\n * safety when integrating vanilla stores with React.\n *\n * @see https://docs.pmnd.rs/zustand/guides/typescript#bounded-usestore-hook-for-vanilla-stores\n */\n\nimport { StoreApi, useStore } from 'zustand';\n\n/**\n * A utility type that infers the state shape from a Zustand `StoreApi`.\n * It extracts the return type of the `getState` method.\n * @template S - The type of the Zustand store (`StoreApi`).\n */\ntype ExtractState<S> = S extends { getState: () => infer T } ? T : never;\n\n/**\n * Creates a bounded `useStore` hook from a vanilla Zustand store.\n *\n * This function takes a vanilla Zustand store instance and returns a React hook\n * that is pre-bound to that store. This approach provides a cleaner API and\n * enhances type inference, eliminating the need to pass the store instance\n * on every use.\n *\n * The returned hook supports two signatures:\n * 1. `useBoundedStore()`: Selects the entire state.\n * 2. `useBoundedStore(selector)`: Selects a slice of the state, returning only what the selector function specifies.\n *\n * @template S - The type of the Zustand store (`StoreApi`).\n * @param {S} store - The vanilla Zustand store instance to bind the hook to.\n * @returns {function} A fully typed React hook for accessing the store's state.\n */\nexport const createBoundedUseStore = ((store) => (selector) => useStore(store, selector)) as <\n S extends StoreApi<unknown>,\n>(\n store: S,\n) => {\n // This implementation uses a Immediately Invoked Function Expression (IIFE)\n // trick combined with casting to achieve the desired overloaded function signature in a concise way.\n (): ExtractState<S>;\n <T>(selector: (state: ExtractState<S>) => T): T;\n};\n","/**\n * @file This file provides a generic utility for creating a polling mechanism to track\n * asynchronous tasks, such as API-based transaction status checks (e.g., for Gelato or Safe).\n */\n\nimport { Transaction } from '../types';\n\n/**\n * Defines the parameters for the fetcher function used within the polling tracker.\n * The fetcher is the core logic that performs the actual API call.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object being tracked.\n */\ntype PollingFetcherParams<R, T> = {\n /** The transaction object being tracked. */\n tx: T;\n /** A callback to stop the polling mechanism, typically called on success or terminal failure. */\n stopPolling: (options?: { withoutRemoving?: boolean }) => void;\n /** Callback to be invoked when the fetcher determines the transaction has succeeded. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the fetcher determines the transaction has failed. */\n onFailure: (response?: R) => void;\n /** Optional callback for each successful poll, useful for updating UI with intermediate states. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced (e.g., speed-up). */\n onReplaced?: (response: R) => void;\n};\n\n/**\n * Defines the configuration object for the `initializePollingTracker` function.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object.\n */\nexport type PollingTrackerConfig<R, T extends Transaction> = {\n /** The transaction object to be tracked. It must include `txKey` and `pending` status. */\n tx: T & Pick<Transaction, 'txKey' | 'pending'>;\n /** The function that performs the data fetching (e.g., an API call) on each interval. */\n fetcher: (params: PollingFetcherParams<R, T>) => Promise<void>;\n /** Callback to be invoked when the transaction successfully completes. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the transaction fails. */\n onFailure: (response?: R) => void;\n /** Optional callback executed once when the tracker is initialized. */\n onInitialize?: () => void;\n /** Optional callback for each successful poll. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced. */\n onReplaced?: (response: R) => void;\n /** Optional function to remove the transaction from the main pool, typically after polling stops. */\n removeTxFromPool?: (txKey: string) => void;\n /** The interval (in milliseconds) between polling attempts. Defaults to 5000ms. */\n pollingInterval?: number;\n /** The number of consecutive failed fetches before stopping the tracker. Defaults to 10. */\n maxRetries?: number;\n};\n\nconst DEFAULT_POLLING_INTERVAL = 5000;\nconst DEFAULT_MAX_RETRIES = 10;\n\n/**\n * Initializes a generic polling tracker that repeatedly calls a fetcher function\n * to monitor the status of an asynchronous task.\n *\n * This function handles the lifecycle of polling, including starting, stopping,\n * and automatic termination after a certain number of failed attempts.\n *\n * @template R The expected type of the API response.\n * @template T The type of the transaction object.\n * @param {PollingTrackerConfig<R, T>} config - The configuration for the tracker.\n */\nexport function initializePollingTracker<R, T extends Transaction>(config: PollingTrackerConfig<R, T>): void {\n const {\n tx,\n fetcher,\n onInitialize,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n removeTxFromPool,\n pollingInterval = DEFAULT_POLLING_INTERVAL,\n maxRetries = DEFAULT_MAX_RETRIES,\n } = config;\n\n // 1. Early exit if the transaction is no longer pending\n if (!tx.pending) {\n return;\n }\n\n // Execute the initialization callback if provided\n onInitialize?.();\n\n let retriesLeft = maxRetries;\n let isPolling = true;\n\n /**\n * Stops the polling interval and optionally removes the transaction from the pool.\n * @param {object} [options] - Options for stopping the tracker.\n * @param {boolean} [options.withoutRemoving=false] - If true, the tx will not be removed from the pool.\n */\n const stopPolling = (options?: { withoutRemoving?: boolean }) => {\n if (!isPolling) return;\n isPolling = false;\n // The interval is cleared in the finally block of the polling loop\n if (removeTxFromPool && !options?.withoutRemoving) {\n removeTxFromPool(tx.txKey);\n }\n };\n\n const pollingLoop = async () => {\n while (isPolling && retriesLeft > 0) {\n try {\n await new Promise((resolve) => setTimeout(resolve, pollingInterval));\n if (!isPolling) break;\n\n // The fetcher's responsibility is to call onSuccess, onFailure, etc., which in turn call stopPolling.\n await fetcher({\n tx,\n stopPolling,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n });\n } catch (error) {\n console.error(`Polling fetcher for txKey ${tx.txKey} threw an error:`, error);\n retriesLeft--;\n }\n }\n\n if (retriesLeft <= 0) {\n console.warn(`Polling for txKey ${tx.txKey} stopped after reaching the maximum number of retries.`);\n onFailure();\n stopPolling();\n }\n };\n\n // Start the asynchronous polling loop\n pollingLoop();\n}\n"]}
1
+ {"version":3,"sources":["../src/store/initializeTxTrackingStore.ts","../src/store/transactionsSelectors.ts","../src/utils/selectAdapterByKey.ts","../src/store/txTrackingStore.ts","../src/types.ts","../src/utils/createBoundedUseStore.ts","../src/utils/initializePollingTracker.ts"],"names":["initializeTxTrackingStore","set","get","tx","state","produce","draft","txKey","fields","selectAllTransactions","transactionsPool","a","b","selectPendingTransactions","selectTxByKey","key","selectAllTransactionsByActiveWallet","from","selectPendingTransactionsByActiveWallet","selectAdapterByKey","adapterKey","adapter","foundAdapter","createPulsarStore","options","createStore","persist","pendingTxs","defaultTracker","actionFunction","onSuccessCallback","params","desiredChainID","restParams","localTimestamp","dayjs","handleTxError","e","errorMessage","error","walletType","walletAddress","txKeyFromAction","updatedTracker","finalTxKey","newTx","TransactionAdapter","TransactionTracker","TransactionStatus","createBoundedUseStore","store","selector","useStore","DEFAULT_POLLING_INTERVAL","DEFAULT_MAX_RETRIES","initializePollingTracker","config","fetcher","onInitialize","onSuccess","onFailure","onIntervalTick","onReplaced","removeTxFromPool","pollingInterval","maxRetries","retriesLeft","isPolling","stopPolling","resolve"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file This file defines the core Zustand slice for managing the state of transactions. It includes the state,\n * actions, and types necessary for initializing the store and performing CRUD operations on the transaction pool.\n */\n\nimport { Draft, produce } from 'immer';\n\nimport { EvmTransaction, InitialTransaction, SolanaTransaction, StoreSlice, Transaction } from '../types';\n\n/**\n * Defines the structure of the transaction pool, a key-value store of transactions indexed by their unique keys.\n * @template T The type of the transaction object being tracked.\n */\nexport type TransactionPool<T extends Transaction> = Record<string, T>;\n\n/**\n * A utility type that creates a union of all fields that can be safely updated\n * on a transaction object via the `updateTxParams` action. This ensures type safety\n * and prevents accidental modification of immutable properties.\n */\ntype UpdatableTransactionFields = Partial<\n Pick<\n EvmTransaction,\n | 'to'\n | 'nonce'\n | 'txKey'\n | 'pending'\n | 'hash'\n | 'status'\n | 'replacedTxHash'\n | 'errorMessage'\n | 'finishedTimestamp'\n | 'isTrackedModalOpen'\n | 'isError'\n | 'maxPriorityFeePerGas'\n | 'maxFeePerGas'\n | 'input'\n | 'value'\n >\n> &\n Partial<Pick<SolanaTransaction, 'slot' | 'confirmations' | 'fee' | 'instructions' | 'recentBlockhash' | 'rpcUrl'>>;\n\n/**\n * The interface for the base transaction tracking store slice.\n * It includes the state and actions for managing the transaction lifecycle.\n * @template T The specific transaction type.\n */\nexport interface IInitializeTxTrackingStore<T extends Transaction> {\n /** A pool of all transactions currently being tracked, indexed by `txKey`. */\n transactionsPool: TransactionPool<T>;\n /** The `txKey` of the most recently added transaction. */\n lastAddedTxKey?: string;\n /** The state for a transaction being initiated, used for UI feedback before it's submitted to the chain. */\n initialTx?: InitialTransaction;\n\n /** Adds a new transaction to the tracking pool and marks it as pending. */\n addTxToPool: (tx: T) => void;\n /** Updates one or more properties of an existing transaction in the pool. */\n updateTxParams: (txKey: string, fields: UpdatableTransactionFields) => void;\n /** Removes a transaction from the tracking pool by its key. */\n removeTxFromPool: (txKey: string) => void;\n /** Closes the tracking modal for a transaction and clears any initial transaction state. */\n closeTxTrackedModal: (txKey?: string) => void;\n /** A selector function to retrieve the key of the last transaction added to the pool. */\n getLastTxKey: () => string | undefined;\n}\n\n/**\n * Creates a Zustand store slice with the core logic for transaction state management.\n * This function is a slice creator intended for use with Zustand's `create` function.\n *\n * @template T The specific transaction type.\n * @param options Configuration for the store slice.\n * @returns A Zustand store slice implementing `IInitializeTxTrackingStore`.\n */\nexport function initializeTxTrackingStore<T extends Transaction>(): StoreSlice<IInitializeTxTrackingStore<T>> {\n return (set, get) => ({\n transactionsPool: {},\n lastAddedTxKey: undefined,\n initialTx: undefined,\n\n addTxToPool: (tx) => {\n set((state) =>\n produce(state, (draft) => {\n draft.lastAddedTxKey = tx.txKey;\n if (tx.txKey) {\n draft.transactionsPool[tx.txKey] = {\n ...tx,\n pending: true, // Ensure all new transactions start as pending.\n } as Draft<T>;\n }\n }),\n );\n },\n\n updateTxParams: (txKey, fields) => {\n set((state) =>\n produce(state, (draft) => {\n const tx = draft.transactionsPool[txKey];\n // Ensure the transaction exists before attempting to update.\n if (tx) {\n Object.assign(tx, fields);\n }\n }),\n );\n },\n\n removeTxFromPool: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n delete draft.transactionsPool[txKey];\n }),\n );\n },\n\n closeTxTrackedModal: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n if (txKey && draft.transactionsPool[txKey]) {\n draft.transactionsPool[txKey].isTrackedModalOpen = false;\n }\n // Always clear the initial transaction state when a modal is closed\n draft.initialTx = undefined;\n }),\n );\n },\n\n getLastTxKey: () => get().lastAddedTxKey,\n });\n}\n","/**\n * @file This file contains selector functions for deriving state from the transaction tracking store.\n * Selectors help abstract the state's shape and provide efficient, memoized access to computed data.\n */\n\nimport { Transaction } from '../types';\nimport { TransactionPool } from './initializeTxTrackingStore';\n\n/**\n * Selects all transactions from the pool and sorts them by their creation timestamp in ascending order.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of all transactions, sorted chronologically.\n */\nexport const selectAllTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return Object.values(transactionsPool).sort((a, b) => Number(a.localTimestamp) - Number(b.localTimestamp));\n};\n\n/**\n * Selects all transactions that are currently in a pending state, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of pending transactions.\n */\nexport const selectPendingTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return selectAllTransactions(transactionsPool).filter((tx) => tx.pending);\n};\n\n/**\n * Selects a single transaction from the pool by its unique key (`txKey`).\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} key - The `txKey` of the transaction to retrieve.\n * @returns {T | undefined} The transaction object if found, otherwise undefined.\n */\nexport const selectTxByKey = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n key: string,\n): T | undefined => {\n return transactionsPool[key];\n};\n\n/**\n * Selects all transactions initiated by a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of transactions associated with the given wallet.\n */\nexport const selectAllTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Filters all transactions to find those matching the provided `from` address.\n return selectAllTransactions(transactionsPool).filter((tx) => tx.from.toLowerCase() === from.toLowerCase());\n};\n\n/**\n * Selects all pending transactions for a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of pending transactions for the given wallet.\n */\nexport const selectPendingTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Reuses the `selectAllTransactionsByActiveWallet` selector for efficiency\n // and then filters for pending transactions.\n return selectAllTransactionsByActiveWallet(transactionsPool, from).filter((tx) => tx.pending);\n};\n","/**\n * @file This file contains a utility function for selecting a specific transaction adapter from a list.\n */\n\nimport { Transaction, TransactionAdapter, TxAdapter } from '../types';\n\n/**\n * Selects a transaction adapter from a list based on a provided key.\n *\n * This function searches through an array of `TxAdapter` instances and returns the one\n * that matches the given `adapterKey`. If no specific adapter is found, it logs a warning\n * and returns the first adapter in the array as a fallback. This fallback mechanism\n * ensures that the system can still function, but it highlights a potential configuration issue.\n *\n * @template T - The transaction type, extending the base `Transaction`.\n *\n * @param {object} params - The parameters for the selection.\n * @param {TransactionAdapter} params.adapterKey - The key of the desired adapter.\n * @param {TxAdapter<T> | TxAdapter<T>[]} params.adapter - Adapter or an array of adapters for different chains or transaction types.\n *\n * @returns {TxAdapter<T> | undefined} The found transaction adapter, the fallback adapter, or undefined if the adapters array is empty.\n */\nexport const selectAdapterByKey = <T extends Transaction>({\n adapterKey,\n adapter,\n}: {\n adapterKey: TransactionAdapter;\n adapter: TxAdapter<T> | TxAdapter<T>[];\n}): TxAdapter<T> | undefined => {\n if (Array.isArray(adapter)) {\n if (adapter.length === 0) {\n console.error('Adapter selection failed: The provided adapters array is empty.');\n return undefined;\n }\n\n const foundAdapter = adapter.find((a) => a.key === adapterKey);\n\n if (foundAdapter) {\n return foundAdapter;\n } else {\n console.warn(\n `No adapter found for key: \"${adapterKey}\". Falling back to the first available adapter: \"${adapter[0].key}\".`,\n );\n return adapter[0];\n }\n }\n return adapter;\n};\n","/**\n * @file This file is the nucleus of the Pulsar store, orchestrating transaction handling, state management,\n * and communication with blockchain adapters. It utilizes Zustand for state management, Immer for safe,\n * immutable updates, and a persistence middleware to maintain state across user sessions.\n */\n\nimport dayjs from 'dayjs';\nimport { produce } from 'immer';\nimport { persist, PersistOptions } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\n\nimport { ITxTrackingStore, Transaction, TxAdapter } from '../types';\nimport { selectAdapterByKey } from '../utils/selectAdapterByKey';\nimport { initializeTxTrackingStore } from './initializeTxTrackingStore';\n\n/**\n * Creates the main Pulsar store for transaction tracking.\n *\n * This function configures a Zustand store enhanced with persistence. It combines the core transaction management\n * slice with a powerful orchestration logic that leverages chain-specific adapters to handle the entire\n * lifecycle of a transaction—from initiation and chain validation to execution and background status tracking.\n *\n * @template T The specific transaction type, extending the base `Transaction`.\n *\n * @param config Configuration object for creating the store.\n * @param config.adapter Adapter or an array of adapters for different chains or transaction types.\n * @param options Configuration for the Zustand `persist` middleware.\n * @returns A fully configured Zustand store instance.\n */\nexport function createPulsarStore<T extends Transaction>({\n adapter,\n ...options\n}: {\n adapter: TxAdapter<T> | TxAdapter<T>[];\n} & PersistOptions<ITxTrackingStore<T>>) {\n return createStore<ITxTrackingStore<T>>()(\n persist(\n (set, get) => ({\n // Initialize the base store slice with core state and actions\n ...initializeTxTrackingStore<T>()(set, get),\n\n /**\n * Initializes trackers for all pending transactions upon store creation.\n * This is crucial for resuming tracking after a page refresh or session restoration.\n */\n initializeTransactionsPool: async () => {\n const pendingTxs = Object.values(get().transactionsPool).filter((tx) => tx.pending);\n\n // Concurrently initialize trackers for all pending transactions\n await Promise.all(\n pendingTxs.map((tx) => {\n const foundAdapter = selectAdapterByKey({\n adapterKey: tx.adapter,\n adapter,\n });\n // Delegate tracker initialization to the appropriate adapter\n return foundAdapter?.checkAndInitializeTrackerInStore({\n tx,\n ...get(),\n });\n }),\n );\n },\n\n /**\n * The primary function to orchestrate sending and tracking a new transaction.\n * It manages the entire lifecycle, from UI state updates and chain switching to\n * signing, submission, and background tracker initialization.\n */\n handleTransaction: async ({ defaultTracker, actionFunction, onSuccessCallback, params }) => {\n const { desiredChainID, ...restParams } = params;\n const localTimestamp = dayjs().unix();\n\n // Step 1: Set initial state for immediate UI feedback (e.g., loading spinner).\n set({\n initialTx: {\n ...params,\n actionFunction,\n localTimestamp,\n isInitializing: true,\n },\n });\n\n const foundAdapter = selectAdapterByKey({\n adapterKey: restParams.adapter,\n adapter,\n });\n\n // Centralized error handler for this transaction flow\n const handleTxError = (e: unknown) => {\n const errorMessage = e instanceof Error ? e.message : String(e);\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.errorMessage = errorMessage;\n }\n }),\n );\n };\n\n if (!foundAdapter) {\n const error = new Error('No adapter found for this transaction.');\n handleTxError(error);\n throw error; // Re-throw to allow the caller to handle it.\n }\n\n try {\n const { walletType, walletAddress } = foundAdapter.getWalletInfo();\n\n // Step 2: Ensure the wallet is connected to the correct chain.\n await foundAdapter.checkChainForTx(desiredChainID);\n\n // Step 3: Execute the provided action (e.g., signing and sending the transaction).\n const txKeyFromAction = await actionFunction();\n\n // If `txKeyFromAction` is undefined, it indicates the user cancelled the action.\n if (!txKeyFromAction) {\n set({ initialTx: undefined });\n return;\n }\n\n // Step 4: Determine the final tracker and txKey from the action's result.\n const { tracker: updatedTracker, txKey: finalTxKey } = foundAdapter.checkTransactionsTracker(\n txKeyFromAction,\n walletType,\n );\n\n // Step 5: Construct the full transaction object for the pool.\n const newTx = {\n ...restParams,\n walletType,\n from: walletAddress,\n tracker: updatedTracker || defaultTracker,\n chainId: desiredChainID,\n localTimestamp,\n txKey: finalTxKey,\n // For EVM, the hash is often the preliminary key from the action.\n hash: updatedTracker === 'ethereum' ? (txKeyFromAction as `0x${string}`) : undefined,\n pending: false, // will be set to true by addTxToPool\n isTrackedModalOpen: params.withTrackedModal,\n };\n\n // Step 6: Add the transaction to the pool.\n get().addTxToPool(newTx as T);\n\n // Step 7: Update the initial state to link it with the newly created transaction.\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.lastTxKey = finalTxKey;\n }\n }),\n );\n\n // Step 8: Initialize the background tracker for the transaction.\n const tx = get().transactionsPool[finalTxKey];\n await foundAdapter.checkAndInitializeTrackerInStore({ tx, onSuccessCallback, ...get() });\n } catch (e) {\n handleTxError(e);\n throw e; // Re-throw for external handling if needed.\n }\n },\n }),\n {\n ...options, // Merges user-provided persistence options.\n },\n ),\n );\n}\n","/**\n * @file This file defines the core data structures and TypeScript types for the Pulsar transaction tracking engine.\n * It specifies the framework-agnostic models for transactions, their lifecycle statuses, and the interfaces for\n * the Zustand-based store and chain-specific adapters.\n */\n\nimport { StoreApi } from 'zustand';\n\nimport { IInitializeTxTrackingStore } from './store/initializeTxTrackingStore';\n\n// =================================================================================================\n// 1. ZUSTAND UTILITY TYPES\n// =================================================================================================\n\n/**\n * A utility type for creating modular Zustand store slices, enabling composable state management.\n * @template T The state slice being defined.\n * @template S The full store state that includes the slice `T`.\n */\nexport type StoreSlice<T extends object, S extends object = T> = (\n set: StoreApi<S extends T ? S : S & T>['setState'],\n get: StoreApi<S extends T ? S : S & T>['getState'],\n) => T;\n\n// =================================================================================================\n// 2. ENUMS AND CORE TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Defines the supported blockchain adapters. Each adapter corresponds to a specific chain architecture.\n */\nexport enum TransactionAdapter {\n /** For Ethereum Virtual Machine (EVM) compatible chains like Ethereum, Polygon, etc. */\n EVM = 'evm',\n /** For the Solana blockchain. */\n SOLANA = 'solana',\n /** For the Starknet L2 network. */\n Starknet = 'starknet',\n}\n\n/**\n * Enum representing the different tracking strategies available for EVM transactions.\n * Each tracker corresponds to a specific method of monitoring a transaction's lifecycle.\n */\nexport enum TransactionTracker {\n /** For standard on-chain EVM transactions tracked by their hash. */\n Ethereum = 'ethereum',\n /** For multi-signature transactions managed and executed via a Safe contract. */\n Safe = 'safe',\n /** For meta-transactions relayed and executed by the Gelato Network. */\n Gelato = 'gelato',\n /** The tracker for monitoring standard Solana transaction signatures. */\n Solana = 'solana',\n}\n\n/**\n * Represents the terminal status of a transaction after it has been processed.\n */\nexport enum TransactionStatus {\n /** The transaction failed to execute due to an on-chain error or rejection. */\n Failed = 'Failed',\n /** The transaction was successfully mined and included in a block. */\n Success = 'Success',\n /** The transaction was replaced by another with the same nonce (e.g., a speed-up or cancel). */\n Replaced = 'Replaced',\n}\n\n/**\n * Defines the shape of the identifier for a Gelato transaction task.\n */\nexport type GelatoTxKey = {\n taskId: string;\n};\n\n/**\n * A union type representing the unique identifier returned by an `actionFunction`\n * after a transaction is submitted to the network or a relay service.\n *\n * This key is crucial for the EVM adapter to determine which tracker should\n * monitor the transaction.\n *\n * It can be one of the following:\n * - A standard `0x...` transaction hash (`Hex`).\n * - A structured object from a relay service like Gelato (`GelatoTxKey`).\n */\nexport type ActionTxKey = `0x${string}` | GelatoTxKey | string;\n\nexport type OnSuccessCallback<T> = {\n /** Callback to execute when the transaction is successfully submitted. */\n onSuccessCallback?: (tx: T) => Promise<void> | void;\n};\n\n/**\n * The fundamental structure for any transaction being tracked by Pulsar.\n * This serves as the base upon which chain-specific transaction types are built.\n */\nexport type BaseTransaction = {\n /** The chain identifier (e.g., 1 for Ethereum Mainnet, 'SN_MAIN' for Starknet). */\n chainId: number | string;\n /**\n * User-facing description. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single description for all states\n * description: 'Swap 1 ETH for 1,500 USDC'\n * // Specific descriptions for each state in order: [pending, success, error, replaced]\n * description: ['Swapping...', 'Swapped Successfully', 'Swap Failed', 'Swap Replaced']\n */\n description?: string | [string, string, string, string];\n /** The error message if the transaction failed. */\n errorMessage?: string;\n /** The on-chain timestamp (in seconds) when the transaction was finalized. */\n finishedTimestamp?: number;\n /** The sender's wallet address. */\n from: string;\n /** A flag indicating if the transaction is in a failed state. */\n isError?: boolean;\n /** A UI flag to control the visibility of a detailed tracking modal for this transaction. */\n isTrackedModalOpen?: boolean;\n /** The local timestamp (in seconds) when the transaction was initiated by the user. */\n localTimestamp: number;\n /** Any additional, custom data associated with the transaction. */\n payload?: object;\n /** A flag indicating if the transaction is still awaiting on-chain confirmation. */\n pending: boolean;\n /** The final on-chain status of the transaction. */\n status?: TransactionStatus;\n /**\n * User-facing title. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single title for all states\n * title: 'ETH/USDC Swap'\n * // Specific titles for each state in order: [pending, success, error, replaced]\n * title: ['Processing Swap', 'Swap Complete', 'Swap Error', 'Swap Replaced']\n */\n title?: string | [string, string, string, string];\n /** The specific tracker responsible for monitoring this transaction's status. */\n tracker: TransactionTracker;\n /** The unique identifier for the transaction (e.g., EVM hash, Gelato task ID). */\n txKey: string;\n /** The application-specific type or category of the transaction (e.g., 'SWAP', 'APPROVE'). */\n type: string;\n /** The type of wallet used to sign the transaction (e.g., 'injected', 'walletConnect'). */\n walletType: string;\n};\n\n// =================================================================================================\n// 3. CHAIN-SPECIFIC TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents an EVM-specific transaction, extending the base properties with EVM fields.\n */\nexport type EvmTransaction = BaseTransaction & {\n adapter: TransactionAdapter.EVM;\n /** The on-chain transaction hash, available after submission. */\n hash?: `0x${string}`;\n /** The data payload for the transaction, typically for smart contract interactions. */\n input?: `0x${string}`;\n /** The maximum fee per gas for an EIP-1559 transaction (in wei). */\n maxFeePerGas?: string;\n /** The maximum priority fee per gas for an EIP-1559 transaction (in wei). */\n maxPriorityFeePerGas?: string;\n /** The transaction nonce, a sequential number for the sender's account. */\n nonce?: number;\n /** The hash of a transaction that this one replaced. */\n replacedTxHash?: `0x${string}`;\n /** The recipient's address or contract address. */\n to?: `0x${string}`;\n /** The amount of native currency (in wei) being sent. */\n value?: string;\n};\n\n/**\n * Represents a Solana-specific transaction, extending the base properties.\n */\nexport type SolanaTransaction = BaseTransaction & {\n adapter: TransactionAdapter.SOLANA;\n /** The transaction fee in lamports. */\n fee?: number;\n /** The instructions included in the transaction. */\n instructions?: unknown[];\n /** The recent blockhash used for the transaction. */\n recentBlockhash?: string;\n /** The slot in which the transaction was processed. */\n slot?: number;\n /** The number of confirmations received. `string` when tx successed. `null` if the transaction is pending or unconfirmed. */\n confirmations?: number | string | null;\n /** The RPC URL used to submit and track this transaction. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a Starknet-specific transaction, extending the base properties.\n */\nexport type StarknetTransaction = BaseTransaction & {\n adapter: TransactionAdapter.Starknet;\n /** The actual fee paid for the transaction. */\n actualFee?: { amount: string; unit: string };\n /** The address of the contract being interacted with. */\n contractAddress?: string;\n};\n\n/** A union type representing any possible transaction structure that Pulsar can handle. */\nexport type Transaction = EvmTransaction | SolanaTransaction | StarknetTransaction;\n\n// =================================================================================================\n// 4. INITIAL TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents the parameters required to initiate a new transaction tracking flow.\n */\nexport type InitialTransactionParams = {\n adapter: TransactionAdapter;\n /** The function that executes the on-chain action (e.g., sending a transaction) and returns a preliminary identifier like a hash. */\n actionFunction: (...args: any[]) => Promise<ActionTxKey | undefined>;\n /** A user-facing description for the transaction. Supports state-specific descriptions. */\n description?: string | [string, string, string, string];\n /** The target chain ID for the transaction. */\n desiredChainID: number | string;\n /** Any custom data to associate with the transaction. */\n payload?: object;\n /** A user-facing title for the transaction. Supports state-specific titles. */\n title?: string | [string, string, string, string];\n /** The application-specific type of the transaction. */\n type: string;\n /** If true, the detailed tracking modal will open automatically upon initiation. */\n withTrackedModal?: boolean;\n /** The RPC URL to use for the transaction. Required for Solana transactions. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a transaction in its temporary, pre-submission state.\n * This is used for UI feedback while the transaction is being signed and sent.\n */\nexport type InitialTransaction = InitialTransactionParams & {\n /** An error message if the initialization fails (e.g., user rejects signature). */\n errorMessage?: string;\n /** A flag indicating if the transaction is being processed (e.g., waiting for signature). */\n isInitializing: boolean;\n /** The `txKey` of the on-chain transaction that this action produced, used for linking the states. */\n lastTxKey?: string;\n /** The local timestamp when the user initiated the action. */\n localTimestamp: number;\n};\n\n// =================================================================================================\n// 5. ADAPTER AND STORE INTERFACES\n// =================================================================================================\n\n/**\n * Defines the interface for a transaction adapter, which provides chain-specific logic and utilities.\n * @template T The specific transaction type, extending `Transaction`.\n */\nexport type TxAdapter<T extends Transaction> = {\n /** The unique key identifying this adapter. */\n key: TransactionAdapter;\n /** Returns information about the currently connected wallet. */\n getWalletInfo: () => {\n walletAddress: string;\n walletType: string;\n };\n /** Ensures the connected wallet is on the correct network for the transaction. Throws an error if the chain is mismatched. */\n checkChainForTx: (chainId: string | number) => Promise<void>;\n /** Determines the appropriate tracker and final `txKey` from the result of an action. */\n checkTransactionsTracker: (\n actionTxKey: ActionTxKey,\n walletType: string,\n ) => { txKey: string; tracker: TransactionTracker };\n /** Selects and initializes the correct background tracker for a given transaction. */\n checkAndInitializeTrackerInStore: (\n params: { tx: T } & OnSuccessCallback<T> &\n Pick<ITxTrackingStore<T>, 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'>,\n ) => Promise<void>;\n /** Returns the base URL for the blockchain explorer for the current network. */\n getExplorerUrl: (url?: string) => string | undefined;\n /** Optional: Fetches a name from a chain-specific name service (e.g., ENS). */\n getName?: (address: string) => Promise<string | null>;\n /** Optional: Fetches an avatar URL from a chain-specific name service. */\n getAvatar?: (name: string) => Promise<string | null>;\n /** Optional: Logic to cancel a pending EVM transaction. */\n cancelTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to speed up a pending EVM transaction. */\n speedUpTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to retry a failed transaction. */\n retryTxAction?: (\n params: {\n txKey: string;\n tx: InitialTransactionParams;\n onClose: (txKey?: string) => void;\n } & Partial<Pick<ITxTrackingStore<T>, 'handleTransaction'>>,\n ) => Promise<void>;\n /**\n * Optional: Constructs a full explorer URL for a specific transaction.\n * May require the full transaction pool to resolve details for replaced transactions.\n */\n getExplorerTxUrl?: (tx: T) => string;\n};\n\n/**\n * The complete interface for the Pulsar transaction tracking store.\n * @template T The transaction type.\n */\nexport type ITxTrackingStore<T extends Transaction> = IInitializeTxTrackingStore<T> & {\n /**\n * The primary method for initiating and tracking a new transaction from start to finish.\n * It manages UI state, executes the on-chain action, and initiates background tracking.\n * @param params The parameters for handling the transaction.\n */\n handleTransaction: (\n params: {\n /** The async function to execute (e.g., a smart contract write call). Must return a unique key or undefined. */\n actionFunction: () => Promise<ActionTxKey | undefined>;\n /** The metadata for the transaction. */\n params: Omit<InitialTransactionParams, 'actionFunction'>;\n /** The default tracker to use if it cannot be determined automatically. */\n defaultTracker?: TransactionTracker;\n } & OnSuccessCallback<T>,\n ) => Promise<void>;\n\n /**\n * Initializes trackers for all pending transactions in the pool.\n * This is essential for resuming tracking after a page reload.\n */\n initializeTransactionsPool: () => Promise<void>;\n};\n","/**\n * @file This file provides a utility for creating a type-safe, bounded Zustand hook from a vanilla store.\n * This pattern is recommended by the official Zustand documentation to ensure full type\n * safety when integrating vanilla stores with React.\n *\n * @see https://docs.pmnd.rs/zustand/guides/typescript#bounded-usestore-hook-for-vanilla-stores\n */\n\nimport { StoreApi, useStore } from 'zustand';\n\n/**\n * A utility type that infers the state shape from a Zustand `StoreApi`.\n * It extracts the return type of the `getState` method.\n * @template S - The type of the Zustand store (`StoreApi`).\n */\ntype ExtractState<S> = S extends { getState: () => infer T } ? T : never;\n\n/**\n * Creates a bounded `useStore` hook from a vanilla Zustand store.\n *\n * This function takes a vanilla Zustand store instance and returns a React hook\n * that is pre-bound to that store. This approach provides a cleaner API and\n * enhances type inference, eliminating the need to pass the store instance\n * on every use.\n *\n * The returned hook supports two signatures:\n * 1. `useBoundedStore()`: Selects the entire state.\n * 2. `useBoundedStore(selector)`: Selects a slice of the state, returning only what the selector function specifies.\n *\n * @template S - The type of the Zustand store (`StoreApi`).\n * @param {S} store - The vanilla Zustand store instance to bind the hook to.\n * @returns {function} A fully typed React hook for accessing the store's state.\n */\nexport const createBoundedUseStore = ((store) => (selector) => useStore(store, selector)) as <\n S extends StoreApi<unknown>,\n>(\n store: S,\n) => {\n // This implementation uses a Immediately Invoked Function Expression (IIFE)\n // trick combined with casting to achieve the desired overloaded function signature in a concise way.\n (): ExtractState<S>;\n <T>(selector: (state: ExtractState<S>) => T): T;\n};\n","/**\n * @file This file provides a generic utility for creating a polling mechanism to track\n * asynchronous tasks, such as API-based transaction status checks (e.g., for Gelato or Safe).\n */\n\nimport { Transaction } from '../types';\n\n/**\n * Defines the parameters for the fetcher function used within the polling tracker.\n * The fetcher is the core logic that performs the actual API call.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object being tracked.\n */\ntype PollingFetcherParams<R, T> = {\n /** The transaction object being tracked. */\n tx: T;\n /** A callback to stop the polling mechanism, typically called on success or terminal failure. */\n stopPolling: (options?: { withoutRemoving?: boolean }) => void;\n /** Callback to be invoked when the fetcher determines the transaction has succeeded. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the fetcher determines the transaction has failed. */\n onFailure: (response?: R) => void;\n /** Optional callback for each successful poll, useful for updating UI with intermediate states. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced (e.g., speed-up). */\n onReplaced?: (response: R) => void;\n};\n\n/**\n * Defines the configuration object for the `initializePollingTracker` function.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object.\n */\nexport type PollingTrackerConfig<R, T extends Transaction> = {\n /** The transaction object to be tracked. It must include `txKey` and `pending` status. */\n tx: T & Pick<Transaction, 'txKey' | 'pending'>;\n /** The function that performs the data fetching (e.g., an API call) on each interval. */\n fetcher: (params: PollingFetcherParams<R, T>) => Promise<void>;\n /** Callback to be invoked when the transaction successfully completes. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the transaction fails. */\n onFailure: (response?: R) => void;\n /** Optional callback executed once when the tracker is initialized. */\n onInitialize?: () => void;\n /** Optional callback for each successful poll. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced. */\n onReplaced?: (response: R) => void;\n /** Optional function to remove the transaction from the main pool, typically after polling stops. */\n removeTxFromPool?: (txKey: string) => void;\n /** The interval (in milliseconds) between polling attempts. Defaults to 5000ms. */\n pollingInterval?: number;\n /** The number of consecutive failed fetches before stopping the tracker. Defaults to 10. */\n maxRetries?: number;\n};\n\nconst DEFAULT_POLLING_INTERVAL = 5000;\nconst DEFAULT_MAX_RETRIES = 10;\n\n/**\n * Initializes a generic polling tracker that repeatedly calls a fetcher function\n * to monitor the status of an asynchronous task.\n *\n * This function handles the lifecycle of polling, including starting, stopping,\n * and automatic termination after a certain number of failed attempts.\n *\n * @template R The expected type of the API response.\n * @template T The type of the transaction object.\n * @param {PollingTrackerConfig<R, T>} config - The configuration for the tracker.\n */\nexport function initializePollingTracker<R, T extends Transaction>(config: PollingTrackerConfig<R, T>): void {\n const {\n tx,\n fetcher,\n onInitialize,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n removeTxFromPool,\n pollingInterval = DEFAULT_POLLING_INTERVAL,\n maxRetries = DEFAULT_MAX_RETRIES,\n } = config;\n\n // 1. Early exit if the transaction is no longer pending\n if (!tx.pending) {\n return;\n }\n\n // Execute the initialization callback if provided\n onInitialize?.();\n\n let retriesLeft = maxRetries;\n let isPolling = true;\n\n /**\n * Stops the polling interval and optionally removes the transaction from the pool.\n * @param {object} [options] - Options for stopping the tracker.\n * @param {boolean} [options.withoutRemoving=false] - If true, the tx will not be removed from the pool.\n */\n const stopPolling = (options?: { withoutRemoving?: boolean }) => {\n if (!isPolling) return;\n isPolling = false;\n // The interval is cleared in the finally block of the polling loop\n if (removeTxFromPool && !options?.withoutRemoving) {\n removeTxFromPool(tx.txKey);\n }\n };\n\n const pollingLoop = async () => {\n while (isPolling && retriesLeft > 0) {\n try {\n await new Promise((resolve) => setTimeout(resolve, pollingInterval));\n if (!isPolling) break;\n\n // The fetcher's responsibility is to call onSuccess, onFailure, etc., which in turn call stopPolling.\n await fetcher({\n tx,\n stopPolling,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n });\n } catch (error) {\n console.error(`Polling fetcher for txKey ${tx.txKey} threw an error:`, error);\n retriesLeft--;\n }\n }\n\n if (retriesLeft <= 0) {\n console.warn(`Polling for txKey ${tx.txKey} stopped after reaching the maximum number of retries.`);\n onFailure();\n stopPolling();\n }\n };\n\n // Start the asynchronous polling loop\n pollingLoop();\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import {produce}from'immer';import w from'dayjs';import {persist}from'zustand/middleware';import {createStore}from'zustand/vanilla';import {useStore}from'zustand';function k(){return (r,t)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:e=>{r(n=>produce(n,i=>{i.lastAddedTxKey=e.txKey,e.txKey&&(i.transactionsPool[e.txKey]={...e,pending:true});}));},updateTxParams:(e,n)=>{r(i=>produce(i,a=>{let l=a.transactionsPool[e];l&&Object.assign(l,n);}));},removeTxFromPool:e=>{r(n=>produce(n,i=>{delete i.transactionsPool[e];}));},closeTxTrackedModal:e=>{r(n=>produce(n,i=>{e&&i.transactionsPool[e]&&(i.transactionsPool[e].isTrackedModalOpen=false),i.initialTx=void 0;}));},getLastTxKey:()=>t().lastAddedTxKey})}var h=r=>Object.values(r).sort((t,e)=>Number(t.localTimestamp)-Number(e.localTimestamp)),N=r=>h(r).filter(t=>t.pending),j=(r,t)=>r[t],b=(r,t)=>h(r).filter(e=>e.from.toLowerCase()===t.toLowerCase()),G=(r,t)=>b(r,t).filter(e=>e.pending);var S=({adapterKey:r,adapter:t})=>{if(Array.isArray(t)){if(t.length===0){console.error("Adapter selection failed: The provided adapters array is empty.");return}let e=t.find(n=>n.key===r);return e||(console.warn(`No adapter found for key: "${r}". Falling back to the first available adapter: "${t[0].key}".`),t[0])}return t};function Y({adapter:r,...t}){return createStore()(persist((e,n)=>({...k()(e,n),initializeTransactionsPool:async()=>{let i=Object.values(n().transactionsPool).filter(a=>a.pending);await Promise.all(i.map(a=>S({adapterKey:a.adapter,adapter:r})?.checkAndInitializeTrackerInStore({tx:a,...n()})));},handleTransaction:async({defaultTracker:i,actionFunction:a,onSucceedCallback:l,params:p})=>{let{desiredChainID:x,...m}=p,g=w().unix();e({initialTx:{...p,actionFunction:a,localTimestamp:g,isInitializing:true}});let c=S({adapterKey:m.adapter,adapter:r}),T=o=>{let u=o instanceof Error?o.message:String(o);e(s=>produce(s,d=>{d.initialTx&&(d.initialTx.isInitializing=false,d.initialTx.errorMessage=u);}));};if(!c){let o=new Error("No adapter found for this transaction.");throw T(o),o}try{let{walletType:o,walletAddress:u}=c.getWalletInfo();await c.checkChainForTx(x);let s=await a();if(!s){e({initialTx:void 0});return}let{tracker:d,txKey:y}=c.checkTransactionsTracker(s,o),v={...m,walletType:o,from:u,tracker:d||i,chainId:x,localTimestamp:g,txKey:y,hash:d==="ethereum"?s:void 0,pending:!1,isTrackedModalOpen:p.withTrackedModal};n().addTxToPool(v),e(K=>produce(K,P=>{P.initialTx&&(P.initialTx.isInitializing=!1,P.initialTx.lastTxKey=y);}));let I=n().transactionsPool[y];await c.checkAndInitializeTrackerInStore({tx:I,onSucceedCallback:l,...n()});}catch(o){throw T(o),o}}}),{...t}))}var E=(n=>(n.EVM="evm",n.SOLANA="solana",n.Starknet="starknet",n))(E||{}),z=(i=>(i.Ethereum="ethereum",i.Safe="safe",i.Gelato="gelato",i.Solana="solana",i))(z||{}),M=(n=>(n.Failed="Failed",n.Success="Success",n.Replaced="Replaced",n))(M||{});var re=(r=>t=>useStore(r,t));var C=5e3,L=10;function oe(r){let{tx:t,fetcher:e,onInitialize:n,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p,removeTxFromPool:x,pollingInterval:m=C,maxRetries:g=L}=r;if(!t.pending)return;n?.();let c=g,T=true,o=s=>{T&&(T=false,x&&!s?.withoutRemoving&&x(t.txKey));};(async()=>{for(;T&&c>0;)try{if(await new Promise(s=>setTimeout(s,m)),!T)break;await e({tx:t,stopPolling:o,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p});}catch(s){console.error(`Polling fetcher for txKey ${t.txKey} threw an error:`,s),c--;}c<=0&&(console.warn(`Polling for txKey ${t.txKey} stopped after reaching the maximum number of retries.`),a(),o());})();}export{E as TransactionAdapter,M as TransactionStatus,z as TransactionTracker,re as createBoundedUseStore,Y as createPulsarStore,oe as initializePollingTracker,k as initializeTxTrackingStore,S as selectAdapterByKey,h as selectAllTransactions,b as selectAllTransactionsByActiveWallet,N as selectPendingTransactions,G as selectPendingTransactionsByActiveWallet,j as selectTxByKey};//# sourceMappingURL=index.mjs.map
1
+ import {produce}from'immer';import w from'dayjs';import {persist}from'zustand/middleware';import {createStore}from'zustand/vanilla';import {useStore}from'zustand';function k(){return (r,t)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:e=>{r(n=>produce(n,i=>{i.lastAddedTxKey=e.txKey,e.txKey&&(i.transactionsPool[e.txKey]={...e,pending:true});}));},updateTxParams:(e,n)=>{r(i=>produce(i,a=>{let l=a.transactionsPool[e];l&&Object.assign(l,n);}));},removeTxFromPool:e=>{r(n=>produce(n,i=>{delete i.transactionsPool[e];}));},closeTxTrackedModal:e=>{r(n=>produce(n,i=>{e&&i.transactionsPool[e]&&(i.transactionsPool[e].isTrackedModalOpen=false),i.initialTx=void 0;}));},getLastTxKey:()=>t().lastAddedTxKey})}var h=r=>Object.values(r).sort((t,e)=>Number(t.localTimestamp)-Number(e.localTimestamp)),N=r=>h(r).filter(t=>t.pending),j=(r,t)=>r[t],b=(r,t)=>h(r).filter(e=>e.from.toLowerCase()===t.toLowerCase()),G=(r,t)=>b(r,t).filter(e=>e.pending);var S=({adapterKey:r,adapter:t})=>{if(Array.isArray(t)){if(t.length===0){console.error("Adapter selection failed: The provided adapters array is empty.");return}let e=t.find(n=>n.key===r);return e||(console.warn(`No adapter found for key: "${r}". Falling back to the first available adapter: "${t[0].key}".`),t[0])}return t};function Y({adapter:r,...t}){return createStore()(persist((e,n)=>({...k()(e,n),initializeTransactionsPool:async()=>{let i=Object.values(n().transactionsPool).filter(a=>a.pending);await Promise.all(i.map(a=>S({adapterKey:a.adapter,adapter:r})?.checkAndInitializeTrackerInStore({tx:a,...n()})));},handleTransaction:async({defaultTracker:i,actionFunction:a,onSuccessCallback:l,params:p})=>{let{desiredChainID:x,...m}=p,g=w().unix();e({initialTx:{...p,actionFunction:a,localTimestamp:g,isInitializing:true}});let c=S({adapterKey:m.adapter,adapter:r}),T=o=>{let u=o instanceof Error?o.message:String(o);e(s=>produce(s,d=>{d.initialTx&&(d.initialTx.isInitializing=false,d.initialTx.errorMessage=u);}));};if(!c){let o=new Error("No adapter found for this transaction.");throw T(o),o}try{let{walletType:o,walletAddress:u}=c.getWalletInfo();await c.checkChainForTx(x);let s=await a();if(!s){e({initialTx:void 0});return}let{tracker:d,txKey:y}=c.checkTransactionsTracker(s,o),v={...m,walletType:o,from:u,tracker:d||i,chainId:x,localTimestamp:g,txKey:y,hash:d==="ethereum"?s:void 0,pending:!1,isTrackedModalOpen:p.withTrackedModal};n().addTxToPool(v),e(K=>produce(K,P=>{P.initialTx&&(P.initialTx.isInitializing=!1,P.initialTx.lastTxKey=y);}));let I=n().transactionsPool[y];await c.checkAndInitializeTrackerInStore({tx:I,onSuccessCallback:l,...n()});}catch(o){throw T(o),o}}}),{...t}))}var E=(n=>(n.EVM="evm",n.SOLANA="solana",n.Starknet="starknet",n))(E||{}),z=(i=>(i.Ethereum="ethereum",i.Safe="safe",i.Gelato="gelato",i.Solana="solana",i))(z||{}),M=(n=>(n.Failed="Failed",n.Success="Success",n.Replaced="Replaced",n))(M||{});var re=(r=>t=>useStore(r,t));var L=5e3,C=10;function oe(r){let{tx:t,fetcher:e,onInitialize:n,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p,removeTxFromPool:x,pollingInterval:m=L,maxRetries:g=C}=r;if(!t.pending)return;n?.();let c=g,T=true,o=s=>{T&&(T=false,x&&!s?.withoutRemoving&&x(t.txKey));};(async()=>{for(;T&&c>0;)try{if(await new Promise(s=>setTimeout(s,m)),!T)break;await e({tx:t,stopPolling:o,onSuccess:i,onFailure:a,onIntervalTick:l,onReplaced:p});}catch(s){console.error(`Polling fetcher for txKey ${t.txKey} threw an error:`,s),c--;}c<=0&&(console.warn(`Polling for txKey ${t.txKey} stopped after reaching the maximum number of retries.`),a(),o());})();}export{E as TransactionAdapter,M as TransactionStatus,z as TransactionTracker,re as createBoundedUseStore,Y as createPulsarStore,oe as initializePollingTracker,k as initializeTxTrackingStore,S as selectAdapterByKey,h as selectAllTransactions,b as selectAllTransactionsByActiveWallet,N as selectPendingTransactions,G as selectPendingTransactionsByActiveWallet,j as selectTxByKey};//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store/initializeTxTrackingStore.ts","../src/store/transactionsSelectors.ts","../src/utils/selectAdapterByKey.ts","../src/store/txTrackingStore.ts","../src/types.ts","../src/utils/createBoundedUseStore.ts","../src/utils/initializePollingTracker.ts"],"names":["initializeTxTrackingStore","set","get","tx","state","produce","draft","txKey","fields","selectAllTransactions","transactionsPool","a","b","selectPendingTransactions","selectTxByKey","key","selectAllTransactionsByActiveWallet","from","selectPendingTransactionsByActiveWallet","selectAdapterByKey","adapterKey","adapter","foundAdapter","createPulsarStore","options","createStore","persist","pendingTxs","defaultTracker","actionFunction","onSucceedCallback","params","desiredChainID","restParams","localTimestamp","dayjs","handleTxError","e","errorMessage","error","walletType","walletAddress","txKeyFromAction","updatedTracker","finalTxKey","newTx","TransactionAdapter","TransactionTracker","TransactionStatus","createBoundedUseStore","store","selector","useStore","DEFAULT_POLLING_INTERVAL","DEFAULT_MAX_RETRIES","initializePollingTracker","config","fetcher","onInitialize","onSuccess","onFailure","onIntervalTick","onReplaced","removeTxFromPool","pollingInterval","maxRetries","retriesLeft","isPolling","stopPolling","resolve"],"mappings":"mKA2EO,SAASA,CAAAA,EAA8F,CAC5G,OAAO,CAACC,CAAAA,CAAKC,KAAS,CACpB,gBAAA,CAAkB,EAAC,CACnB,cAAA,CAAgB,OAChB,SAAA,CAAW,MAAA,CAEX,YAAcC,CAAAA,EAAO,CACnBF,CAAAA,CAAKG,CAAAA,EACHC,QAAQD,CAAAA,CAAQE,CAAAA,EAAU,CACxBA,CAAAA,CAAM,cAAA,CAAiBH,EAAG,KAAA,CACtBA,CAAAA,CAAG,QACLG,CAAAA,CAAM,gBAAA,CAAiBH,EAAG,KAAK,CAAA,CAAI,CACjC,GAAGA,CAAAA,CACH,QAAS,IACX,CAAA,EAEJ,CAAC,CACH,EACF,CAAA,CAEA,cAAA,CAAgB,CAACI,CAAAA,CAAOC,CAAAA,GAAW,CACjCP,CAAAA,CAAKG,CAAAA,EACHC,QAAQD,CAAAA,CAAQE,CAAAA,EAAU,CACxB,IAAMH,CAAAA,CAAKG,EAAM,gBAAA,CAAiBC,CAAK,EAEnCJ,CAAAA,EACF,MAAA,CAAO,OAAOA,CAAAA,CAAIK,CAAM,EAE5B,CAAC,CACH,EACF,CAAA,CAEA,gBAAA,CAAmBD,GAAU,CAC3BN,CAAAA,CAAKG,GACHC,OAAAA,CAAQD,CAAAA,CAAQE,GAAU,CACxB,OAAOA,EAAM,gBAAA,CAAiBC,CAAK,EACrC,CAAC,CACH,EACF,CAAA,CAEA,oBAAsBA,CAAAA,EAAU,CAC9BN,EAAKG,CAAAA,EACHC,OAAAA,CAAQD,EAAQE,CAAAA,EAAU,CACpBC,GAASD,CAAAA,CAAM,gBAAA,CAAiBC,CAAK,CAAA,GACvCD,CAAAA,CAAM,iBAAiBC,CAAK,CAAA,CAAE,mBAAqB,KAAA,CAAA,CAGrDD,CAAAA,CAAM,UAAY,OACpB,CAAC,CACH,EACF,CAAA,CAEA,aAAc,IAAMJ,CAAAA,GAAM,cAC5B,CAAA,CACF,CCnHO,IAAMO,CAAAA,CAAgDC,GACpD,MAAA,CAAO,MAAA,CAAOA,CAAgB,CAAA,CAAE,IAAA,CAAK,CAACC,CAAAA,CAAGC,CAAAA,GAAM,MAAA,CAAOD,CAAAA,CAAE,cAAc,CAAA,CAAI,MAAA,CAAOC,EAAE,cAAc,CAAC,EAS9FC,CAAAA,CAAoDH,CAAAA,EACxDD,EAAsBC,CAAgB,CAAA,CAAE,OAAQP,CAAAA,EAAOA,CAAAA,CAAG,OAAO,CAAA,CAU7DW,CAAAA,CAAgB,CAC3BJ,CAAAA,CACAK,CAAAA,GAEOL,EAAiBK,CAAG,CAAA,CAUhBC,EAAsC,CACjDN,CAAAA,CACAO,IAGOR,CAAAA,CAAsBC,CAAgB,EAAE,MAAA,CAAQP,CAAAA,EAAOA,EAAG,IAAA,CAAK,WAAA,KAAkBc,CAAAA,CAAK,WAAA,EAAa,CAAA,CAU/FC,CAAAA,CAA0C,CACrDR,CAAAA,CACAO,CAAAA,GAIOD,CAAAA,CAAoCN,CAAAA,CAAkBO,CAAI,CAAA,CAAE,MAAA,CAAQd,GAAOA,CAAAA,CAAG,OAAO,EChDvF,IAAMgB,CAAAA,CAAqB,CAAwB,CACxD,UAAA,CAAAC,EACA,OAAA,CAAAC,CACF,IAGgC,CAC9B,GAAI,MAAM,OAAA,CAAQA,CAAO,EAAG,CAC1B,GAAIA,EAAQ,MAAA,GAAW,CAAA,CAAG,CACxB,OAAA,CAAQ,KAAA,CAAM,iEAAiE,CAAA,CAC/E,MACF,CAEA,IAAMC,EAAeD,CAAAA,CAAQ,IAAA,CAAMV,GAAMA,CAAAA,CAAE,GAAA,GAAQS,CAAU,CAAA,CAE7D,OAAIE,IAGF,OAAA,CAAQ,IAAA,CACN,8BAA8BF,CAAU,CAAA,iDAAA,EAAoDC,EAAQ,CAAC,CAAA,CAAE,GAAG,CAAA,EAAA,CAC5G,CAAA,CACOA,EAAQ,CAAC,CAAA,CAEpB,CACA,OAAOA,CACT,EClBO,SAASE,CAAAA,CAAyC,CACvD,OAAA,CAAAF,CAAAA,CACA,GAAGG,CACL,CAAA,CAEyC,CACvC,OAAOC,WAAAA,GACLC,OAAAA,CACE,CAACzB,CAAAA,CAAKC,CAAAA,IAAS,CAEb,GAAGF,CAAAA,GAA+BC,CAAAA,CAAKC,CAAG,EAM1C,0BAAA,CAA4B,SAAY,CACtC,IAAMyB,CAAAA,CAAa,OAAO,MAAA,CAAOzB,CAAAA,GAAM,gBAAgB,CAAA,CAAE,OAAQC,CAAAA,EAAOA,CAAAA,CAAG,OAAO,CAAA,CAGlF,MAAM,QAAQ,GAAA,CACZwB,CAAAA,CAAW,IAAKxB,CAAAA,EACOgB,CAAAA,CAAmB,CACtC,UAAA,CAAYhB,CAAAA,CAAG,QACf,OAAA,CAAAkB,CACF,CAAC,CAAA,EAEoB,gCAAA,CAAiC,CACpD,EAAA,CAAAlB,CAAAA,CACA,GAAGD,CAAAA,EACL,CAAC,CACF,CACH,EACF,CAAA,CAOA,kBAAmB,MAAO,CAAE,eAAA0B,CAAAA,CAAgB,cAAA,CAAAC,EAAgB,iBAAA,CAAAC,CAAAA,CAAmB,OAAAC,CAAO,CAAA,GAAM,CAC1F,GAAM,CAAE,eAAAC,CAAAA,CAAgB,GAAGC,CAAW,CAAA,CAAIF,CAAAA,CACpCG,EAAiBC,CAAAA,EAAM,CAAE,MAAK,CAGpClC,CAAAA,CAAI,CACF,SAAA,CAAW,CACT,GAAG8B,CAAAA,CACH,cAAA,CAAAF,EACA,cAAA,CAAAK,CAAAA,CACA,eAAgB,IAClB,CACF,CAAC,CAAA,CAED,IAAMZ,CAAAA,CAAeH,CAAAA,CAAmB,CACtC,UAAA,CAAYc,CAAAA,CAAW,QACvB,OAAA,CAAAZ,CACF,CAAC,CAAA,CAGKe,CAAAA,CAAiBC,GAAe,CACpC,IAAMC,EAAeD,CAAAA,YAAa,KAAA,CAAQA,EAAE,OAAA,CAAU,MAAA,CAAOA,CAAC,CAAA,CAC9DpC,CAAAA,CAAKG,CAAAA,EACHC,OAAAA,CAAQD,EAAQE,CAAAA,EAAU,CACpBA,EAAM,SAAA,GACRA,CAAAA,CAAM,UAAU,cAAA,CAAiB,KAAA,CACjCA,EAAM,SAAA,CAAU,YAAA,CAAegC,GAEnC,CAAC,CACH,EACF,CAAA,CAEA,GAAI,CAAChB,CAAAA,CAAc,CACjB,IAAMiB,CAAAA,CAAQ,IAAI,KAAA,CAAM,wCAAwC,EAChE,MAAAH,CAAAA,CAAcG,CAAK,CAAA,CACbA,CACR,CAEA,GAAI,CACF,GAAM,CAAE,UAAA,CAAAC,EAAY,aAAA,CAAAC,CAAc,EAAInB,CAAAA,CAAa,aAAA,GAGnD,MAAMA,CAAAA,CAAa,gBAAgBU,CAAc,CAAA,CAGjD,IAAMU,CAAAA,CAAkB,MAAMb,GAAe,CAG7C,GAAI,CAACa,CAAAA,CAAiB,CACpBzC,EAAI,CAAE,SAAA,CAAW,MAAU,CAAC,CAAA,CAC5B,MACF,CAGA,GAAM,CAAE,OAAA,CAAS0C,EAAgB,KAAA,CAAOC,CAAW,EAAItB,CAAAA,CAAa,wBAAA,CAClEoB,EACAF,CACF,CAAA,CAGMK,EAAQ,CACZ,GAAGZ,EACH,UAAA,CAAAO,CAAAA,CACA,KAAMC,CAAAA,CACN,OAAA,CAASE,GAAkBf,CAAAA,CAC3B,OAAA,CAASI,EACT,cAAA,CAAAE,CAAAA,CACA,MAAOU,CAAAA,CAEP,IAAA,CAAMD,IAAmB,UAAA,CAAcD,CAAAA,CAAoC,OAC3E,OAAA,CAAS,CAAA,CAAA,CACT,mBAAoBX,CAAAA,CAAO,gBAC7B,EAGA7B,CAAAA,EAAI,CAAE,YAAY2C,CAAU,CAAA,CAG5B5C,EAAKG,CAAAA,EACHC,OAAAA,CAAQD,CAAAA,CAAQE,CAAAA,EAAU,CACpBA,CAAAA,CAAM,SAAA,GACRA,EAAM,SAAA,CAAU,cAAA,CAAiB,GACjCA,CAAAA,CAAM,SAAA,CAAU,UAAYsC,CAAAA,EAEhC,CAAC,CACH,CAAA,CAGA,IAAMzC,EAAKD,CAAAA,EAAI,CAAE,iBAAiB0C,CAAU,CAAA,CAC5C,MAAMtB,CAAAA,CAAa,gCAAA,CAAiC,CAAE,EAAA,CAAAnB,CAAAA,CAAI,kBAAA2B,CAAAA,CAAmB,GAAG5B,GAAM,CAAC,EACzF,CAAA,MAASmC,CAAAA,CAAG,CACV,MAAAD,CAAAA,CAAcC,CAAC,CAAA,CACTA,CACR,CACF,CACF,CAAA,CAAA,CACA,CACE,GAAGb,CACL,CACF,CACF,CACF,CC3IO,IAAKsB,OAEVA,CAAAA,CAAA,GAAA,CAAM,MAENA,CAAAA,CAAA,MAAA,CAAS,SAETA,CAAAA,CAAA,QAAA,CAAW,WANDA,CAAAA,CAAAA,EAAAA,CAAAA,EAAA,EAAA,CAAA,CAaAC,OAEVA,CAAAA,CAAA,QAAA,CAAW,WAEXA,CAAAA,CAAA,IAAA,CAAO,OAEPA,CAAAA,CAAA,MAAA,CAAS,SAETA,CAAAA,CAAA,MAAA,CAAS,SARCA,CAAAA,CAAAA,EAAAA,CAAAA,EAAA,EAAA,CAAA,CAcAC,OAEVA,CAAAA,CAAA,MAAA,CAAS,SAETA,CAAAA,CAAA,OAAA,CAAU,UAEVA,CAAAA,CAAA,QAAA,CAAW,WANDA,CAAAA,CAAAA,EAAAA,CAAAA,EAAA,EAAA,ECzBL,IAAMC,IAA0BC,CAAAA,EAAWC,CAAAA,EAAaC,SAASF,CAAAA,CAAOC,CAAQ,GCuBvF,IAAME,CAAAA,CAA2B,IAC3BC,CAAAA,CAAsB,EAAA,CAarB,SAASC,EAAAA,CAAmDC,CAAAA,CAA0C,CAC3G,GAAM,CACJ,GAAArD,CAAAA,CACA,OAAA,CAAAsD,EACA,YAAA,CAAAC,CAAAA,CACA,UAAAC,CAAAA,CACA,SAAA,CAAAC,EACA,cAAA,CAAAC,CAAAA,CACA,WAAAC,CAAAA,CACA,gBAAA,CAAAC,EACA,eAAA,CAAAC,CAAAA,CAAkBX,EAClB,UAAA,CAAAY,CAAAA,CAAaX,CACf,CAAA,CAAIE,CAAAA,CAGJ,GAAI,CAACrD,EAAG,OAAA,CACN,OAIFuD,KAAe,CAEf,IAAIQ,EAAcD,CAAAA,CACdE,CAAAA,CAAY,KAOVC,CAAAA,CAAe5C,CAAAA,EAA4C,CAC1D2C,CAAAA,GACLA,CAAAA,CAAY,MAERJ,CAAAA,EAAoB,CAACvC,GAAS,eAAA,EAChCuC,CAAAA,CAAiB5D,EAAG,KAAK,CAAA,EAE7B,GAEoB,SAAY,CAC9B,KAAOgE,CAAAA,EAAaD,CAAAA,CAAc,GAChC,GAAI,CAEF,GADA,MAAM,IAAI,QAASG,CAAAA,EAAY,UAAA,CAAWA,EAASL,CAAe,CAAC,EAC/D,CAACG,CAAAA,CAAW,MAGhB,MAAMV,EAAQ,CACZ,EAAA,CAAAtD,EACA,WAAA,CAAAiE,CAAAA,CACA,UAAAT,CAAAA,CACA,SAAA,CAAAC,EACA,cAAA,CAAAC,CAAAA,CACA,WAAAC,CACF,CAAC,EACH,CAAA,MAASvB,CAAAA,CAAO,CACd,OAAA,CAAQ,KAAA,CAAM,6BAA6BpC,CAAAA,CAAG,KAAK,mBAAoBoC,CAAK,CAAA,CAC5E2B,IACF,CAGEA,CAAAA,EAAe,IACjB,OAAA,CAAQ,IAAA,CAAK,qBAAqB/D,CAAAA,CAAG,KAAK,wDAAwD,CAAA,CAClGyD,CAAAA,GACAQ,CAAAA,EAAY,EAEhB,KAIF","file":"index.mjs","sourcesContent":["/**\n * @file This file defines the core Zustand slice for managing the state of transactions. It includes the state,\n * actions, and types necessary for initializing the store and performing CRUD operations on the transaction pool.\n */\n\nimport { Draft, produce } from 'immer';\n\nimport { EvmTransaction, InitialTransaction, SolanaTransaction, StoreSlice, Transaction } from '../types';\n\n/**\n * Defines the structure of the transaction pool, a key-value store of transactions indexed by their unique keys.\n * @template T The type of the transaction object being tracked.\n */\nexport type TransactionPool<T extends Transaction> = Record<string, T>;\n\n/**\n * A utility type that creates a union of all fields that can be safely updated\n * on a transaction object via the `updateTxParams` action. This ensures type safety\n * and prevents accidental modification of immutable properties.\n */\ntype UpdatableTransactionFields = Partial<\n Pick<\n EvmTransaction,\n | 'to'\n | 'nonce'\n | 'txKey'\n | 'pending'\n | 'hash'\n | 'status'\n | 'replacedTxHash'\n | 'errorMessage'\n | 'finishedTimestamp'\n | 'isTrackedModalOpen'\n | 'isError'\n | 'maxPriorityFeePerGas'\n | 'maxFeePerGas'\n | 'input'\n | 'value'\n >\n> &\n Partial<Pick<SolanaTransaction, 'slot' | 'confirmations' | 'fee' | 'instructions' | 'recentBlockhash' | 'rpcUrl'>>;\n\n/**\n * The interface for the base transaction tracking store slice.\n * It includes the state and actions for managing the transaction lifecycle.\n * @template T The specific transaction type.\n */\nexport interface IInitializeTxTrackingStore<T extends Transaction> {\n /** A pool of all transactions currently being tracked, indexed by `txKey`. */\n transactionsPool: TransactionPool<T>;\n /** The `txKey` of the most recently added transaction. */\n lastAddedTxKey?: string;\n /** The state for a transaction being initiated, used for UI feedback before it's submitted to the chain. */\n initialTx?: InitialTransaction;\n\n /** Adds a new transaction to the tracking pool and marks it as pending. */\n addTxToPool: (tx: T) => void;\n /** Updates one or more properties of an existing transaction in the pool. */\n updateTxParams: (txKey: string, fields: UpdatableTransactionFields) => void;\n /** Removes a transaction from the tracking pool by its key. */\n removeTxFromPool: (txKey: string) => void;\n /** Closes the tracking modal for a transaction and clears any initial transaction state. */\n closeTxTrackedModal: (txKey?: string) => void;\n /** A selector function to retrieve the key of the last transaction added to the pool. */\n getLastTxKey: () => string | undefined;\n}\n\n/**\n * Creates a Zustand store slice with the core logic for transaction state management.\n * This function is a slice creator intended for use with Zustand's `create` function.\n *\n * @template T The specific transaction type.\n * @param options Configuration for the store slice.\n * @returns A Zustand store slice implementing `IInitializeTxTrackingStore`.\n */\nexport function initializeTxTrackingStore<T extends Transaction>(): StoreSlice<IInitializeTxTrackingStore<T>> {\n return (set, get) => ({\n transactionsPool: {},\n lastAddedTxKey: undefined,\n initialTx: undefined,\n\n addTxToPool: (tx) => {\n set((state) =>\n produce(state, (draft) => {\n draft.lastAddedTxKey = tx.txKey;\n if (tx.txKey) {\n draft.transactionsPool[tx.txKey] = {\n ...tx,\n pending: true, // Ensure all new transactions start as pending.\n } as Draft<T>;\n }\n }),\n );\n },\n\n updateTxParams: (txKey, fields) => {\n set((state) =>\n produce(state, (draft) => {\n const tx = draft.transactionsPool[txKey];\n // Ensure the transaction exists before attempting to update.\n if (tx) {\n Object.assign(tx, fields);\n }\n }),\n );\n },\n\n removeTxFromPool: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n delete draft.transactionsPool[txKey];\n }),\n );\n },\n\n closeTxTrackedModal: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n if (txKey && draft.transactionsPool[txKey]) {\n draft.transactionsPool[txKey].isTrackedModalOpen = false;\n }\n // Always clear the initial transaction state when a modal is closed\n draft.initialTx = undefined;\n }),\n );\n },\n\n getLastTxKey: () => get().lastAddedTxKey,\n });\n}\n","/**\n * @file This file contains selector functions for deriving state from the transaction tracking store.\n * Selectors help abstract the state's shape and provide efficient, memoized access to computed data.\n */\n\nimport { Transaction } from '../types';\nimport { TransactionPool } from './initializeTxTrackingStore';\n\n/**\n * Selects all transactions from the pool and sorts them by their creation timestamp in ascending order.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of all transactions, sorted chronologically.\n */\nexport const selectAllTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return Object.values(transactionsPool).sort((a, b) => Number(a.localTimestamp) - Number(b.localTimestamp));\n};\n\n/**\n * Selects all transactions that are currently in a pending state, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of pending transactions.\n */\nexport const selectPendingTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return selectAllTransactions(transactionsPool).filter((tx) => tx.pending);\n};\n\n/**\n * Selects a single transaction from the pool by its unique key (`txKey`).\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} key - The `txKey` of the transaction to retrieve.\n * @returns {T | undefined} The transaction object if found, otherwise undefined.\n */\nexport const selectTxByKey = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n key: string,\n): T | undefined => {\n return transactionsPool[key];\n};\n\n/**\n * Selects all transactions initiated by a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of transactions associated with the given wallet.\n */\nexport const selectAllTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Filters all transactions to find those matching the provided `from` address.\n return selectAllTransactions(transactionsPool).filter((tx) => tx.from.toLowerCase() === from.toLowerCase());\n};\n\n/**\n * Selects all pending transactions for a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of pending transactions for the given wallet.\n */\nexport const selectPendingTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Reuses the `selectAllTransactionsByActiveWallet` selector for efficiency\n // and then filters for pending transactions.\n return selectAllTransactionsByActiveWallet(transactionsPool, from).filter((tx) => tx.pending);\n};\n","/**\n * @file This file contains a utility function for selecting a specific transaction adapter from a list.\n */\n\nimport { Transaction, TransactionAdapter, TxAdapter } from '../types';\n\n/**\n * Selects a transaction adapter from a list based on a provided key.\n *\n * This function searches through an array of `TxAdapter` instances and returns the one\n * that matches the given `adapterKey`. If no specific adapter is found, it logs a warning\n * and returns the first adapter in the array as a fallback. This fallback mechanism\n * ensures that the system can still function, but it highlights a potential configuration issue.\n *\n * @template T - The transaction type, extending the base `Transaction`.\n *\n * @param {object} params - The parameters for the selection.\n * @param {TransactionAdapter} params.adapterKey - The key of the desired adapter.\n * @param {TxAdapter<T> | TxAdapter<T>[]} params.adapter - Adapter or an array of adapters for different chains or transaction types.\n *\n * @returns {TxAdapter<T> | undefined} The found transaction adapter, the fallback adapter, or undefined if the adapters array is empty.\n */\nexport const selectAdapterByKey = <T extends Transaction>({\n adapterKey,\n adapter,\n}: {\n adapterKey: TransactionAdapter;\n adapter: TxAdapter<T> | TxAdapter<T>[];\n}): TxAdapter<T> | undefined => {\n if (Array.isArray(adapter)) {\n if (adapter.length === 0) {\n console.error('Adapter selection failed: The provided adapters array is empty.');\n return undefined;\n }\n\n const foundAdapter = adapter.find((a) => a.key === adapterKey);\n\n if (foundAdapter) {\n return foundAdapter;\n } else {\n console.warn(\n `No adapter found for key: \"${adapterKey}\". Falling back to the first available adapter: \"${adapter[0].key}\".`,\n );\n return adapter[0];\n }\n }\n return adapter;\n};\n","/**\n * @file This file is the nucleus of the Pulsar store, orchestrating transaction handling, state management,\n * and communication with blockchain adapters. It utilizes Zustand for state management, Immer for safe,\n * immutable updates, and a persistence middleware to maintain state across user sessions.\n */\n\nimport dayjs from 'dayjs';\nimport { produce } from 'immer';\nimport { persist, PersistOptions } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\n\nimport { ITxTrackingStore, Transaction, TxAdapter } from '../types';\nimport { selectAdapterByKey } from '../utils/selectAdapterByKey';\nimport { initializeTxTrackingStore } from './initializeTxTrackingStore';\n\n/**\n * Creates the main Pulsar store for transaction tracking.\n *\n * This function configures a Zustand store enhanced with persistence. It combines the core transaction management\n * slice with a powerful orchestration logic that leverages chain-specific adapters to handle the entire\n * lifecycle of a transaction—from initiation and chain validation to execution and background status tracking.\n *\n * @template T The specific transaction type, extending the base `Transaction`.\n *\n * @param config Configuration object for creating the store.\n * @param config.adapter Adapter or an array of adapters for different chains or transaction types.\n * @param options Configuration for the Zustand `persist` middleware.\n * @returns A fully configured Zustand store instance.\n */\nexport function createPulsarStore<T extends Transaction>({\n adapter,\n ...options\n}: {\n adapter: TxAdapter<T> | TxAdapter<T>[];\n} & PersistOptions<ITxTrackingStore<T>>) {\n return createStore<ITxTrackingStore<T>>()(\n persist(\n (set, get) => ({\n // Initialize the base store slice with core state and actions\n ...initializeTxTrackingStore<T>()(set, get),\n\n /**\n * Initializes trackers for all pending transactions upon store creation.\n * This is crucial for resuming tracking after a page refresh or session restoration.\n */\n initializeTransactionsPool: async () => {\n const pendingTxs = Object.values(get().transactionsPool).filter((tx) => tx.pending);\n\n // Concurrently initialize trackers for all pending transactions\n await Promise.all(\n pendingTxs.map((tx) => {\n const foundAdapter = selectAdapterByKey({\n adapterKey: tx.adapter,\n adapter,\n });\n // Delegate tracker initialization to the appropriate adapter\n return foundAdapter?.checkAndInitializeTrackerInStore({\n tx,\n ...get(),\n });\n }),\n );\n },\n\n /**\n * The primary function to orchestrate sending and tracking a new transaction.\n * It manages the entire lifecycle, from UI state updates and chain switching to\n * signing, submission, and background tracker initialization.\n */\n handleTransaction: async ({ defaultTracker, actionFunction, onSucceedCallback, params }) => {\n const { desiredChainID, ...restParams } = params;\n const localTimestamp = dayjs().unix();\n\n // Step 1: Set initial state for immediate UI feedback (e.g., loading spinner).\n set({\n initialTx: {\n ...params,\n actionFunction,\n localTimestamp,\n isInitializing: true,\n },\n });\n\n const foundAdapter = selectAdapterByKey({\n adapterKey: restParams.adapter,\n adapter,\n });\n\n // Centralized error handler for this transaction flow\n const handleTxError = (e: unknown) => {\n const errorMessage = e instanceof Error ? e.message : String(e);\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.errorMessage = errorMessage;\n }\n }),\n );\n };\n\n if (!foundAdapter) {\n const error = new Error('No adapter found for this transaction.');\n handleTxError(error);\n throw error; // Re-throw to allow the caller to handle it.\n }\n\n try {\n const { walletType, walletAddress } = foundAdapter.getWalletInfo();\n\n // Step 2: Ensure the wallet is connected to the correct chain.\n await foundAdapter.checkChainForTx(desiredChainID);\n\n // Step 3: Execute the provided action (e.g., signing and sending the transaction).\n const txKeyFromAction = await actionFunction();\n\n // If `txKeyFromAction` is undefined, it indicates the user cancelled the action.\n if (!txKeyFromAction) {\n set({ initialTx: undefined });\n return;\n }\n\n // Step 4: Determine the final tracker and txKey from the action's result.\n const { tracker: updatedTracker, txKey: finalTxKey } = foundAdapter.checkTransactionsTracker(\n txKeyFromAction,\n walletType,\n );\n\n // Step 5: Construct the full transaction object for the pool.\n const newTx = {\n ...restParams,\n walletType,\n from: walletAddress,\n tracker: updatedTracker || defaultTracker,\n chainId: desiredChainID,\n localTimestamp,\n txKey: finalTxKey,\n // For EVM, the hash is often the preliminary key from the action.\n hash: updatedTracker === 'ethereum' ? (txKeyFromAction as `0x${string}`) : undefined,\n pending: false, // will be set to true by addTxToPool\n isTrackedModalOpen: params.withTrackedModal,\n };\n\n // Step 6: Add the transaction to the pool.\n get().addTxToPool(newTx as T);\n\n // Step 7: Update the initial state to link it with the newly created transaction.\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.lastTxKey = finalTxKey;\n }\n }),\n );\n\n // Step 8: Initialize the background tracker for the transaction.\n const tx = get().transactionsPool[finalTxKey];\n await foundAdapter.checkAndInitializeTrackerInStore({ tx, onSucceedCallback, ...get() });\n } catch (e) {\n handleTxError(e);\n throw e; // Re-throw for external handling if needed.\n }\n },\n }),\n {\n ...options, // Merges user-provided persistence options.\n },\n ),\n );\n}\n","/**\n * @file This file defines the core data structures and TypeScript types for the Pulsar transaction tracking engine.\n * It specifies the framework-agnostic models for transactions, their lifecycle statuses, and the interfaces for\n * the Zustand-based store and chain-specific adapters.\n */\n\nimport { StoreApi } from 'zustand';\n\nimport { IInitializeTxTrackingStore } from './store/initializeTxTrackingStore';\n\n// =================================================================================================\n// 1. ZUSTAND UTILITY TYPES\n// =================================================================================================\n\n/**\n * A utility type for creating modular Zustand store slices, enabling composable state management.\n * @template T The state slice being defined.\n * @template S The full store state that includes the slice `T`.\n */\nexport type StoreSlice<T extends object, S extends object = T> = (\n set: StoreApi<S extends T ? S : S & T>['setState'],\n get: StoreApi<S extends T ? S : S & T>['getState'],\n) => T;\n\n// =================================================================================================\n// 2. ENUMS AND CORE TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Defines the supported blockchain adapters. Each adapter corresponds to a specific chain architecture.\n */\nexport enum TransactionAdapter {\n /** For Ethereum Virtual Machine (EVM) compatible chains like Ethereum, Polygon, etc. */\n EVM = 'evm',\n /** For the Solana blockchain. */\n SOLANA = 'solana',\n /** For the Starknet L2 network. */\n Starknet = 'starknet',\n}\n\n/**\n * Enum representing the different tracking strategies available for EVM transactions.\n * Each tracker corresponds to a specific method of monitoring a transaction's lifecycle.\n */\nexport enum TransactionTracker {\n /** For standard on-chain EVM transactions tracked by their hash. */\n Ethereum = 'ethereum',\n /** For multi-signature transactions managed and executed via a Safe contract. */\n Safe = 'safe',\n /** For meta-transactions relayed and executed by the Gelato Network. */\n Gelato = 'gelato',\n /** The tracker for monitoring standard Solana transaction signatures. */\n Solana = 'solana',\n}\n\n/**\n * Represents the terminal status of a transaction after it has been processed.\n */\nexport enum TransactionStatus {\n /** The transaction failed to execute due to an on-chain error or rejection. */\n Failed = 'Failed',\n /** The transaction was successfully mined and included in a block. */\n Success = 'Success',\n /** The transaction was replaced by another with the same nonce (e.g., a speed-up or cancel). */\n Replaced = 'Replaced',\n}\n\n/**\n * Defines the shape of the identifier for a Gelato transaction task.\n */\nexport type GelatoTxKey = {\n taskId: string;\n};\n\n/**\n * A union type representing the unique identifier returned by an `actionFunction`\n * after a transaction is submitted to the network or a relay service.\n *\n * This key is crucial for the EVM adapter to determine which tracker should\n * monitor the transaction.\n *\n * It can be one of the following:\n * - A standard `0x...` transaction hash (`Hex`).\n * - A structured object from a relay service like Gelato (`GelatoTxKey`).\n */\nexport type ActionTxKey = `0x${string}` | GelatoTxKey | string;\n\nexport type OnSuccessCallback<T> = (tx: T) => Promise<void> | void;\n\n/**\n * The fundamental structure for any transaction being tracked by Pulsar.\n * This serves as the base upon which chain-specific transaction types are built.\n */\nexport type BaseTransaction = {\n /** The chain identifier (e.g., 1 for Ethereum Mainnet, 'SN_MAIN' for Starknet). */\n chainId: number | string;\n /**\n * User-facing description. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single description for all states\n * description: 'Swap 1 ETH for 1,500 USDC'\n * // Specific descriptions for each state in order: [pending, success, error, replaced]\n * description: ['Swapping...', 'Swapped Successfully', 'Swap Failed', 'Swap Replaced']\n */\n description?: string | [string, string, string, string];\n /** The error message if the transaction failed. */\n errorMessage?: string;\n /** The on-chain timestamp (in seconds) when the transaction was finalized. */\n finishedTimestamp?: number;\n /** The sender's wallet address. */\n from: string;\n /** A flag indicating if the transaction is in a failed state. */\n isError?: boolean;\n /** A UI flag to control the visibility of a detailed tracking modal for this transaction. */\n isTrackedModalOpen?: boolean;\n /** The local timestamp (in seconds) when the transaction was initiated by the user. */\n localTimestamp: number;\n /** Any additional, custom data associated with the transaction. */\n payload?: object;\n /** A flag indicating if the transaction is still awaiting on-chain confirmation. */\n pending: boolean;\n /** The final on-chain status of the transaction. */\n status?: TransactionStatus;\n /**\n * User-facing title. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single title for all states\n * title: 'ETH/USDC Swap'\n * // Specific titles for each state in order: [pending, success, error, replaced]\n * title: ['Processing Swap', 'Swap Complete', 'Swap Error', 'Swap Replaced']\n */\n title?: string | [string, string, string, string];\n /** The specific tracker responsible for monitoring this transaction's status. */\n tracker: TransactionTracker;\n /** The unique identifier for the transaction (e.g., EVM hash, Gelato task ID). */\n txKey: string;\n /** The application-specific type or category of the transaction (e.g., 'SWAP', 'APPROVE'). */\n type: string;\n /** The type of wallet used to sign the transaction (e.g., 'injected', 'walletConnect'). */\n walletType: string;\n};\n\n// =================================================================================================\n// 3. CHAIN-SPECIFIC TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents an EVM-specific transaction, extending the base properties with EVM fields.\n */\nexport type EvmTransaction = BaseTransaction & {\n adapter: TransactionAdapter.EVM;\n /** The on-chain transaction hash, available after submission. */\n hash?: `0x${string}`;\n /** The data payload for the transaction, typically for smart contract interactions. */\n input?: `0x${string}`;\n /** The maximum fee per gas for an EIP-1559 transaction (in wei). */\n maxFeePerGas?: string;\n /** The maximum priority fee per gas for an EIP-1559 transaction (in wei). */\n maxPriorityFeePerGas?: string;\n /** The transaction nonce, a sequential number for the sender's account. */\n nonce?: number;\n /** The hash of a transaction that this one replaced. */\n replacedTxHash?: `0x${string}`;\n /** The recipient's address or contract address. */\n to?: `0x${string}`;\n /** The amount of native currency (in wei) being sent. */\n value?: string;\n};\n\n/**\n * Represents a Solana-specific transaction, extending the base properties.\n */\nexport type SolanaTransaction = BaseTransaction & {\n adapter: TransactionAdapter.SOLANA;\n /** The transaction fee in lamports. */\n fee?: number;\n /** The instructions included in the transaction. */\n instructions?: unknown[];\n /** The recent blockhash used for the transaction. */\n recentBlockhash?: string;\n /** The slot in which the transaction was processed. */\n slot?: number;\n /** The number of confirmations received. `string` when tx successed. `null` if the transaction is pending or unconfirmed. */\n confirmations?: number | string | null;\n /** The RPC URL used to submit and track this transaction. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a Starknet-specific transaction, extending the base properties.\n */\nexport type StarknetTransaction = BaseTransaction & {\n adapter: TransactionAdapter.Starknet;\n /** The actual fee paid for the transaction. */\n actualFee?: { amount: string; unit: string };\n /** The address of the contract being interacted with. */\n contractAddress?: string;\n};\n\n/** A union type representing any possible transaction structure that Pulsar can handle. */\nexport type Transaction = EvmTransaction | SolanaTransaction | StarknetTransaction;\n\n// =================================================================================================\n// 4. INITIAL TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents the parameters required to initiate a new transaction tracking flow.\n */\nexport type InitialTransactionParams = {\n adapter: TransactionAdapter;\n /** The function that executes the on-chain action (e.g., sending a transaction) and returns a preliminary identifier like a hash. */\n actionFunction: (...args: any[]) => Promise<ActionTxKey | undefined>;\n /** A user-facing description for the transaction. Supports state-specific descriptions. */\n description?: string | [string, string, string, string];\n /** The target chain ID for the transaction. */\n desiredChainID: number | string;\n /** Any custom data to associate with the transaction. */\n payload?: object;\n /** A user-facing title for the transaction. Supports state-specific titles. */\n title?: string | [string, string, string, string];\n /** The application-specific type of the transaction. */\n type: string;\n /** If true, the detailed tracking modal will open automatically upon initiation. */\n withTrackedModal?: boolean;\n /** The RPC URL to use for the transaction. Required for Solana transactions. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a transaction in its temporary, pre-submission state.\n * This is used for UI feedback while the transaction is being signed and sent.\n */\nexport type InitialTransaction = InitialTransactionParams & {\n /** An error message if the initialization fails (e.g., user rejects signature). */\n errorMessage?: string;\n /** A flag indicating if the transaction is being processed (e.g., waiting for signature). */\n isInitializing: boolean;\n /** The `txKey` of the on-chain transaction that this action produced, used for linking the states. */\n lastTxKey?: string;\n /** The local timestamp when the user initiated the action. */\n localTimestamp: number;\n};\n\n// =================================================================================================\n// 5. ADAPTER AND STORE INTERFACES\n// =================================================================================================\n\n/**\n * Defines the interface for a transaction adapter, which provides chain-specific logic and utilities.\n * @template T The specific transaction type, extending `Transaction`.\n */\nexport type TxAdapter<T extends Transaction> = {\n /** The unique key identifying this adapter. */\n key: TransactionAdapter;\n /** Returns information about the currently connected wallet. */\n getWalletInfo: () => {\n walletAddress: string;\n walletType: string;\n };\n /** Ensures the connected wallet is on the correct network for the transaction. Throws an error if the chain is mismatched. */\n checkChainForTx: (chainId: string | number) => Promise<void>;\n /** Determines the appropriate tracker and final `txKey` from the result of an action. */\n checkTransactionsTracker: (\n actionTxKey: ActionTxKey,\n walletType: string,\n ) => { txKey: string; tracker: TransactionTracker };\n /** Selects and initializes the correct background tracker for a given transaction. */\n checkAndInitializeTrackerInStore: (\n params: { tx: T; onSucceedCallback?: OnSuccessCallback<T> } & Pick<\n ITxTrackingStore<T>,\n 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'\n >,\n ) => Promise<void>;\n /** Returns the base URL for the blockchain explorer for the current network. */\n getExplorerUrl: (url?: string) => string | undefined;\n /** Optional: Fetches a name from a chain-specific name service (e.g., ENS). */\n getName?: (address: string) => Promise<string | null>;\n /** Optional: Fetches an avatar URL from a chain-specific name service. */\n getAvatar?: (name: string) => Promise<string | null>;\n /** Optional: Logic to cancel a pending EVM transaction. */\n cancelTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to speed up a pending EVM transaction. */\n speedUpTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to retry a failed transaction. */\n retryTxAction?: (\n params: {\n txKey: string;\n tx: InitialTransactionParams;\n onClose: (txKey?: string) => void;\n } & Partial<Pick<ITxTrackingStore<T>, 'handleTransaction'>>,\n ) => Promise<void>;\n /**\n * Optional: Constructs a full explorer URL for a specific transaction.\n * May require the full transaction pool to resolve details for replaced transactions.\n */\n getExplorerTxUrl?: (tx: T) => string;\n};\n\n/**\n * The complete interface for the Pulsar transaction tracking store.\n * @template T The transaction type.\n */\nexport type ITxTrackingStore<T extends Transaction> = IInitializeTxTrackingStore<T> & {\n /**\n * The primary method for initiating and tracking a new transaction from start to finish.\n * It manages UI state, executes the on-chain action, and initiates background tracking.\n * @param params The parameters for handling the transaction.\n */\n handleTransaction: (params: {\n /** The async function to execute (e.g., a smart contract write call). Must return a unique key or undefined. */\n actionFunction: () => Promise<ActionTxKey | undefined>;\n /** The metadata for the transaction. */\n params: Omit<InitialTransactionParams, 'actionFunction'>;\n /** The default tracker to use if it cannot be determined automatically. */\n defaultTracker?: TransactionTracker;\n /** Callback to execute when the transaction is successfully submitted. */\n onSucceedCallback?: OnSuccessCallback<T>;\n }) => Promise<void>;\n\n /**\n * Initializes trackers for all pending transactions in the pool.\n * This is essential for resuming tracking after a page reload.\n */\n initializeTransactionsPool: () => Promise<void>;\n};\n","/**\n * @file This file provides a utility for creating a type-safe, bounded Zustand hook from a vanilla store.\n * This pattern is recommended by the official Zustand documentation to ensure full type\n * safety when integrating vanilla stores with React.\n *\n * @see https://docs.pmnd.rs/zustand/guides/typescript#bounded-usestore-hook-for-vanilla-stores\n */\n\nimport { StoreApi, useStore } from 'zustand';\n\n/**\n * A utility type that infers the state shape from a Zustand `StoreApi`.\n * It extracts the return type of the `getState` method.\n * @template S - The type of the Zustand store (`StoreApi`).\n */\ntype ExtractState<S> = S extends { getState: () => infer T } ? T : never;\n\n/**\n * Creates a bounded `useStore` hook from a vanilla Zustand store.\n *\n * This function takes a vanilla Zustand store instance and returns a React hook\n * that is pre-bound to that store. This approach provides a cleaner API and\n * enhances type inference, eliminating the need to pass the store instance\n * on every use.\n *\n * The returned hook supports two signatures:\n * 1. `useBoundedStore()`: Selects the entire state.\n * 2. `useBoundedStore(selector)`: Selects a slice of the state, returning only what the selector function specifies.\n *\n * @template S - The type of the Zustand store (`StoreApi`).\n * @param {S} store - The vanilla Zustand store instance to bind the hook to.\n * @returns {function} A fully typed React hook for accessing the store's state.\n */\nexport const createBoundedUseStore = ((store) => (selector) => useStore(store, selector)) as <\n S extends StoreApi<unknown>,\n>(\n store: S,\n) => {\n // This implementation uses a Immediately Invoked Function Expression (IIFE)\n // trick combined with casting to achieve the desired overloaded function signature in a concise way.\n (): ExtractState<S>;\n <T>(selector: (state: ExtractState<S>) => T): T;\n};\n","/**\n * @file This file provides a generic utility for creating a polling mechanism to track\n * asynchronous tasks, such as API-based transaction status checks (e.g., for Gelato or Safe).\n */\n\nimport { Transaction } from '../types';\n\n/**\n * Defines the parameters for the fetcher function used within the polling tracker.\n * The fetcher is the core logic that performs the actual API call.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object being tracked.\n */\ntype PollingFetcherParams<R, T> = {\n /** The transaction object being tracked. */\n tx: T;\n /** A callback to stop the polling mechanism, typically called on success or terminal failure. */\n stopPolling: (options?: { withoutRemoving?: boolean }) => void;\n /** Callback to be invoked when the fetcher determines the transaction has succeeded. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the fetcher determines the transaction has failed. */\n onFailure: (response?: R) => void;\n /** Optional callback for each successful poll, useful for updating UI with intermediate states. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced (e.g., speed-up). */\n onReplaced?: (response: R) => void;\n};\n\n/**\n * Defines the configuration object for the `initializePollingTracker` function.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object.\n */\nexport type PollingTrackerConfig<R, T extends Transaction> = {\n /** The transaction object to be tracked. It must include `txKey` and `pending` status. */\n tx: T & Pick<Transaction, 'txKey' | 'pending'>;\n /** The function that performs the data fetching (e.g., an API call) on each interval. */\n fetcher: (params: PollingFetcherParams<R, T>) => Promise<void>;\n /** Callback to be invoked when the transaction successfully completes. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the transaction fails. */\n onFailure: (response?: R) => void;\n /** Optional callback executed once when the tracker is initialized. */\n onInitialize?: () => void;\n /** Optional callback for each successful poll. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced. */\n onReplaced?: (response: R) => void;\n /** Optional function to remove the transaction from the main pool, typically after polling stops. */\n removeTxFromPool?: (txKey: string) => void;\n /** The interval (in milliseconds) between polling attempts. Defaults to 5000ms. */\n pollingInterval?: number;\n /** The number of consecutive failed fetches before stopping the tracker. Defaults to 10. */\n maxRetries?: number;\n};\n\nconst DEFAULT_POLLING_INTERVAL = 5000;\nconst DEFAULT_MAX_RETRIES = 10;\n\n/**\n * Initializes a generic polling tracker that repeatedly calls a fetcher function\n * to monitor the status of an asynchronous task.\n *\n * This function handles the lifecycle of polling, including starting, stopping,\n * and automatic termination after a certain number of failed attempts.\n *\n * @template R The expected type of the API response.\n * @template T The type of the transaction object.\n * @param {PollingTrackerConfig<R, T>} config - The configuration for the tracker.\n */\nexport function initializePollingTracker<R, T extends Transaction>(config: PollingTrackerConfig<R, T>): void {\n const {\n tx,\n fetcher,\n onInitialize,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n removeTxFromPool,\n pollingInterval = DEFAULT_POLLING_INTERVAL,\n maxRetries = DEFAULT_MAX_RETRIES,\n } = config;\n\n // 1. Early exit if the transaction is no longer pending\n if (!tx.pending) {\n return;\n }\n\n // Execute the initialization callback if provided\n onInitialize?.();\n\n let retriesLeft = maxRetries;\n let isPolling = true;\n\n /**\n * Stops the polling interval and optionally removes the transaction from the pool.\n * @param {object} [options] - Options for stopping the tracker.\n * @param {boolean} [options.withoutRemoving=false] - If true, the tx will not be removed from the pool.\n */\n const stopPolling = (options?: { withoutRemoving?: boolean }) => {\n if (!isPolling) return;\n isPolling = false;\n // The interval is cleared in the finally block of the polling loop\n if (removeTxFromPool && !options?.withoutRemoving) {\n removeTxFromPool(tx.txKey);\n }\n };\n\n const pollingLoop = async () => {\n while (isPolling && retriesLeft > 0) {\n try {\n await new Promise((resolve) => setTimeout(resolve, pollingInterval));\n if (!isPolling) break;\n\n // The fetcher's responsibility is to call onSuccess, onFailure, etc., which in turn call stopPolling.\n await fetcher({\n tx,\n stopPolling,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n });\n } catch (error) {\n console.error(`Polling fetcher for txKey ${tx.txKey} threw an error:`, error);\n retriesLeft--;\n }\n }\n\n if (retriesLeft <= 0) {\n console.warn(`Polling for txKey ${tx.txKey} stopped after reaching the maximum number of retries.`);\n onFailure();\n stopPolling();\n }\n };\n\n // Start the asynchronous polling loop\n pollingLoop();\n}\n"]}
1
+ {"version":3,"sources":["../src/store/initializeTxTrackingStore.ts","../src/store/transactionsSelectors.ts","../src/utils/selectAdapterByKey.ts","../src/store/txTrackingStore.ts","../src/types.ts","../src/utils/createBoundedUseStore.ts","../src/utils/initializePollingTracker.ts"],"names":["initializeTxTrackingStore","set","get","tx","state","produce","draft","txKey","fields","selectAllTransactions","transactionsPool","a","b","selectPendingTransactions","selectTxByKey","key","selectAllTransactionsByActiveWallet","from","selectPendingTransactionsByActiveWallet","selectAdapterByKey","adapterKey","adapter","foundAdapter","createPulsarStore","options","createStore","persist","pendingTxs","defaultTracker","actionFunction","onSuccessCallback","params","desiredChainID","restParams","localTimestamp","dayjs","handleTxError","e","errorMessage","error","walletType","walletAddress","txKeyFromAction","updatedTracker","finalTxKey","newTx","TransactionAdapter","TransactionTracker","TransactionStatus","createBoundedUseStore","store","selector","useStore","DEFAULT_POLLING_INTERVAL","DEFAULT_MAX_RETRIES","initializePollingTracker","config","fetcher","onInitialize","onSuccess","onFailure","onIntervalTick","onReplaced","removeTxFromPool","pollingInterval","maxRetries","retriesLeft","isPolling","stopPolling","resolve"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @file This file defines the core Zustand slice for managing the state of transactions. It includes the state,\n * actions, and types necessary for initializing the store and performing CRUD operations on the transaction pool.\n */\n\nimport { Draft, produce } from 'immer';\n\nimport { EvmTransaction, InitialTransaction, SolanaTransaction, StoreSlice, Transaction } from '../types';\n\n/**\n * Defines the structure of the transaction pool, a key-value store of transactions indexed by their unique keys.\n * @template T The type of the transaction object being tracked.\n */\nexport type TransactionPool<T extends Transaction> = Record<string, T>;\n\n/**\n * A utility type that creates a union of all fields that can be safely updated\n * on a transaction object via the `updateTxParams` action. This ensures type safety\n * and prevents accidental modification of immutable properties.\n */\ntype UpdatableTransactionFields = Partial<\n Pick<\n EvmTransaction,\n | 'to'\n | 'nonce'\n | 'txKey'\n | 'pending'\n | 'hash'\n | 'status'\n | 'replacedTxHash'\n | 'errorMessage'\n | 'finishedTimestamp'\n | 'isTrackedModalOpen'\n | 'isError'\n | 'maxPriorityFeePerGas'\n | 'maxFeePerGas'\n | 'input'\n | 'value'\n >\n> &\n Partial<Pick<SolanaTransaction, 'slot' | 'confirmations' | 'fee' | 'instructions' | 'recentBlockhash' | 'rpcUrl'>>;\n\n/**\n * The interface for the base transaction tracking store slice.\n * It includes the state and actions for managing the transaction lifecycle.\n * @template T The specific transaction type.\n */\nexport interface IInitializeTxTrackingStore<T extends Transaction> {\n /** A pool of all transactions currently being tracked, indexed by `txKey`. */\n transactionsPool: TransactionPool<T>;\n /** The `txKey` of the most recently added transaction. */\n lastAddedTxKey?: string;\n /** The state for a transaction being initiated, used for UI feedback before it's submitted to the chain. */\n initialTx?: InitialTransaction;\n\n /** Adds a new transaction to the tracking pool and marks it as pending. */\n addTxToPool: (tx: T) => void;\n /** Updates one or more properties of an existing transaction in the pool. */\n updateTxParams: (txKey: string, fields: UpdatableTransactionFields) => void;\n /** Removes a transaction from the tracking pool by its key. */\n removeTxFromPool: (txKey: string) => void;\n /** Closes the tracking modal for a transaction and clears any initial transaction state. */\n closeTxTrackedModal: (txKey?: string) => void;\n /** A selector function to retrieve the key of the last transaction added to the pool. */\n getLastTxKey: () => string | undefined;\n}\n\n/**\n * Creates a Zustand store slice with the core logic for transaction state management.\n * This function is a slice creator intended for use with Zustand's `create` function.\n *\n * @template T The specific transaction type.\n * @param options Configuration for the store slice.\n * @returns A Zustand store slice implementing `IInitializeTxTrackingStore`.\n */\nexport function initializeTxTrackingStore<T extends Transaction>(): StoreSlice<IInitializeTxTrackingStore<T>> {\n return (set, get) => ({\n transactionsPool: {},\n lastAddedTxKey: undefined,\n initialTx: undefined,\n\n addTxToPool: (tx) => {\n set((state) =>\n produce(state, (draft) => {\n draft.lastAddedTxKey = tx.txKey;\n if (tx.txKey) {\n draft.transactionsPool[tx.txKey] = {\n ...tx,\n pending: true, // Ensure all new transactions start as pending.\n } as Draft<T>;\n }\n }),\n );\n },\n\n updateTxParams: (txKey, fields) => {\n set((state) =>\n produce(state, (draft) => {\n const tx = draft.transactionsPool[txKey];\n // Ensure the transaction exists before attempting to update.\n if (tx) {\n Object.assign(tx, fields);\n }\n }),\n );\n },\n\n removeTxFromPool: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n delete draft.transactionsPool[txKey];\n }),\n );\n },\n\n closeTxTrackedModal: (txKey) => {\n set((state) =>\n produce(state, (draft) => {\n if (txKey && draft.transactionsPool[txKey]) {\n draft.transactionsPool[txKey].isTrackedModalOpen = false;\n }\n // Always clear the initial transaction state when a modal is closed\n draft.initialTx = undefined;\n }),\n );\n },\n\n getLastTxKey: () => get().lastAddedTxKey,\n });\n}\n","/**\n * @file This file contains selector functions for deriving state from the transaction tracking store.\n * Selectors help abstract the state's shape and provide efficient, memoized access to computed data.\n */\n\nimport { Transaction } from '../types';\nimport { TransactionPool } from './initializeTxTrackingStore';\n\n/**\n * Selects all transactions from the pool and sorts them by their creation timestamp in ascending order.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of all transactions, sorted chronologically.\n */\nexport const selectAllTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return Object.values(transactionsPool).sort((a, b) => Number(a.localTimestamp) - Number(b.localTimestamp));\n};\n\n/**\n * Selects all transactions that are currently in a pending state, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @returns {T[]} An array of pending transactions.\n */\nexport const selectPendingTransactions = <T extends Transaction>(transactionsPool: TransactionPool<T>): T[] => {\n return selectAllTransactions(transactionsPool).filter((tx) => tx.pending);\n};\n\n/**\n * Selects a single transaction from the pool by its unique key (`txKey`).\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} key - The `txKey` of the transaction to retrieve.\n * @returns {T | undefined} The transaction object if found, otherwise undefined.\n */\nexport const selectTxByKey = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n key: string,\n): T | undefined => {\n return transactionsPool[key];\n};\n\n/**\n * Selects all transactions initiated by a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of transactions associated with the given wallet.\n */\nexport const selectAllTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Filters all transactions to find those matching the provided `from` address.\n return selectAllTransactions(transactionsPool).filter((tx) => tx.from.toLowerCase() === from.toLowerCase());\n};\n\n/**\n * Selects all pending transactions for a specific wallet address, sorted chronologically.\n * @template T - The transaction type.\n * @param {TransactionPool<T>} transactionsPool - The entire transaction pool from the store.\n * @param {string} from - The wallet address (`from` address) to filter transactions by.\n * @returns {T[]} An array of pending transactions for the given wallet.\n */\nexport const selectPendingTransactionsByActiveWallet = <T extends Transaction>(\n transactionsPool: TransactionPool<T>,\n from: string,\n): T[] => {\n // Reuses the `selectAllTransactionsByActiveWallet` selector for efficiency\n // and then filters for pending transactions.\n return selectAllTransactionsByActiveWallet(transactionsPool, from).filter((tx) => tx.pending);\n};\n","/**\n * @file This file contains a utility function for selecting a specific transaction adapter from a list.\n */\n\nimport { Transaction, TransactionAdapter, TxAdapter } from '../types';\n\n/**\n * Selects a transaction adapter from a list based on a provided key.\n *\n * This function searches through an array of `TxAdapter` instances and returns the one\n * that matches the given `adapterKey`. If no specific adapter is found, it logs a warning\n * and returns the first adapter in the array as a fallback. This fallback mechanism\n * ensures that the system can still function, but it highlights a potential configuration issue.\n *\n * @template T - The transaction type, extending the base `Transaction`.\n *\n * @param {object} params - The parameters for the selection.\n * @param {TransactionAdapter} params.adapterKey - The key of the desired adapter.\n * @param {TxAdapter<T> | TxAdapter<T>[]} params.adapter - Adapter or an array of adapters for different chains or transaction types.\n *\n * @returns {TxAdapter<T> | undefined} The found transaction adapter, the fallback adapter, or undefined if the adapters array is empty.\n */\nexport const selectAdapterByKey = <T extends Transaction>({\n adapterKey,\n adapter,\n}: {\n adapterKey: TransactionAdapter;\n adapter: TxAdapter<T> | TxAdapter<T>[];\n}): TxAdapter<T> | undefined => {\n if (Array.isArray(adapter)) {\n if (adapter.length === 0) {\n console.error('Adapter selection failed: The provided adapters array is empty.');\n return undefined;\n }\n\n const foundAdapter = adapter.find((a) => a.key === adapterKey);\n\n if (foundAdapter) {\n return foundAdapter;\n } else {\n console.warn(\n `No adapter found for key: \"${adapterKey}\". Falling back to the first available adapter: \"${adapter[0].key}\".`,\n );\n return adapter[0];\n }\n }\n return adapter;\n};\n","/**\n * @file This file is the nucleus of the Pulsar store, orchestrating transaction handling, state management,\n * and communication with blockchain adapters. It utilizes Zustand for state management, Immer for safe,\n * immutable updates, and a persistence middleware to maintain state across user sessions.\n */\n\nimport dayjs from 'dayjs';\nimport { produce } from 'immer';\nimport { persist, PersistOptions } from 'zustand/middleware';\nimport { createStore } from 'zustand/vanilla';\n\nimport { ITxTrackingStore, Transaction, TxAdapter } from '../types';\nimport { selectAdapterByKey } from '../utils/selectAdapterByKey';\nimport { initializeTxTrackingStore } from './initializeTxTrackingStore';\n\n/**\n * Creates the main Pulsar store for transaction tracking.\n *\n * This function configures a Zustand store enhanced with persistence. It combines the core transaction management\n * slice with a powerful orchestration logic that leverages chain-specific adapters to handle the entire\n * lifecycle of a transaction—from initiation and chain validation to execution and background status tracking.\n *\n * @template T The specific transaction type, extending the base `Transaction`.\n *\n * @param config Configuration object for creating the store.\n * @param config.adapter Adapter or an array of adapters for different chains or transaction types.\n * @param options Configuration for the Zustand `persist` middleware.\n * @returns A fully configured Zustand store instance.\n */\nexport function createPulsarStore<T extends Transaction>({\n adapter,\n ...options\n}: {\n adapter: TxAdapter<T> | TxAdapter<T>[];\n} & PersistOptions<ITxTrackingStore<T>>) {\n return createStore<ITxTrackingStore<T>>()(\n persist(\n (set, get) => ({\n // Initialize the base store slice with core state and actions\n ...initializeTxTrackingStore<T>()(set, get),\n\n /**\n * Initializes trackers for all pending transactions upon store creation.\n * This is crucial for resuming tracking after a page refresh or session restoration.\n */\n initializeTransactionsPool: async () => {\n const pendingTxs = Object.values(get().transactionsPool).filter((tx) => tx.pending);\n\n // Concurrently initialize trackers for all pending transactions\n await Promise.all(\n pendingTxs.map((tx) => {\n const foundAdapter = selectAdapterByKey({\n adapterKey: tx.adapter,\n adapter,\n });\n // Delegate tracker initialization to the appropriate adapter\n return foundAdapter?.checkAndInitializeTrackerInStore({\n tx,\n ...get(),\n });\n }),\n );\n },\n\n /**\n * The primary function to orchestrate sending and tracking a new transaction.\n * It manages the entire lifecycle, from UI state updates and chain switching to\n * signing, submission, and background tracker initialization.\n */\n handleTransaction: async ({ defaultTracker, actionFunction, onSuccessCallback, params }) => {\n const { desiredChainID, ...restParams } = params;\n const localTimestamp = dayjs().unix();\n\n // Step 1: Set initial state for immediate UI feedback (e.g., loading spinner).\n set({\n initialTx: {\n ...params,\n actionFunction,\n localTimestamp,\n isInitializing: true,\n },\n });\n\n const foundAdapter = selectAdapterByKey({\n adapterKey: restParams.adapter,\n adapter,\n });\n\n // Centralized error handler for this transaction flow\n const handleTxError = (e: unknown) => {\n const errorMessage = e instanceof Error ? e.message : String(e);\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.errorMessage = errorMessage;\n }\n }),\n );\n };\n\n if (!foundAdapter) {\n const error = new Error('No adapter found for this transaction.');\n handleTxError(error);\n throw error; // Re-throw to allow the caller to handle it.\n }\n\n try {\n const { walletType, walletAddress } = foundAdapter.getWalletInfo();\n\n // Step 2: Ensure the wallet is connected to the correct chain.\n await foundAdapter.checkChainForTx(desiredChainID);\n\n // Step 3: Execute the provided action (e.g., signing and sending the transaction).\n const txKeyFromAction = await actionFunction();\n\n // If `txKeyFromAction` is undefined, it indicates the user cancelled the action.\n if (!txKeyFromAction) {\n set({ initialTx: undefined });\n return;\n }\n\n // Step 4: Determine the final tracker and txKey from the action's result.\n const { tracker: updatedTracker, txKey: finalTxKey } = foundAdapter.checkTransactionsTracker(\n txKeyFromAction,\n walletType,\n );\n\n // Step 5: Construct the full transaction object for the pool.\n const newTx = {\n ...restParams,\n walletType,\n from: walletAddress,\n tracker: updatedTracker || defaultTracker,\n chainId: desiredChainID,\n localTimestamp,\n txKey: finalTxKey,\n // For EVM, the hash is often the preliminary key from the action.\n hash: updatedTracker === 'ethereum' ? (txKeyFromAction as `0x${string}`) : undefined,\n pending: false, // will be set to true by addTxToPool\n isTrackedModalOpen: params.withTrackedModal,\n };\n\n // Step 6: Add the transaction to the pool.\n get().addTxToPool(newTx as T);\n\n // Step 7: Update the initial state to link it with the newly created transaction.\n set((state) =>\n produce(state, (draft) => {\n if (draft.initialTx) {\n draft.initialTx.isInitializing = false;\n draft.initialTx.lastTxKey = finalTxKey;\n }\n }),\n );\n\n // Step 8: Initialize the background tracker for the transaction.\n const tx = get().transactionsPool[finalTxKey];\n await foundAdapter.checkAndInitializeTrackerInStore({ tx, onSuccessCallback, ...get() });\n } catch (e) {\n handleTxError(e);\n throw e; // Re-throw for external handling if needed.\n }\n },\n }),\n {\n ...options, // Merges user-provided persistence options.\n },\n ),\n );\n}\n","/**\n * @file This file defines the core data structures and TypeScript types for the Pulsar transaction tracking engine.\n * It specifies the framework-agnostic models for transactions, their lifecycle statuses, and the interfaces for\n * the Zustand-based store and chain-specific adapters.\n */\n\nimport { StoreApi } from 'zustand';\n\nimport { IInitializeTxTrackingStore } from './store/initializeTxTrackingStore';\n\n// =================================================================================================\n// 1. ZUSTAND UTILITY TYPES\n// =================================================================================================\n\n/**\n * A utility type for creating modular Zustand store slices, enabling composable state management.\n * @template T The state slice being defined.\n * @template S The full store state that includes the slice `T`.\n */\nexport type StoreSlice<T extends object, S extends object = T> = (\n set: StoreApi<S extends T ? S : S & T>['setState'],\n get: StoreApi<S extends T ? S : S & T>['getState'],\n) => T;\n\n// =================================================================================================\n// 2. ENUMS AND CORE TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Defines the supported blockchain adapters. Each adapter corresponds to a specific chain architecture.\n */\nexport enum TransactionAdapter {\n /** For Ethereum Virtual Machine (EVM) compatible chains like Ethereum, Polygon, etc. */\n EVM = 'evm',\n /** For the Solana blockchain. */\n SOLANA = 'solana',\n /** For the Starknet L2 network. */\n Starknet = 'starknet',\n}\n\n/**\n * Enum representing the different tracking strategies available for EVM transactions.\n * Each tracker corresponds to a specific method of monitoring a transaction's lifecycle.\n */\nexport enum TransactionTracker {\n /** For standard on-chain EVM transactions tracked by their hash. */\n Ethereum = 'ethereum',\n /** For multi-signature transactions managed and executed via a Safe contract. */\n Safe = 'safe',\n /** For meta-transactions relayed and executed by the Gelato Network. */\n Gelato = 'gelato',\n /** The tracker for monitoring standard Solana transaction signatures. */\n Solana = 'solana',\n}\n\n/**\n * Represents the terminal status of a transaction after it has been processed.\n */\nexport enum TransactionStatus {\n /** The transaction failed to execute due to an on-chain error or rejection. */\n Failed = 'Failed',\n /** The transaction was successfully mined and included in a block. */\n Success = 'Success',\n /** The transaction was replaced by another with the same nonce (e.g., a speed-up or cancel). */\n Replaced = 'Replaced',\n}\n\n/**\n * Defines the shape of the identifier for a Gelato transaction task.\n */\nexport type GelatoTxKey = {\n taskId: string;\n};\n\n/**\n * A union type representing the unique identifier returned by an `actionFunction`\n * after a transaction is submitted to the network or a relay service.\n *\n * This key is crucial for the EVM adapter to determine which tracker should\n * monitor the transaction.\n *\n * It can be one of the following:\n * - A standard `0x...` transaction hash (`Hex`).\n * - A structured object from a relay service like Gelato (`GelatoTxKey`).\n */\nexport type ActionTxKey = `0x${string}` | GelatoTxKey | string;\n\nexport type OnSuccessCallback<T> = {\n /** Callback to execute when the transaction is successfully submitted. */\n onSuccessCallback?: (tx: T) => Promise<void> | void;\n};\n\n/**\n * The fundamental structure for any transaction being tracked by Pulsar.\n * This serves as the base upon which chain-specific transaction types are built.\n */\nexport type BaseTransaction = {\n /** The chain identifier (e.g., 1 for Ethereum Mainnet, 'SN_MAIN' for Starknet). */\n chainId: number | string;\n /**\n * User-facing description. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single description for all states\n * description: 'Swap 1 ETH for 1,500 USDC'\n * // Specific descriptions for each state in order: [pending, success, error, replaced]\n * description: ['Swapping...', 'Swapped Successfully', 'Swap Failed', 'Swap Replaced']\n */\n description?: string | [string, string, string, string];\n /** The error message if the transaction failed. */\n errorMessage?: string;\n /** The on-chain timestamp (in seconds) when the transaction was finalized. */\n finishedTimestamp?: number;\n /** The sender's wallet address. */\n from: string;\n /** A flag indicating if the transaction is in a failed state. */\n isError?: boolean;\n /** A UI flag to control the visibility of a detailed tracking modal for this transaction. */\n isTrackedModalOpen?: boolean;\n /** The local timestamp (in seconds) when the transaction was initiated by the user. */\n localTimestamp: number;\n /** Any additional, custom data associated with the transaction. */\n payload?: object;\n /** A flag indicating if the transaction is still awaiting on-chain confirmation. */\n pending: boolean;\n /** The final on-chain status of the transaction. */\n status?: TransactionStatus;\n /**\n * User-facing title. Can be a single string for all states, or a tuple for specific states.\n * @example\n * // A single title for all states\n * title: 'ETH/USDC Swap'\n * // Specific titles for each state in order: [pending, success, error, replaced]\n * title: ['Processing Swap', 'Swap Complete', 'Swap Error', 'Swap Replaced']\n */\n title?: string | [string, string, string, string];\n /** The specific tracker responsible for monitoring this transaction's status. */\n tracker: TransactionTracker;\n /** The unique identifier for the transaction (e.g., EVM hash, Gelato task ID). */\n txKey: string;\n /** The application-specific type or category of the transaction (e.g., 'SWAP', 'APPROVE'). */\n type: string;\n /** The type of wallet used to sign the transaction (e.g., 'injected', 'walletConnect'). */\n walletType: string;\n};\n\n// =================================================================================================\n// 3. CHAIN-SPECIFIC TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents an EVM-specific transaction, extending the base properties with EVM fields.\n */\nexport type EvmTransaction = BaseTransaction & {\n adapter: TransactionAdapter.EVM;\n /** The on-chain transaction hash, available after submission. */\n hash?: `0x${string}`;\n /** The data payload for the transaction, typically for smart contract interactions. */\n input?: `0x${string}`;\n /** The maximum fee per gas for an EIP-1559 transaction (in wei). */\n maxFeePerGas?: string;\n /** The maximum priority fee per gas for an EIP-1559 transaction (in wei). */\n maxPriorityFeePerGas?: string;\n /** The transaction nonce, a sequential number for the sender's account. */\n nonce?: number;\n /** The hash of a transaction that this one replaced. */\n replacedTxHash?: `0x${string}`;\n /** The recipient's address or contract address. */\n to?: `0x${string}`;\n /** The amount of native currency (in wei) being sent. */\n value?: string;\n};\n\n/**\n * Represents a Solana-specific transaction, extending the base properties.\n */\nexport type SolanaTransaction = BaseTransaction & {\n adapter: TransactionAdapter.SOLANA;\n /** The transaction fee in lamports. */\n fee?: number;\n /** The instructions included in the transaction. */\n instructions?: unknown[];\n /** The recent blockhash used for the transaction. */\n recentBlockhash?: string;\n /** The slot in which the transaction was processed. */\n slot?: number;\n /** The number of confirmations received. `string` when tx successed. `null` if the transaction is pending or unconfirmed. */\n confirmations?: number | string | null;\n /** The RPC URL used to submit and track this transaction. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a Starknet-specific transaction, extending the base properties.\n */\nexport type StarknetTransaction = BaseTransaction & {\n adapter: TransactionAdapter.Starknet;\n /** The actual fee paid for the transaction. */\n actualFee?: { amount: string; unit: string };\n /** The address of the contract being interacted with. */\n contractAddress?: string;\n};\n\n/** A union type representing any possible transaction structure that Pulsar can handle. */\nexport type Transaction = EvmTransaction | SolanaTransaction | StarknetTransaction;\n\n// =================================================================================================\n// 4. INITIAL TRANSACTION TYPES\n// =================================================================================================\n\n/**\n * Represents the parameters required to initiate a new transaction tracking flow.\n */\nexport type InitialTransactionParams = {\n adapter: TransactionAdapter;\n /** The function that executes the on-chain action (e.g., sending a transaction) and returns a preliminary identifier like a hash. */\n actionFunction: (...args: any[]) => Promise<ActionTxKey | undefined>;\n /** A user-facing description for the transaction. Supports state-specific descriptions. */\n description?: string | [string, string, string, string];\n /** The target chain ID for the transaction. */\n desiredChainID: number | string;\n /** Any custom data to associate with the transaction. */\n payload?: object;\n /** A user-facing title for the transaction. Supports state-specific titles. */\n title?: string | [string, string, string, string];\n /** The application-specific type of the transaction. */\n type: string;\n /** If true, the detailed tracking modal will open automatically upon initiation. */\n withTrackedModal?: boolean;\n /** The RPC URL to use for the transaction. Required for Solana transactions. */\n rpcUrl?: string;\n};\n\n/**\n * Represents a transaction in its temporary, pre-submission state.\n * This is used for UI feedback while the transaction is being signed and sent.\n */\nexport type InitialTransaction = InitialTransactionParams & {\n /** An error message if the initialization fails (e.g., user rejects signature). */\n errorMessage?: string;\n /** A flag indicating if the transaction is being processed (e.g., waiting for signature). */\n isInitializing: boolean;\n /** The `txKey` of the on-chain transaction that this action produced, used for linking the states. */\n lastTxKey?: string;\n /** The local timestamp when the user initiated the action. */\n localTimestamp: number;\n};\n\n// =================================================================================================\n// 5. ADAPTER AND STORE INTERFACES\n// =================================================================================================\n\n/**\n * Defines the interface for a transaction adapter, which provides chain-specific logic and utilities.\n * @template T The specific transaction type, extending `Transaction`.\n */\nexport type TxAdapter<T extends Transaction> = {\n /** The unique key identifying this adapter. */\n key: TransactionAdapter;\n /** Returns information about the currently connected wallet. */\n getWalletInfo: () => {\n walletAddress: string;\n walletType: string;\n };\n /** Ensures the connected wallet is on the correct network for the transaction. Throws an error if the chain is mismatched. */\n checkChainForTx: (chainId: string | number) => Promise<void>;\n /** Determines the appropriate tracker and final `txKey` from the result of an action. */\n checkTransactionsTracker: (\n actionTxKey: ActionTxKey,\n walletType: string,\n ) => { txKey: string; tracker: TransactionTracker };\n /** Selects and initializes the correct background tracker for a given transaction. */\n checkAndInitializeTrackerInStore: (\n params: { tx: T } & OnSuccessCallback<T> &\n Pick<ITxTrackingStore<T>, 'updateTxParams' | 'removeTxFromPool' | 'transactionsPool'>,\n ) => Promise<void>;\n /** Returns the base URL for the blockchain explorer for the current network. */\n getExplorerUrl: (url?: string) => string | undefined;\n /** Optional: Fetches a name from a chain-specific name service (e.g., ENS). */\n getName?: (address: string) => Promise<string | null>;\n /** Optional: Fetches an avatar URL from a chain-specific name service. */\n getAvatar?: (name: string) => Promise<string | null>;\n /** Optional: Logic to cancel a pending EVM transaction. */\n cancelTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to speed up a pending EVM transaction. */\n speedUpTxAction?: (tx: T) => Promise<string>;\n /** Optional: Logic to retry a failed transaction. */\n retryTxAction?: (\n params: {\n txKey: string;\n tx: InitialTransactionParams;\n onClose: (txKey?: string) => void;\n } & Partial<Pick<ITxTrackingStore<T>, 'handleTransaction'>>,\n ) => Promise<void>;\n /**\n * Optional: Constructs a full explorer URL for a specific transaction.\n * May require the full transaction pool to resolve details for replaced transactions.\n */\n getExplorerTxUrl?: (tx: T) => string;\n};\n\n/**\n * The complete interface for the Pulsar transaction tracking store.\n * @template T The transaction type.\n */\nexport type ITxTrackingStore<T extends Transaction> = IInitializeTxTrackingStore<T> & {\n /**\n * The primary method for initiating and tracking a new transaction from start to finish.\n * It manages UI state, executes the on-chain action, and initiates background tracking.\n * @param params The parameters for handling the transaction.\n */\n handleTransaction: (\n params: {\n /** The async function to execute (e.g., a smart contract write call). Must return a unique key or undefined. */\n actionFunction: () => Promise<ActionTxKey | undefined>;\n /** The metadata for the transaction. */\n params: Omit<InitialTransactionParams, 'actionFunction'>;\n /** The default tracker to use if it cannot be determined automatically. */\n defaultTracker?: TransactionTracker;\n } & OnSuccessCallback<T>,\n ) => Promise<void>;\n\n /**\n * Initializes trackers for all pending transactions in the pool.\n * This is essential for resuming tracking after a page reload.\n */\n initializeTransactionsPool: () => Promise<void>;\n};\n","/**\n * @file This file provides a utility for creating a type-safe, bounded Zustand hook from a vanilla store.\n * This pattern is recommended by the official Zustand documentation to ensure full type\n * safety when integrating vanilla stores with React.\n *\n * @see https://docs.pmnd.rs/zustand/guides/typescript#bounded-usestore-hook-for-vanilla-stores\n */\n\nimport { StoreApi, useStore } from 'zustand';\n\n/**\n * A utility type that infers the state shape from a Zustand `StoreApi`.\n * It extracts the return type of the `getState` method.\n * @template S - The type of the Zustand store (`StoreApi`).\n */\ntype ExtractState<S> = S extends { getState: () => infer T } ? T : never;\n\n/**\n * Creates a bounded `useStore` hook from a vanilla Zustand store.\n *\n * This function takes a vanilla Zustand store instance and returns a React hook\n * that is pre-bound to that store. This approach provides a cleaner API and\n * enhances type inference, eliminating the need to pass the store instance\n * on every use.\n *\n * The returned hook supports two signatures:\n * 1. `useBoundedStore()`: Selects the entire state.\n * 2. `useBoundedStore(selector)`: Selects a slice of the state, returning only what the selector function specifies.\n *\n * @template S - The type of the Zustand store (`StoreApi`).\n * @param {S} store - The vanilla Zustand store instance to bind the hook to.\n * @returns {function} A fully typed React hook for accessing the store's state.\n */\nexport const createBoundedUseStore = ((store) => (selector) => useStore(store, selector)) as <\n S extends StoreApi<unknown>,\n>(\n store: S,\n) => {\n // This implementation uses a Immediately Invoked Function Expression (IIFE)\n // trick combined with casting to achieve the desired overloaded function signature in a concise way.\n (): ExtractState<S>;\n <T>(selector: (state: ExtractState<S>) => T): T;\n};\n","/**\n * @file This file provides a generic utility for creating a polling mechanism to track\n * asynchronous tasks, such as API-based transaction status checks (e.g., for Gelato or Safe).\n */\n\nimport { Transaction } from '../types';\n\n/**\n * Defines the parameters for the fetcher function used within the polling tracker.\n * The fetcher is the core logic that performs the actual API call.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object being tracked.\n */\ntype PollingFetcherParams<R, T> = {\n /** The transaction object being tracked. */\n tx: T;\n /** A callback to stop the polling mechanism, typically called on success or terminal failure. */\n stopPolling: (options?: { withoutRemoving?: boolean }) => void;\n /** Callback to be invoked when the fetcher determines the transaction has succeeded. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the fetcher determines the transaction has failed. */\n onFailure: (response?: R) => void;\n /** Optional callback for each successful poll, useful for updating UI with intermediate states. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced (e.g., speed-up). */\n onReplaced?: (response: R) => void;\n};\n\n/**\n * Defines the configuration object for the `initializePollingTracker` function.\n * @template R The expected type of the successful API response.\n * @template T The type of the transaction object.\n */\nexport type PollingTrackerConfig<R, T extends Transaction> = {\n /** The transaction object to be tracked. It must include `txKey` and `pending` status. */\n tx: T & Pick<Transaction, 'txKey' | 'pending'>;\n /** The function that performs the data fetching (e.g., an API call) on each interval. */\n fetcher: (params: PollingFetcherParams<R, T>) => Promise<void>;\n /** Callback to be invoked when the transaction successfully completes. */\n onSuccess: (response: R) => void;\n /** Callback to be invoked when the transaction fails. */\n onFailure: (response?: R) => void;\n /** Optional callback executed once when the tracker is initialized. */\n onInitialize?: () => void;\n /** Optional callback for each successful poll. */\n onIntervalTick?: (response: R) => void;\n /** Optional callback for when a transaction is replaced. */\n onReplaced?: (response: R) => void;\n /** Optional function to remove the transaction from the main pool, typically after polling stops. */\n removeTxFromPool?: (txKey: string) => void;\n /** The interval (in milliseconds) between polling attempts. Defaults to 5000ms. */\n pollingInterval?: number;\n /** The number of consecutive failed fetches before stopping the tracker. Defaults to 10. */\n maxRetries?: number;\n};\n\nconst DEFAULT_POLLING_INTERVAL = 5000;\nconst DEFAULT_MAX_RETRIES = 10;\n\n/**\n * Initializes a generic polling tracker that repeatedly calls a fetcher function\n * to monitor the status of an asynchronous task.\n *\n * This function handles the lifecycle of polling, including starting, stopping,\n * and automatic termination after a certain number of failed attempts.\n *\n * @template R The expected type of the API response.\n * @template T The type of the transaction object.\n * @param {PollingTrackerConfig<R, T>} config - The configuration for the tracker.\n */\nexport function initializePollingTracker<R, T extends Transaction>(config: PollingTrackerConfig<R, T>): void {\n const {\n tx,\n fetcher,\n onInitialize,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n removeTxFromPool,\n pollingInterval = DEFAULT_POLLING_INTERVAL,\n maxRetries = DEFAULT_MAX_RETRIES,\n } = config;\n\n // 1. Early exit if the transaction is no longer pending\n if (!tx.pending) {\n return;\n }\n\n // Execute the initialization callback if provided\n onInitialize?.();\n\n let retriesLeft = maxRetries;\n let isPolling = true;\n\n /**\n * Stops the polling interval and optionally removes the transaction from the pool.\n * @param {object} [options] - Options for stopping the tracker.\n * @param {boolean} [options.withoutRemoving=false] - If true, the tx will not be removed from the pool.\n */\n const stopPolling = (options?: { withoutRemoving?: boolean }) => {\n if (!isPolling) return;\n isPolling = false;\n // The interval is cleared in the finally block of the polling loop\n if (removeTxFromPool && !options?.withoutRemoving) {\n removeTxFromPool(tx.txKey);\n }\n };\n\n const pollingLoop = async () => {\n while (isPolling && retriesLeft > 0) {\n try {\n await new Promise((resolve) => setTimeout(resolve, pollingInterval));\n if (!isPolling) break;\n\n // The fetcher's responsibility is to call onSuccess, onFailure, etc., which in turn call stopPolling.\n await fetcher({\n tx,\n stopPolling,\n onSuccess,\n onFailure,\n onIntervalTick,\n onReplaced,\n });\n } catch (error) {\n console.error(`Polling fetcher for txKey ${tx.txKey} threw an error:`, error);\n retriesLeft--;\n }\n }\n\n if (retriesLeft <= 0) {\n console.warn(`Polling for txKey ${tx.txKey} stopped after reaching the maximum number of retries.`);\n onFailure();\n stopPolling();\n }\n };\n\n // Start the asynchronous polling loop\n pollingLoop();\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuwaio/pulsar-core",
3
- "version": "1.0.0-fix-callbacks-alpha.3.e3b3acb",
3
+ "version": "1.0.0-fix-callbacks-alpha.4.795d3ad",
4
4
  "private": false,
5
5
  "author": "Oleksandr Tkach",
6
6
  "license": "Apache-2.0",