@tuwaio/pulsar-core 0.6.11 → 0.7.0
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 +23 -5
- package/dist/index.d.ts +23 -5
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +9 -9
package/dist/index.d.mts
CHANGED
|
@@ -18,10 +18,15 @@ declare enum TransactionTracker {
|
|
|
18
18
|
Ethereum = "ethereum",
|
|
19
19
|
/** For multi-signature transactions managed and executed via a Safe contract. */
|
|
20
20
|
Safe = "safe",
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* For meta-transactions relayed and executed by the Gelato Network.
|
|
23
|
+
* @deprecated Gelato gasless relay is deprecated. Use TransactionTracker.ERC4337 instead.
|
|
24
|
+
*/
|
|
22
25
|
Gelato = "gelato",
|
|
23
26
|
/** The tracker for monitoring standard Solana transaction signatures. */
|
|
24
|
-
Solana = "solana"
|
|
27
|
+
Solana = "solana",
|
|
28
|
+
/** For native ERC-4337 UserOperation transactions tracked via bundler RPC. */
|
|
29
|
+
ERC4337 = "erc4337"
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* Represents the terminal status of a transaction after it has been processed.
|
|
@@ -135,6 +140,10 @@ type EvmTransaction = BaseTransaction & {
|
|
|
135
140
|
to?: `0x${string}`;
|
|
136
141
|
/** The amount of native currency (in wei) being sent. */
|
|
137
142
|
value?: string;
|
|
143
|
+
/** Optional custom bundler RPC URL for ERC-4337 UserOperation tracking. */
|
|
144
|
+
bundlerUrl?: string;
|
|
145
|
+
/** Optional Pimlico API key for ERC-4337 UserOperation tracking. */
|
|
146
|
+
pimlicoApiKey?: string;
|
|
138
147
|
};
|
|
139
148
|
/**
|
|
140
149
|
* Represents a Solana-specific transaction, extending the base properties.
|
|
@@ -170,7 +179,7 @@ type Transaction = EvmTransaction | SolanaTransaction | StarknetTransaction;
|
|
|
170
179
|
/**
|
|
171
180
|
* Represents the parameters required to initiate a new transaction tracking flow.
|
|
172
181
|
*/
|
|
173
|
-
type InitialTransactionParams = Pick<BaseTransaction, 'description' | 'title' | 'type' | 'requiredConfirmations' | 'rpcUrl' | 'payload'> & {
|
|
182
|
+
type InitialTransactionParams = Pick<BaseTransaction, 'description' | 'title' | 'type' | 'requiredConfirmations' | 'rpcUrl' | 'payload'> & Pick<EvmTransaction, 'bundlerUrl' | 'pimlicoApiKey'> & {
|
|
174
183
|
/** The specific blockchain adapter for this transaction. */
|
|
175
184
|
adapter: OrbitAdapter;
|
|
176
185
|
/** The function that executes the on-chain action (e.g., sending a transaction) and returns a preliminary identifier like a hash. */
|
|
@@ -179,8 +188,10 @@ type InitialTransactionParams = Pick<BaseTransaction, 'description' | 'title' |
|
|
|
179
188
|
desiredChainID: number | string;
|
|
180
189
|
/** If true, the detailed tracking modal will open automatically upon initiation. */
|
|
181
190
|
withTrackedModal?: boolean;
|
|
182
|
-
/** The specific tracker responsible for monitoring this transaction's status. Required for Gelato tracker. */
|
|
191
|
+
/** The specific tracker responsible for monitoring this transaction's status. Required for Gelato / ERC-4337 tracker. */
|
|
183
192
|
tracker?: TransactionTracker;
|
|
193
|
+
/** @deprecated Gelato relay is deprecated. */
|
|
194
|
+
gelatoApiKey?: string;
|
|
184
195
|
};
|
|
185
196
|
/**
|
|
186
197
|
* Represents a transaction in its temporary, pre-submission state.
|
|
@@ -242,13 +253,20 @@ type PulsarAdapter<T extends Transaction> = OrbitGenericAdapter<TxAdapter<T>> &
|
|
|
242
253
|
* @property {ActionTxKey} actionTxKey - The key identifying the specific action related to the transaction.
|
|
243
254
|
* @property {string} connectorType - The type of connector used for the transaction (e.g., wallet provider, blockchain interface).
|
|
244
255
|
* @property {TransactionTracker} [tracker] - An optional tracker object that monitors the status and progress of the transaction.
|
|
245
|
-
* @property {string} [gelatoApiKey] -
|
|
256
|
+
* @property {string} [gelatoApiKey] - @deprecated Gelato API key for Gelato relayer integration.
|
|
257
|
+
* @property {string} [bundlerUrl] - Optional custom bundler RPC URL for ERC-4337 UserOperation tracking.
|
|
258
|
+
* @property {string} [pimlicoApiKey] - Optional Pimlico API key for ERC-4337 UserOperation tracking.
|
|
246
259
|
*/
|
|
247
260
|
type CheckTxTracker = {
|
|
248
261
|
actionTxKey: ActionTxKey;
|
|
249
262
|
connectorType: string;
|
|
250
263
|
tracker?: TransactionTracker;
|
|
264
|
+
/** @deprecated Gelato relay is deprecated. Use bundlerUrl / pimlicoApiKey with ERC-4337 instead. */
|
|
251
265
|
gelatoApiKey?: string;
|
|
266
|
+
/** Optional custom bundler RPC URL for ERC-4337 UserOperation tracking. */
|
|
267
|
+
bundlerUrl?: string;
|
|
268
|
+
/** Optional Pimlico API key for ERC-4337 UserOperation tracking. */
|
|
269
|
+
pimlicoApiKey?: string;
|
|
252
270
|
};
|
|
253
271
|
/**
|
|
254
272
|
* Defines the interface for a transaction adapter, which provides chain-specific logic and utilities.
|
package/dist/index.d.ts
CHANGED
|
@@ -18,10 +18,15 @@ declare enum TransactionTracker {
|
|
|
18
18
|
Ethereum = "ethereum",
|
|
19
19
|
/** For multi-signature transactions managed and executed via a Safe contract. */
|
|
20
20
|
Safe = "safe",
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* For meta-transactions relayed and executed by the Gelato Network.
|
|
23
|
+
* @deprecated Gelato gasless relay is deprecated. Use TransactionTracker.ERC4337 instead.
|
|
24
|
+
*/
|
|
22
25
|
Gelato = "gelato",
|
|
23
26
|
/** The tracker for monitoring standard Solana transaction signatures. */
|
|
24
|
-
Solana = "solana"
|
|
27
|
+
Solana = "solana",
|
|
28
|
+
/** For native ERC-4337 UserOperation transactions tracked via bundler RPC. */
|
|
29
|
+
ERC4337 = "erc4337"
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* Represents the terminal status of a transaction after it has been processed.
|
|
@@ -135,6 +140,10 @@ type EvmTransaction = BaseTransaction & {
|
|
|
135
140
|
to?: `0x${string}`;
|
|
136
141
|
/** The amount of native currency (in wei) being sent. */
|
|
137
142
|
value?: string;
|
|
143
|
+
/** Optional custom bundler RPC URL for ERC-4337 UserOperation tracking. */
|
|
144
|
+
bundlerUrl?: string;
|
|
145
|
+
/** Optional Pimlico API key for ERC-4337 UserOperation tracking. */
|
|
146
|
+
pimlicoApiKey?: string;
|
|
138
147
|
};
|
|
139
148
|
/**
|
|
140
149
|
* Represents a Solana-specific transaction, extending the base properties.
|
|
@@ -170,7 +179,7 @@ type Transaction = EvmTransaction | SolanaTransaction | StarknetTransaction;
|
|
|
170
179
|
/**
|
|
171
180
|
* Represents the parameters required to initiate a new transaction tracking flow.
|
|
172
181
|
*/
|
|
173
|
-
type InitialTransactionParams = Pick<BaseTransaction, 'description' | 'title' | 'type' | 'requiredConfirmations' | 'rpcUrl' | 'payload'> & {
|
|
182
|
+
type InitialTransactionParams = Pick<BaseTransaction, 'description' | 'title' | 'type' | 'requiredConfirmations' | 'rpcUrl' | 'payload'> & Pick<EvmTransaction, 'bundlerUrl' | 'pimlicoApiKey'> & {
|
|
174
183
|
/** The specific blockchain adapter for this transaction. */
|
|
175
184
|
adapter: OrbitAdapter;
|
|
176
185
|
/** The function that executes the on-chain action (e.g., sending a transaction) and returns a preliminary identifier like a hash. */
|
|
@@ -179,8 +188,10 @@ type InitialTransactionParams = Pick<BaseTransaction, 'description' | 'title' |
|
|
|
179
188
|
desiredChainID: number | string;
|
|
180
189
|
/** If true, the detailed tracking modal will open automatically upon initiation. */
|
|
181
190
|
withTrackedModal?: boolean;
|
|
182
|
-
/** The specific tracker responsible for monitoring this transaction's status. Required for Gelato tracker. */
|
|
191
|
+
/** The specific tracker responsible for monitoring this transaction's status. Required for Gelato / ERC-4337 tracker. */
|
|
183
192
|
tracker?: TransactionTracker;
|
|
193
|
+
/** @deprecated Gelato relay is deprecated. */
|
|
194
|
+
gelatoApiKey?: string;
|
|
184
195
|
};
|
|
185
196
|
/**
|
|
186
197
|
* Represents a transaction in its temporary, pre-submission state.
|
|
@@ -242,13 +253,20 @@ type PulsarAdapter<T extends Transaction> = OrbitGenericAdapter<TxAdapter<T>> &
|
|
|
242
253
|
* @property {ActionTxKey} actionTxKey - The key identifying the specific action related to the transaction.
|
|
243
254
|
* @property {string} connectorType - The type of connector used for the transaction (e.g., wallet provider, blockchain interface).
|
|
244
255
|
* @property {TransactionTracker} [tracker] - An optional tracker object that monitors the status and progress of the transaction.
|
|
245
|
-
* @property {string} [gelatoApiKey] -
|
|
256
|
+
* @property {string} [gelatoApiKey] - @deprecated Gelato API key for Gelato relayer integration.
|
|
257
|
+
* @property {string} [bundlerUrl] - Optional custom bundler RPC URL for ERC-4337 UserOperation tracking.
|
|
258
|
+
* @property {string} [pimlicoApiKey] - Optional Pimlico API key for ERC-4337 UserOperation tracking.
|
|
246
259
|
*/
|
|
247
260
|
type CheckTxTracker = {
|
|
248
261
|
actionTxKey: ActionTxKey;
|
|
249
262
|
connectorType: string;
|
|
250
263
|
tracker?: TransactionTracker;
|
|
264
|
+
/** @deprecated Gelato relay is deprecated. Use bundlerUrl / pimlicoApiKey with ERC-4337 instead. */
|
|
251
265
|
gelatoApiKey?: string;
|
|
266
|
+
/** Optional custom bundler RPC URL for ERC-4337 UserOperation tracking. */
|
|
267
|
+
bundlerUrl?: string;
|
|
268
|
+
/** Optional Pimlico API key for ERC-4337 UserOperation tracking. */
|
|
269
|
+
pimlicoApiKey?: string;
|
|
252
270
|
};
|
|
253
271
|
/**
|
|
254
272
|
* Defines the interface for a transaction adapter, which provides chain-specific logic and utilities.
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var immer=require('immer'),vanilla=require('zustand/vanilla'),orbitCore=require('@tuwaio/orbit-core'),
|
|
1
|
+
'use strict';var immer=require('immer'),vanilla=require('zustand/vanilla'),orbitCore=require('@tuwaio/orbit-core'),ae=require('dayjs'),middleware=require('zustand/middleware'),zustand=require('zustand');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var ae__default=/*#__PURE__*/_interopDefault(ae);var ee=(i=>(i.Ethereum="ethereum",i.Safe="safe",i.Gelato="gelato",i.Solana="solana",i.ERC4337="erc4337",i))(ee||{}),K=(T=>(T.Failed="Failed",T.Success="Success",T.Replaced="Replaced",T))(K||{});var U=100,B=300,N=10*1024,ne=[/\beval\s*\(/i,/\bFunction\s*\(/,/\bset(?:Timeout|Interval)\s*\(\s*['"`]/i,/javascript\s*:/i],P=class extends Error{field;constructor(e,o){super(o),this.name="PulsarTransactionValidationError",this.field=e;}};function $(n){w({field:"title",value:n.title,maxLength:U}),w({field:"description",value:n.description,maxLength:B}),j(n.payload);}function v(n){w({field:"title",value:n.title,maxLength:U}),w({field:"description",value:n.description,maxLength:B}),j(n.payload);}function w({field:n,value:e,maxLength:o}){if(e===void 0)return;(Array.isArray(e)?e:[e]).forEach((d,i)=>{let t=Array.isArray(e)?`${n}[${i}]`:n;if(typeof d!="string")throw new P(t,`${t} must be a string.`);if(d.length>o)throw new P(t,`${t} must be ${o} characters or less.`);C(t,d);});}function j(n){if(n===void 0)return;let e;try{e=JSON.stringify(n);}catch{throw new P("payload","payload must be JSON-serializable.")}if(e===void 0)throw new P("payload","payload must be JSON-serializable.");if(new TextEncoder().encode(e).length>N)throw new P("payload",`payload must be ${N} bytes or less when serialized.`);_(n);}function _(n,e="payload"){if(typeof n=="string"){C(e,n);return}n===null||typeof n!="object"||Object.entries(n).forEach(([o,T])=>{let d=`${e}.${o}`;C(d,o),_(T,d);});}function C(n,e){if(ne.some(o=>o.test(e)))throw new P(n,`${n} contains a blocked executable-like pattern.`)}function G({maxTransactions:n,onRemoteCreate:e}){let o=false;return (T,d)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,unsyncedTxKeys:{},reconcileUnsyncedTransactions:async()=>{if(!e||o)return;let i=Object.keys(d().unsyncedTxKeys||{});if(i.length!==0){o=true;try{for(let t of i){let l=d().transactionsPool[t];if(!l){T(r=>immer.produce(r,s=>{s.unsyncedTxKeys&&delete s.unsyncedTxKeys[t];}));continue}try{await e(l),T(r=>immer.produce(r,s=>{let c=s.transactionsPool[t];c&&(c.syncStatus="synced"),s.unsyncedTxKeys&&delete s.unsyncedTxKeys[t];}));}catch(r){console.warn(`[Pulsar] Failed to reconcile tx ${t}:`,r);}}}finally{o=false;}}},addTxToPool:i=>{v(i);let t={...i,pending:true};return (async()=>{let r=false;if(e)try{await e(t),t.syncStatus="synced";}catch(s){console.warn("[Pulsar] onRemoteCreate failed, transaction queued for background sync:",s),t.syncStatus="pending-sync",r=true;}T(s=>immer.produce(s,c=>{if(c.lastAddedTxKey=i.txKey,r&&(c.unsyncedTxKeys||(c.unsyncedTxKeys={}),c.unsyncedTxKeys[i.txKey]=true),i.txKey){if(Object.keys(c.transactionsPool).length>=n){let p=Object.values(c.transactionsPool).sort((x,f)=>x.localTimestamp-f.localTimestamp);if(p.length>0){let x=p[0];delete c.transactionsPool[x.txKey];}}c.transactionsPool[i.txKey]=t;}}));})()},updateTxParams:(i,t)=>{T(r=>immer.produce(r,s=>{let c=s.transactionsPool[i];c&&Object.assign(c,t);})),(t.status==="Success"||t.status==="Failed"||t.status==="Replaced")&&d().reconcileUnsyncedTransactions&&d().unsyncedTxKeys?.[i]&&d().reconcileUnsyncedTransactions().catch(r=>{console.error("[Pulsar] Terminal reconciliation failed:",r);});},removeTxFromPool:i=>{T(t=>immer.produce(t,l=>{delete l.transactionsPool[i];}));},closeTxTrackedModal:i=>{T(t=>immer.produce(t,l=>{if(i&&l.transactionsPool[i]){let r=l.transactionsPool[i];l.transactionsPool[i]={...r,isTrackedModalOpen:false};}l.initialTx=void 0;}));},getLastTxKey:()=>d().lastAddedTxKey})}var D=n=>Object.values(n).sort((e,o)=>Number(e.localTimestamp)-Number(o.localTimestamp)),be=n=>D(n).filter(e=>e.pending),Ae=(n,e)=>n[e],te=(n,e)=>D(n).filter(o=>o.from.toLowerCase()===e.toLowerCase()),ke=(n,e)=>te(n,e).filter(o=>o.pending);var X=n=>n==="Success"||n==="Replaced",H=(n,e)=>{let o=n[e.txKey];if(o){if(X(o.status))return false;if(o.pending){if(X(e.status))return n[e.txKey]={...o,...e},true;let T=typeof e.confirmations=="number"?e.confirmations:0,d=typeof o.confirmations=="number"?o.confirmations:0;return T>d?(n[e.txKey]={...o,...e},true):false}}return n[e.txKey]=e,true};function Fe({localTransactionsPool:n,reconcileUnsyncedTransactions:e,getHistory:o,onHistoryFetched:T}){immer.setAutoFreeze(false);let d=t=>({isLoading:t,isError:false}),i=t=>t?(T&&queueMicrotask(()=>T(t.docs)),l=>immer.produce(l,r=>{let s=r.transactionsPool;for(let c of t.docs)H(s,c);r.currentPage=t.page,r.hasMore=t.hasNextPage,r.isLoading=false;})):l=>l;return vanilla.createStore()((t,l)=>({transactionsPool:n,isLoading:false,isError:false,hasMore:false,currentPage:1,syncWithLocalPool:r=>{t(s=>immer.produce(s,c=>{let a=c.transactionsPool;for(let p of Object.values(r))H(a,p);}));},fetchInitial:async r=>{if(!(!o||!r)){t(d(true)),e&&await e();try{let s=await o({page:1,walletAddress:r});t(i(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:c,currentPage:a}=l();if(!(!o||!s||c||!r)){t(d(true));try{let p=a+1,x=await o({page:p,walletAddress:r});t(i(x));}catch(p){console.error(`[Pulsar] Failed to fetch transaction history page ${a+1}:`,p),t({isLoading:false,isError:true});}}}}))}function We({adapter:n,maxTransactions:e=50,onRemoteCreate:o,gelatoApiKey:T,beforeTxProcess:d,abortOnTxError:i,...t}){return vanilla.createStore()(middleware.persist((l,r)=>({...G({maxTransactions:e,onRemoteCreate:o})(l,r),getAdapter:()=>n,initializeTransactionsPool:async()=>{let c=Object.values(r().transactionsPool).filter(a=>a.pending).filter(a=>{try{return v(a),!0}catch(p){return console.warn("[Pulsar] Removed invalid persisted transaction:",p),r().removeTxFromPool(a.txKey),false}});await Promise.all(c.map(a=>orbitCore.selectAdapterByKey({adapterKey:a.adapter,adapter:n})?.checkAndInitializeTrackerInStore({tx:a,gelatoApiKey:T,...r()})));},injectExternalPendingTxs:async s=>{let a=r().getAdapter(),p=[],x=s.filter(f=>{try{return v(f),!0}catch(m){return console.warn("[Pulsar] Skipped invalid remote transaction:",m),false}});l(f=>immer.produce(f,m=>{let S=m.transactionsPool;x.forEach(u=>{let g=S[u.txKey];u.pending&&!g&&(S[u.txKey]=u,p.push(u));let I=u.status==="Success"||u.status==="Failed"||u.status==="Replaced";g?.pending&&I&&(g.status=u.status,g.pending=false,u.txKey&&(g.txKey=u.txKey),u.finishedTimestamp&&(g.finishedTimestamp=u.finishedTimestamp));});})),p.length>0&&await Promise.all(p.map(f=>orbitCore.selectAdapterByKey({adapterKey:f.adapter,adapter:a})?.checkAndInitializeTrackerInStore({tx:f,gelatoApiKey:T,...r()})));},executeTxAction:async({defaultTracker:s,actionFunction:c,params:a,beforeTxProcess:p,abortOnTxError:x,...f})=>{let m=x??i??true,S=ae__default.default().unix();r().reconcileUnsyncedTransactions().catch(y=>console.error("[Pulsar] Reconciliation failed:",y)),$(a);let{desiredChainID:u,tracker:g,...I}=a,{onSuccess:J,onError:V,onReplaced:Y}=f;l({initialTx:{...a,actionFunction:c,localTimestamp:S,isInitializing:true}});let A=orbitCore.selectAdapterByKey({adapterKey:I.adapter,adapter:n}),M=y=>{l(E=>immer.produce(E,h=>{h.initialTx&&(h.initialTx.isInitializing=false,h.initialTx.error=orbitCore.normalizeError(y));}));};if(!A){let y=new Error("No adapter found for this transaction.");throw M(y),y}try{let{connectorType:y,walletAddress:E}=A.getConnectorInfo();await A.checkChainForTx(u);try{await(p??d)?.();}catch(k){if(m)throw l({initialTx:{...a,actionFunction:c,localTimestamp:S,isInitializing:!1,error:orbitCore.normalizeError(k)}}),k;console.warn("[Pulsar] beforeTxProcess failed:",k);}let h=await c();if(!h){l({initialTx:void 0});return}let{tracker:z,txKey:R}=A.checkTransactionsTracker({actionTxKey:h,connectorType:y,tracker:g,gelatoApiKey:a.gelatoApiKey??T,bundlerUrl:a.bundlerUrl,pimlicoApiKey:a.pimlicoApiKey}),Q={...I,connectorType:y,from:E,tracker:z||s,chainId:orbitCore.setChainId(u),localTimestamp:S,txKey:R,bundlerUrl:a.bundlerUrl,pimlicoApiKey:a.pimlicoApiKey,hash:z==="ethereum"?h:void 0,pending:!1,isTrackedModalOpen:a.withTrackedModal};await r().addTxToPool(Q),l(k=>immer.produce(k,O=>{O.initialTx&&(O.initialTx.isInitializing=!1,O.initialTx.lastTxKey=R);}));let Z=r().transactionsPool[R];await A.checkAndInitializeTrackerInStore({tx:Z,onSuccess:J,onError:V,onReplaced:Y,gelatoApiKey:T,...r()});}catch(y){throw M(y),y}}}),{...t}))}var Qe=(n=>e=>zustand.useStore(n,e));var Te=5e3,de=10;function en(n){let{tx:e,fetcher:o,onInitialize:T,onSuccess:d,onFailure:i,onIntervalTick:t,onReplaced:l,removeTxFromPool:r,pollingInterval:s=Te,maxRetries:c=de}=n;if(!e.pending)return;T?.();let a=c,p=true,x=m=>{p&&(p=false,r&&!m?.withoutRemoving&&r(e.txKey));};(async()=>{for(;p&&a>0;)try{if(await new Promise(m=>setTimeout(m,s)),!p)break;await o({tx:e,stopPolling:x,onSuccess:d,onFailure:i,onIntervalTick:t,onReplaced:l});}catch(m){console.error(`Polling fetcher for txKey ${e.txKey} threw an error:`,m),a--;}a<=0&&(console.warn(`Polling for txKey ${e.txKey} stopped after reaching the maximum number of retries.`),i(),x());})();}exports.MAX_TRANSACTION_DESCRIPTION_LENGTH=B;exports.MAX_TRANSACTION_PAYLOAD_BYTES=N;exports.MAX_TRANSACTION_TITLE_LENGTH=U;exports.PulsarTransactionValidationError=P;exports.TransactionStatus=K;exports.TransactionTracker=ee;exports.createBoundedUseStore=Qe;exports.createPulsarStore=We;exports.createTxInMemoryStore=Fe;exports.initializePollingTracker=en;exports.initializeTxTrackingStore=G;exports.selectAllTransactions=D;exports.selectAllTransactionsByActiveWallet=te;exports.selectPendingTransactions=be;exports.selectPendingTransactionsByActiveWallet=ke;exports.selectTxByKey=Ae;exports.validateInitialTransactionParams=$;exports.validateTransaction=v;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {produce,setAutoFreeze}from'immer';import {createStore}from'zustand/vanilla';import {normalizeError,
|
|
1
|
+
import {produce,setAutoFreeze}from'immer';import {createStore}from'zustand/vanilla';import {selectAdapterByKey,normalizeError,setChainId}from'@tuwaio/orbit-core';import ae from'dayjs';import {persist}from'zustand/middleware';import {useStore}from'zustand';var ee=(i=>(i.Ethereum="ethereum",i.Safe="safe",i.Gelato="gelato",i.Solana="solana",i.ERC4337="erc4337",i))(ee||{}),K=(T=>(T.Failed="Failed",T.Success="Success",T.Replaced="Replaced",T))(K||{});var U=100,B=300,N=10*1024,ne=[/\beval\s*\(/i,/\bFunction\s*\(/,/\bset(?:Timeout|Interval)\s*\(\s*['"`]/i,/javascript\s*:/i],P=class extends Error{field;constructor(e,o){super(o),this.name="PulsarTransactionValidationError",this.field=e;}};function $(n){w({field:"title",value:n.title,maxLength:U}),w({field:"description",value:n.description,maxLength:B}),j(n.payload);}function v(n){w({field:"title",value:n.title,maxLength:U}),w({field:"description",value:n.description,maxLength:B}),j(n.payload);}function w({field:n,value:e,maxLength:o}){if(e===void 0)return;(Array.isArray(e)?e:[e]).forEach((d,i)=>{let t=Array.isArray(e)?`${n}[${i}]`:n;if(typeof d!="string")throw new P(t,`${t} must be a string.`);if(d.length>o)throw new P(t,`${t} must be ${o} characters or less.`);C(t,d);});}function j(n){if(n===void 0)return;let e;try{e=JSON.stringify(n);}catch{throw new P("payload","payload must be JSON-serializable.")}if(e===void 0)throw new P("payload","payload must be JSON-serializable.");if(new TextEncoder().encode(e).length>N)throw new P("payload",`payload must be ${N} bytes or less when serialized.`);_(n);}function _(n,e="payload"){if(typeof n=="string"){C(e,n);return}n===null||typeof n!="object"||Object.entries(n).forEach(([o,T])=>{let d=`${e}.${o}`;C(d,o),_(T,d);});}function C(n,e){if(ne.some(o=>o.test(e)))throw new P(n,`${n} contains a blocked executable-like pattern.`)}function G({maxTransactions:n,onRemoteCreate:e}){let o=false;return (T,d)=>({transactionsPool:{},lastAddedTxKey:void 0,initialTx:void 0,unsyncedTxKeys:{},reconcileUnsyncedTransactions:async()=>{if(!e||o)return;let i=Object.keys(d().unsyncedTxKeys||{});if(i.length!==0){o=true;try{for(let t of i){let l=d().transactionsPool[t];if(!l){T(r=>produce(r,s=>{s.unsyncedTxKeys&&delete s.unsyncedTxKeys[t];}));continue}try{await e(l),T(r=>produce(r,s=>{let c=s.transactionsPool[t];c&&(c.syncStatus="synced"),s.unsyncedTxKeys&&delete s.unsyncedTxKeys[t];}));}catch(r){console.warn(`[Pulsar] Failed to reconcile tx ${t}:`,r);}}}finally{o=false;}}},addTxToPool:i=>{v(i);let t={...i,pending:true};return (async()=>{let r=false;if(e)try{await e(t),t.syncStatus="synced";}catch(s){console.warn("[Pulsar] onRemoteCreate failed, transaction queued for background sync:",s),t.syncStatus="pending-sync",r=true;}T(s=>produce(s,c=>{if(c.lastAddedTxKey=i.txKey,r&&(c.unsyncedTxKeys||(c.unsyncedTxKeys={}),c.unsyncedTxKeys[i.txKey]=true),i.txKey){if(Object.keys(c.transactionsPool).length>=n){let p=Object.values(c.transactionsPool).sort((x,f)=>x.localTimestamp-f.localTimestamp);if(p.length>0){let x=p[0];delete c.transactionsPool[x.txKey];}}c.transactionsPool[i.txKey]=t;}}));})()},updateTxParams:(i,t)=>{T(r=>produce(r,s=>{let c=s.transactionsPool[i];c&&Object.assign(c,t);})),(t.status==="Success"||t.status==="Failed"||t.status==="Replaced")&&d().reconcileUnsyncedTransactions&&d().unsyncedTxKeys?.[i]&&d().reconcileUnsyncedTransactions().catch(r=>{console.error("[Pulsar] Terminal reconciliation failed:",r);});},removeTxFromPool:i=>{T(t=>produce(t,l=>{delete l.transactionsPool[i];}));},closeTxTrackedModal:i=>{T(t=>produce(t,l=>{if(i&&l.transactionsPool[i]){let r=l.transactionsPool[i];l.transactionsPool[i]={...r,isTrackedModalOpen:false};}l.initialTx=void 0;}));},getLastTxKey:()=>d().lastAddedTxKey})}var D=n=>Object.values(n).sort((e,o)=>Number(e.localTimestamp)-Number(o.localTimestamp)),be=n=>D(n).filter(e=>e.pending),Ae=(n,e)=>n[e],te=(n,e)=>D(n).filter(o=>o.from.toLowerCase()===e.toLowerCase()),ke=(n,e)=>te(n,e).filter(o=>o.pending);var X=n=>n==="Success"||n==="Replaced",H=(n,e)=>{let o=n[e.txKey];if(o){if(X(o.status))return false;if(o.pending){if(X(e.status))return n[e.txKey]={...o,...e},true;let T=typeof e.confirmations=="number"?e.confirmations:0,d=typeof o.confirmations=="number"?o.confirmations:0;return T>d?(n[e.txKey]={...o,...e},true):false}}return n[e.txKey]=e,true};function Fe({localTransactionsPool:n,reconcileUnsyncedTransactions:e,getHistory:o,onHistoryFetched:T}){setAutoFreeze(false);let d=t=>({isLoading:t,isError:false}),i=t=>t?(T&&queueMicrotask(()=>T(t.docs)),l=>produce(l,r=>{let s=r.transactionsPool;for(let c of t.docs)H(s,c);r.currentPage=t.page,r.hasMore=t.hasNextPage,r.isLoading=false;})):l=>l;return createStore()((t,l)=>({transactionsPool:n,isLoading:false,isError:false,hasMore:false,currentPage:1,syncWithLocalPool:r=>{t(s=>produce(s,c=>{let a=c.transactionsPool;for(let p of Object.values(r))H(a,p);}));},fetchInitial:async r=>{if(!(!o||!r)){t(d(true)),e&&await e();try{let s=await o({page:1,walletAddress:r});t(i(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:c,currentPage:a}=l();if(!(!o||!s||c||!r)){t(d(true));try{let p=a+1,x=await o({page:p,walletAddress:r});t(i(x));}catch(p){console.error(`[Pulsar] Failed to fetch transaction history page ${a+1}:`,p),t({isLoading:false,isError:true});}}}}))}function We({adapter:n,maxTransactions:e=50,onRemoteCreate:o,gelatoApiKey:T,beforeTxProcess:d,abortOnTxError:i,...t}){return createStore()(persist((l,r)=>({...G({maxTransactions:e,onRemoteCreate:o})(l,r),getAdapter:()=>n,initializeTransactionsPool:async()=>{let c=Object.values(r().transactionsPool).filter(a=>a.pending).filter(a=>{try{return v(a),!0}catch(p){return console.warn("[Pulsar] Removed invalid persisted transaction:",p),r().removeTxFromPool(a.txKey),false}});await Promise.all(c.map(a=>selectAdapterByKey({adapterKey:a.adapter,adapter:n})?.checkAndInitializeTrackerInStore({tx:a,gelatoApiKey:T,...r()})));},injectExternalPendingTxs:async s=>{let a=r().getAdapter(),p=[],x=s.filter(f=>{try{return v(f),!0}catch(m){return console.warn("[Pulsar] Skipped invalid remote transaction:",m),false}});l(f=>produce(f,m=>{let S=m.transactionsPool;x.forEach(u=>{let g=S[u.txKey];u.pending&&!g&&(S[u.txKey]=u,p.push(u));let I=u.status==="Success"||u.status==="Failed"||u.status==="Replaced";g?.pending&&I&&(g.status=u.status,g.pending=false,u.txKey&&(g.txKey=u.txKey),u.finishedTimestamp&&(g.finishedTimestamp=u.finishedTimestamp));});})),p.length>0&&await Promise.all(p.map(f=>selectAdapterByKey({adapterKey:f.adapter,adapter:a})?.checkAndInitializeTrackerInStore({tx:f,gelatoApiKey:T,...r()})));},executeTxAction:async({defaultTracker:s,actionFunction:c,params:a,beforeTxProcess:p,abortOnTxError:x,...f})=>{let m=x??i??true,S=ae().unix();r().reconcileUnsyncedTransactions().catch(y=>console.error("[Pulsar] Reconciliation failed:",y)),$(a);let{desiredChainID:u,tracker:g,...I}=a,{onSuccess:J,onError:V,onReplaced:Y}=f;l({initialTx:{...a,actionFunction:c,localTimestamp:S,isInitializing:true}});let A=selectAdapterByKey({adapterKey:I.adapter,adapter:n}),M=y=>{l(E=>produce(E,h=>{h.initialTx&&(h.initialTx.isInitializing=false,h.initialTx.error=normalizeError(y));}));};if(!A){let y=new Error("No adapter found for this transaction.");throw M(y),y}try{let{connectorType:y,walletAddress:E}=A.getConnectorInfo();await A.checkChainForTx(u);try{await(p??d)?.();}catch(k){if(m)throw l({initialTx:{...a,actionFunction:c,localTimestamp:S,isInitializing:!1,error:normalizeError(k)}}),k;console.warn("[Pulsar] beforeTxProcess failed:",k);}let h=await c();if(!h){l({initialTx:void 0});return}let{tracker:z,txKey:R}=A.checkTransactionsTracker({actionTxKey:h,connectorType:y,tracker:g,gelatoApiKey:a.gelatoApiKey??T,bundlerUrl:a.bundlerUrl,pimlicoApiKey:a.pimlicoApiKey}),Q={...I,connectorType:y,from:E,tracker:z||s,chainId:setChainId(u),localTimestamp:S,txKey:R,bundlerUrl:a.bundlerUrl,pimlicoApiKey:a.pimlicoApiKey,hash:z==="ethereum"?h:void 0,pending:!1,isTrackedModalOpen:a.withTrackedModal};await r().addTxToPool(Q),l(k=>produce(k,O=>{O.initialTx&&(O.initialTx.isInitializing=!1,O.initialTx.lastTxKey=R);}));let Z=r().transactionsPool[R];await A.checkAndInitializeTrackerInStore({tx:Z,onSuccess:J,onError:V,onReplaced:Y,gelatoApiKey:T,...r()});}catch(y){throw M(y),y}}}),{...t}))}var Qe=(n=>e=>useStore(n,e));var Te=5e3,de=10;function en(n){let{tx:e,fetcher:o,onInitialize:T,onSuccess:d,onFailure:i,onIntervalTick:t,onReplaced:l,removeTxFromPool:r,pollingInterval:s=Te,maxRetries:c=de}=n;if(!e.pending)return;T?.();let a=c,p=true,x=m=>{p&&(p=false,r&&!m?.withoutRemoving&&r(e.txKey));};(async()=>{for(;p&&a>0;)try{if(await new Promise(m=>setTimeout(m,s)),!p)break;await o({tx:e,stopPolling:x,onSuccess:d,onFailure:i,onIntervalTick:t,onReplaced:l});}catch(m){console.error(`Polling fetcher for txKey ${e.txKey} threw an error:`,m),a--;}a<=0&&(console.warn(`Polling for txKey ${e.txKey} stopped after reaching the maximum number of retries.`),i(),x());})();}export{B as MAX_TRANSACTION_DESCRIPTION_LENGTH,N as MAX_TRANSACTION_PAYLOAD_BYTES,U as MAX_TRANSACTION_TITLE_LENGTH,P as PulsarTransactionValidationError,K as TransactionStatus,ee as TransactionTracker,Qe as createBoundedUseStore,We as createPulsarStore,Fe as createTxInMemoryStore,en as initializePollingTracker,G as initializeTxTrackingStore,D as selectAllTransactions,te as selectAllTransactionsByActiveWallet,be as selectPendingTransactions,ke as selectPendingTransactionsByActiveWallet,Ae as selectTxByKey,$ as validateInitialTransactionParams,v as validateTransaction};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuwaio/pulsar-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Oleksandr Tkach",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -41,23 +41,23 @@
|
|
|
41
41
|
}
|
|
42
42
|
],
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@tuwaio/orbit-core": ">=0.
|
|
44
|
+
"@tuwaio/orbit-core": ">=0.3",
|
|
45
45
|
"dayjs": "1.x.x",
|
|
46
46
|
"immer": "11.x.x",
|
|
47
47
|
"zustand": "5.x.x"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@tuwaio/orbit-core": "^0.
|
|
51
|
-
"dayjs": "^1.11.
|
|
52
|
-
"immer": "^11.1.
|
|
50
|
+
"@tuwaio/orbit-core": "^0.3.0",
|
|
51
|
+
"dayjs": "^1.11.23",
|
|
52
|
+
"immer": "^11.1.18",
|
|
53
53
|
"tsup": "^8.5.1",
|
|
54
|
-
"typescript": "
|
|
55
|
-
"vitest": "^
|
|
56
|
-
"zustand": "^5.0.
|
|
54
|
+
"typescript": "6.0.3",
|
|
55
|
+
"vitest": "^5.0.0",
|
|
56
|
+
"zustand": "^5.0.15"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"start": "tsup src/index.ts --watch",
|
|
60
60
|
"build": "tsup",
|
|
61
|
-
"test": "vitest"
|
|
61
|
+
"test": "vitest run"
|
|
62
62
|
}
|
|
63
63
|
}
|