@etherkit/viem-tx-tracker 0.0.8 → 0.1.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/src/types.ts CHANGED
@@ -47,12 +47,31 @@ export type UnknownTypeMetadata = {
47
47
  */
48
48
  export type PopulatedMetadata = FunctionCallMetadata | UnknownTypeMetadata;
49
49
 
50
+ /**
51
+ * Conditional type that makes the `source` construction option required or
52
+ * optional based on TSource. Mirrors {@link MetadataField}.
53
+ *
54
+ * If TSource includes undefined (the default), `source` is optional.
55
+ * Otherwise a client cannot be constructed without supplying one.
56
+ *
57
+ * The value can be given directly or as a thunk. The thunk is evaluated at
58
+ * broadcast time, once per send, so a client whose signing route can change
59
+ * over its lifetime always stamps the route that actually signed.
60
+ *
61
+ * Note: a TSource that is itself a function type cannot be distinguished from
62
+ * a thunk at runtime - wrap it (e.g. `{fn: ...}`) if you need one.
63
+ */
64
+ export type SourceField<TSource> = undefined extends TSource
65
+ ? {source?: TSource | (() => TSource)}
66
+ : {source: TSource | (() => TSource)};
67
+
50
68
  /**
51
69
  * Options for creating a tracked wallet client.
52
70
  */
53
- export interface CreateTrackedWalletClientOptions<
71
+ export type CreateTrackedWalletClientOptions<
54
72
  TPopulate extends boolean = false,
55
- > {
73
+ TSource = undefined,
74
+ > = {
56
75
  /**
57
76
  * When true, writeContract and writeContractSync automatically populate
58
77
  * operation, functionName and args in the metadata from the contract call parameters.
@@ -65,7 +84,7 @@ export interface CreateTrackedWalletClientOptions<
65
84
  * Defaults to Date.now.
66
85
  */
67
86
  clock?: () => number;
68
- }
87
+ } & SourceField<TSource>;
69
88
 
70
89
  /**
71
90
  * Block tags that can be used to specify nonce fetching strategy
@@ -113,11 +132,19 @@ export type IntendedGasParameters = {
113
132
  * Common fields for all tracked transactions.
114
133
  * All field paths are stable - you can always access tx.field
115
134
  */
116
- type CommonTrackedFields<TMetadata> = {
135
+ type CommonTrackedFields<TMetadata, TSource = undefined> = {
117
136
  readonly hash: `0x${string}`;
118
137
  readonly from: `0x${string}`;
119
138
  readonly broadcastTimestampMs: number;
120
139
  readonly metadata: TMetadata;
140
+ /**
141
+ * Opaque marker of which signing route produced this transaction.
142
+ * Supplied once at client construction and carried verbatim: the tracker
143
+ * never inspects or interprets it. Sibling of (never nested inside)
144
+ * metadata, since it is a fact observed at dispatch rather than something
145
+ * the application said the transaction means.
146
+ */
147
+ readonly source: TSource;
121
148
  readonly to: `0x${string}` | null;
122
149
  readonly value: bigint;
123
150
  readonly data: `0x${string}`;
@@ -135,39 +162,41 @@ type CommonTrackedFields<TMetadata> = {
135
162
  * - accessList at top level, presence depends on txType
136
163
  * - gasParameters at top level with txType-specific fields
137
164
  */
138
- export type KnownTrackedTransaction<TMetadata> =
139
- CommonTrackedFields<TMetadata> & {
140
- readonly known: true;
141
- } & (
142
- | {
143
- readonly txType: 'eip1559';
144
- readonly chainId: number;
145
- readonly accessList?: AccessList;
146
- readonly gasParameters: {
147
- readonly gas: bigint;
148
- readonly maxFeePerGas: bigint;
149
- readonly maxPriorityFeePerGas: bigint;
150
- };
151
- }
152
- | {
153
- readonly txType: 'legacy';
154
- readonly chainId?: number;
155
- // accessList not available for legacy
156
- readonly gasParameters: {
157
- readonly gas: bigint;
158
- readonly gasPrice: bigint;
159
- };
160
- }
161
- | {
162
- readonly txType: 'eip2930';
163
- readonly chainId: number;
164
- readonly accessList: AccessList;
165
- readonly gasParameters: {
166
- readonly gas: bigint;
167
- readonly gasPrice: bigint;
168
- };
169
- }
170
- );
165
+ export type KnownTrackedTransaction<
166
+ TMetadata,
167
+ TSource = undefined,
168
+ > = CommonTrackedFields<TMetadata, TSource> & {
169
+ readonly known: true;
170
+ } & (
171
+ | {
172
+ readonly txType: 'eip1559';
173
+ readonly chainId: number;
174
+ readonly accessList?: AccessList;
175
+ readonly gasParameters: {
176
+ readonly gas: bigint;
177
+ readonly maxFeePerGas: bigint;
178
+ readonly maxPriorityFeePerGas: bigint;
179
+ };
180
+ }
181
+ | {
182
+ readonly txType: 'legacy';
183
+ readonly chainId?: number;
184
+ // accessList not available for legacy
185
+ readonly gasParameters: {
186
+ readonly gas: bigint;
187
+ readonly gasPrice: bigint;
188
+ };
189
+ }
190
+ | {
191
+ readonly txType: 'eip2930';
192
+ readonly chainId: number;
193
+ readonly accessList: AccessList;
194
+ readonly gasParameters: {
195
+ readonly gas: bigint;
196
+ readonly gasPrice: bigint;
197
+ };
198
+ }
199
+ );
171
200
 
172
201
  /**
173
202
  * A partially known tracked transaction with intended/provided values.
@@ -184,58 +213,60 @@ export type KnownTrackedTransaction<TMetadata> =
184
213
  * - gasPrice only → 'legacy'
185
214
  * - undefined → wallet will determine type
186
215
  */
187
- export type UnknownTrackedTransaction<TMetadata> =
188
- CommonTrackedFields<TMetadata> & {
189
- readonly known: false;
190
- } & (
191
- | {
192
- // txType inferred as eip1559
193
- readonly txType: 'eip1559';
194
- readonly chainId?: number;
195
- readonly accessList?: AccessList;
196
- readonly gasParameters: {
197
- readonly gas?: bigint;
198
- readonly maxFeePerGas?: bigint;
199
- readonly maxPriorityFeePerGas?: bigint;
200
- };
201
- }
202
- | {
203
- // txType inferred as legacy
204
- readonly txType: 'legacy';
205
- readonly chainId?: number;
206
- // no accessList for legacy
207
- readonly gasParameters: {
208
- readonly gas?: bigint;
209
- readonly gasPrice?: bigint;
210
- };
211
- }
212
- | {
213
- // txType inferred as eip2930
214
- readonly txType: 'eip2930';
215
- readonly chainId?: number;
216
- readonly accessList?: AccessList;
217
- readonly gasParameters: {
218
- readonly gas?: bigint;
219
- readonly gasPrice?: bigint;
220
- };
221
- }
222
- | {
223
- // txType unknown - wallet will decide
224
- readonly txType?: undefined;
225
- readonly chainId?: number;
226
- readonly accessList?: AccessList;
227
- readonly gasParameters: IntendedGasParameters;
228
- }
229
- );
216
+ export type UnknownTrackedTransaction<
217
+ TMetadata,
218
+ TSource = undefined,
219
+ > = CommonTrackedFields<TMetadata, TSource> & {
220
+ readonly known: false;
221
+ } & (
222
+ | {
223
+ // txType inferred as eip1559
224
+ readonly txType: 'eip1559';
225
+ readonly chainId?: number;
226
+ readonly accessList?: AccessList;
227
+ readonly gasParameters: {
228
+ readonly gas?: bigint;
229
+ readonly maxFeePerGas?: bigint;
230
+ readonly maxPriorityFeePerGas?: bigint;
231
+ };
232
+ }
233
+ | {
234
+ // txType inferred as legacy
235
+ readonly txType: 'legacy';
236
+ readonly chainId?: number;
237
+ // no accessList for legacy
238
+ readonly gasParameters: {
239
+ readonly gas?: bigint;
240
+ readonly gasPrice?: bigint;
241
+ };
242
+ }
243
+ | {
244
+ // txType inferred as eip2930
245
+ readonly txType: 'eip2930';
246
+ readonly chainId?: number;
247
+ readonly accessList?: AccessList;
248
+ readonly gasParameters: {
249
+ readonly gas?: bigint;
250
+ readonly gasPrice?: bigint;
251
+ };
252
+ }
253
+ | {
254
+ // txType unknown - wallet will decide
255
+ readonly txType?: undefined;
256
+ readonly chainId?: number;
257
+ readonly accessList?: AccessList;
258
+ readonly gasParameters: IntendedGasParameters;
259
+ }
260
+ );
230
261
 
231
262
  /**
232
263
  * A tracked transaction - discriminated by 'known' field.
233
264
  * - known=true: Values are confirmed from chain fetch
234
265
  * - known=false: Values are intended/provided, may differ from actual
235
266
  */
236
- export type TrackedTransaction<TMetadata> =
237
- | KnownTrackedTransaction<TMetadata>
238
- | UnknownTrackedTransaction<TMetadata>;
267
+ export type TrackedTransaction<TMetadata, TSource = undefined> =
268
+ | KnownTrackedTransaction<TMetadata, TSource>
269
+ | UnknownTrackedTransaction<TMetadata, TSource>;
239
270
 
240
271
  /**
241
272
  * Extended WriteContractParameters with metadata and flexible nonce.
@@ -410,6 +441,7 @@ export interface TrackedWalletClient<
410
441
  TTransport extends Transport = Transport,
411
442
  TChain extends Chain | undefined = Chain | undefined,
412
443
  TAccount extends Account | undefined = Account | undefined,
444
+ TSource = undefined,
413
445
  > {
414
446
  /**
415
447
  * The underlying wallet client.
@@ -532,9 +564,11 @@ export interface TrackedWalletClient<
532
564
  * @param listener - Callback function receiving the event data
533
565
  * @returns Unsubscribe function
534
566
  */
535
- on<TEvent extends keyof TrackedWalletClientEvents<TMetadata>>(
567
+ on<TEvent extends keyof TrackedWalletClientEvents<TMetadata, TSource>>(
536
568
  event: TEvent,
537
- listener: (data: TrackedWalletClientEvents<TMetadata>[TEvent]) => void,
569
+ listener: (
570
+ data: TrackedWalletClientEvents<TMetadata, TSource>[TEvent],
571
+ ) => void,
538
572
  ): () => void;
539
573
 
540
574
  /**
@@ -542,25 +576,27 @@ export interface TrackedWalletClient<
542
576
  * @param event - The event type to unsubscribe from
543
577
  * @param listener - The same listener function passed to on
544
578
  */
545
- off<TEvent extends keyof TrackedWalletClientEvents<TMetadata>>(
579
+ off<TEvent extends keyof TrackedWalletClientEvents<TMetadata, TSource>>(
546
580
  event: TEvent,
547
- listener: (data: TrackedWalletClientEvents<TMetadata>[TEvent]) => void,
581
+ listener: (
582
+ data: TrackedWalletClientEvents<TMetadata, TSource>[TEvent],
583
+ ) => void,
548
584
  ): void;
549
585
  }
550
586
 
551
587
  /**
552
588
  * Event map for TrackedWalletClient events.
553
589
  */
554
- export type TrackedWalletClientEvents<TMetadata> = {
590
+ export type TrackedWalletClientEvents<TMetadata, TSource = undefined> = {
555
591
  /**
556
592
  * Emitted immediately after a transaction is successfully broadcast.
557
593
  */
558
- 'transaction:broadcasted': TrackedTransaction<TMetadata>;
594
+ 'transaction:broadcasted': TrackedTransaction<TMetadata, TSource>;
559
595
  /**
560
596
  * Emitted when full transaction data is successfully fetched from chain.
561
597
  * Not guaranteed to fire if fetch fails (tx not in mempool yet, network issues, etc.)
562
598
  */
563
- 'transaction:fetched': KnownTrackedTransaction<TMetadata>;
599
+ 'transaction:fetched': KnownTrackedTransaction<TMetadata, TSource>;
564
600
  };
565
601
 
566
602
  /**
@@ -573,6 +609,7 @@ export interface TrackedWalletClientAutoPopulate<
573
609
  TTransport extends Transport = Transport,
574
610
  TChain extends Chain | undefined = Chain | undefined,
575
611
  TAccount extends Account | undefined = Account | undefined,
612
+ TSource = undefined,
576
613
  > {
577
614
  /**
578
615
  * The underlying wallet client.
@@ -697,9 +734,11 @@ export interface TrackedWalletClientAutoPopulate<
697
734
  * @param listener - Callback function receiving the event data
698
735
  * @returns Unsubscribe function
699
736
  */
700
- on<TEvent extends keyof TrackedWalletClientEvents<TMetadata>>(
737
+ on<TEvent extends keyof TrackedWalletClientEvents<TMetadata, TSource>>(
701
738
  event: TEvent,
702
- listener: (data: TrackedWalletClientEvents<TMetadata>[TEvent]) => void,
739
+ listener: (
740
+ data: TrackedWalletClientEvents<TMetadata, TSource>[TEvent],
741
+ ) => void,
703
742
  ): () => void;
704
743
 
705
744
  /**
@@ -707,8 +746,77 @@ export interface TrackedWalletClientAutoPopulate<
707
746
  * @param event - The event type to unsubscribe from
708
747
  * @param listener - The same listener function passed to on
709
748
  */
710
- off<TEvent extends keyof TrackedWalletClientEvents<TMetadata>>(
749
+ off<TEvent extends keyof TrackedWalletClientEvents<TMetadata, TSource>>(
711
750
  event: TEvent,
712
- listener: (data: TrackedWalletClientEvents<TMetadata>[TEvent]) => void,
751
+ listener: (
752
+ data: TrackedWalletClientEvents<TMetadata, TSource>[TEvent],
753
+ ) => void,
713
754
  ): void;
714
755
  }
756
+
757
+ /**
758
+ * Utility type to get the return type of createTrackedWalletClient().using().
759
+ *
760
+ * Use this type to declare variables that will hold a TrackedWalletClient
761
+ * without having to manually specify all the type parameters.
762
+ *
763
+ * @typeParam TMetadata - The metadata type for transaction tracking
764
+ * @typeParam TAutoPopulate - Whether auto-population is enabled (default: false)
765
+ * @typeParam TTransport - The transport type (default: Transport)
766
+ * @typeParam TChain - The chain type (default: Chain | undefined)
767
+ * @typeParam TAccount - The account type (default: Account | undefined)
768
+ * @typeParam TSource - The opaque signing-route marker (default: undefined)
769
+ *
770
+ * @example
771
+ * ```typescript
772
+ * // Standard mode with required metadata
773
+ * type MyClient = TrackedWalletClientType<{purpose: string}>;
774
+ * let client: MyClient;
775
+ *
776
+ * // Standard mode with optional metadata
777
+ * type MyOptionalClient = TrackedWalletClientType<{purpose: string} | undefined>;
778
+ *
779
+ * // Auto-populate mode with default PopulatedMetadata
780
+ * type MyAutoClient = TrackedWalletClientType<PopulatedMetadata, true>;
781
+ *
782
+ * // Auto-populate mode with extended metadata
783
+ * type MyExtendedMetadata = FunctionCallMetadata & { priority: number };
784
+ * type MyExtendedClient = TrackedWalletClientType<MyExtendedMetadata, true>;
785
+ *
786
+ * // With an opaque source describing which signing route produced the tx
787
+ * type MySource = 'connected-wallet' | 'local-signer' | 'payer-wallet';
788
+ * type MySourcedClient = TrackedWalletClientType<
789
+ * {purpose: string},
790
+ * false,
791
+ * Transport,
792
+ * Chain | undefined,
793
+ * Account | undefined,
794
+ * MySource
795
+ * >;
796
+ *
797
+ * // With specific wallet client types
798
+ * type MySpecificClient = TrackedWalletClientType<
799
+ * {purpose: string},
800
+ * false,
801
+ * HttpTransport,
802
+ * typeof mainnet,
803
+ * PrivateKeyAccount
804
+ * >;
805
+ * ```
806
+ */
807
+ export type TrackedWalletClientType<
808
+ TMetadata,
809
+ TAutoPopulate extends boolean = false,
810
+ TTransport extends Transport = Transport,
811
+ TChain extends Chain | undefined = Chain | undefined,
812
+ TAccount extends Account | undefined = Account | undefined,
813
+ TSource = undefined,
814
+ > = TAutoPopulate extends true
815
+ ? TrackedWalletClientAutoPopulate<
816
+ TMetadata,
817
+ TTransport,
818
+ TChain,
819
+ TAccount,
820
+ TSource
821
+ >
822
+ : TrackedWalletClient<TMetadata, TTransport, TChain, TAccount, TSource>;