@tuwaio/pulsar-core 0.6.0 → 0.6.2

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/README.md CHANGED
@@ -116,6 +116,135 @@ The package also provides a set of selector functions to help you efficiently qu
116
116
 
117
117
  ---
118
118
 
119
+ ### `createTxInMemoryStore({ ... })`
120
+
121
+ While `createPulsarStore` is the primary entry point for tracking _active_ transactions, `createTxInMemoryStore` provides an in-memory transaction store with synchronized local and remote sources. It is designed to keep a local transaction pool in sync with remote history, preserve terminal transaction states, and support paginated history loading.
122
+
123
+ #### **Configuration Example**
124
+
125
+ ```ts
126
+ // src/app/actions - next js app routes example of server actions
127
+ 'use server';
128
+
129
+ import { Quasar, Transaction } from '@tuwaio/quasar-sdk';
130
+
131
+ const quasar = new Quasar({
132
+ secretKey: process.env.QUASAR_SDK_SK ?? '',
133
+ });
134
+
135
+ // --- Server Action for syncCreate ---
136
+ export async function syncTransaction(tx: Transaction) {
137
+ try {
138
+ console.log('Syncing tx to Quasar...', tx.txKey);
139
+
140
+ await quasar.pulsar.syncCreate(tx, RP_NAME);
141
+
142
+ return { success: true };
143
+ } catch (error) {
144
+ console.error('Sync failed', error);
145
+ throw error;
146
+ }
147
+ }
148
+
149
+ // --- Server Action for getHistory ---
150
+ export async function getHistory(params?: {
151
+ walletAddress: string;
152
+ page?: number;
153
+ limit?: number;
154
+ chainId?: string;
155
+ status?: string;
156
+ txKey?: string;
157
+ appName?: string;
158
+ }) {
159
+ try {
160
+ const history = await quasar.pulsar.getHistory({
161
+ ...params,
162
+ });
163
+
164
+ return history;
165
+ } catch (error) {
166
+ console.error('Get history failed', error);
167
+ throw error;
168
+ }
169
+ }
170
+ ```
171
+
172
+ ```ts
173
+ // src/store/pulsarStoreHook.ts - pulsar store hook example
174
+
175
+ 'use client';
176
+
177
+ import { createBoundedUseStore, createPulsarStore, createTxInMemoryStore } from '@tuwaio/pulsar-core';
178
+ import { pulsarEvmAdapter } from '@tuwaio/pulsar-evm';
179
+
180
+ import { appChains, config } from '@/configs/wagmiConfig';
181
+ import { getHistory, syncTransaction } from '@/app/actions';
182
+
183
+ const storageName = 'transactions-tracking-storage-with-bd';
184
+
185
+ export enum TxType {
186
+ example = 'example',
187
+ }
188
+
189
+ type ExampleTx = Transaction & {
190
+ type: TxType.example;
191
+ payload: {
192
+ value: number;
193
+ };
194
+ };
195
+
196
+ export type TransactionUnion = ExampleTx;
197
+
198
+ const initialStore = createPulsarStore<TransactionUnion>({
199
+ name: storageName,
200
+ adapter: [
201
+ pulsarEvmAdapter(config, appChains),
202
+ ],
203
+ onRemoteCreate: async (tx) => {
204
+ await syncTransaction(tx);
205
+ },
206
+ });
207
+
208
+ export const usePulsarStore = createBoundedUseStore(initialStore);
209
+
210
+ const pulsarInMemoryStore = createTxInMemoryStore<TransactionUnion>({
211
+ localTransactionsPool: initialStore.getState().transactionsPool,
212
+
213
+ getHistory: async ({ page, walletAddress }) => {
214
+ try {
215
+ const history = await getHistory({
216
+ walletAddress,
217
+ page,
218
+ limit: 10,
219
+ appName: 'Example App',
220
+ });
221
+
222
+ if (!history) {
223
+ return null;
224
+ }
225
+
226
+ return {
227
+ ...history,
228
+ docs: history.docs as TransactionUnion[],
229
+ };
230
+ } catch (error) {
231
+ console.error('[PulsarHook] Failed to fetch history:', error);
232
+ throw error;
233
+ }
234
+ },
235
+
236
+ onHistoryFetched: async (remoteTxs) => {
237
+ await initialStore.getState().injectExternalPendingTxs(remoteTxs);
238
+ },
239
+ });
240
+
241
+ initialStore.subscribe((state) => pulsarInMemoryStore.getState().syncWithLocalPool(state.transactionsPool));
242
+
243
+ export const usePulsarInMemoryStore = createBoundedUseStore(pulsarInMemoryStore);
244
+ ```
245
+
246
+ ---
247
+
119
248
  ## 🛠️ Advanced Usage: `initializePollingTracker`
120
249
 
121
250
  For custom tracking requirements (like server-side tracking or non-standard APIs), you can use the low-level `initializePollingTracker` utility. This is the same engine used internally by Pulsar adapters for Gelato, Safe, and Solana.
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var immer=require('immer'),vanilla=require('zustand/vanilla'),orbitCore=require('@tuwaio/orbit-core'),D=require('dayjs'),middleware=require('zustand/middleware'),zustand=require('zustand');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var D__default=/*#__PURE__*/_interopDefault(D);function E({maxTransactions:o,onRemoteCreate:t}){return (s,p)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:a=>{let e={...a,pending:true};s(n=>immer.produce(n,r=>{if(r.lastAddedTxKey=a.txKey,a.txKey){if(Object.keys(r.transactionsPool).length>=o){let l=Object.values(r.transactionsPool).sort((T,c)=>T.localTimestamp-c.localTimestamp);if(l.length>0){let T=l[0];delete r.transactionsPool[T.txKey];}}r.transactionsPool[a.txKey]=e;}})),t&&t(e).catch(n=>console.error("[Pulsar Sync] Create failed:",n));},updateTxParams:(a,e)=>{s(n=>immer.produce(n,r=>{let i=r.transactionsPool[a];i&&Object.assign(i,e);}));},removeTxFromPool:a=>{s(e=>immer.produce(e,n=>{delete n.transactionsPool[a];}));},closeTxTrackedModal:a=>{s(e=>immer.produce(e,n=>{if(a&&n.transactionsPool[a]){let r=n.transactionsPool[a];n.transactionsPool[a]={...r,isTrackedModalOpen:false};}n.initialTx=void 0;}));},getLastTxKey:()=>p().lastAddedTxKey})}var F=o=>Object.values(o).sort((t,s)=>Number(t.localTimestamp)-Number(s.localTimestamp)),Y=o=>F(o).filter(t=>t.pending),Z=(o,t)=>o[t],B=(o,t)=>F(o).filter(s=>s.from.toLowerCase()===t.toLowerCase()),ee=(o,t)=>B(o,t).filter(s=>s.pending);var N=(a=>(a.Ethereum="ethereum",a.Safe="safe",a.Gelato="gelato",a.Solana="solana",a))(N||{}),A=(p=>(p.Failed="Failed",p.Success="Success",p.Replaced="Replaced",p))(A||{});var $=o=>o==="Success"||o==="Replaced",M=(o,t)=>{let s=o[t.txKey];return $(s?.status)?false:(o[t.txKey]=t,true)};function Te({localTransactionsPool:o,getHistory:t,onHistoryFetched:s}){immer.setAutoFreeze(false);let p=e=>({isLoading:e,isError:false}),a=e=>e?(s&&queueMicrotask(()=>s(e.docs)),n=>immer.produce(n,r=>{let i=r.transactionsPool;for(let l of e.docs)M(i,l);r.currentPage=e.page,r.hasMore=e.hasNextPage,r.isLoading=false;})):n=>n;return vanilla.createStore()((e,n)=>({transactionsPool:o,isLoading:false,isError:false,hasMore:false,currentPage:1,syncWithLocalPool:r=>{e(i=>immer.produce(i,l=>{let T=l.transactionsPool;for(let c of Object.values(r))M(T,c);}));},fetchInitial:async r=>{if(!(!t||!r)){e(p(true));try{let i=await t({page:1,walletAddress:r});e(a(i));}catch(i){console.error("[Pulsar] Failed to fetch initial transaction history:",i),e({isLoading:false,isError:true});}}},fetchNextPage:async r=>{let{hasMore:i,isLoading:l,currentPage:T}=n();if(!(!t||!i||l||!r)){e(p(true));try{let c=T+1,m=await t({page:c,walletAddress:r});e(a(m));}catch(c){console.error(`[Pulsar] Failed to fetch transaction history page ${T+1}:`,c),e({isLoading:false,isError:true});}}}}))}function Ae({adapter:o,maxTransactions:t=50,onRemoteCreate:s,gelatoApiKey:p,...a}){return vanilla.createStore()(middleware.persist((e,n)=>({...E({maxTransactions:t,onRemoteCreate:s})(e,n),getAdapter:()=>o,initializeTransactionsPool:async()=>{let r=Object.values(n().transactionsPool).filter(i=>i.pending);await Promise.all(r.map(i=>orbitCore.selectAdapterByKey({adapterKey:i.adapter,adapter:o})?.checkAndInitializeTrackerInStore({tx:i,gelatoApiKey:p,...n()})));},injectExternalPendingTxs:async r=>{let l=n().getAdapter(),T=[];e(c=>immer.produce(c,m=>{let g=m.transactionsPool;r.forEach(d=>{let x=g[d.txKey];d.pending&&!x&&(g[d.txKey]=d,T.push(d));let S=d.status==="Success"||d.status==="Failed"||d.status==="Replaced";x?.pending&&S&&(x.status=d.status,x.pending=false,d.txKey&&(x.txKey=d.txKey),d.finishedTimestamp&&(x.finishedTimestamp=d.finishedTimestamp));});})),T.length>0&&await Promise.all(T.map(c=>orbitCore.selectAdapterByKey({adapterKey:c.adapter,adapter:l})?.checkAndInitializeTrackerInStore({tx:c,gelatoApiKey:p,...n()})));},executeTxAction:async({defaultTracker:r,actionFunction:i,params:l,...T})=>{let{desiredChainID:c,tracker:m,...g}=l,{onSuccess:d,onError:x,onReplaced:S}=T,K=D__default.default().unix();e({initialTx:{...l,actionFunction:i,localTimestamp:K,isInitializing:true}});let f=orbitCore.selectAdapterByKey({adapterKey:g.adapter,adapter:o}),w=u=>{e(k=>immer.produce(k,P=>{P.initialTx&&(P.initialTx.isInitializing=false,P.initialTx.error=orbitCore.normalizeError(u));}));};if(!f){let u=new Error("No adapter found for this transaction.");throw w(u),u}try{let{connectorType:u,walletAddress:k}=f.getConnectorInfo();await f.checkChainForTx(c);let P=await i();if(!P){e({initialTx:void 0});return}let{tracker:R,txKey:h}=f.checkTransactionsTracker({actionTxKey:P,connectorType:u,tracker:m,gelatoApiKey:p}),O={...g,connectorType:u,from:k,tracker:R||r,chainId:orbitCore.setChainId(c),localTimestamp:K,txKey:h,hash:R==="ethereum"?P:void 0,pending:!1,isTrackedModalOpen:l.withTrackedModal};n().addTxToPool(O),e(L=>immer.produce(L,I=>{I.initialTx&&(I.initialTx.isInitializing=!1,I.initialTx.lastTxKey=h);}));let z=n().transactionsPool[h];await f.checkAndInitializeTrackerInStore({tx:z,onSuccess:d,onError:x,onReplaced:S,gelatoApiKey:p,...n()});}catch(u){throw w(u),u}}}),{...a}))}var we=(o=>t=>zustand.useStore(o,t));var V=5e3,X=10;function Ee(o){let{tx:t,fetcher:s,onInitialize:p,onSuccess:a,onFailure:e,onIntervalTick:n,onReplaced:r,removeTxFromPool:i,pollingInterval:l=V,maxRetries:T=X}=o;if(!t.pending)return;p?.();let c=T,m=true,g=x=>{m&&(m=false,i&&!x?.withoutRemoving&&i(t.txKey));};(async()=>{for(;m&&c>0;)try{if(await new Promise(x=>setTimeout(x,l)),!m)break;await s({tx:t,stopPolling:g,onSuccess:a,onFailure:e,onIntervalTick:n,onReplaced:r});}catch(x){console.error(`Polling fetcher for txKey ${t.txKey} threw an error:`,x),c--;}c<=0&&(console.warn(`Polling for txKey ${t.txKey} stopped after reaching the maximum number of retries.`),e(),g());})();}exports.TransactionStatus=A;exports.TransactionTracker=N;exports.createBoundedUseStore=we;exports.createPulsarStore=Ae;exports.createTxInMemoryStore=Te;exports.initializePollingTracker=Ee;exports.initializeTxTrackingStore=E;exports.selectAllTransactions=F;exports.selectAllTransactionsByActiveWallet=B;exports.selectPendingTransactions=Y;exports.selectPendingTransactionsByActiveWallet=ee;exports.selectTxByKey=Z;
1
+ 'use strict';var immer=require('immer'),vanilla=require('zustand/vanilla'),orbitCore=require('@tuwaio/orbit-core'),D=require('dayjs'),middleware=require('zustand/middleware'),zustand=require('zustand');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var D__default=/*#__PURE__*/_interopDefault(D);function E({maxTransactions:n,onRemoteCreate:e}){return (i,l)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:a=>{let t={...a,pending:true};i(o=>immer.produce(o,r=>{if(r.lastAddedTxKey=a.txKey,a.txKey){if(Object.keys(r.transactionsPool).length>=n){let T=Object.values(r.transactionsPool).sort((d,c)=>d.localTimestamp-c.localTimestamp);if(T.length>0){let d=T[0];delete r.transactionsPool[d.txKey];}}r.transactionsPool[a.txKey]=t;}})),e&&e(t).catch(o=>console.error("[Pulsar Sync] Create failed:",o));},updateTxParams:(a,t)=>{i(o=>immer.produce(o,r=>{let s=r.transactionsPool[a];s&&Object.assign(s,t);}));},removeTxFromPool:a=>{i(t=>immer.produce(t,o=>{delete o.transactionsPool[a];}));},closeTxTrackedModal:a=>{i(t=>immer.produce(t,o=>{if(a&&o.transactionsPool[a]){let r=o.transactionsPool[a];o.transactionsPool[a]={...r,isTrackedModalOpen:false};}o.initialTx=void 0;}));},getLastTxKey:()=>l().lastAddedTxKey})}var F=n=>Object.values(n).sort((e,i)=>Number(e.localTimestamp)-Number(i.localTimestamp)),Y=n=>F(n).filter(e=>e.pending),Z=(n,e)=>n[e],N=(n,e)=>F(n).filter(i=>i.from.toLowerCase()===e.toLowerCase()),ee=(n,e)=>N(n,e).filter(i=>i.pending);var j=(a=>(a.Ethereum="ethereum",a.Safe="safe",a.Gelato="gelato",a.Solana="solana",a))(j||{}),A=(l=>(l.Failed="Failed",l.Success="Success",l.Replaced="Replaced",l))(A||{});var M=n=>n==="Success"||n==="Replaced",O=(n,e)=>{let i=n[e.txKey];if(i){if(M(i.status))return false;if(i.pending){if(M(e.status))return n[e.txKey]={...i,...e},true;let l=typeof e.confirmations=="number"?e.confirmations:0,a=typeof i.confirmations=="number"?i.confirmations:0;return l>a?(n[e.txKey]={...i,...e},true):false}}return n[e.txKey]=e,true};function Te({localTransactionsPool:n,getHistory:e,onHistoryFetched:i}){immer.setAutoFreeze(false);let l=t=>({isLoading:t,isError:false}),a=t=>t?(i&&queueMicrotask(()=>i(t.docs)),o=>immer.produce(o,r=>{let s=r.transactionsPool;for(let T of t.docs)O(s,T);r.currentPage=t.page,r.hasMore=t.hasNextPage,r.isLoading=false;})):o=>o;return vanilla.createStore()((t,o)=>({transactionsPool:n,isLoading:false,isError:false,hasMore:false,currentPage:1,syncWithLocalPool:r=>{t(s=>immer.produce(s,T=>{let d=T.transactionsPool;for(let c of Object.values(r))O(d,c);}));},fetchInitial:async r=>{if(!(!e||!r)){t(l(true));try{let s=await e({page:1,walletAddress:r});t(a(s));}catch(s){console.error("[Pulsar] Failed to fetch initial transaction history:",s),t({isLoading:false,isError:true});}}},fetchNextPage:async r=>{let{hasMore:s,isLoading:T,currentPage:d}=o();if(!(!e||!s||T||!r)){t(l(true));try{let c=d+1,m=await e({page:c,walletAddress:r});t(a(m));}catch(c){console.error(`[Pulsar] Failed to fetch transaction history page ${d+1}:`,c),t({isLoading:false,isError:true});}}}}))}function Ae({adapter:n,maxTransactions:e=50,onRemoteCreate:i,gelatoApiKey:l,...a}){return vanilla.createStore()(middleware.persist((t,o)=>({...E({maxTransactions:e,onRemoteCreate:i})(t,o),getAdapter:()=>n,initializeTransactionsPool:async()=>{let r=Object.values(o().transactionsPool).filter(s=>s.pending);await Promise.all(r.map(s=>orbitCore.selectAdapterByKey({adapterKey:s.adapter,adapter:n})?.checkAndInitializeTrackerInStore({tx:s,gelatoApiKey:l,...o()})));},injectExternalPendingTxs:async r=>{let T=o().getAdapter(),d=[];t(c=>immer.produce(c,m=>{let g=m.transactionsPool;r.forEach(p=>{let x=g[p.txKey];p.pending&&!x&&(g[p.txKey]=p,d.push(p));let S=p.status==="Success"||p.status==="Failed"||p.status==="Replaced";x?.pending&&S&&(x.status=p.status,x.pending=false,p.txKey&&(x.txKey=p.txKey),p.finishedTimestamp&&(x.finishedTimestamp=p.finishedTimestamp));});})),d.length>0&&await Promise.all(d.map(c=>orbitCore.selectAdapterByKey({adapterKey:c.adapter,adapter:T})?.checkAndInitializeTrackerInStore({tx:c,gelatoApiKey:l,...o()})));},executeTxAction:async({defaultTracker:r,actionFunction:s,params:T,...d})=>{let{desiredChainID:c,tracker:m,...g}=T,{onSuccess:p,onError:x,onReplaced:S}=d,K=D__default.default().unix();t({initialTx:{...T,actionFunction:s,localTimestamp:K,isInitializing:true}});let f=orbitCore.selectAdapterByKey({adapterKey:g.adapter,adapter:n}),w=u=>{t(k=>immer.produce(k,P=>{P.initialTx&&(P.initialTx.isInitializing=false,P.initialTx.error=orbitCore.normalizeError(u));}));};if(!f){let u=new Error("No adapter found for this transaction.");throw w(u),u}try{let{connectorType:u,walletAddress:k}=f.getConnectorInfo();await f.checkChainForTx(c);let P=await s();if(!P){t({initialTx:void 0});return}let{tracker:R,txKey:h}=f.checkTransactionsTracker({actionTxKey:P,connectorType:u,tracker:m,gelatoApiKey:l}),z={...g,connectorType:u,from:k,tracker:R||r,chainId:orbitCore.setChainId(c),localTimestamp:K,txKey:h,hash:R==="ethereum"?P:void 0,pending:!1,isTrackedModalOpen:T.withTrackedModal};o().addTxToPool(z),t(B=>immer.produce(B,I=>{I.initialTx&&(I.initialTx.isInitializing=!1,I.initialTx.lastTxKey=h);}));let L=o().transactionsPool[h];await f.checkAndInitializeTrackerInStore({tx:L,onSuccess:p,onError:x,onReplaced:S,gelatoApiKey:l,...o()});}catch(u){throw w(u),u}}}),{...a}))}var we=(n=>e=>zustand.useStore(n,e));var V=5e3,X=10;function Ee(n){let{tx:e,fetcher:i,onInitialize:l,onSuccess:a,onFailure:t,onIntervalTick:o,onReplaced:r,removeTxFromPool:s,pollingInterval:T=V,maxRetries:d=X}=n;if(!e.pending)return;l?.();let c=d,m=true,g=x=>{m&&(m=false,s&&!x?.withoutRemoving&&s(e.txKey));};(async()=>{for(;m&&c>0;)try{if(await new Promise(x=>setTimeout(x,T)),!m)break;await i({tx:e,stopPolling:g,onSuccess:a,onFailure:t,onIntervalTick:o,onReplaced:r});}catch(x){console.error(`Polling fetcher for txKey ${e.txKey} threw an error:`,x),c--;}c<=0&&(console.warn(`Polling for txKey ${e.txKey} stopped after reaching the maximum number of retries.`),t(),g());})();}exports.TransactionStatus=A;exports.TransactionTracker=j;exports.createBoundedUseStore=we;exports.createPulsarStore=Ae;exports.createTxInMemoryStore=Te;exports.initializePollingTracker=Ee;exports.initializeTxTrackingStore=E;exports.selectAllTransactions=F;exports.selectAllTransactionsByActiveWallet=N;exports.selectPendingTransactions=Y;exports.selectPendingTransactionsByActiveWallet=ee;exports.selectTxByKey=Z;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {produce,setAutoFreeze}from'immer';import {createStore}from'zustand/vanilla';import {selectAdapterByKey,setChainId,normalizeError}from'@tuwaio/orbit-core';import D from'dayjs';import {persist}from'zustand/middleware';import {useStore}from'zustand';function E({maxTransactions:o,onRemoteCreate:t}){return (s,p)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:a=>{let e={...a,pending:true};s(n=>produce(n,r=>{if(r.lastAddedTxKey=a.txKey,a.txKey){if(Object.keys(r.transactionsPool).length>=o){let l=Object.values(r.transactionsPool).sort((T,c)=>T.localTimestamp-c.localTimestamp);if(l.length>0){let T=l[0];delete r.transactionsPool[T.txKey];}}r.transactionsPool[a.txKey]=e;}})),t&&t(e).catch(n=>console.error("[Pulsar Sync] Create failed:",n));},updateTxParams:(a,e)=>{s(n=>produce(n,r=>{let i=r.transactionsPool[a];i&&Object.assign(i,e);}));},removeTxFromPool:a=>{s(e=>produce(e,n=>{delete n.transactionsPool[a];}));},closeTxTrackedModal:a=>{s(e=>produce(e,n=>{if(a&&n.transactionsPool[a]){let r=n.transactionsPool[a];n.transactionsPool[a]={...r,isTrackedModalOpen:false};}n.initialTx=void 0;}));},getLastTxKey:()=>p().lastAddedTxKey})}var F=o=>Object.values(o).sort((t,s)=>Number(t.localTimestamp)-Number(s.localTimestamp)),Y=o=>F(o).filter(t=>t.pending),Z=(o,t)=>o[t],B=(o,t)=>F(o).filter(s=>s.from.toLowerCase()===t.toLowerCase()),ee=(o,t)=>B(o,t).filter(s=>s.pending);var N=(a=>(a.Ethereum="ethereum",a.Safe="safe",a.Gelato="gelato",a.Solana="solana",a))(N||{}),A=(p=>(p.Failed="Failed",p.Success="Success",p.Replaced="Replaced",p))(A||{});var $=o=>o==="Success"||o==="Replaced",M=(o,t)=>{let s=o[t.txKey];return $(s?.status)?false:(o[t.txKey]=t,true)};function Te({localTransactionsPool:o,getHistory:t,onHistoryFetched:s}){setAutoFreeze(false);let p=e=>({isLoading:e,isError:false}),a=e=>e?(s&&queueMicrotask(()=>s(e.docs)),n=>produce(n,r=>{let i=r.transactionsPool;for(let l of e.docs)M(i,l);r.currentPage=e.page,r.hasMore=e.hasNextPage,r.isLoading=false;})):n=>n;return createStore()((e,n)=>({transactionsPool:o,isLoading:false,isError:false,hasMore:false,currentPage:1,syncWithLocalPool:r=>{e(i=>produce(i,l=>{let T=l.transactionsPool;for(let c of Object.values(r))M(T,c);}));},fetchInitial:async r=>{if(!(!t||!r)){e(p(true));try{let i=await t({page:1,walletAddress:r});e(a(i));}catch(i){console.error("[Pulsar] Failed to fetch initial transaction history:",i),e({isLoading:false,isError:true});}}},fetchNextPage:async r=>{let{hasMore:i,isLoading:l,currentPage:T}=n();if(!(!t||!i||l||!r)){e(p(true));try{let c=T+1,m=await t({page:c,walletAddress:r});e(a(m));}catch(c){console.error(`[Pulsar] Failed to fetch transaction history page ${T+1}:`,c),e({isLoading:false,isError:true});}}}}))}function Ae({adapter:o,maxTransactions:t=50,onRemoteCreate:s,gelatoApiKey:p,...a}){return createStore()(persist((e,n)=>({...E({maxTransactions:t,onRemoteCreate:s})(e,n),getAdapter:()=>o,initializeTransactionsPool:async()=>{let r=Object.values(n().transactionsPool).filter(i=>i.pending);await Promise.all(r.map(i=>selectAdapterByKey({adapterKey:i.adapter,adapter:o})?.checkAndInitializeTrackerInStore({tx:i,gelatoApiKey:p,...n()})));},injectExternalPendingTxs:async r=>{let l=n().getAdapter(),T=[];e(c=>produce(c,m=>{let g=m.transactionsPool;r.forEach(d=>{let x=g[d.txKey];d.pending&&!x&&(g[d.txKey]=d,T.push(d));let S=d.status==="Success"||d.status==="Failed"||d.status==="Replaced";x?.pending&&S&&(x.status=d.status,x.pending=false,d.txKey&&(x.txKey=d.txKey),d.finishedTimestamp&&(x.finishedTimestamp=d.finishedTimestamp));});})),T.length>0&&await Promise.all(T.map(c=>selectAdapterByKey({adapterKey:c.adapter,adapter:l})?.checkAndInitializeTrackerInStore({tx:c,gelatoApiKey:p,...n()})));},executeTxAction:async({defaultTracker:r,actionFunction:i,params:l,...T})=>{let{desiredChainID:c,tracker:m,...g}=l,{onSuccess:d,onError:x,onReplaced:S}=T,K=D().unix();e({initialTx:{...l,actionFunction:i,localTimestamp:K,isInitializing:true}});let f=selectAdapterByKey({adapterKey:g.adapter,adapter:o}),w=u=>{e(k=>produce(k,P=>{P.initialTx&&(P.initialTx.isInitializing=false,P.initialTx.error=normalizeError(u));}));};if(!f){let u=new Error("No adapter found for this transaction.");throw w(u),u}try{let{connectorType:u,walletAddress:k}=f.getConnectorInfo();await f.checkChainForTx(c);let P=await i();if(!P){e({initialTx:void 0});return}let{tracker:R,txKey:h}=f.checkTransactionsTracker({actionTxKey:P,connectorType:u,tracker:m,gelatoApiKey:p}),O={...g,connectorType:u,from:k,tracker:R||r,chainId:setChainId(c),localTimestamp:K,txKey:h,hash:R==="ethereum"?P:void 0,pending:!1,isTrackedModalOpen:l.withTrackedModal};n().addTxToPool(O),e(L=>produce(L,I=>{I.initialTx&&(I.initialTx.isInitializing=!1,I.initialTx.lastTxKey=h);}));let z=n().transactionsPool[h];await f.checkAndInitializeTrackerInStore({tx:z,onSuccess:d,onError:x,onReplaced:S,gelatoApiKey:p,...n()});}catch(u){throw w(u),u}}}),{...a}))}var we=(o=>t=>useStore(o,t));var V=5e3,X=10;function Ee(o){let{tx:t,fetcher:s,onInitialize:p,onSuccess:a,onFailure:e,onIntervalTick:n,onReplaced:r,removeTxFromPool:i,pollingInterval:l=V,maxRetries:T=X}=o;if(!t.pending)return;p?.();let c=T,m=true,g=x=>{m&&(m=false,i&&!x?.withoutRemoving&&i(t.txKey));};(async()=>{for(;m&&c>0;)try{if(await new Promise(x=>setTimeout(x,l)),!m)break;await s({tx:t,stopPolling:g,onSuccess:a,onFailure:e,onIntervalTick:n,onReplaced:r});}catch(x){console.error(`Polling fetcher for txKey ${t.txKey} threw an error:`,x),c--;}c<=0&&(console.warn(`Polling for txKey ${t.txKey} stopped after reaching the maximum number of retries.`),e(),g());})();}export{A as TransactionStatus,N as TransactionTracker,we as createBoundedUseStore,Ae as createPulsarStore,Te as createTxInMemoryStore,Ee as initializePollingTracker,E as initializeTxTrackingStore,F as selectAllTransactions,B as selectAllTransactionsByActiveWallet,Y as selectPendingTransactions,ee as selectPendingTransactionsByActiveWallet,Z as selectTxByKey};
1
+ import {produce,setAutoFreeze}from'immer';import {createStore}from'zustand/vanilla';import {selectAdapterByKey,setChainId,normalizeError}from'@tuwaio/orbit-core';import D from'dayjs';import {persist}from'zustand/middleware';import {useStore}from'zustand';function E({maxTransactions:n,onRemoteCreate:e}){return (i,l)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,addTxToPool:a=>{let t={...a,pending:true};i(o=>produce(o,r=>{if(r.lastAddedTxKey=a.txKey,a.txKey){if(Object.keys(r.transactionsPool).length>=n){let T=Object.values(r.transactionsPool).sort((d,c)=>d.localTimestamp-c.localTimestamp);if(T.length>0){let d=T[0];delete r.transactionsPool[d.txKey];}}r.transactionsPool[a.txKey]=t;}})),e&&e(t).catch(o=>console.error("[Pulsar Sync] Create failed:",o));},updateTxParams:(a,t)=>{i(o=>produce(o,r=>{let s=r.transactionsPool[a];s&&Object.assign(s,t);}));},removeTxFromPool:a=>{i(t=>produce(t,o=>{delete o.transactionsPool[a];}));},closeTxTrackedModal:a=>{i(t=>produce(t,o=>{if(a&&o.transactionsPool[a]){let r=o.transactionsPool[a];o.transactionsPool[a]={...r,isTrackedModalOpen:false};}o.initialTx=void 0;}));},getLastTxKey:()=>l().lastAddedTxKey})}var F=n=>Object.values(n).sort((e,i)=>Number(e.localTimestamp)-Number(i.localTimestamp)),Y=n=>F(n).filter(e=>e.pending),Z=(n,e)=>n[e],N=(n,e)=>F(n).filter(i=>i.from.toLowerCase()===e.toLowerCase()),ee=(n,e)=>N(n,e).filter(i=>i.pending);var j=(a=>(a.Ethereum="ethereum",a.Safe="safe",a.Gelato="gelato",a.Solana="solana",a))(j||{}),A=(l=>(l.Failed="Failed",l.Success="Success",l.Replaced="Replaced",l))(A||{});var M=n=>n==="Success"||n==="Replaced",O=(n,e)=>{let i=n[e.txKey];if(i){if(M(i.status))return false;if(i.pending){if(M(e.status))return n[e.txKey]={...i,...e},true;let l=typeof e.confirmations=="number"?e.confirmations:0,a=typeof i.confirmations=="number"?i.confirmations:0;return l>a?(n[e.txKey]={...i,...e},true):false}}return n[e.txKey]=e,true};function Te({localTransactionsPool:n,getHistory:e,onHistoryFetched:i}){setAutoFreeze(false);let l=t=>({isLoading:t,isError:false}),a=t=>t?(i&&queueMicrotask(()=>i(t.docs)),o=>produce(o,r=>{let s=r.transactionsPool;for(let T of t.docs)O(s,T);r.currentPage=t.page,r.hasMore=t.hasNextPage,r.isLoading=false;})):o=>o;return createStore()((t,o)=>({transactionsPool:n,isLoading:false,isError:false,hasMore:false,currentPage:1,syncWithLocalPool:r=>{t(s=>produce(s,T=>{let d=T.transactionsPool;for(let c of Object.values(r))O(d,c);}));},fetchInitial:async r=>{if(!(!e||!r)){t(l(true));try{let s=await e({page:1,walletAddress:r});t(a(s));}catch(s){console.error("[Pulsar] Failed to fetch initial transaction history:",s),t({isLoading:false,isError:true});}}},fetchNextPage:async r=>{let{hasMore:s,isLoading:T,currentPage:d}=o();if(!(!e||!s||T||!r)){t(l(true));try{let c=d+1,m=await e({page:c,walletAddress:r});t(a(m));}catch(c){console.error(`[Pulsar] Failed to fetch transaction history page ${d+1}:`,c),t({isLoading:false,isError:true});}}}}))}function Ae({adapter:n,maxTransactions:e=50,onRemoteCreate:i,gelatoApiKey:l,...a}){return createStore()(persist((t,o)=>({...E({maxTransactions:e,onRemoteCreate:i})(t,o),getAdapter:()=>n,initializeTransactionsPool:async()=>{let r=Object.values(o().transactionsPool).filter(s=>s.pending);await Promise.all(r.map(s=>selectAdapterByKey({adapterKey:s.adapter,adapter:n})?.checkAndInitializeTrackerInStore({tx:s,gelatoApiKey:l,...o()})));},injectExternalPendingTxs:async r=>{let T=o().getAdapter(),d=[];t(c=>produce(c,m=>{let g=m.transactionsPool;r.forEach(p=>{let x=g[p.txKey];p.pending&&!x&&(g[p.txKey]=p,d.push(p));let S=p.status==="Success"||p.status==="Failed"||p.status==="Replaced";x?.pending&&S&&(x.status=p.status,x.pending=false,p.txKey&&(x.txKey=p.txKey),p.finishedTimestamp&&(x.finishedTimestamp=p.finishedTimestamp));});})),d.length>0&&await Promise.all(d.map(c=>selectAdapterByKey({adapterKey:c.adapter,adapter:T})?.checkAndInitializeTrackerInStore({tx:c,gelatoApiKey:l,...o()})));},executeTxAction:async({defaultTracker:r,actionFunction:s,params:T,...d})=>{let{desiredChainID:c,tracker:m,...g}=T,{onSuccess:p,onError:x,onReplaced:S}=d,K=D().unix();t({initialTx:{...T,actionFunction:s,localTimestamp:K,isInitializing:true}});let f=selectAdapterByKey({adapterKey:g.adapter,adapter:n}),w=u=>{t(k=>produce(k,P=>{P.initialTx&&(P.initialTx.isInitializing=false,P.initialTx.error=normalizeError(u));}));};if(!f){let u=new Error("No adapter found for this transaction.");throw w(u),u}try{let{connectorType:u,walletAddress:k}=f.getConnectorInfo();await f.checkChainForTx(c);let P=await s();if(!P){t({initialTx:void 0});return}let{tracker:R,txKey:h}=f.checkTransactionsTracker({actionTxKey:P,connectorType:u,tracker:m,gelatoApiKey:l}),z={...g,connectorType:u,from:k,tracker:R||r,chainId:setChainId(c),localTimestamp:K,txKey:h,hash:R==="ethereum"?P:void 0,pending:!1,isTrackedModalOpen:T.withTrackedModal};o().addTxToPool(z),t(B=>produce(B,I=>{I.initialTx&&(I.initialTx.isInitializing=!1,I.initialTx.lastTxKey=h);}));let L=o().transactionsPool[h];await f.checkAndInitializeTrackerInStore({tx:L,onSuccess:p,onError:x,onReplaced:S,gelatoApiKey:l,...o()});}catch(u){throw w(u),u}}}),{...a}))}var we=(n=>e=>useStore(n,e));var V=5e3,X=10;function Ee(n){let{tx:e,fetcher:i,onInitialize:l,onSuccess:a,onFailure:t,onIntervalTick:o,onReplaced:r,removeTxFromPool:s,pollingInterval:T=V,maxRetries:d=X}=n;if(!e.pending)return;l?.();let c=d,m=true,g=x=>{m&&(m=false,s&&!x?.withoutRemoving&&s(e.txKey));};(async()=>{for(;m&&c>0;)try{if(await new Promise(x=>setTimeout(x,T)),!m)break;await i({tx:e,stopPolling:g,onSuccess:a,onFailure:t,onIntervalTick:o,onReplaced:r});}catch(x){console.error(`Polling fetcher for txKey ${e.txKey} threw an error:`,x),c--;}c<=0&&(console.warn(`Polling for txKey ${e.txKey} stopped after reaching the maximum number of retries.`),t(),g());})();}export{A as TransactionStatus,j as TransactionTracker,we as createBoundedUseStore,Ae as createPulsarStore,Te as createTxInMemoryStore,Ee as initializePollingTracker,E as initializeTxTrackingStore,F as selectAllTransactions,N as selectAllTransactionsByActiveWallet,Y as selectPendingTransactions,ee as selectPendingTransactionsByActiveWallet,Z as selectTxByKey};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuwaio/pulsar-core",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "private": false,
5
5
  "author": "Oleksandr Tkach",
6
6
  "license": "Apache-2.0",
@@ -48,10 +48,10 @@
48
48
  "devDependencies": {
49
49
  "@tuwaio/orbit-core": "^0.2.8",
50
50
  "dayjs": "^1.11.20",
51
- "immer": "^11.1.4",
51
+ "immer": "^11.1.8",
52
52
  "tsup": "^8.5.1",
53
53
  "typescript": "^6.0.3",
54
- "zustand": "^5.0.12"
54
+ "zustand": "^5.0.13"
55
55
  },
56
56
  "scripts": {
57
57
  "start": "tsup src/index.ts --watch",