@valve-tech/wallet-adapter 0.4.0 → 0.5.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/CHANGELOG.md CHANGED
@@ -6,8 +6,111 @@ this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/).
8
8
 
9
+ ## [0.5.0] — 2026-05-05
10
+
11
+ ### Changed — BREAKING
12
+
13
+ - **All lifecycle event payloads are now rich info bags carrying full
14
+ `TxContext` (`chainId` + original `request`) plus phase-specific
15
+ fields.** The previous shapes forced consumers (especially the
16
+ upcoming `@valve-tech/tx-tracker`) to maintain a side-channel
17
+ `hash → request` map and re-fetch chainId / block timestamp / block
18
+ baseFeePerGas from the public client. The lib already has all of
19
+ that in scope when it fires events, so the consumer should never
20
+ have to re-gather it.
21
+
22
+ Old / new signatures:
23
+
24
+ | Hook | Old | New |
25
+ | ---- | --- | --- |
26
+ | `onAwaitingSignature` | `() => void` | `(info: TxContext) => void` |
27
+ | `onTransactionHash` (per-call & global) | `(hash: Hex) => void` | `(info: TxContext<{ hash }>) => void` |
28
+ | `onConfirmed` | `(receipt: TransactionReceipt) => void` | `(info: TxContext<{ hash, receipt, block? }>) => void` |
29
+ | `onFailed` | `(error: Error) => void` | `(info: TxContext<{ error, hash?, receipt?, block? }>) => void` |
30
+ | `onDropped` | `(info: { hash }) => void` | `(info: TxContext<{ hash }>) => void` |
31
+ | `onReplaced` | `(info: { original, replacement, receipt? }) => void` | `(info: TxContext<{ original, replacement, receipt?, block? }>) => void` |
32
+
33
+ The `onPhase` discriminated-union event gains the same enrichment;
34
+ `WritePhaseEvent` is now derived mechanically from a `WritePhaseSteps`
35
+ map intersected with `TxContext<Steps[K]>`, so adding a phase or a
36
+ shared field is a one-line edit instead of seven.
37
+
38
+ - **`awaitReceiptWithHooks` now requires `request` in its options and
39
+ fetches the containing `Block` by default.** Signature gained
40
+ `request: WalletSendTransactionRequest` (used to populate `TxContext`
41
+ in event payloads) and `includeBlock?: boolean = true`. When
42
+ `includeBlock` is left at its default, the helper calls
43
+ `publicClient.getBlock({ blockHash })` once after a successful
44
+ receipt-await; the resulting `Block` is attached to `confirmed` /
45
+ receipt-bearing `failed` event payloads. Pass `includeBlock: false`
46
+ to skip the extra RPC round trip in environments that don't need
47
+ block-level data.
48
+
49
+ - **`ReceiptAwaiter` interface gained `getBlock`.** Mocks need to add
50
+ a `getBlock(args: { blockHash: Hex }): Promise<Block>` field. Real
51
+ viem `PublicClient` instances satisfy the new shape unchanged.
52
+
53
+ ### Added
54
+
55
+ - `WritePhaseSteps` interface — phase-name → per-phase delta map.
56
+ Declared as `interface` (not `type`) so consumers can extend the
57
+ lifecycle via declaration merging without forking the union.
58
+ - `TxContext<Extra extends object = object>` — generic always-present
59
+ context (`chainId`, `request`) intersected with `Extra` to derive
60
+ per-phase event shapes. Defaulting `Extra` keeps `TxContext` usable
61
+ bare.
62
+ - `WritePhaseEvent` is now derived mechanically as
63
+ `{ [K in keyof WritePhaseSteps]: { phase: K } & TxContext<WritePhaseSteps[K]> }[keyof WritePhaseSteps]`.
64
+ - `confirmed` and `replaced` events carry an optional `block: Block`.
65
+ `failed` events carry the receipt-bearing context (hash, receipt,
66
+ block) when the failure has one (revert), and just the error +
67
+ context otherwise (wallet rejection, network timeout).
68
+
69
+ ### Migration
70
+
71
+ ```ts
72
+ // before — v0.4.x
73
+ hooks: {
74
+ onConfirmed: (receipt) => recordTx({ hash: receipt.transactionHash, receipt }),
75
+ onFailed: (error) => logFailure(error),
76
+ }
77
+ await awaitReceiptWithHooks({ publicClient, hash, hooks })
78
+
79
+ // after — v0.5.x
80
+ hooks: {
81
+ onConfirmed: (info) => recordTx(info), // info.chainId, info.request, info.hash, info.receipt, info.block
82
+ onFailed: (info) => logFailure(info.error, info.chainId, info.request),
83
+ }
84
+ await awaitReceiptWithHooks({ publicClient, hash, request, hooks })
85
+ // ^^^^^^^ new required field
86
+ ```
87
+
88
+ ## [0.4.1] — 2026-05-04
89
+
90
+ ### Fixed
91
+
92
+ - **`workspace:^` leak in the published `0.4.0` manifest.** The
93
+ `0.4.0` tarball shipped with `dependencies: { "@valve-tech/viem-errors":
94
+ "workspace:^" }` literally inside its `package.json` —
95
+ `workspace:` is a yarn-only protocol that npm cannot resolve, so
96
+ `npm install @valve-tech/wallet-adapter@0.4.0` fails for any consumer
97
+ without a manual resolution override. Root cause: the release
98
+ workflow used `npm publish` directly, which leaves the manifest
99
+ unmodified. Fixed by switching the publish step to `yarn pack`
100
+ (which rewrites `workspace:^` to the real semver range, e.g.
101
+ `^0.4.1`) followed by `npm publish <tarball>` (which preserves
102
+ `--provenance`).
103
+ - Consumers on `0.4.0` should bump to `0.4.1` and remove any
104
+ `resolutions` / `overrides` entry forcing
105
+ `@valve-tech/wallet-adapter`'s `@valve-tech/viem-errors` dep to a
106
+ real range.
107
+
9
108
  ## [0.4.0] — 2026-05-04
10
109
 
110
+ > **`0.4.0` is broken — use `0.4.1` or later.** See the `0.4.1`
111
+ > entry above for the workspace-protocol leak. `0.4.0` will be
112
+ > deprecated on npm.
113
+
11
114
  ### Added
12
115
 
13
116
  - Initial implementation. Vocabulary lifted from a real-world dapp
package/README.md CHANGED
@@ -13,21 +13,27 @@ all agree on the same surface:
13
13
  named hooks (`onAwaitingSignature`, `onTransactionHash`,
14
14
  `onConfirmed`, `onFailed`, `onDropped`, `onReplaced`) plus a
15
15
  complementary single-callback shape (`onPhase(event)`) with a
16
- discriminated-union payload. Fire-ers fire BOTH shapes for every
17
- transitionexactly once each — so wiring named hooks doesn't
18
- preclude `onPhase` and vice versa.
16
+ discriminated-union payload. Every payload is a `TxContext` info
17
+ bag`{ chainId, request, ...phase-specific }` — so consumers
18
+ never have to side-channel the originating chain or the original
19
+ send request. Fire-ers fire BOTH shapes for every transition —
20
+ exactly once each — so wiring named hooks doesn't preclude
21
+ `onPhase` and vice versa.
19
22
  3. **`sendTransactionWithHooks(options)`** — wallet-side helper. Fires
20
23
  the pre-wallet (`onAwaitingSignature`, `onPhase('awaiting-signature')`)
21
- and post-hash (`onTransactionHash`, `onPhase('pending', { hash })`)
24
+ and post-hash (`onTransactionHash`, `onPhase('pending', { ..., hash })`)
22
25
  transitions. Converts wallet rejections to a typed
23
- `WalletRejectedError`, fires `onFailed` + `onPhase('failed', { error })`,
26
+ `WalletRejectedError`, fires `onFailed` + `onPhase('failed', { ..., error })`,
24
27
  then throws.
25
28
  4. **`awaitReceiptWithHooks(options)`** — chain-side helper. Awaits
26
- `waitForTransactionReceipt`, fires `onConfirmed` +
27
- `onPhase('confirmed', { hash, receipt })` on success, or `onFailed`
28
- + `onPhase('failed', ...)` with a typed `ContractRevertedError` on
29
- `status: 'reverted'`. Other receipt-await errors re-thrown unchanged
30
- after firing the failure hooks.
29
+ `waitForTransactionReceipt`, fetches the containing block (so
30
+ downstream consumers don't re-fetch it for `timestamp` /
31
+ `baseFeePerGas`), then fires `onConfirmed` +
32
+ `onPhase('confirmed', { ..., hash, receipt, block })` on success,
33
+ or `onFailed` + `onPhase('failed', ...)` with a typed
34
+ `ContractRevertedError` on `status: 'reverted'`. Other receipt-await
35
+ errors re-thrown unchanged after firing the failure hooks. Pass
36
+ `includeBlock: false` to skip the block fetch.
31
37
  5. **`TX_STATUS` / `TrackedTx`** — vocabulary for "this transaction is
32
38
  in flight" UIs (toast strips, inline indicators, history panes) so
33
39
  they can sit on top of any tracker without redefining state names.
@@ -93,28 +99,35 @@ export class MyClient {
93
99
  private wallet: WalletAdapter,
94
100
  private chainId: number,
95
101
  private escrow: `0x${string}`,
96
- /** Optional global / analytics channel — fires alongside the per-call hook. */
97
- private onTransactionHash?: (hash: `0x${string}`) => void,
102
+ /**
103
+ * Optional global / analytics channel — fires alongside the per-call hook.
104
+ * Receives the rich `{ chainId, request, hash }` info bag, so analytics
105
+ * observers see the originating chain and request without a side channel.
106
+ */
107
+ private onTransactionHash?: WriteHookParams['onTransactionHash'],
98
108
  ) {}
99
109
 
100
110
  async deposit(params: MyWriteParams & WriteHookParams) {
111
+ const request = {
112
+ to: this.escrow,
113
+ data: this.encodeDeposit(params),
114
+ chainId: this.chainId,
115
+ }
101
116
  try {
102
117
  const hash = await sendTransactionWithHooks({
103
118
  wallet: this.wallet,
104
- request: {
105
- to: this.escrow,
106
- data: this.encodeDeposit(params),
107
- chainId: this.chainId,
108
- },
119
+ request,
109
120
  hooks: params,
110
121
  onTransactionHash: this.onTransactionHash,
111
122
  })
112
123
  const receipt = await awaitReceiptWithHooks({
113
124
  publicClient: this.publicClient,
114
125
  hash,
126
+ request, // carried into every phase event as part of TxContext
115
127
  hooks: params,
116
128
  })
117
129
  // protocol-specific work here (decode logs, etc.) — onConfirmed already fired
130
+ // with { chainId, request, hash, receipt, block } in scope
118
131
  return { hash, receipt }
119
132
  } catch (err) {
120
133
  if (err instanceof WalletRejectedError) {
@@ -131,28 +144,33 @@ export class MyClient {
131
144
 
132
145
  `sendTransactionWithHooks` guarantees:
133
146
 
134
- - `onAwaitingSignature` fires **once**, **immediately before**
135
- `wallet.sendTransaction`.
136
- - `onTransactionHash` (per-call **and** global) fires **once each**,
137
- **after** `sendTransaction` resolves and **before** any receipt-await.
147
+ - `onAwaitingSignature` fires **once** with `{ chainId, request }`,
148
+ **immediately before** `wallet.sendTransaction`.
149
+ - `onTransactionHash` (per-call **and** global) fires **once each**
150
+ with `{ chainId, request, hash }`, **after** `sendTransaction`
151
+ resolves and **before** any receipt-await.
138
152
  - Wallet rejections — detected via the three-signal check in
139
153
  `@valve-tech/viem-errors` (EIP-1193 `code === 4001`, viem class name,
140
154
  message regex, anywhere in the cause chain) — are thrown as
141
- `WalletRejectedError`. `onFailed` fires with the rejection error
142
- before the throw.
143
- - Any other thrown error fires `onFailed` and re-throws unchanged so
144
- the SDK keeps control of its error mapping.
155
+ `WalletRejectedError`. `onFailed` fires with
156
+ `{ chainId, request, error: <WalletRejectedError> }` before the throw.
157
+ - Any other thrown error fires `onFailed` (with `error: <thrown>`) and
158
+ re-throws unchanged so the SDK keeps control of its error mapping.
145
159
 
146
160
  `awaitReceiptWithHooks` guarantees:
147
161
 
148
- - On `receipt.status === 'success'`: fires `onConfirmed(receipt)` and
162
+ - On `receipt.status === 'success'`: fetches the containing block
163
+ (unless `includeBlock: false`), then fires
164
+ `onConfirmed({ chainId, request, hash, receipt, block? })` and
149
165
  resolves with the receipt.
150
- - On `receipt.status === 'reverted'`: fires `onFailed` with a
151
- `ContractRevertedError` (carrying `hash` + the full `receipt` for
152
- log inspection) and throws it.
166
+ - On `receipt.status === 'reverted'`: fetches the block, then fires
167
+ `onFailed({ chainId, request, hash, receipt, block?, error: <ContractRevertedError> })`
168
+ and throws the error. `ContractRevertedError` carries `hash` + the
169
+ full `receipt` for log inspection.
153
170
  - On any thrown error during the receipt-await (network / RPC /
154
- abort): fires `onFailed` with the original error and re-throws
155
- unchanged.
171
+ abort): fires `onFailed({ chainId, request, error })` (no
172
+ `hash`/`receipt`/`block`) and re-throws unchanged. The block fetch
173
+ is skipped when the receipt itself fails to arrive.
156
174
 
157
175
  A `WriteHookParams` consumer (toast strip, inline indicator, etc.)
158
176
  that wires all four hooks can drive its full state machine — pre-wallet
@@ -184,11 +202,13 @@ function subtitle(tx: TrackedTx): string {
184
202
  | `WalletAdapter` | type | `{ address?, sendTransaction(req), readContract?(req) }` |
185
203
  | `WalletSendTransactionRequest` | type | EIP-1559 send shape — `{ to, data, value?, chainId, maxFeePerGas?, maxPriorityFeePerGas? }` |
186
204
  | `WalletReadContractRequest` | type | `{ address, abi, functionName, args?, chainId? }` |
187
- | `WriteHookParams` | type | six named hooks (`onAwaitingSignature`, `onTransactionHash`, `onConfirmed`, `onFailed`, `onDropped`, `onReplaced`) + `onPhase(event)` |
205
+ | `WriteHookParams` | type | six named hooks (`onAwaitingSignature`, `onTransactionHash`, `onConfirmed`, `onFailed`, `onDropped`, `onReplaced`) + `onPhase(event)`. Every callback receives a `TxContext<Steps[K]>` info bag. |
188
206
  | `WritePhase` | type | `'preparing' \| 'awaiting-signature' \| 'pending' \| 'confirmed' \| 'failed' \| 'dropped' \| 'replaced'` |
189
- | `WritePhaseEvent` | type | discriminated union of phase + payload |
207
+ | `WritePhaseSteps` | interface | per-phase data delta map. `pending: { hash }`, `confirmed: { hash, receipt, block? }`, etc. Open to declaration merging. |
208
+ | `TxContext<Extra>` | type | `{ chainId, request } & Extra`. The always-present context intersected with the per-phase delta. Defaults `Extra` to `object`. |
209
+ | `WritePhaseEvent` | type | derived `{ [K in keyof WritePhaseSteps]: { phase: K } & TxContext<WritePhaseSteps[K]> }[keyof WritePhaseSteps]`. |
190
210
  | `sendTransactionWithHooks(opts)` | function | `{ wallet, request, hooks?, onTransactionHash? } → Promise<Hex>`. Wallet-side helper. |
191
- | `awaitReceiptWithHooks(opts)` | function | `{ publicClient, hash, hooks? } → Promise<TransactionReceipt>`. Chain-side helper. |
211
+ | `awaitReceiptWithHooks(opts)` | function | `{ publicClient, hash, request, includeBlock?, hooks? } → Promise<TransactionReceipt>`. Chain-side helper; fetches the containing block by default. |
192
212
  | `WalletRejectedError` | class | `Error` subclass with `cause: Error`. Thrown by `sendTransactionWithHooks` on user rejection. |
193
213
  | `ContractRevertedError` | class | `Error` subclass with `hash` + `receipt`. Thrown by `awaitReceiptWithHooks` on `status: reverted`. |
194
214
  | `SendTransactionWithHooksOptions` / `AwaitReceiptWithHooksOptions` / `ReceiptAwaiter` | type | options + minimal client shape |
@@ -206,6 +226,22 @@ function subtitle(tx: TrackedTx): string {
206
226
  state-machine consumers. Fire-ers fire BOTH shapes for every
207
227
  transition — exactly once each. Wiring named hooks doesn't preclude
208
228
  `onPhase` and vice versa.
229
+ - **Rich payloads, not bare arguments.** Every event carries
230
+ `TxContext` (`chainId` + the original `request`) on top of its
231
+ phase-specific fields. The lib already has all of that in scope
232
+ when it fires events; the alternative — `(receipt) => void` and
233
+ `(hash) => void` — forces every consumer to maintain a side-channel
234
+ `hash → request` map and call `client.chain.id` from inside their
235
+ callbacks. `awaitReceiptWithHooks` also fetches the containing
236
+ block once and includes it on `confirmed` / receipt-bearing
237
+ `failed` events, so downstream consumers (notably
238
+ `@valve-tech/tx-tracker`) skip the round trip.
239
+ - **`WritePhaseSteps` is the single source of truth for phase
240
+ shapes.** `WritePhaseEvent` is derived mechanically as
241
+ `{ [K in keyof WritePhaseSteps]: { phase: K } & TxContext<WritePhaseSteps[K]> }`,
242
+ so adding a phase is one entry in the map plus a fire-er. Adding a
243
+ shared field is one entry in `TxContext`. Both stay in lockstep
244
+ with the named hook signatures.
209
245
  - **`onFailed` is the unified failure callback for revert / rejection /
210
246
  network errors.** Wallet rejection, on-chain revert, and network
211
247
  errors all flow through it. `instanceof` against
package/dist/hooks.d.ts CHANGED
@@ -29,8 +29,17 @@
29
29
  *
30
30
  * Fire-ers fire BOTH shapes for every transition — exactly once each —
31
31
  * so consumers can choose which to wire without affecting the other.
32
+ *
33
+ * Every payload carries a `TxContext` (`chainId` + `request`) so
34
+ * consumers never have to side-channel the originating chain or the
35
+ * original send request to use the events. `confirmed` and
36
+ * receipt-bearing `failed` events also carry the containing `Block` so
37
+ * downstream trackers don't have to re-fetch it for timestamp / fee
38
+ * analytics — that's the "don't make consumers re-gather what we
39
+ * already have" rule the events are designed around.
32
40
  */
33
- import type { Hex, TransactionReceipt } from 'viem';
41
+ import type { Block, Hex, TransactionReceipt } from 'viem';
42
+ import type { WalletSendTransactionRequest } from './wallet.js';
34
43
  /**
35
44
  * Every lifecycle phase a tracked transaction can be in, from intent
36
45
  * through terminal observation. Carriers (helpers, trackers, SDKs)
@@ -39,45 +48,98 @@ import type { Hex, TransactionReceipt } from 'viem';
39
48
  */
40
49
  export type WritePhase = 'preparing' | 'awaiting-signature' | 'pending' | 'confirmed' | 'failed' | 'dropped' | 'replaced';
41
50
  /**
42
- * Discriminated-union event payload for the `onPhase` callback. Switch
43
- * on `event.phase` and TypeScript narrows the rest of the fields
44
- * automatically — no `event.context?.receipt` indirection.
51
+ * Per-phase data delta. Each entry is exactly the fields available at
52
+ * that phase BEYOND the always-present `TxContext` (`chainId`,
53
+ * `request`).
54
+ *
55
+ * Adding a new phase is one entry here plus a fire-er; adding a new
56
+ * shared field is one entry in `TxContext`. The `WritePhaseEvent`
57
+ * union is derived from this map, so variants stay in lockstep
58
+ * mechanically.
59
+ *
60
+ * Declared as an `interface` (not a `type`) so consumers can use
61
+ * declaration merging to extend the map with their own phases — useful
62
+ * for trackers that surface implementation-specific transitions
63
+ * without forking the union.
64
+ *
65
+ * `block` is optional on `confirmed` / `failed` / `replaced` because
66
+ * `awaitReceiptWithHooks` allows opting out of the block fetch via
67
+ * `includeBlock: false`. Trackers that observe `replaced` from the
68
+ * mempool may also fire it without a receipt.
69
+ */
70
+ export interface WritePhaseSteps {
71
+ preparing: object;
72
+ 'awaiting-signature': object;
73
+ pending: {
74
+ hash: Hex;
75
+ };
76
+ confirmed: {
77
+ hash: Hex;
78
+ receipt: TransactionReceipt;
79
+ block?: Block;
80
+ };
81
+ failed: {
82
+ error: Error;
83
+ hash?: Hex;
84
+ receipt?: TransactionReceipt;
85
+ block?: Block;
86
+ };
87
+ dropped: {
88
+ hash: Hex;
89
+ };
90
+ replaced: {
91
+ original: Hex;
92
+ replacement: Hex;
93
+ receipt?: TransactionReceipt;
94
+ block?: Block;
95
+ };
96
+ }
97
+ /**
98
+ * Always-present transaction context surrounding every phase event.
99
+ * Carries the chain identity and the original send request so
100
+ * consumers don't have to maintain a side-channel `hash → request` map
101
+ * or call `client.chain.id` from inside their callbacks.
102
+ *
103
+ * `Extra` is the per-phase delta from `WritePhaseSteps[K]`. Defaulting
104
+ * `Extra` to `object` lets the bare `TxContext` describe phases with
105
+ * no extra fields (`preparing`, `awaiting-signature`).
106
+ */
107
+ export type TxContext<Extra extends object = object> = {
108
+ /** Chain id this transaction targets. Pulled off `request.chainId`. */
109
+ chainId: number;
110
+ /**
111
+ * The fully-formed wallet send request as passed into the SDK
112
+ * (`to`, `data`, `value`, `chainId`, gas inputs). Carried verbatim
113
+ * so consumers can construct a tracked-tx record or replay the
114
+ * input without extra plumbing.
115
+ */
116
+ request: WalletSendTransactionRequest;
117
+ } & Extra;
118
+ /**
119
+ * Discriminated-union event payload for the `onPhase` callback.
120
+ * Switch on `event.phase` and TypeScript narrows the rest of the
121
+ * fields automatically — no `event.context?.receipt` indirection.
122
+ *
123
+ * Derived mechanically from `WritePhaseSteps` × `TxContext` so adding
124
+ * a phase to the map is the only edit needed to extend the union.
45
125
  */
46
126
  export type WritePhaseEvent = {
47
- phase: 'preparing';
48
- } | {
49
- phase: 'awaiting-signature';
50
- } | {
51
- phase: 'pending';
52
- hash: Hex;
53
- } | {
54
- phase: 'confirmed';
55
- hash: Hex;
56
- receipt: TransactionReceipt;
57
- } | {
58
- phase: 'failed';
59
- error: Error;
60
- hash?: Hex;
61
- receipt?: TransactionReceipt;
62
- } | {
63
- phase: 'dropped';
64
- hash: Hex;
65
- } | {
66
- phase: 'replaced';
67
- original: Hex;
68
- replacement: Hex;
69
- receipt?: TransactionReceipt;
70
- };
127
+ [K in keyof WritePhaseSteps]: {
128
+ phase: K;
129
+ } & TxContext<WritePhaseSteps[K]>;
130
+ }[keyof WritePhaseSteps];
71
131
  /**
72
132
  * Per-call hooks fired at real boundaries during a tracked tx's
73
133
  * lifecycle. Every field is optional. SDKs and trackers fire whichever
74
134
  * subset corresponds to phases they actually observe; consumers wire
75
135
  * only the ones their UI needs.
76
136
  *
77
- * Named hooks vs `onPhase`: complementary, not alternatives. A
78
- * fire-er fires both for every transition the named hook (if a
79
- * consumer wired it) and `onPhase` (if a consumer wired it) so no
80
- * transition is observable from one shape but not the other.
137
+ * Each named hook receives a `TxContext<WritePhaseSteps[K]>` info bag
138
+ * matching the phase. The `onPhase` complement receives the full
139
+ * discriminated union. Fire-ers fire both for every transitionthe
140
+ * named hook (if a consumer wired it) and `onPhase` (if a consumer
141
+ * wired it) — so no transition is observable from one shape but not
142
+ * the other.
81
143
  */
82
144
  export interface WriteHookParams {
83
145
  /**
@@ -85,34 +147,42 @@ export interface WriteHookParams {
85
147
  * from "preparing" to "awaiting wallet signature" at the precise
86
148
  * boundary, regardless of how much pre-wallet work the SDK did.
87
149
  */
88
- onAwaitingSignature?: () => void;
150
+ onAwaitingSignature?: (info: TxContext<WritePhaseSteps['awaiting-signature']>) => void;
89
151
  /**
90
- * Called once with the on-chain tx hash, immediately after
91
- * `sendTransaction` resolves and *before* any receipt-await. UI flips
92
- * from "awaiting" to "pending" the moment the hash exists.
152
+ * Called once with the on-chain tx hash plus full context,
153
+ * immediately after `sendTransaction` resolves and *before* any
154
+ * receipt-await. UI flips from "awaiting" to "pending" the moment
155
+ * the hash exists.
93
156
  *
94
157
  * Per-call vs constructor-level: SDKs may also expose a separate
95
158
  * constructor-level `onTransactionHash` channel for analytics /
96
- * global observers — they're complementary, fire on the same line.
159
+ * global observers — they're complementary, fire on the same line
160
+ * with the same payload.
97
161
  */
98
- onTransactionHash?: (hash: Hex) => void;
162
+ onTransactionHash?: (info: TxContext<WritePhaseSteps['pending']>) => void;
99
163
  /**
100
- * Called once with the mined receipt when `receipt.status === 'success'`.
101
- * UI flips to a terminal "confirmed" state. Receives the full receipt
102
- * so consumers can extract block number, gas used, decoded events.
164
+ * Called once when `receipt.status === 'success'`. Receives the full
165
+ * info bag `hash`, `receipt`, optional `block` (present unless
166
+ * `includeBlock: false` was passed), plus `chainId` and `request`
167
+ * so consumers don't re-fetch the block for timestamp / baseFee
168
+ * UI.
103
169
  */
104
- onConfirmed?: (receipt: TransactionReceipt) => void;
170
+ onConfirmed?: (info: TxContext<WritePhaseSteps['confirmed']>) => void;
105
171
  /**
106
- * Called once with the underlying error on any terminal failure that
107
- * is NOT a replacement or a drop:
172
+ * Called once with the underlying error (and any context available
173
+ * at the failure point) on any terminal failure that is NOT a
174
+ * replacement or a drop:
108
175
  * - wallet rejection (`WalletRejectedError`)
109
176
  * - on-chain revert (`ContractRevertedError`)
110
177
  * - any other thrown error from the wallet or RPC.
111
178
  *
112
- * Use `instanceof` against `WalletRejectedError` / `ContractRevertedError`
113
- * to discriminate; everything else is a plain `Error`.
179
+ * Use `instanceof` against `WalletRejectedError` /
180
+ * `ContractRevertedError` to discriminate; everything else is a
181
+ * plain `Error`. Wallet-side failures carry no `hash` / `receipt`;
182
+ * receipt-bearing failures (revert) carry both, plus the block
183
+ * (unless `includeBlock: false`).
114
184
  */
115
- onFailed?: (error: Error) => void;
185
+ onFailed?: (info: TxContext<WritePhaseSteps['failed']>) => void;
116
186
  /**
117
187
  * Called once when a tracker has determined the tx will not be
118
188
  * included — typically: not seen in mempool for N consecutive blocks
@@ -124,24 +194,18 @@ export interface WriteHookParams {
124
194
  * observation. Wire this against a `tx-tracker` instance, not against
125
195
  * `awaitReceiptWithHooks`.
126
196
  */
127
- onDropped?: (info: {
128
- hash: Hex;
129
- }) => void;
197
+ onDropped?: (info: TxContext<WritePhaseSteps['dropped']>) => void;
130
198
  /**
131
199
  * Called once when a tracker observes that a *different* tx with the
132
200
  * same nonce mined in place of the one we were watching — typically
133
201
  * the user's own speed-up / cancel from their wallet, or a
134
202
  * fee-replacement broadcast separately.
135
203
  *
136
- * `replacement.receipt` is included when the replacement has been
137
- * mined; trackers may emit `replaced` with no receipt if they only
138
- * saw the replacement in the mempool.
204
+ * `info.receipt` and `info.block` are populated when the replacement
205
+ * has been mined; trackers may emit `replaced` without them if they
206
+ * only saw the replacement in the mempool.
139
207
  */
140
- onReplaced?: (info: {
141
- original: Hex;
142
- replacement: Hex;
143
- receipt?: TransactionReceipt;
144
- }) => void;
208
+ onReplaced?: (info: TxContext<WritePhaseSteps['replaced']>) => void;
145
209
  /**
146
210
  * Single-callback complement to the named hooks. Fires for every
147
211
  * lifecycle transition with a discriminated-union payload. Useful
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAA;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,oBAAoB,GACpB,SAAS,GACT,WAAW,GACX,QAAQ,GACR,SAAS,GACT,UAAU,CAAA;AAEd;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,oBAAoB,CAAA;CAAE,GAC/B;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,GAC/B;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,GAC9D;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAC;IAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAAE,GAC3E;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,GAC/B;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAC;IAAC,WAAW,EAAE,GAAG,CAAC;IAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAExF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAA;IAChC;;;;;;;;OAQG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAA;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACnD;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACjC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,KAAK,IAAI,CAAA;IACzC;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,GAAG,CAAC;QAAC,WAAW,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAA;IAC9F;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;CAC3C"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAA;AAC1D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,oBAAoB,GACpB,SAAS,GACT,WAAW,GACX,QAAQ,GACR,SAAS,GACT,UAAU,CAAA;AAEd;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,OAAO,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAA;IACtB,SAAS,EAAE;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,EAAE,kBAAkB,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAA;IACpE,MAAM,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAA;IACjF,OAAO,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAA;IACtB,QAAQ,EAAE;QAAE,QAAQ,EAAE,GAAG,CAAC;QAAC,WAAW,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAA;CAC3F;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,SAAS,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI;IACrD,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,OAAO,EAAE,4BAA4B,CAAA;CACtC,GAAG,KAAK,CAAA;AAET;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;KAC3B,CAAC,IAAI,MAAM,eAAe,GAAG;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CAC3E,CAAC,MAAM,eAAe,CAAC,CAAA;AAExB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC,KAAK,IAAI,CAAA;IACtF;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,CAAA;IACzE;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAA;IACrE;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,CAAA;IAC/D;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,CAAA;IACjE;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,KAAK,IAAI,CAAA;IACnE;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;CAC3C"}
package/dist/hooks.js CHANGED
@@ -29,6 +29,14 @@
29
29
  *
30
30
  * Fire-ers fire BOTH shapes for every transition — exactly once each —
31
31
  * so consumers can choose which to wire without affecting the other.
32
+ *
33
+ * Every payload carries a `TxContext` (`chainId` + `request`) so
34
+ * consumers never have to side-channel the originating chain or the
35
+ * original send request to use the events. `confirmed` and
36
+ * receipt-bearing `failed` events also carry the containing `Block` so
37
+ * downstream trackers don't have to re-fetch it for timestamp / fee
38
+ * analytics — that's the "don't make consumers re-gather what we
39
+ * already have" rule the events are designed around.
32
40
  */
33
41
  export {};
34
42
  //# sourceMappingURL=hooks.js.map
package/dist/hooks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG"}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG"}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Public API of `@valve-tech/wallet-adapter`.
3
3
  */
4
4
  export type { WalletAdapter, WalletSendTransactionRequest, WalletReadContractRequest, } from './wallet.js';
5
- export type { WriteHookParams, WritePhase, WritePhaseEvent, } from './hooks.js';
5
+ export type { TxContext, WriteHookParams, WritePhase, WritePhaseEvent, WritePhaseSteps, } from './hooks.js';
6
6
  export { TX_STATUS, TX_FLOW, STALE_TX_AGE_MS, CONFIRMED_DISPLAY_MS, FAILED_DISPLAY_MS, } from './tx-status.js';
7
7
  export type { TrackedTx, TrackedTxGas, TrackedTxStatus, TxFlow, TxConfirmedCallback, } from './tx-status.js';
8
8
  export { sendTransactionWithHooks, WalletRejectedError, type SendTransactionWithHooksOptions, } from './send.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EACV,aAAa,EACb,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,aAAa,CAAA;AAEpB,YAAY,EACV,eAAe,EACf,UAAU,EACV,eAAe,GAChB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,SAAS,EACT,OAAO,EACP,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,SAAS,EACT,YAAY,EACZ,eAAe,EACf,MAAM,EACN,mBAAmB,GACpB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,KAAK,+BAA+B,GACrC,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,cAAc,GACpB,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EACV,aAAa,EACb,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,aAAa,CAAA;AAEpB,YAAY,EACV,SAAS,EACT,eAAe,EACf,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,SAAS,EACT,OAAO,EACP,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,SAAS,EACT,YAAY,EACZ,eAAe,EACf,MAAM,EACN,mBAAmB,GACpB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,KAAK,+BAA+B,GACrC,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,cAAc,GACpB,MAAM,cAAc,CAAA"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH,OAAO,EACL,SAAS,EACT,OAAO,EACP,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AAUvB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GAEpB,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GAGtB,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgBH,OAAO,EACL,SAAS,EACT,OAAO,EACP,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AAUvB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GAEpB,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GAGtB,MAAM,cAAc,CAAA"}
package/dist/receipt.d.ts CHANGED
@@ -3,14 +3,19 @@
3
3
  *
4
4
  * After the wallet returns a hash, an SDK has to:
5
5
  * 1. Await the receipt.
6
- * 2. Distinguish `success` from `reverted`.
7
- * 3. Fire `onConfirmed` (success) or `onFailed` with a typed revert
6
+ * 2. Pull the containing block (so consumers don't re-fetch it just
7
+ * for `timestamp` / `baseFeePerGas`).
8
+ * 3. Distinguish `success` from `reverted`.
9
+ * 4. Fire `onConfirmed` (success) or `onFailed` with a typed revert
8
10
  * error (reverted), plus `onPhase` for the same transition, so any
9
11
  * UI wired against `WriteHookParams` flips to a terminal state.
10
12
  *
11
- * Without a helper for this, every SDK re-implements the receipt-await +
12
- * status-check + typed-error-throw block — the same drift trap
13
- * `sendTransactionWithHooks` was added to fix on the wallet side.
13
+ * Without a helper for this, every SDK re-implements: the receipt-await
14
+ * + block-fetch + status-check + typed-error-throw block — and worse,
15
+ * every SDK either re-fetches the block in its own callbacks or skips
16
+ * it and forces every consumer to fetch it. The amortization here is
17
+ * the whole point: one `getBlock` call inside this helper, every
18
+ * downstream consumer skips it.
14
19
  *
15
20
  * Drop detection (tx vanished from mempool without inclusion) and
16
21
  * replacement detection are deliberately NOT in this helper. They
@@ -20,8 +25,9 @@
20
25
  * defines `onDropped` / `onReplaced` so consumers can wire them once;
21
26
  * this helper just doesn't fire them.
22
27
  */
23
- import type { Hex, TransactionReceipt } from 'viem';
28
+ import type { Block, Hex, TransactionReceipt } from 'viem';
24
29
  import type { WriteHookParams } from './hooks.js';
30
+ import type { WalletSendTransactionRequest } from './wallet.js';
25
31
  /**
26
32
  * Thrown by `awaitReceiptWithHooks` when the receipt arrives with
27
33
  * `status === 'reverted'`. The full receipt is preserved so consumers
@@ -42,30 +48,60 @@ export declare class ContractRevertedError extends Error {
42
48
  * Minimal viem `PublicClient` slice the helper needs. Defining it
43
49
  * locally lets consumers pass a viem client OR a hand-rolled mock
44
50
  * without depending on the full `PublicClient` surface.
51
+ *
52
+ * `getBlock` is invoked once on receipt success / revert (when
53
+ * `includeBlock` is left at its default of `true`) so the resulting
54
+ * `confirmed` / `failed` event payload carries the block timestamp,
55
+ * baseFeePerGas, etc. without forcing every consumer to fetch it.
45
56
  */
46
57
  export interface ReceiptAwaiter {
47
58
  waitForTransactionReceipt(args: {
48
59
  hash: Hex;
49
60
  }): Promise<TransactionReceipt>;
61
+ getBlock(args: {
62
+ blockHash: Hex;
63
+ }): Promise<Block>;
50
64
  }
51
65
  export interface AwaitReceiptWithHooksOptions {
52
- /** A viem `PublicClient` (or anything with the same `waitForTransactionReceipt` shape). */
66
+ /** A viem `PublicClient` (or anything with the same shape). */
53
67
  publicClient: ReceiptAwaiter;
54
68
  /** The hash returned by `sendTransactionWithHooks` (or any other broadcast path). */
55
69
  hash: Hex;
56
- /** Per-call hooks. `onMined` and `onFailed` fire from this helper. */
70
+ /**
71
+ * The original send request. Carried into all phase events as part
72
+ * of the always-present `TxContext` so consumers don't have to
73
+ * maintain a side-channel `hash → request` map.
74
+ */
75
+ request: WalletSendTransactionRequest;
76
+ /**
77
+ * Whether to fetch and attach the containing `Block` to `confirmed`
78
+ * and revert-`failed` event payloads. Defaults to `true` — the
79
+ * helper is the right place to amortize the block fetch on behalf of
80
+ * every downstream consumer. Pass `false` to skip the extra RPC
81
+ * round trip when no consumer needs block-level data.
82
+ */
83
+ includeBlock?: boolean;
84
+ /** Per-call hooks. `onConfirmed` and `onFailed` fire from this helper. */
57
85
  hooks?: WriteHookParams;
58
86
  }
59
87
  /**
60
- * Await a transaction receipt and fire the post-hash lifecycle hooks.
88
+ * Await a transaction receipt and fire the post-hash lifecycle hooks
89
+ * with rich payloads (chainId, request, hash, receipt, block).
90
+ *
61
91
  * Throws `ContractRevertedError` on revert. Other errors during the
62
92
  * receipt-await are re-thrown unchanged after `onFailed` fires.
63
93
  *
64
94
  * @example
65
95
  * ```ts
66
96
  * const hash = await sendTransactionWithHooks({ wallet, request, hooks })
67
- * const receipt = await awaitReceiptWithHooks({ publicClient, hash, hooks })
68
- * // Both onMined / onFailed have fired by the time we get here.
97
+ * const receipt = await awaitReceiptWithHooks({
98
+ * publicClient,
99
+ * hash,
100
+ * request, // same shape passed to sendTransactionWithHooks
101
+ * hooks,
102
+ * })
103
+ * // onConfirmed / onFailed have fired by the time we get here, with
104
+ * // chainId + request + hash + receipt + block in scope.
69
105
  * ```
70
106
  */
71
107
  export declare function awaitReceiptWithHooks(options: AwaitReceiptWithHooksOptions): Promise<TransactionReceipt>;
@@ -1 +1 @@
1
- {"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAA;IAClB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;gBAExB,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB;CAMnD;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,yBAAyB,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CAC5E;AAED,MAAM,WAAW,4BAA4B;IAC3C,2FAA2F;IAC3F,YAAY,EAAE,cAAc,CAAA;IAC5B,qFAAqF;IACrF,IAAI,EAAE,GAAG,CAAA;IACT,sEAAsE;IACtE,KAAK,CAAC,EAAE,eAAe,CAAA;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,kBAAkB,CAAC,CAuB7B"}
1
+ {"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAA;AAC1D,OAAO,KAAK,EAAa,eAAe,EAAmB,MAAM,YAAY,CAAA;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA;AAE/D;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAA;IAClB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;gBAExB,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB;CAMnD;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,yBAAyB,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC3E,QAAQ,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,4BAA4B;IAC3C,+DAA+D;IAC/D,YAAY,EAAE,cAAc,CAAA;IAC5B,qFAAqF;IACrF,IAAI,EAAE,GAAG,CAAA;IACT;;;;OAIG;IACH,OAAO,EAAE,4BAA4B,CAAA;IACrC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,eAAe,CAAA;CACxB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,kBAAkB,CAAC,CA0C7B"}
package/dist/receipt.js CHANGED
@@ -3,14 +3,19 @@
3
3
  *
4
4
  * After the wallet returns a hash, an SDK has to:
5
5
  * 1. Await the receipt.
6
- * 2. Distinguish `success` from `reverted`.
7
- * 3. Fire `onConfirmed` (success) or `onFailed` with a typed revert
6
+ * 2. Pull the containing block (so consumers don't re-fetch it just
7
+ * for `timestamp` / `baseFeePerGas`).
8
+ * 3. Distinguish `success` from `reverted`.
9
+ * 4. Fire `onConfirmed` (success) or `onFailed` with a typed revert
8
10
  * error (reverted), plus `onPhase` for the same transition, so any
9
11
  * UI wired against `WriteHookParams` flips to a terminal state.
10
12
  *
11
- * Without a helper for this, every SDK re-implements the receipt-await +
12
- * status-check + typed-error-throw block — the same drift trap
13
- * `sendTransactionWithHooks` was added to fix on the wallet side.
13
+ * Without a helper for this, every SDK re-implements: the receipt-await
14
+ * + block-fetch + status-check + typed-error-throw block — and worse,
15
+ * every SDK either re-fetches the block in its own callbacks or skips
16
+ * it and forces every consumer to fetch it. The amortization here is
17
+ * the whole point: one `getBlock` call inside this helper, every
18
+ * downstream consumer skips it.
14
19
  *
15
20
  * Drop detection (tx vanished from mempool without inclusion) and
16
21
  * replacement detection are deliberately NOT in this helper. They
@@ -38,37 +43,63 @@ export class ContractRevertedError extends Error {
38
43
  }
39
44
  }
40
45
  /**
41
- * Await a transaction receipt and fire the post-hash lifecycle hooks.
46
+ * Await a transaction receipt and fire the post-hash lifecycle hooks
47
+ * with rich payloads (chainId, request, hash, receipt, block).
48
+ *
42
49
  * Throws `ContractRevertedError` on revert. Other errors during the
43
50
  * receipt-await are re-thrown unchanged after `onFailed` fires.
44
51
  *
45
52
  * @example
46
53
  * ```ts
47
54
  * const hash = await sendTransactionWithHooks({ wallet, request, hooks })
48
- * const receipt = await awaitReceiptWithHooks({ publicClient, hash, hooks })
49
- * // Both onMined / onFailed have fired by the time we get here.
55
+ * const receipt = await awaitReceiptWithHooks({
56
+ * publicClient,
57
+ * hash,
58
+ * request, // same shape passed to sendTransactionWithHooks
59
+ * hooks,
60
+ * })
61
+ * // onConfirmed / onFailed have fired by the time we get here, with
62
+ * // chainId + request + hash + receipt + block in scope.
50
63
  * ```
51
64
  */
52
65
  export async function awaitReceiptWithHooks(options) {
53
- const { publicClient, hash, hooks } = options;
66
+ const { publicClient, hash, request, includeBlock = true, hooks } = options;
67
+ const ctx = { chainId: request.chainId, request };
54
68
  let receipt;
55
69
  try {
56
70
  receipt = await publicClient.waitForTransactionReceipt({ hash });
57
71
  }
58
72
  catch (err) {
59
73
  const failure = err instanceof Error ? err : new Error(String(err));
60
- hooks?.onFailed?.(failure);
61
- hooks?.onPhase?.({ phase: 'failed', error: failure, hash });
74
+ const failedInfo = { ...ctx, error: failure };
75
+ hooks?.onFailed?.(failedInfo);
76
+ hooks?.onPhase?.({ phase: 'failed', ...failedInfo });
62
77
  throw failure;
63
78
  }
79
+ const block = includeBlock
80
+ ? await publicClient.getBlock({ blockHash: receipt.blockHash })
81
+ : undefined;
64
82
  if (receipt.status === 'reverted') {
65
83
  const revert = new ContractRevertedError(hash, receipt);
66
- hooks?.onFailed?.(revert);
67
- hooks?.onPhase?.({ phase: 'failed', error: revert, hash, receipt });
84
+ const failedInfo = {
85
+ ...ctx,
86
+ error: revert,
87
+ hash,
88
+ receipt,
89
+ ...(block ? { block } : {}),
90
+ };
91
+ hooks?.onFailed?.(failedInfo);
92
+ hooks?.onPhase?.({ phase: 'failed', ...failedInfo });
68
93
  throw revert;
69
94
  }
70
- hooks?.onConfirmed?.(receipt);
71
- hooks?.onPhase?.({ phase: 'confirmed', hash, receipt });
95
+ const confirmedInfo = {
96
+ ...ctx,
97
+ hash,
98
+ receipt,
99
+ ...(block ? { block } : {}),
100
+ };
101
+ hooks?.onConfirmed?.(confirmedInfo);
102
+ hooks?.onPhase?.({ phase: 'confirmed', ...confirmedInfo });
72
103
  return receipt;
73
104
  }
74
105
  //# sourceMappingURL=receipt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"receipt.js","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH;;;;;;;;GAQG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAM9C,YAAY,IAAS,EAAE,OAA2B;QAChD,KAAK,CAAC,gCAAgC,CAAC,CAAA;QACvC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;CACF;AAoBD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAqC;IAErC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAE7C,IAAI,OAA2B,CAAA;IAC/B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,YAAY,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;IAClE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACnE,KAAK,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAA;QAC1B,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAC3D,MAAM,OAAO,CAAA;IACf,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,qBAAqB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACvD,KAAK,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAA;QACzB,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;QACnE,MAAM,MAAM,CAAA;IACd,CAAC;IAED,KAAK,EAAE,WAAW,EAAE,CAAC,OAAO,CAAC,CAAA;IAC7B,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACvD,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"receipt.js","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH;;;;;;;;GAQG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAM9C,YAAY,IAAS,EAAE,OAA2B;QAChD,KAAK,CAAC,gCAAgC,CAAC,CAAA;QACvC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;CACF;AAwCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAqC;IAErC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAC3E,MAAM,GAAG,GAAc,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,CAAA;IAE5D,IAAI,OAA2B,CAAA;IAC/B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,YAAY,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;IAClE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACnE,MAAM,UAAU,GAAyC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;QACnF,KAAK,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;QAC7B,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAE,CAAC,CAAA;QACpD,MAAM,OAAO,CAAA;IACf,CAAC;IAED,MAAM,KAAK,GAAG,YAAY;QACxB,CAAC,CAAC,MAAM,YAAY,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;QAC/D,CAAC,CAAC,SAAS,CAAA;IAEb,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,qBAAqB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACvD,MAAM,UAAU,GAAyC;YACvD,GAAG,GAAG;YACN,KAAK,EAAE,MAAM;YACb,IAAI;YACJ,OAAO;YACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5B,CAAA;QACD,KAAK,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;QAC7B,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAE,CAAC,CAAA;QACpD,MAAM,MAAM,CAAA;IACd,CAAC;IAED,MAAM,aAAa,GAA4C;QAC7D,GAAG,GAAG;QACN,IAAI;QACJ,OAAO;QACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5B,CAAA;IACD,KAAK,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,CAAA;IACnC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,aAAa,EAAE,CAAC,CAAA;IAC1D,OAAO,OAAO,CAAA;AAChB,CAAC"}
package/dist/send.d.ts CHANGED
@@ -5,13 +5,14 @@
5
5
  * This is the runtime piece of the lifecycle contract. SDK authors call
6
6
  * this from inside any write method that opens a wallet popup; it
7
7
  * guarantees:
8
- * - `onAwaitingSignature` fires exactly once, immediately before
9
- * `wallet.sendTransaction(...)`.
8
+ * - `onAwaitingSignature` fires exactly once with `TxContext` (chainId
9
+ * + request), immediately before `wallet.sendTransaction(...)`.
10
10
  * - `onTransactionHash` (per-call) and the global `onTransactionHash`
11
- * channel fire exactly once each, after `sendTransaction` resolves
12
- * and BEFORE the SDK awaits any receipt — so callers can flip their
13
- * UI from `awaiting-signature` to `pending` the moment a hash exists
14
- * instead of stalling for the full inclusion window.
11
+ * channel fire exactly once each with `TxContext + hash`, after
12
+ * `sendTransaction` resolves and BEFORE the SDK awaits any receipt
13
+ * — so callers can flip their UI from `awaiting-signature` to
14
+ * `pending` the moment a hash exists instead of stalling for the
15
+ * full inclusion window.
15
16
  * - Wallet rejections (EIP-1193 `code === 4001`, viem's
16
17
  * `UserRejectedRequestError`, or matching message text — see
17
18
  * `@valve-tech/viem-errors` for the detection signals) are converted
@@ -19,14 +20,13 @@
19
20
  * them. Non-rejection errors are re-thrown unchanged so the SDK
20
21
  * can map them to its own typed error vocabulary.
21
22
  *
22
- * Without this helper, every SDK that wants the contract has to
23
- * re-implement: the error-mapping `try/catch` block (with the
24
- * three-signal rejection check), the constructor-vs-per-call hook
25
- * fan-out, and the precise ordering relative to `sendTransaction`.
23
+ * Every payload carries `chainId` and the original `request`, so
24
+ * analytics observers and tx-tracker consumers don't need to keep a
25
+ * side-channel `hash request` map or read chainId off the client.
26
26
  */
27
27
  import type { Hex } from 'viem';
28
28
  import type { WalletAdapter, WalletSendTransactionRequest } from './wallet.js';
29
- import type { WriteHookParams } from './hooks.js';
29
+ import type { TxContext, WriteHookParams, WritePhaseSteps } from './hooks.js';
30
30
  /**
31
31
  * Thrown by `sendTransactionWithHooks` when the wallet rejection is
32
32
  * detected at any level of the cause chain. The original error is
@@ -47,16 +47,16 @@ export interface SendTransactionWithHooksOptions {
47
47
  wallet: WalletAdapter;
48
48
  /** The fully-formed send request (calldata, gas inputs, chainId). */
49
49
  request: WalletSendTransactionRequest;
50
- /** Per-call lifecycle hooks. Both fields are optional. */
50
+ /** Per-call lifecycle hooks. All fields are optional. */
51
51
  hooks?: WriteHookParams;
52
52
  /**
53
53
  * Optional global / constructor-level `onTransactionHash` channel.
54
54
  * Fires alongside `hooks.onTransactionHash` on the same line —
55
- * complementary, not alternatives. Use this for analytics or
56
- * debug-logging that should observe every write regardless of which
57
- * caller fired it.
55
+ * complementary, not alternatives. Receives the same rich
56
+ * `TxContext + hash` payload so analytics observers don't have to
57
+ * resolve chainId / request from a side channel.
58
58
  */
59
- onTransactionHash?: (hash: Hex) => void;
59
+ onTransactionHash?: (info: TxContext<WritePhaseSteps['pending']>) => void;
60
60
  }
61
61
  /**
62
62
  * Submit a transaction through the wallet adapter, firing lifecycle
@@ -72,7 +72,12 @@ export interface SendTransactionWithHooksOptions {
72
72
  * hooks: params, // user-supplied per-call hooks
73
73
  * onTransactionHash: this.onHash, // constructor-level / analytics
74
74
  * })
75
- * const receipt = await this.publicClient.waitForTransactionReceipt({ hash })
75
+ * const receipt = await awaitReceiptWithHooks({
76
+ * publicClient,
77
+ * hash,
78
+ * request: { ...prepared, ...gasInputs },
79
+ * hooks: params,
80
+ * })
76
81
  * return { hash, receipt }
77
82
  * } catch (err) {
78
83
  * if (err instanceof WalletRejectedError) {
@@ -1 +1 @@
1
- {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;gBAET,KAAK,EAAE,KAAK;CAKzB;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,iEAAiE;IACjE,MAAM,EAAE,aAAa,CAAA;IACrB,qEAAqE;IACrE,OAAO,EAAE,4BAA4B,CAAA;IACrC,0DAA0D;IAC1D,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAA;CACxC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,GAAG,CAAC,CAuBd"}
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE7E;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;gBAET,KAAK,EAAE,KAAK;CAKzB;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,iEAAiE;IACjE,MAAM,EAAE,aAAa,CAAA;IACrB,qEAAqE;IACrE,OAAO,EAAE,4BAA4B,CAAA;IACrC,yDAAyD;IACzD,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,CAAA;CAC1E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,GAAG,CAAC,CA0Bd"}
package/dist/send.js CHANGED
@@ -5,13 +5,14 @@
5
5
  * This is the runtime piece of the lifecycle contract. SDK authors call
6
6
  * this from inside any write method that opens a wallet popup; it
7
7
  * guarantees:
8
- * - `onAwaitingSignature` fires exactly once, immediately before
9
- * `wallet.sendTransaction(...)`.
8
+ * - `onAwaitingSignature` fires exactly once with `TxContext` (chainId
9
+ * + request), immediately before `wallet.sendTransaction(...)`.
10
10
  * - `onTransactionHash` (per-call) and the global `onTransactionHash`
11
- * channel fire exactly once each, after `sendTransaction` resolves
12
- * and BEFORE the SDK awaits any receipt — so callers can flip their
13
- * UI from `awaiting-signature` to `pending` the moment a hash exists
14
- * instead of stalling for the full inclusion window.
11
+ * channel fire exactly once each with `TxContext + hash`, after
12
+ * `sendTransaction` resolves and BEFORE the SDK awaits any receipt
13
+ * — so callers can flip their UI from `awaiting-signature` to
14
+ * `pending` the moment a hash exists instead of stalling for the
15
+ * full inclusion window.
15
16
  * - Wallet rejections (EIP-1193 `code === 4001`, viem's
16
17
  * `UserRejectedRequestError`, or matching message text — see
17
18
  * `@valve-tech/viem-errors` for the detection signals) are converted
@@ -19,10 +20,9 @@
19
20
  * them. Non-rejection errors are re-thrown unchanged so the SDK
20
21
  * can map them to its own typed error vocabulary.
21
22
  *
22
- * Without this helper, every SDK that wants the contract has to
23
- * re-implement: the error-mapping `try/catch` block (with the
24
- * three-signal rejection check), the constructor-vs-per-call hook
25
- * fan-out, and the precise ordering relative to `sendTransaction`.
23
+ * Every payload carries `chainId` and the original `request`, so
24
+ * analytics observers and tx-tracker consumers don't need to keep a
25
+ * side-channel `hash request` map or read chainId off the client.
26
26
  */
27
27
  import { isUserRejectionError } from '@valve-tech/viem-errors';
28
28
  /**
@@ -53,7 +53,12 @@ export class WalletRejectedError extends Error {
53
53
  * hooks: params, // user-supplied per-call hooks
54
54
  * onTransactionHash: this.onHash, // constructor-level / analytics
55
55
  * })
56
- * const receipt = await this.publicClient.waitForTransactionReceipt({ hash })
56
+ * const receipt = await awaitReceiptWithHooks({
57
+ * publicClient,
58
+ * hash,
59
+ * request: { ...prepared, ...gasInputs },
60
+ * hooks: params,
61
+ * })
57
62
  * return { hash, receipt }
58
63
  * } catch (err) {
59
64
  * if (err instanceof WalletRejectedError) {
@@ -65,10 +70,11 @@ export class WalletRejectedError extends Error {
65
70
  */
66
71
  export async function sendTransactionWithHooks(options) {
67
72
  const { wallet, request, hooks, onTransactionHash } = options;
73
+ const ctx = { chainId: request.chainId, request };
68
74
  let hash;
69
75
  try {
70
- hooks?.onAwaitingSignature?.();
71
- hooks?.onPhase?.({ phase: 'awaiting-signature' });
76
+ hooks?.onAwaitingSignature?.(ctx);
77
+ hooks?.onPhase?.({ phase: 'awaiting-signature', ...ctx });
72
78
  hash = await wallet.sendTransaction(request);
73
79
  }
74
80
  catch (err) {
@@ -77,13 +83,15 @@ export async function sendTransactionWithHooks(options) {
77
83
  : err instanceof Error
78
84
  ? err
79
85
  : new Error(String(err));
80
- hooks?.onFailed?.(failure);
81
- hooks?.onPhase?.({ phase: 'failed', error: failure });
86
+ const failedInfo = { ...ctx, error: failure };
87
+ hooks?.onFailed?.(failedInfo);
88
+ hooks?.onPhase?.({ phase: 'failed', ...failedInfo });
82
89
  throw failure;
83
90
  }
84
- onTransactionHash?.(hash);
85
- hooks?.onTransactionHash?.(hash);
86
- hooks?.onPhase?.({ phase: 'pending', hash });
91
+ const pendingInfo = { ...ctx, hash };
92
+ onTransactionHash?.(pendingInfo);
93
+ hooks?.onTransactionHash?.(pendingInfo);
94
+ hooks?.onPhase?.({ phase: 'pending', ...pendingInfo });
87
95
  return hash;
88
96
  }
89
97
  //# sourceMappingURL=send.js.map
package/dist/send.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAI9D;;;;;;GAMG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAI5C,YAAY,KAAY;QACtB,KAAK,CAAC,qCAAqC,CAAC,CAAA;QAC5C,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF;AAsBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAwC;IAExC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAA;IAE7D,IAAI,IAAS,CAAA;IACb,IAAI,CAAC;QACH,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAA;QAC9B,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAA;QACjD,IAAI,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC;YACvC,CAAC,CAAC,IAAI,mBAAmB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9E,CAAC,CAAC,GAAG,YAAY,KAAK;gBACpB,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAA;QAC1B,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QACrD,MAAM,OAAO,CAAA;IACf,CAAC;IAED,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAA;IACzB,KAAK,EAAE,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAA;IAChC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,OAAO,IAAI,CAAA;AACb,CAAC"}
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAI9D;;;;;;GAMG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAI5C,YAAY,KAAY;QACtB,KAAK,CAAC,qCAAqC,CAAC,CAAA;QAC5C,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAwC;IAExC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAA;IAC7D,MAAM,GAAG,GAAc,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,CAAA;IAE5D,IAAI,IAAS,CAAA;IACb,IAAI,CAAC;QACH,KAAK,EAAE,mBAAmB,EAAE,CAAC,GAAG,CAAC,CAAA;QACjC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,GAAG,GAAG,EAAE,CAAC,CAAA;QACzD,IAAI,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC;YACvC,CAAC,CAAC,IAAI,mBAAmB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9E,CAAC,CAAC,GAAG,YAAY,KAAK;gBACpB,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAC5B,MAAM,UAAU,GAAyC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;QACnF,KAAK,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;QAC7B,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAE,CAAC,CAAA;QACpD,MAAM,OAAO,CAAA;IACf,CAAC;IAED,MAAM,WAAW,GAA0C,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAA;IAC3E,iBAAiB,EAAE,CAAC,WAAW,CAAC,CAAA;IAChC,KAAK,EAAE,iBAAiB,EAAE,CAAC,WAAW,CAAC,CAAA;IACvC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,CAAC,CAAA;IACtD,OAAO,IAAI,CAAA;AACb,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valve-tech/wallet-adapter",
3
- "version": "0.4.0",
4
- "description": "Framework-agnostic vocabulary + runtime helpers for EVM dapp wallet integration. WalletAdapter interface (sign + send), WriteHookParams full lifecycle (onAwaitingSignature, onTransactionHash, onConfirmed, onFailed, onDropped, onReplaced) plus complementary onPhase(event) discriminated-union shape, sendTransactionWithHooks + awaitReceiptWithHooks helpers that fire the hooks at real boundaries, typed WalletRejectedError + ContractRevertedError for instanceof-discriminated catch, plus TX_STATUS / TX_FLOW / TrackedTx for tx-state UI. Lets SDKs and dapps share one contract instead of each redefining it. Part of the valve-tech/evm-toolkit synchronized release line.",
3
+ "version": "0.5.0",
4
+ "description": "Framework-agnostic vocabulary + runtime helpers for EVM dapp wallet integration. WalletAdapter interface (sign + send), WriteHookParams full lifecycle with rich TxContext payloads (chainId + original request) on every event so consumers don't side-channel; six named hooks (onAwaitingSignature, onTransactionHash, onConfirmed, onFailed, onDropped, onReplaced) plus complementary onPhase(event) discriminated-union shape derived from the WritePhaseSteps phase-map; sendTransactionWithHooks + awaitReceiptWithHooks helpers that fire the hooks at real boundaries (with awaitReceiptWithHooks fetching the containing block once on behalf of all downstream consumers); typed WalletRejectedError + ContractRevertedError for instanceof-discriminated catch; plus TX_STATUS / TX_FLOW / TrackedTx for tx-state UI. Lets SDKs and dapps share one contract instead of each redefining it. Part of the valve-tech/evm-toolkit synchronized release line.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/valve-tech/evm-toolkit/tree/main/packages/wallet-adapter#readme",
7
7
  "repository": {
@@ -46,9 +46,9 @@
46
46
  "prepare": "yarn build"
47
47
  },
48
48
  "dependencies": {
49
- "@valve-tech/viem-errors": "workspace:^"
49
+ "@valve-tech/viem-errors": "^0.5.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "viem": "^2.0.0"
53
53
  }
54
- }
54
+ }