@lukso/transaction-view-headless 0.4.3 → 0.4.4-dev.c5d67d4
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/.turbo/turbo-build.log +32 -30
- package/.turbo/turbo-typecheck.log +1 -1
- package/ARG_ACCESS_PATTERN.md +265 -0
- package/CHANGELOG.md +14 -0
- package/CONTEXT_VARIANT_GUIDE.md +4 -4
- package/EVENT_SYSTEM_SUMMARY.md +601 -0
- package/HEADER_EVENTS_GUIDE.md +291 -0
- package/RECOMPOSITION_REFERENCE.md +303 -0
- package/TRANSACTION_MODIFICATION_GUIDE.md +927 -0
- package/dist/composables/index.cjs +1 -1
- package/dist/composables/index.cjs.map +1 -1
- package/dist/composables/index.d.cts +3 -3
- package/dist/composables/index.d.ts +3 -3
- package/dist/composables/index.js +1 -1
- package/dist/composables/index.js.map +1 -1
- package/dist/{index-CSDjhiKV.d.cts → index-BqL7xdDl.d.cts} +2 -2
- package/dist/{index-CMLU1hKL.d.ts → index-CNUHqRdk.d.ts} +2 -2
- package/dist/index.cjs +60 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +55 -1
- package/dist/index.js.map +1 -1
- package/dist/registry/index.d.cts +2 -2
- package/dist/registry/index.d.ts +2 -2
- package/dist/types/index.cjs +68 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +229 -169
- package/dist/types/index.d.ts +229 -169
- package/dist/types/index.js +55 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.cts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/{view-loader-dD86QAlQ.d.cts → view-loader--mEEI8YY.d.ts} +1 -1
- package/dist/{view-loader-ii8AxiQR.d.ts → view-loader-D7ApWfdN.d.cts} +1 -1
- package/dist/view-matching-C9G3z8lQ.d.cts +203 -0
- package/dist/view-matching-C9G3z8lQ.d.ts +203 -0
- package/package.json +3 -3
- package/src/composables/use-transaction-playback.ts +8 -5
- package/src/types/index.ts +14 -0
- package/src/types/view-events.ts +322 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
# Transaction View Event System - Complete Summary
|
|
2
|
+
|
|
3
|
+
This document provides a comprehensive overview of the event-based communication system for transaction views.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Transaction views use a custom event system to communicate with their parent containers. This enables:
|
|
8
|
+
|
|
9
|
+
1. **Header Information** - Views tell parents what addresses to display
|
|
10
|
+
2. **Transaction Modifications** - Views modify transaction parameters (e.g., force transfers, slippage)
|
|
11
|
+
3. **Context-Aware Rendering** - Views adapt UI based on WHERE they're displayed
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌──────────────────────────────────────────────────────────┐
|
|
17
|
+
│ View Component │
|
|
18
|
+
│ (e.g., LSP7TransferView, SwapView, SetDataView) │
|
|
19
|
+
│ │
|
|
20
|
+
│ Emits: │
|
|
21
|
+
│ - header-info (visual: from/to/title) │
|
|
22
|
+
│ - transaction-modification (data changes) │
|
|
23
|
+
└────────────────────┬─────────────────────────────────────┘
|
|
24
|
+
│ bubbles: true, composed: true
|
|
25
|
+
▼
|
|
26
|
+
┌──────────────────────────────────────────────────────────┐
|
|
27
|
+
│ <transaction-playback index="1"> │
|
|
28
|
+
│ │
|
|
29
|
+
│ - Receives events from child view │
|
|
30
|
+
│ - Validates (index match, re-decode) │
|
|
31
|
+
│ - Adds context (transaction path) │
|
|
32
|
+
│ - Re-emits for parent │
|
|
33
|
+
└────────────────────┬─────────────────────────────────────┘
|
|
34
|
+
│ bubbles: true, composed: true
|
|
35
|
+
▼
|
|
36
|
+
┌──────────────────────────────────────────────────────────┐
|
|
37
|
+
│ Top-Level Container │
|
|
38
|
+
│ (Wallet, App, Approval Screen) │
|
|
39
|
+
│ │
|
|
40
|
+
│ - Updates header display (from/to) │
|
|
41
|
+
│ - Applies transaction modifications │
|
|
42
|
+
│ - Re-wraps modified data (Execute/Batch) │
|
|
43
|
+
│ - Re-decodes and updates UI │
|
|
44
|
+
│ - Shows modification summary to user │
|
|
45
|
+
└──────────────────────────────────────────────────────────┘
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Event Types
|
|
49
|
+
|
|
50
|
+
### 1. Header Info Event
|
|
51
|
+
|
|
52
|
+
**Purpose:** Tell parent containers what addresses/title to display in the header.
|
|
53
|
+
|
|
54
|
+
**Event Name:** `header-info`
|
|
55
|
+
|
|
56
|
+
**Use Case:**
|
|
57
|
+
- Transfer view shows transfer's from/to (not Execute's from/to)
|
|
58
|
+
- Swap view shows token pair info
|
|
59
|
+
- SetData view shows profile being modified
|
|
60
|
+
|
|
61
|
+
**Type:**
|
|
62
|
+
```typescript
|
|
63
|
+
interface TransactionHeaderInfo {
|
|
64
|
+
from?: string // Override "from" address
|
|
65
|
+
to?: string // Override "to" address
|
|
66
|
+
title?: string // Transaction title
|
|
67
|
+
subtitle?: string // Additional description
|
|
68
|
+
icon?: string // Icon identifier
|
|
69
|
+
metadata?: Record<string, any> // Custom data
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Usage:**
|
|
74
|
+
```typescript
|
|
75
|
+
import { dispatchHeaderInfo, getArgByName } from '@lukso/transaction-view-headless'
|
|
76
|
+
|
|
77
|
+
connectedCallback() {
|
|
78
|
+
super.connectedCallback()
|
|
79
|
+
|
|
80
|
+
dispatchHeaderInfo(this, {
|
|
81
|
+
from: getArgByName(this.transaction.args, 'from') as string,
|
|
82
|
+
to: getArgByName(this.transaction.args, 'to') as string,
|
|
83
|
+
title: 'LSP7 Token Transfer'
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Documentation:** [HEADER_EVENTS_GUIDE.md](./HEADER_EVENTS_GUIDE.md)
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### 2. Transaction Modification Event
|
|
93
|
+
|
|
94
|
+
**Purpose:** Modify transaction data before submission (e.g., enable force transfer, adjust slippage).
|
|
95
|
+
|
|
96
|
+
**Event Name:** `transaction-modification`
|
|
97
|
+
|
|
98
|
+
**Use Case:**
|
|
99
|
+
- Force transfer checkbox (modify `force` arg)
|
|
100
|
+
- Slippage tolerance slider (modify `minOutput` arg)
|
|
101
|
+
- Gas limit adjustment
|
|
102
|
+
- Allowance amount changes
|
|
103
|
+
|
|
104
|
+
**Type:**
|
|
105
|
+
```typescript
|
|
106
|
+
interface TransactionModification {
|
|
107
|
+
data: `0x${string}` // Modified LOCAL transaction data
|
|
108
|
+
transactionIndex: number // Playback index (matches <transaction-playback index>)
|
|
109
|
+
reason: string // Human-readable explanation
|
|
110
|
+
userInitiated: boolean // User action vs automatic
|
|
111
|
+
summary?: { // Optional quick summary
|
|
112
|
+
field: string
|
|
113
|
+
oldValue: string
|
|
114
|
+
newValue: string
|
|
115
|
+
}
|
|
116
|
+
validation?: { // Optional validation
|
|
117
|
+
isValid: boolean
|
|
118
|
+
error?: string
|
|
119
|
+
warnings?: string[]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Extended Type (after transaction-playback adds context):**
|
|
125
|
+
```typescript
|
|
126
|
+
interface TransactionModificationWithContext extends TransactionModification {
|
|
127
|
+
transactionPath: number[] // Path from root: [0, 2] = batch[0].children[2]
|
|
128
|
+
localTransaction: DecoderResult
|
|
129
|
+
decodedResult: DecoderResult // Re-decoded for verification
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Usage:**
|
|
134
|
+
```typescript
|
|
135
|
+
import { dispatchTransactionModification } from '@lukso/transaction-view-headless'
|
|
136
|
+
import { encodeFunctionData } from 'viem'
|
|
137
|
+
|
|
138
|
+
private handleForceCheckbox(checked: boolean) {
|
|
139
|
+
const newData = encodeFunctionData({
|
|
140
|
+
abi: LSP7_ABI,
|
|
141
|
+
functionName: 'transfer',
|
|
142
|
+
args: [from, to, amount, checked, data] // force = checked
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
dispatchTransactionModification(this, {
|
|
146
|
+
data: newData,
|
|
147
|
+
transactionIndex: this.index,
|
|
148
|
+
reason: 'User enabled force transfer to EOA',
|
|
149
|
+
userInitiated: true,
|
|
150
|
+
summary: {
|
|
151
|
+
field: 'force',
|
|
152
|
+
oldValue: 'false',
|
|
153
|
+
newValue: 'true'
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Documentation:** [TRANSACTION_MODIFICATION_GUIDE.md](./TRANSACTION_MODIFICATION_GUIDE.md)
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Props Passed to Views
|
|
164
|
+
|
|
165
|
+
Views receive these props from `<transaction-playback>`:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
interface ViewProps {
|
|
169
|
+
transaction: DecoderResult // Required: Full decoded transaction
|
|
170
|
+
context?: ViewContext // Optional: WHERE displayed (approval, feed, detail, etc.)
|
|
171
|
+
variant?: ViewVariant // Optional: HOW to render (implementation-specific)
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Context Values:**
|
|
176
|
+
- `approval` - Transaction approval flow (security-focused, interactive)
|
|
177
|
+
- `feed` - Activity feed (compact, read-only)
|
|
178
|
+
- `list` - Transaction lists (scannable)
|
|
179
|
+
- `detail` - Full transaction detail (comprehensive)
|
|
180
|
+
- `review` - Pre-submission review
|
|
181
|
+
- `notification` - Push notifications (minimal)
|
|
182
|
+
- `embed` - Embedded in other interfaces
|
|
183
|
+
- `batch-summary` - Inside a batch summary
|
|
184
|
+
|
|
185
|
+
**Context-Aware Rendering Example:**
|
|
186
|
+
```typescript
|
|
187
|
+
render() {
|
|
188
|
+
if (this.context === 'approval') {
|
|
189
|
+
// Show interactive checkbox
|
|
190
|
+
return html`
|
|
191
|
+
<label>
|
|
192
|
+
<input type="checkbox" @change=${this.handleForceChange} />
|
|
193
|
+
Force transfer
|
|
194
|
+
</label>
|
|
195
|
+
`
|
|
196
|
+
} else if (this.context === 'feed') {
|
|
197
|
+
// Show read-only status
|
|
198
|
+
return html`
|
|
199
|
+
${this.force ? html`<div>⚠️ Forced transfer</div>` : ''}
|
|
200
|
+
`
|
|
201
|
+
} else {
|
|
202
|
+
// Detail view - full info
|
|
203
|
+
return html`<div>Force: ${this.force ? 'Yes' : 'No'}</div>`
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Helper Functions
|
|
211
|
+
|
|
212
|
+
### Header Events
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Dispatch header info
|
|
216
|
+
dispatchHeaderInfo(element: HTMLElement, info: TransactionHeaderInfo): void
|
|
217
|
+
|
|
218
|
+
// Type guard
|
|
219
|
+
isHeaderInfoEvent(event: Event): event is HeaderInfoEvent
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Transaction Modifications
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Dispatch modification
|
|
226
|
+
dispatchTransactionModification(element: HTMLElement, mod: TransactionModification): void
|
|
227
|
+
|
|
228
|
+
// Type guard
|
|
229
|
+
isTransactionModificationEvent(event: Event): event is TransactionModificationEvent
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Event Constants
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { TransactionViewEvents } from '@lukso/transaction-view-headless'
|
|
236
|
+
|
|
237
|
+
TransactionViewEvents.HEADER_INFO // 'header-info'
|
|
238
|
+
TransactionViewEvents.TRANSACTION_MODIFICATION // 'transaction-modification'
|
|
239
|
+
TransactionViewEvents.STATE_CHANGE // 'view-state-change' (future)
|
|
240
|
+
TransactionViewEvents.ACTION // 'view-action' (future)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Complete Example: Force Transfer
|
|
246
|
+
|
|
247
|
+
### 1. View Component
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { dispatchHeaderInfo, dispatchTransactionModification, getArgByName } from '@lukso/transaction-view-headless'
|
|
251
|
+
import { encodeFunctionData } from 'viem'
|
|
252
|
+
|
|
253
|
+
@customElement('lsp7-transfer-view')
|
|
254
|
+
export class LSP7TransferView extends CoreLitElement {
|
|
255
|
+
@property({ type: Object }) transaction!: DecoderResult
|
|
256
|
+
@property({ type: String }) context?: ViewContext
|
|
257
|
+
@property({ type: Number }) index!: number
|
|
258
|
+
|
|
259
|
+
@state()
|
|
260
|
+
private force = false
|
|
261
|
+
|
|
262
|
+
connectedCallback() {
|
|
263
|
+
super.connectedCallback()
|
|
264
|
+
|
|
265
|
+
// Emit header info
|
|
266
|
+
dispatchHeaderInfo(this, {
|
|
267
|
+
from: getArgByName(this.transaction.args, 'from') as string,
|
|
268
|
+
to: getArgByName(this.transaction.args, 'to') as string,
|
|
269
|
+
title: 'LSP7 Transfer'
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// Initialize force from transaction
|
|
273
|
+
this.force = (getArgByName(this.transaction.args, 'force') as boolean) ?? false
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private handleForceChange(checked: boolean) {
|
|
277
|
+
this.force = checked
|
|
278
|
+
|
|
279
|
+
// Re-encode with new force value
|
|
280
|
+
const newData = encodeFunctionData({
|
|
281
|
+
abi: LSP7_ABI,
|
|
282
|
+
functionName: 'transfer',
|
|
283
|
+
args: [
|
|
284
|
+
getArgByName(this.transaction.args, 'from'),
|
|
285
|
+
getArgByName(this.transaction.args, 'to'),
|
|
286
|
+
getArgByName(this.transaction.args, 'amount'),
|
|
287
|
+
checked,
|
|
288
|
+
getArgByName(this.transaction.args, 'data')
|
|
289
|
+
]
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// Emit modification
|
|
293
|
+
dispatchTransactionModification(this, {
|
|
294
|
+
data: newData,
|
|
295
|
+
transactionIndex: this.index,
|
|
296
|
+
reason: checked
|
|
297
|
+
? 'User enabled force transfer to EOA'
|
|
298
|
+
: 'User disabled force transfer',
|
|
299
|
+
userInitiated: true,
|
|
300
|
+
summary: {
|
|
301
|
+
field: 'force',
|
|
302
|
+
oldValue: String(!checked),
|
|
303
|
+
newValue: String(checked)
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
render() {
|
|
309
|
+
// Context-aware rendering
|
|
310
|
+
if (this.context === 'approval') {
|
|
311
|
+
return html`
|
|
312
|
+
<div>
|
|
313
|
+
<h3>Transfer ${this.formatAmount()} tokens</h3>
|
|
314
|
+
<label>
|
|
315
|
+
<input
|
|
316
|
+
type="checkbox"
|
|
317
|
+
.checked=${this.force}
|
|
318
|
+
@change=${(e) => this.handleForceChange(e.target.checked)}
|
|
319
|
+
/>
|
|
320
|
+
Force transfer (required for EOA recipients)
|
|
321
|
+
</label>
|
|
322
|
+
</div>
|
|
323
|
+
`
|
|
324
|
+
} else {
|
|
325
|
+
return html`
|
|
326
|
+
<div>
|
|
327
|
+
Transfer ${this.formatAmount()} tokens
|
|
328
|
+
${this.force ? html`<span>⚠️ Forced</span>` : ''}
|
|
329
|
+
</div>
|
|
330
|
+
`
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 2. Transaction Playback (handles automatically)
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
// Already implemented in transaction-playback.ts
|
|
340
|
+
// - Listens for header-info and transaction-modification events
|
|
341
|
+
// - Validates modifications
|
|
342
|
+
// - Re-decodes modified data
|
|
343
|
+
// - Adds transaction path context
|
|
344
|
+
// - Re-emits for top-level container
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### 3. Top-Level Container
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
class ApprovalScreen extends LitElement {
|
|
351
|
+
@state() originalTx: DecoderResult
|
|
352
|
+
@state() modifiedTx?: DecoderResult
|
|
353
|
+
@state() modifications: TransactionModificationWithContext[] = []
|
|
354
|
+
@state() headerInfo?: TransactionHeaderInfo
|
|
355
|
+
|
|
356
|
+
render() {
|
|
357
|
+
return html`
|
|
358
|
+
<div class="approval">
|
|
359
|
+
<!-- Header with from/to from header-info event -->
|
|
360
|
+
<div class="header">
|
|
361
|
+
<h2>${this.headerInfo?.title ?? 'Transaction'}</h2>
|
|
362
|
+
<div>From: ${this.headerInfo?.from ?? this.originalTx.from}</div>
|
|
363
|
+
<div>To: ${this.headerInfo?.to ?? this.originalTx.to}</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<!-- Show modifications -->
|
|
367
|
+
${this.modifications.length > 0 ? html`
|
|
368
|
+
<div class="modifications">
|
|
369
|
+
<h3>User Modifications:</h3>
|
|
370
|
+
${this.modifications.map(mod => html`
|
|
371
|
+
<div>
|
|
372
|
+
${mod.reason}
|
|
373
|
+
${mod.summary ? html`
|
|
374
|
+
<span>(${mod.summary.field}: ${mod.summary.oldValue} → ${mod.summary.newValue})</span>
|
|
375
|
+
` : ''}
|
|
376
|
+
</div>
|
|
377
|
+
`)}
|
|
378
|
+
</div>
|
|
379
|
+
` : ''}
|
|
380
|
+
|
|
381
|
+
<!-- Transaction view -->
|
|
382
|
+
<transaction-playback
|
|
383
|
+
.transaction=${this.modifiedTx ?? this.originalTx}
|
|
384
|
+
index="1"
|
|
385
|
+
context="approval"
|
|
386
|
+
@header-info=${this.handleHeaderInfo}
|
|
387
|
+
@transaction-modification=${this.handleModification}
|
|
388
|
+
></transaction-playback>
|
|
389
|
+
|
|
390
|
+
<button @click=${this.approve}>Approve</button>
|
|
391
|
+
</div>
|
|
392
|
+
`
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
private handleHeaderInfo(e: CustomEvent) {
|
|
396
|
+
this.headerInfo = e.detail
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private async handleModification(e: CustomEvent) {
|
|
400
|
+
const mod: TransactionModificationWithContext = e.detail
|
|
401
|
+
|
|
402
|
+
// Store modification
|
|
403
|
+
this.modifications.push(mod)
|
|
404
|
+
|
|
405
|
+
// Apply to transaction tree
|
|
406
|
+
this.modifiedTx = await this.applyModification(this.originalTx, mod)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private async applyModification(
|
|
410
|
+
rootTx: DecoderResult,
|
|
411
|
+
mod: TransactionModificationWithContext
|
|
412
|
+
): Promise<DecoderResult> {
|
|
413
|
+
// Navigate transaction tree using mod.transactionPath
|
|
414
|
+
// Apply mod.data at the correct level
|
|
415
|
+
// Re-encode wrappers (Execute/Batch)
|
|
416
|
+
// Re-decode entire tree
|
|
417
|
+
// Return modified transaction
|
|
418
|
+
// (See TRANSACTION_MODIFICATION_GUIDE.md for full implementation)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private async approve() {
|
|
422
|
+
const finalTx = this.modifiedTx ?? this.originalTx
|
|
423
|
+
await this.wallet.sendTransaction(finalTx)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Key Concepts
|
|
431
|
+
|
|
432
|
+
### 1. **Separation of Concerns**
|
|
433
|
+
|
|
434
|
+
- **Header Info** = Visual display (from/to/title)
|
|
435
|
+
- **Transaction Modification** = Data changes (args, value, gas)
|
|
436
|
+
|
|
437
|
+
These are separate events because they serve different purposes.
|
|
438
|
+
|
|
439
|
+
### 2. **Local Transaction Data**
|
|
440
|
+
|
|
441
|
+
Views emit their **local** transaction data (not wrapped in Execute/Batch).
|
|
442
|
+
Parents handle re-wrapping.
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
View emits: transfer(from, to, amount, force, data)
|
|
446
|
+
Parent re-wraps: execute(target, 0, transferData)
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### 3. **Re-decoding for Security**
|
|
450
|
+
|
|
451
|
+
Parents **must** re-decode modified data to verify it's valid:
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
const reDecoded = await decoder.decode({ ...tx, data: mod.data })
|
|
455
|
+
if (!reDecoded) throw new Error('Invalid modification')
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### 4. **Context-Aware Rendering**
|
|
459
|
+
|
|
460
|
+
Views adapt UI based on `context` prop:
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
approval → Interactive controls (checkboxes, sliders)
|
|
464
|
+
feed → Read-only status indicators
|
|
465
|
+
detail → Full information display
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### 5. **Transaction Paths**
|
|
469
|
+
|
|
470
|
+
Path notation for nested transactions:
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
[1] → Simple transaction
|
|
474
|
+
[0, 2] → Batch[0].children[2]
|
|
475
|
+
[0, 1, 0] → Batch[0].children[1].children[0]
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Best Practices
|
|
481
|
+
|
|
482
|
+
### For View Developers
|
|
483
|
+
|
|
484
|
+
1. ✅ Emit header info in `connectedCallback()` (early)
|
|
485
|
+
2. ✅ Include correct `transactionIndex` (from props)
|
|
486
|
+
3. ✅ Provide clear, human-readable `reason` strings
|
|
487
|
+
4. ✅ Use context prop to adapt UI
|
|
488
|
+
5. ✅ Validate user input before emitting modifications
|
|
489
|
+
6. ✅ Use helper functions (`dispatchHeaderInfo`, `dispatchTransactionModification`)
|
|
490
|
+
|
|
491
|
+
### For Container Developers
|
|
492
|
+
|
|
493
|
+
1. ✅ Always re-decode modified data
|
|
494
|
+
2. ✅ Show modifications to user before submission
|
|
495
|
+
3. ✅ Validate modifications (check `validation` field)
|
|
496
|
+
4. ✅ Keep audit trail of modifications
|
|
497
|
+
5. ✅ Handle re-wrapping for Execute/Batch transactions
|
|
498
|
+
|
|
499
|
+
### Security
|
|
500
|
+
|
|
501
|
+
1. ✅ Re-decode all modifications
|
|
502
|
+
2. ✅ Display what changed to user
|
|
503
|
+
3. ✅ Validate before applying
|
|
504
|
+
4. ✅ Log modifications for audit
|
|
505
|
+
5. ✅ Never skip verification
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Debugging
|
|
510
|
+
|
|
511
|
+
### Enable Debug Mode
|
|
512
|
+
|
|
513
|
+
```html
|
|
514
|
+
<transaction-playback
|
|
515
|
+
.transaction="${tx}"
|
|
516
|
+
debug
|
|
517
|
+
></transaction-playback>
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Shows:
|
|
521
|
+
- Playback info (index, batch status)
|
|
522
|
+
- Matched view ID
|
|
523
|
+
- **Header info received** (from/to/title)
|
|
524
|
+
- Transaction path
|
|
525
|
+
|
|
526
|
+
### Console Logging
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
// In view
|
|
530
|
+
console.log('Emitting modification:', modification)
|
|
531
|
+
|
|
532
|
+
// In parent
|
|
533
|
+
console.log('Received modification:', event.detail)
|
|
534
|
+
console.log('Transaction path:', event.detail.transactionPath)
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## Type Imports
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
// Header events
|
|
543
|
+
import type {
|
|
544
|
+
TransactionHeaderInfo,
|
|
545
|
+
HeaderInfoEvent
|
|
546
|
+
} from '@lukso/transaction-view-headless'
|
|
547
|
+
|
|
548
|
+
// Transaction modifications
|
|
549
|
+
import type {
|
|
550
|
+
TransactionModification,
|
|
551
|
+
TransactionModificationWithContext,
|
|
552
|
+
TransactionModificationEvent
|
|
553
|
+
} from '@lukso/transaction-view-headless'
|
|
554
|
+
|
|
555
|
+
// Helpers and constants
|
|
556
|
+
import {
|
|
557
|
+
TransactionViewEvents,
|
|
558
|
+
dispatchHeaderInfo,
|
|
559
|
+
dispatchTransactionModification,
|
|
560
|
+
isHeaderInfoEvent,
|
|
561
|
+
isTransactionModificationEvent
|
|
562
|
+
} from '@lukso/transaction-view-headless'
|
|
563
|
+
|
|
564
|
+
// View props
|
|
565
|
+
import type {
|
|
566
|
+
ViewContext,
|
|
567
|
+
ViewVariant
|
|
568
|
+
} from '@lukso/transaction-view-headless'
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## Related Documentation
|
|
574
|
+
|
|
575
|
+
- [HEADER_EVENTS_GUIDE.md](./HEADER_EVENTS_GUIDE.md) - Complete header events documentation
|
|
576
|
+
- [TRANSACTION_MODIFICATION_GUIDE.md](./TRANSACTION_MODIFICATION_GUIDE.md) - Complete modification guide
|
|
577
|
+
- [CONTEXT_VARIANT_GUIDE.md](./CONTEXT_VARIANT_GUIDE.md) - Context and variant system
|
|
578
|
+
- [PLAYBACK_GUIDE.md](./PLAYBACK_GUIDE.md) - Transaction playback system
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## Quick Reference
|
|
583
|
+
|
|
584
|
+
| Need to... | Use... | Event | Documentation |
|
|
585
|
+
|------------|--------|-------|---------------|
|
|
586
|
+
| Change header from/to | `dispatchHeaderInfo` | `header-info` | HEADER_EVENTS_GUIDE.md |
|
|
587
|
+
| Modify transaction data | `dispatchTransactionModification` | `transaction-modification` | TRANSACTION_MODIFICATION_GUIDE.md |
|
|
588
|
+
| Adapt UI by context | Check `this.context` prop | - | CONTEXT_VARIANT_GUIDE.md |
|
|
589
|
+
| Listen in parent | `@header-info`, `@transaction-modification` | Both | This doc |
|
|
590
|
+
| Debug events | Set `debug` prop on playback | - | This doc |
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## Future Events
|
|
595
|
+
|
|
596
|
+
These events are planned but not yet implemented:
|
|
597
|
+
|
|
598
|
+
- **`view-state-change`** - For loading, error, interactive state changes
|
|
599
|
+
- **`view-action`** - For user actions like "Approve", "Reject", "Sign"
|
|
600
|
+
|
|
601
|
+
The event system is designed to be extensible for future needs.
|