@lukso/transaction-view-core 0.6.5 → 0.6.6-dev.45215f7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukso/transaction-view-core",
3
- "version": "0.6.5",
3
+ "version": "0.6.6-dev.45215f7",
4
4
  "description": "Core components and hooks for LUKSO transaction display",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -30,18 +30,23 @@
30
30
  }
31
31
  },
32
32
  "dependencies": {
33
- "@lit/task": "^1.0.3",
34
- "@lukso/web-components": "^1.172.18",
33
+ "@lukso/web-components": "^1.172.19",
35
34
  "@preact/signals-core": "^1.12.1",
36
35
  "ethereum-blockies-base64": "^1.0.2",
37
- "lit": "3.3.1",
38
- "@lukso/transaction-decoder": "1.3.5",
39
- "@lukso/transaction-view-headless": "0.4.5"
36
+ "viem": "^2.43.5",
37
+ "@lukso/transaction-decoder": "1.3.6-dev.45215f7",
38
+ "@lukso/transaction-view-headless": "0.4.6-dev.45215f7"
39
+ },
40
+ "peerDependencies": {
41
+ "@lit/task": "^1.0.3",
42
+ "lit": "^3.3.0"
40
43
  },
41
44
  "devDependencies": {
45
+ "@lit/task": "^1.0.3",
42
46
  "@tailwindcss/typography": "^0.5.19",
43
47
  "@types/postcss-import": "^14.0.3",
44
48
  "autoprefixer": "^10.4.23",
49
+ "lit": "3.3.1",
45
50
  "postcss": "^8.5.6",
46
51
  "postcss-import": "^16.1.1",
47
52
  "tailwindcss": "^3.4.19",
@@ -1,4 +1,8 @@
1
- import type { DecoderResult } from '@lukso/transaction-decoder'
1
+ import {
2
+ type DecoderResult,
3
+ decodeTransaction,
4
+ JSONbigString,
5
+ } from '@lukso/transaction-decoder'
2
6
  import {
3
7
  createTransactionPlayback,
4
8
  isHeaderInfoEvent,
@@ -13,15 +17,26 @@ import { html, type PropertyValues } from 'lit'
13
17
  import { property, state } from 'lit/decorators.js'
14
18
  import { createRef, type Ref, ref } from 'lit/directives/ref.js'
15
19
  import { when } from 'lit/directives/when.js'
20
+ import type { Chain, Transaction } from 'viem'
16
21
  import { getGlobalLitLoader } from '../registry/lit-view-loader'
17
22
  import { safeCustomElement } from '../shared/safe-custom-element'
18
23
  import { CoreLitElement } from '../styles'
19
24
 
25
+ /**
26
+ * Raw transaction input type - accepts transactions with JSON, BigintJSON, or bigint values
27
+ */
28
+ export type RawTransactionInput = Transaction | Record<string, any>
29
+
20
30
  /**
21
31
  * Lit Transaction Playback Component
22
32
  *
23
- * Provides index-based rendering of transactions with support for batch navigation
24
- * and context-aware view matching.
33
+ * Provides index-based rendering of transactions with support for batch navigation,
34
+ * context-aware view matching, and local transaction decoding.
35
+ *
36
+ * **Transaction Input:**
37
+ * - Pass a decoded transaction via `transaction` prop, OR
38
+ * - Pass a raw transaction via `rawTransaction` + `chain` props for local decoding
39
+ * - Raw transactions can have JSON, BigintJSON ("123n"), or bigint values
25
40
  *
26
41
  * **Single Transaction:**
27
42
  * - index="0" → renders the transaction
@@ -45,38 +60,38 @@ import { CoreLitElement } from '../styles'
45
60
  *
46
61
  * @example
47
62
  * ```html
48
- * <!-- Activity feed: compact view -->
63
+ * <!-- With decoded transaction -->
49
64
  * <transaction-playback
50
- * .transaction="${transaction}"
65
+ * .transaction="${decodedTransaction}"
51
66
  * index="1"
52
67
  * context="feed"
53
68
  * variant="compact"
54
- * @header-info="${(e) => console.log('Header:', e.detail)}"
55
69
  * ></transaction-playback>
56
70
  *
57
- * <!-- Approval flow: full detailed view -->
71
+ * <!-- With raw transaction (local decoding) -->
58
72
  * <transaction-playback
59
- * .transaction="${transaction}"
73
+ * .rawTransaction="${rawTx}"
74
+ * .chain="${lukso}"
60
75
  * index="1"
61
76
  * context="approval"
62
77
  * variant="full"
63
78
  * ></transaction-playback>
64
- *
65
- * <!-- Batch summary: collapsed view -->
66
- * <transaction-playback
67
- * .transaction="${transaction}"
68
- * index="0"
69
- * context="batch-summary"
70
- * variant="collapsed"
71
- * ></transaction-playback>
72
79
  * ```
73
80
  */
74
81
  @safeCustomElement('transaction-playback')
75
82
  export class TransactionPlayback extends CoreLitElement {
76
- /** The root transaction to display */
83
+ /** The decoded transaction to display */
77
84
  @property({ type: Object })
78
85
  transaction?: DecoderResult
79
86
 
87
+ /** Raw transaction to decode internally (alternative to passing decoded transaction) */
88
+ @property({ type: Object })
89
+ rawTransaction?: RawTransactionInput
90
+
91
+ /** Chain configuration for decoding (required when using rawTransaction) */
92
+ @property({ type: Object })
93
+ chain?: Chain
94
+
80
95
  /** Index to display (0, 1, 2, ...) */
81
96
  @property({ type: Number })
82
97
  index = 1 // Default to index 1 (main transaction, skips batch summary)
@@ -106,6 +121,9 @@ export class TransactionPlayback extends CoreLitElement {
106
121
  @state()
107
122
  private isLoading = false
108
123
 
124
+ @state()
125
+ private isDecoding = false
126
+
109
127
  @state()
110
128
  private error?: string
111
129
 
@@ -115,13 +133,29 @@ export class TransactionPlayback extends CoreLitElement {
115
133
  @state()
116
134
  private headerInfo?: TransactionHeaderInfo
117
135
 
136
+ @state()
137
+ private decodedTransaction?: DecoderResult
138
+
118
139
  private componentContainerRef: Ref<HTMLDivElement> = createRef()
119
140
  private loader = getGlobalLitLoader()
141
+ private decodingUnsubscribe?: () => void
120
142
 
121
143
  protected willUpdate(changedProperties: PropertyValues): void {
144
+ // Decode raw transaction if provided
145
+ if (
146
+ changedProperties.has('rawTransaction') ||
147
+ changedProperties.has('chain')
148
+ ) {
149
+ if (this.rawTransaction) {
150
+ this.decodeRawTransaction()
151
+ return // Don't load view yet, wait for decoding
152
+ }
153
+ }
154
+
122
155
  // Reload when transaction, index, context, or variant changes
123
156
  if (
124
157
  changedProperties.has('transaction') ||
158
+ changedProperties.has('decodedTransaction') ||
125
159
  changedProperties.has('index') ||
126
160
  changedProperties.has('context') ||
127
161
  changedProperties.has('variant')
@@ -155,6 +189,102 @@ export class TransactionPlayback extends CoreLitElement {
155
189
  TransactionViewEvents.HEADER_INFO,
156
190
  this.handleHeaderInfo as EventListener
157
191
  )
192
+ // Clean up decoding subscription
193
+ if (this.decodingUnsubscribe) {
194
+ this.decodingUnsubscribe()
195
+ this.decodingUnsubscribe = undefined
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Decode a raw transaction locally (no server API calls)
201
+ * Accepts transactions with JSON, BigintJSON, or bigint values
202
+ *
203
+ * Two-phase decoding:
204
+ * 1. Immediate sync decoding - shows basic decoded transaction right away
205
+ * 2. Async enhancement - progressively enhances with additional data
206
+ */
207
+ private async decodeRawTransaction() {
208
+ if (!this.rawTransaction) {
209
+ return
210
+ }
211
+
212
+ if (!this.chain) {
213
+ this.error =
214
+ 'Chain configuration is required when decoding raw transactions'
215
+ return
216
+ }
217
+
218
+ this.isDecoding = true
219
+ this.error = undefined
220
+ this.decodedTransaction = undefined
221
+
222
+ // Clean up previous subscription
223
+ if (this.decodingUnsubscribe) {
224
+ this.decodingUnsubscribe()
225
+ this.decodingUnsubscribe = undefined
226
+ }
227
+
228
+ try {
229
+ // Normalize the transaction input (convert BigintJSON strings to BigInt)
230
+ const normalizedTx = JSONbigString.read(this.rawTransaction)
231
+
232
+ // Decode the transaction locally with async enhancement enabled
233
+ // This returns immediately with sync-decoded result, then enhances asynchronously
234
+ const result = await decodeTransaction(normalizedTx, {
235
+ chain: this.chain,
236
+ plugins: [], // Use auto-registered plugins
237
+ schemaPlugins: [], // Use auto-registered schema plugins
238
+ wrappers: [], // No wrapper detection needed for raw transactions
239
+ async: true, // Enable async enhancement (ABI fetching, address resolution)
240
+ })
241
+
242
+ // Handle single transaction result (not batch)
243
+ if (!Array.isArray(result)) {
244
+ // PHASE 1: Store the immediate sync-decoded result and show it right away
245
+ this.decodedTransaction = result.immediate
246
+ this.isDecoding = false
247
+
248
+ // Load the view with the immediate result
249
+ this.loadView()
250
+
251
+ // PHASE 2: Subscribe to async enhancement updates
252
+ // The signal will emit updates as async decoding progresses
253
+ this.decodingUnsubscribe = result.signal.subscribe((state: any) => {
254
+ if (state.data && state.data !== this.decodedTransaction) {
255
+ this.decodedTransaction = state.data
256
+ // Re-render the view with enhanced data
257
+ this.loadView()
258
+ }
259
+ })
260
+ } else {
261
+ // For batch transactions, use the first result
262
+ const firstResult = result[0]
263
+
264
+ // PHASE 1: Immediate result
265
+ this.decodedTransaction = firstResult.immediate
266
+ this.isDecoding = false
267
+
268
+ // Load the view with the immediate result
269
+ this.loadView()
270
+
271
+ // PHASE 2: Subscribe to async enhancement updates
272
+ this.decodingUnsubscribe = firstResult.signal.subscribe(
273
+ (state: any) => {
274
+ if (state.data && state.data !== this.decodedTransaction) {
275
+ this.decodedTransaction = state.data
276
+ // Re-render the view with enhanced data
277
+ this.loadView()
278
+ }
279
+ }
280
+ )
281
+ }
282
+ } catch (err) {
283
+ this.error =
284
+ err instanceof Error ? err.message : 'Failed to decode transaction'
285
+ this.isDecoding = false
286
+ console.error('Transaction decoding error:', err)
287
+ }
158
288
  }
159
289
 
160
290
  private handleHeaderInfo = (event: Event): void => {
@@ -173,7 +303,10 @@ export class TransactionPlayback extends CoreLitElement {
173
303
  }
174
304
 
175
305
  private async loadView() {
176
- if (!this.transaction) {
306
+ // Use decodedTransaction if available (from rawTransaction), otherwise use passed transaction
307
+ const txToDisplay = this.decodedTransaction || this.transaction
308
+
309
+ if (!txToDisplay) {
177
310
  this.error = 'No transaction provided'
178
311
  return
179
312
  }
@@ -185,7 +318,7 @@ export class TransactionPlayback extends CoreLitElement {
185
318
 
186
319
  try {
187
320
  // Create playback state
188
- this.playbackState = createTransactionPlayback(this.transaction, {
321
+ this.playbackState = createTransactionPlayback(txToDisplay, {
189
322
  framework: 'lit',
190
323
  fallbackViewId: this.fallbackViewId,
191
324
  context: this.context,
@@ -323,6 +456,16 @@ export class TransactionPlayback extends CoreLitElement {
323
456
  }
324
457
 
325
458
  render() {
459
+ // Decoding state
460
+ if (this.isDecoding) {
461
+ return html`
462
+ <div class="flex flex-col items-center justify-center py-15 px-5 text-neutral-60">
463
+ <div class="text-32 mb-4 animate-spin">🔄</div>
464
+ <div class="text-16 font-500">Decoding transaction...</div>
465
+ </div>
466
+ `
467
+ }
468
+
326
469
  // Loading state
327
470
  if (this.isLoading) {
328
471
  return html`
package/tsup.config.ts CHANGED
@@ -85,6 +85,9 @@ export default defineConfig({
85
85
  '@lukso/web-components',
86
86
  '@preact/signals-core',
87
87
  'lit',
88
+ 'lit-html',
89
+ 'lit-element',
90
+ '@lit/reactive-element',
88
91
  '@lit/task',
89
92
  ],
90
93
  })