@canton-network/core-tx-parser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,997 @@
1
+ import { HOLDING_INTERFACE_ID, TRANSFER_FACTORY_INTERFACE_ID, TRANSFER_INSTRUCTION_INTERFACE_ID, ALLOCATION_FACTORY_INTERFACE_ID, ALLOCATION_INSTRUCTION_INTERFACE_ID, ALLOCATION_INTERFACE_ID, ALLOCATION_REQUEST_INTERFACE_ID } from '@canton-network/core-token-standard';
2
+ import { EventFilterBySetup } from '@canton-network/core-ledger-client-types';
3
+ import BigNumber from 'bignumber.js';
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
+ function splitInterfaceId(interfaceId) {
9
+ const regExp = /^#?([^:]+):([^:]+):([^:]+)$/;
10
+ const match = regExp.exec(interfaceId);
11
+ if (!match) return null;
12
+ const [, packageName, moduleName, entityName] = match;
13
+ return {
14
+ packageName,
15
+ moduleName,
16
+ entityName
17
+ };
18
+ }
19
+ function matchInterfaceIds(a, b) {
20
+ const aParts = splitInterfaceId(a);
21
+ const bParts = splitInterfaceId(b);
22
+ return aParts !== null && bParts !== null && aParts.moduleName === bParts.moduleName && aParts.entityName === bParts.entityName;
23
+ }
24
+ var TokenStandardTransactionInterfaces = [
25
+ HOLDING_INTERFACE_ID,
26
+ TRANSFER_FACTORY_INTERFACE_ID,
27
+ TRANSFER_INSTRUCTION_INTERFACE_ID,
28
+ ALLOCATION_FACTORY_INTERFACE_ID,
29
+ ALLOCATION_INSTRUCTION_INTERFACE_ID,
30
+ ALLOCATION_INTERFACE_ID,
31
+ ALLOCATION_REQUEST_INTERFACE_ID
32
+ ];
33
+ var SpliceMetaKeyPrefix = "splice.lfdecentralizedtrust.org/";
34
+ var TxKindMetaKey = `${SpliceMetaKeyPrefix}tx-kind`;
35
+ var SenderMetaKey = `${SpliceMetaKeyPrefix}sender`;
36
+ var ReasonMetaKey = `${SpliceMetaKeyPrefix}reason`;
37
+ var BurnedMetaKey = `${SpliceMetaKeyPrefix}burned`;
38
+ var AllKnownMetaKeys = [
39
+ TxKindMetaKey,
40
+ SenderMetaKey,
41
+ ReasonMetaKey,
42
+ BurnedMetaKey
43
+ ];
44
+ function hasInterface(interfaceId, event) {
45
+ return (event.implementedInterfaces || []).some(
46
+ (id) => matchInterfaceIds(id, interfaceId)
47
+ );
48
+ }
49
+ function getInterfaceView(createdEvent) {
50
+ const interfaceViews = createdEvent.interfaceViews || null;
51
+ return interfaceViews && interfaceViews[0] || null;
52
+ }
53
+ function getKnownInterfaceView(createdEvent) {
54
+ const interfaceView = getInterfaceView(createdEvent);
55
+ if (!interfaceView) {
56
+ return null;
57
+ } else if (matchInterfaceIds(HOLDING_INTERFACE_ID, interfaceView.interfaceId)) {
58
+ return {
59
+ type: "Holding",
60
+ viewValue: interfaceView.viewValue
61
+ };
62
+ } else if (matchInterfaceIds(
63
+ TRANSFER_INSTRUCTION_INTERFACE_ID,
64
+ interfaceView.interfaceId
65
+ )) {
66
+ return {
67
+ type: "TransferInstruction",
68
+ viewValue: interfaceView.viewValue
69
+ };
70
+ } else {
71
+ return null;
72
+ }
73
+ }
74
+ function ensureInterfaceViewIsPresent(createdEvent, interfaceId) {
75
+ const interfaceView = getInterfaceView(createdEvent);
76
+ if (!interfaceView) {
77
+ throw new Error(
78
+ `Expected to have interface views, but didn't: ${JSON.stringify(
79
+ createdEvent
80
+ )}`
81
+ );
82
+ }
83
+ if (!matchInterfaceIds(interfaceId, interfaceView.interfaceId)) {
84
+ throw new Error(
85
+ `Not a ${interfaceId} but a ${interfaceView.interfaceId}: ${JSON.stringify(createdEvent)}`
86
+ );
87
+ }
88
+ return interfaceView;
89
+ }
90
+ function mergeMetas(event, extra) {
91
+ const choiceArgument = event.choiceArgument;
92
+ const lastWriteWins = [
93
+ choiceArgument?.transfer?.meta,
94
+ choiceArgument?.extraArgs?.meta,
95
+ choiceArgument?.meta,
96
+ extra,
97
+ event.exerciseResult?.meta
98
+ ];
99
+ const result = {};
100
+ lastWriteWins.forEach((meta) => {
101
+ const values = meta?.values || {};
102
+ Object.entries(values).forEach(([k, v]) => {
103
+ result[k] = v;
104
+ });
105
+ });
106
+ if (Object.keys(result).length === 0) {
107
+ return void 0;
108
+ } else {
109
+ return { values: result };
110
+ }
111
+ }
112
+ function getMetaKeyValue(key, meta) {
113
+ return (meta?.values || {})[key] || null;
114
+ }
115
+ function removeParsedMetaKeys(meta) {
116
+ return {
117
+ values: Object.fromEntries(
118
+ Object.entries(meta?.values || {}).filter(
119
+ ([k]) => !AllKnownMetaKeys.includes(k)
120
+ )
121
+ )
122
+ };
123
+ }
124
+
125
+ // src/types.ts
126
+ var renderTransaction = (t) => {
127
+ return { ...t, events: t.events.map(renderTransactionEvent) };
128
+ };
129
+ var renderTransactionEvent = (e) => {
130
+ const lockedHoldingsChangeSummaries = e.lockedHoldingsChangeSummaries.map(renderHoldingsChangeSummary).filter((s) => s !== null);
131
+ const unlockedHoldingsChangeSummaries = e.unlockedHoldingsChangeSummaries.map(renderHoldingsChangeSummary).filter((s) => s !== null);
132
+ const lockedHoldingsChange = renderHoldingsChange(e.lockedHoldingsChange);
133
+ const unlockedHoldingsChange = renderHoldingsChange(
134
+ e.unlockedHoldingsChange
135
+ );
136
+ return {
137
+ ...e,
138
+ lockedHoldingsChange,
139
+ unlockedHoldingsChange,
140
+ lockedHoldingsChangeSummaries,
141
+ // Deprecated
142
+ lockedHoldingsChangeSummary: renderHoldingsChangeSummary(
143
+ e.lockedHoldingsChangeSummary
144
+ ),
145
+ unlockedHoldingsChangeSummaries,
146
+ // Deprecated
147
+ unlockedHoldingsChangeSummary: renderHoldingsChangeSummary(
148
+ e.unlockedHoldingsChangeSummary
149
+ )
150
+ };
151
+ };
152
+ var renderHoldingsChangeSummary = (s) => {
153
+ if (s.numInputs === 0 && s.numOutputs === 0 && s.inputAmount === "0" && s.outputAmount === "0" && s.amountChange === "0") {
154
+ return null;
155
+ }
156
+ return {
157
+ ...(s.instrumentId.admin !== "" || s.instrumentId.id !== "") && {
158
+ instrumentId: s.instrumentId
159
+ },
160
+ ...s.numInputs !== 0 && { numInputs: s.numInputs },
161
+ ...s.inputAmount !== "0" && { inputAmount: s.inputAmount },
162
+ ...s.numOutputs !== 0 && { numOutputs: s.numOutputs },
163
+ ...s.outputAmount !== "0" && { outputAmount: s.outputAmount },
164
+ ...s.amountChange !== "0" && { amountChange: s.amountChange }
165
+ };
166
+ };
167
+ var renderHoldingsChange = (c) => {
168
+ if (c.creates.length === 0 && c.archives.length === 0) {
169
+ return null;
170
+ }
171
+ return {
172
+ ...c.creates.length !== 0 && { creates: c.creates },
173
+ ...c.archives.length !== 0 && { archives: c.archives }
174
+ };
175
+ };
176
+
177
+ // src/instrumentmap.ts
178
+ var InstrumentMap = class {
179
+ constructor() {
180
+ __publicField(this, "map");
181
+ this.map = /* @__PURE__ */ new Map();
182
+ }
183
+ encodeKey(instrumentId) {
184
+ return JSON.stringify([instrumentId.admin, instrumentId.id]);
185
+ }
186
+ decodeKey(key) {
187
+ const [admin, id] = JSON.parse(key);
188
+ return { admin, id };
189
+ }
190
+ set(key, value) {
191
+ this.map.set(this.encodeKey(key), value);
192
+ }
193
+ get(key) {
194
+ return this.map.get(this.encodeKey(key));
195
+ }
196
+ has(key) {
197
+ return this.map.has(this.encodeKey(key));
198
+ }
199
+ delete(key) {
200
+ return this.map.delete(this.encodeKey(key));
201
+ }
202
+ *entries() {
203
+ for (const [key, value] of this.map.entries()) {
204
+ yield [this.decodeKey(key), value];
205
+ }
206
+ }
207
+ };
208
+ function currentStatusFromChoiceOrResult(choice, resultTag) {
209
+ if (resultTag === "TransferInstructionResult_Failed") return "Failed";
210
+ if (resultTag === "TransferInstructionResult_Completed") return "Completed";
211
+ switch (choice) {
212
+ case "TransferInstruction_Reject":
213
+ return "Rejected";
214
+ case "TransferInstruction_Withdraw":
215
+ return "Withdrawn";
216
+ case "TransferInstruction_Accept":
217
+ case "TransferInstruction_Update":
218
+ return "Pending";
219
+ default:
220
+ return "Pending";
221
+ }
222
+ }
223
+ function getCorrelationIdFromTransferInstruction(currentInstructionCid, originalInstructionCid) {
224
+ return originalInstructionCid ?? currentInstructionCid;
225
+ }
226
+ function getPendingTransferInstructionCid(exercisedEvent) {
227
+ const output = exercisedEvent.exerciseResult?.output;
228
+ if (output?.tag !== "TransferInstructionResult_Pending") return void 0;
229
+ const cid = output.value?.transferInstructionCid;
230
+ return cid ?? void 0;
231
+ }
232
+ function isTransferObject(value) {
233
+ if (!value || typeof value !== "object") return false;
234
+ const v = value;
235
+ const instrumentId = v.instrumentId;
236
+ const meta = v.meta;
237
+ return typeof v.sender === "string" && typeof v.receiver === "string" && typeof v.amount === "string" && typeof v.requestedAt === "string" && typeof v.executeBefore === "string" && Array.isArray(v.inputHoldingCids) && v.inputHoldingCids.every((cid) => typeof cid === "string") && !!instrumentId && typeof instrumentId.admin === "string" && typeof instrumentId.id === "string" && !!meta && typeof meta.values === "object" && meta.values !== null;
238
+ }
239
+ var TransactionParser = class {
240
+ constructor(transaction, ledgerClient, partyId, isMasterUser) {
241
+ __publicField(this, "ledgerClient");
242
+ __publicField(this, "partyId");
243
+ __publicField(this, "transaction");
244
+ __publicField(this, "isMasterUser");
245
+ this.ledgerClient = ledgerClient;
246
+ this.partyId = partyId;
247
+ this.transaction = transaction;
248
+ this.isMasterUser = isMasterUser;
249
+ }
250
+ async parseTransaction() {
251
+ const tx = this.transaction;
252
+ const events = await this.parseEvents([...tx.events || []].reverse());
253
+ return {
254
+ updateId: tx.updateId,
255
+ offset: tx.offset,
256
+ recordTime: tx.recordTime,
257
+ synchronizerId: tx.synchronizerId,
258
+ events
259
+ };
260
+ }
261
+ async parseTransferObjects() {
262
+ const eventsStack = [...this.transaction.events || []].reverse();
263
+ const results = await this.fetchTransferObjectChoice(eventsStack);
264
+ return results;
265
+ }
266
+ async fetchTransferObjectChoice(eventsStack) {
267
+ const result = [];
268
+ while (eventsStack.length > 0) {
269
+ const currentEvent = eventsStack.pop();
270
+ const { exercisedEvent } = getNodeIdAndEvent(currentEvent);
271
+ if (exercisedEvent && (exercisedEvent.choice === "TransferFactory_Transfer" || exercisedEvent.choice === "TransferRule_Transfer")) {
272
+ const { choiceArgument } = exercisedEvent;
273
+ if (choiceArgument && typeof choiceArgument === "object" && "transfer" in choiceArgument) {
274
+ const transfer = choiceArgument.transfer;
275
+ if (isTransferObject(transfer)) {
276
+ result.push(transfer);
277
+ }
278
+ }
279
+ }
280
+ }
281
+ return result;
282
+ }
283
+ async parseEvents(eventsStack) {
284
+ let callStack = [];
285
+ let continueAfterNodeId = -1;
286
+ const result = [];
287
+ while (eventsStack.length > 0) {
288
+ const currentEvent = eventsStack.pop();
289
+ const { nodeId, createdEvent, archivedEvent, exercisedEvent } = getNodeIdAndEvent(currentEvent);
290
+ callStack = callStack.filter((s) => s.untilNodeId <= nodeId);
291
+ const parentChoice = callStack[callStack.length - 1] && callStack[callStack.length - 1].parentChoiceName || "none (root node)";
292
+ let parsed;
293
+ if (nodeId <= continueAfterNodeId) {
294
+ parsed = null;
295
+ } else if (createdEvent) {
296
+ parsed = this.parseRawCreate(createdEvent, parentChoice);
297
+ } else if (archivedEvent) {
298
+ parsed = await this.parseRawArchive(archivedEvent, parentChoice);
299
+ } else if (exercisedEvent) {
300
+ parsed = await this.parseExercise(exercisedEvent);
301
+ } else {
302
+ throw new Error(
303
+ `Impossible event: ${JSON.stringify(currentEvent)}`
304
+ );
305
+ }
306
+ if (parsed && isLeafEventNode(parsed)) {
307
+ if (holdingChangesNonEmpty(parsed.event)) {
308
+ result.push({
309
+ ...parsed.event,
310
+ label: {
311
+ ...parsed.event.label,
312
+ meta: removeParsedMetaKeys(parsed.event.label.meta)
313
+ }
314
+ });
315
+ }
316
+ continueAfterNodeId = parsed.continueAfterNodeId;
317
+ } else if (parsed) {
318
+ callStack.push({
319
+ parentChoiceName: parsed.parentChoiceName,
320
+ untilNodeId: parsed.lastDescendantNodeId
321
+ });
322
+ }
323
+ }
324
+ return result;
325
+ }
326
+ parseRawCreate(create, parentChoice) {
327
+ return this.buildRawEvent(create, create.nodeId, (result) => {
328
+ return {
329
+ // TODO: this code currently only looks at the first instrument
330
+ // to determine the type of the Event.
331
+ type: Number(
332
+ result.lockedHoldingsChangeSummaries[0]?.amountChange
333
+ ) > 0 ? "Lock" : "Create",
334
+ parentChoice,
335
+ contractId: create.contractId,
336
+ offset: create.offset,
337
+ templateId: create.templateId,
338
+ payload: result.payload,
339
+ packageName: create.packageName,
340
+ meta: void 0
341
+ };
342
+ });
343
+ }
344
+ async parseRawArchive(archive, parentChoice) {
345
+ const events = await this.getEventsForArchive(archive);
346
+ if (!events) {
347
+ return null;
348
+ }
349
+ return this.buildRawEvent(
350
+ events.created.createdEvent,
351
+ archive.nodeId,
352
+ (result) => {
353
+ return {
354
+ type: "Archive",
355
+ parentChoice,
356
+ contractId: archive.contractId,
357
+ offset: archive.offset,
358
+ templateId: archive.templateId,
359
+ packageName: archive.packageName,
360
+ actingParties: archive.actingParties || [],
361
+ payload: result.payload,
362
+ meta: void 0
363
+ };
364
+ }
365
+ );
366
+ }
367
+ buildRawEvent(originalCreate, nodeId, buildLabel) {
368
+ const view = getKnownInterfaceView(originalCreate);
369
+ let result;
370
+ switch (view?.type) {
371
+ case "Holding": {
372
+ const holdingView = view.viewValue;
373
+ if (this.partyId !== holdingView.owner) {
374
+ result = null;
375
+ } else {
376
+ const isLocked = !!holdingView.lock;
377
+ const summary = {
378
+ instrumentId: holdingView.instrumentId,
379
+ amountChange: holdingView.amount,
380
+ numInputs: 0,
381
+ inputAmount: "0",
382
+ numOutputs: 1,
383
+ outputAmount: holdingView.amount
384
+ };
385
+ const lockedHoldingsChangeSummaries = isLocked ? [summary] : [];
386
+ const unlockedHoldingsChangeSummaries = isLocked ? [] : [summary];
387
+ result = {
388
+ payload: holdingView,
389
+ unlockedHoldingsChange: {
390
+ creates: isLocked ? [] : [holdingView],
391
+ archives: []
392
+ },
393
+ lockedHoldingsChange: {
394
+ creates: isLocked ? [holdingView] : [],
395
+ archives: []
396
+ },
397
+ lockedHoldingsChangeSummaries,
398
+ lockedHoldingsChangeSummary: lockedHoldingsChangeSummaries[0] ?? emptyHoldingsChangeSummary,
399
+ unlockedHoldingsChangeSummaries,
400
+ unlockedHoldingsChangeSummary: unlockedHoldingsChangeSummaries[0] ?? emptyHoldingsChangeSummary,
401
+ transferInstruction: null
402
+ };
403
+ }
404
+ break;
405
+ }
406
+ case "TransferInstruction": {
407
+ const transferInstructionView = view.viewValue;
408
+ if (![
409
+ transferInstructionView.transfer.sender,
410
+ transferInstructionView.transfer.receiver
411
+ ].some((stakeholder) => stakeholder === this.partyId)) {
412
+ result = null;
413
+ } else {
414
+ const multiStepCorrelationId = getCorrelationIdFromTransferInstruction(
415
+ originalCreate.contractId,
416
+ transferInstructionView.originalInstructionCid ?? null
417
+ );
418
+ result = {
419
+ payload: transferInstructionView,
420
+ transferInstruction: {
421
+ originalInstructionCid: transferInstructionView.originalInstructionCid,
422
+ transfer: transferInstructionView.transfer,
423
+ meta: transferInstructionView.meta,
424
+ status: {
425
+ before: transferInstructionView.status,
426
+ // raw DAML pending sub-state
427
+ current: { tag: "Pending", value: {} }
428
+ // normalized
429
+ },
430
+ multiStepCorrelationId
431
+ },
432
+ unlockedHoldingsChange: { creates: [], archives: [] },
433
+ lockedHoldingsChange: { creates: [], archives: [] },
434
+ unlockedHoldingsChangeSummaries: [],
435
+ unlockedHoldingsChangeSummary: emptyHoldingsChangeSummary,
436
+ lockedHoldingsChangeSummaries: [],
437
+ lockedHoldingsChangeSummary: emptyHoldingsChangeSummary
438
+ };
439
+ }
440
+ break;
441
+ }
442
+ default:
443
+ result = null;
444
+ }
445
+ return result && {
446
+ continueAfterNodeId: nodeId,
447
+ event: {
448
+ label: buildLabel(result),
449
+ unlockedHoldingsChange: result.unlockedHoldingsChange,
450
+ lockedHoldingsChange: result.lockedHoldingsChange,
451
+ lockedHoldingsChangeSummaries: result.lockedHoldingsChangeSummaries,
452
+ lockedHoldingsChangeSummary: result.lockedHoldingsChangeSummary,
453
+ unlockedHoldingsChangeSummaries: result.unlockedHoldingsChangeSummaries,
454
+ unlockedHoldingsChangeSummary: result.unlockedHoldingsChangeSummary,
455
+ transferInstruction: result.transferInstruction
456
+ }
457
+ };
458
+ }
459
+ async parseExercise(exercise) {
460
+ let result = null;
461
+ const tokenStandardChoice = {
462
+ name: exercise.choice,
463
+ choiceArgument: exercise.choiceArgument,
464
+ exerciseResult: exercise.exerciseResult
465
+ };
466
+ switch (exercise.choice) {
467
+ case "TransferRule_Transfer":
468
+ case "TransferFactory_Transfer":
469
+ result = await this.buildTransfer(exercise, tokenStandardChoice);
470
+ break;
471
+ case "TransferInstruction_Accept":
472
+ case "TransferInstruction_Reject":
473
+ case "TransferInstruction_Withdraw":
474
+ case "TransferInstruction_Update":
475
+ result = await this.buildFromTransferInstructionExercise(
476
+ exercise,
477
+ tokenStandardChoice
478
+ );
479
+ break;
480
+ case "BurnMintFactory_BurnMint":
481
+ result = await this.buildMergeSplit(
482
+ exercise,
483
+ tokenStandardChoice
484
+ );
485
+ break;
486
+ default: {
487
+ const meta = mergeMetas(exercise);
488
+ const txKind = getMetaKeyValue(TxKindMetaKey, meta);
489
+ if (txKind) {
490
+ result = await this.parseViaTxKind(exercise, txKind);
491
+ }
492
+ break;
493
+ }
494
+ }
495
+ if (!result) {
496
+ return {
497
+ lastDescendantNodeId: exercise.lastDescendantNodeId,
498
+ parentChoiceName: exercise.choice
499
+ };
500
+ } else {
501
+ const lockedHoldingsChange = {
502
+ creates: result.children.creates.filter(
503
+ (h) => !!h.lock && h.owner === this.partyId
504
+ ),
505
+ archives: result.children.archives.filter(
506
+ (h) => !!h.lock && h.owner === this.partyId
507
+ )
508
+ };
509
+ const unlockedHoldingsChange = {
510
+ creates: result.children.creates.filter(
511
+ (h) => !h.lock && h.owner === this.partyId
512
+ ),
513
+ archives: result.children.archives.filter(
514
+ (h) => !h.lock && h.owner === this.partyId
515
+ )
516
+ };
517
+ const lockedHoldingsChangeSummaries = computeSummaries(
518
+ lockedHoldingsChange,
519
+ this.partyId
520
+ );
521
+ const unlockedHoldingsChangeSummaries = computeSummaries(
522
+ unlockedHoldingsChange,
523
+ this.partyId
524
+ );
525
+ return {
526
+ event: {
527
+ label: result.label,
528
+ lockedHoldingsChange,
529
+ lockedHoldingsChangeSummaries,
530
+ lockedHoldingsChangeSummary: lockedHoldingsChangeSummaries[0] ?? emptyHoldingsChangeSummary,
531
+ unlockedHoldingsChange,
532
+ unlockedHoldingsChangeSummaries,
533
+ unlockedHoldingsChangeSummary: unlockedHoldingsChangeSummaries[0] ?? emptyHoldingsChangeSummary,
534
+ transferInstruction: result.transferInstruction
535
+ },
536
+ continueAfterNodeId: exercise.lastDescendantNodeId
537
+ };
538
+ }
539
+ }
540
+ async parseViaTxKind(exercisedEvent, txKind) {
541
+ switch (txKind) {
542
+ case "transfer":
543
+ return await this.buildTransfer(exercisedEvent, null);
544
+ case "merge-split":
545
+ case "burn":
546
+ case "mint":
547
+ return await this.buildMergeSplit(exercisedEvent, null);
548
+ case "unlock":
549
+ return await this.buildBasic(exercisedEvent, "Unlock", null);
550
+ case "expire-dust":
551
+ return await this.buildBasic(exercisedEvent, "ExpireDust", null);
552
+ default:
553
+ throw new Error(
554
+ `Unknown tx-kind '${txKind}' in ${JSON.stringify(exercisedEvent)}`
555
+ );
556
+ }
557
+ }
558
+ async buildTransfer(exercisedEvent, tokenStandardChoice, transferInstructions) {
559
+ const meta = mergeMetas(
560
+ exercisedEvent,
561
+ transferInstructions?.transfer?.meta
562
+ );
563
+ const reason = getMetaKeyValue(ReasonMetaKey, meta);
564
+ const choiceArgumentTransfer = exercisedEvent.choiceArgument.transfer;
565
+ const sender = transferInstructions?.transfer?.sender || getMetaKeyValue(SenderMetaKey, meta) || choiceArgumentTransfer.sender;
566
+ if (!sender) {
567
+ console.error(
568
+ `Malformed transfer didn't contain sender. Will instead attempt to parse the children.
569
+ Transfer: ${JSON.stringify(exercisedEvent)}`
570
+ );
571
+ return null;
572
+ }
573
+ const resultTag = exercisedEvent.exerciseResult?.output?.tag || void 0;
574
+ const pendingCid = getPendingTransferInstructionCid(exercisedEvent);
575
+ const currentTag = currentStatusFromChoiceOrResult(
576
+ exercisedEvent.choice,
577
+ resultTag
578
+ );
579
+ const children = await this.getChildren(exercisedEvent);
580
+ const receiverAmounts = /* @__PURE__ */ new Map();
581
+ children.creates.filter((h) => h.owner !== sender).forEach(
582
+ (holding) => receiverAmounts.set(
583
+ holding.owner,
584
+ (receiverAmounts.get(holding.owner) || BigNumber("0")).plus(
585
+ BigNumber(holding.amount)
586
+ )
587
+ )
588
+ );
589
+ const amountChanges = computeAmountChanges(children, meta, this.partyId);
590
+ let label;
591
+ if (receiverAmounts.size === 0) {
592
+ label = {
593
+ ...amountChanges,
594
+ type: "MergeSplit",
595
+ tokenStandardChoice,
596
+ reason,
597
+ meta
598
+ };
599
+ } else if (sender === this.partyId) {
600
+ label = {
601
+ ...amountChanges,
602
+ type: "TransferOut",
603
+ receiverAmounts: [...receiverAmounts].map(([k, v]) => {
604
+ return { receiver: k, amount: v.toString() };
605
+ }),
606
+ tokenStandardChoice,
607
+ reason,
608
+ meta
609
+ };
610
+ } else {
611
+ label = {
612
+ type: "TransferIn",
613
+ // for Transfers, the burn/mint is always 0 for the receiving party (i.e., 0 for TransferIn)
614
+ burnAmount: "0",
615
+ mintAmount: "0",
616
+ sender,
617
+ tokenStandardChoice,
618
+ reason,
619
+ meta
620
+ };
621
+ }
622
+ if (transferInstructions) {
623
+ transferInstructions.status.current = transferInstructions.status.current || { tag: currentTag, value: {} };
624
+ return {
625
+ label,
626
+ children,
627
+ transferInstruction: transferInstructions
628
+ };
629
+ }
630
+ const transferInstruction = {
631
+ originalInstructionCid: null,
632
+ ...choiceArgumentTransfer !== void 0 && {
633
+ transfer: choiceArgumentTransfer
634
+ },
635
+ status: {
636
+ before: null,
637
+ current: { tag: currentTag, value: {} }
638
+ },
639
+ meta: null,
640
+ ...pendingCid ? { multiStepCorrelationId: pendingCid } : {}
641
+ };
642
+ return {
643
+ label,
644
+ children,
645
+ transferInstruction
646
+ };
647
+ }
648
+ async buildMergeSplit(exercisedEvent, tokenStandardChoice) {
649
+ let type;
650
+ const meta = mergeMetas(exercisedEvent);
651
+ switch (getMetaKeyValue(TxKindMetaKey, meta)) {
652
+ case "burn":
653
+ type = "Burn";
654
+ break;
655
+ case "mint":
656
+ type = "Mint";
657
+ break;
658
+ default:
659
+ type = "MergeSplit";
660
+ }
661
+ const reason = getMetaKeyValue(ReasonMetaKey, meta);
662
+ const children = await this.getChildren(exercisedEvent);
663
+ const amountChanges = computeAmountChanges(children, meta, this.partyId);
664
+ const label = {
665
+ ...amountChanges,
666
+ type,
667
+ tokenStandardChoice,
668
+ reason,
669
+ meta
670
+ };
671
+ return {
672
+ label,
673
+ children,
674
+ transferInstruction: null
675
+ };
676
+ }
677
+ async buildFromTransferInstructionExercise(exercisedEvent, tokenStandardChoice) {
678
+ const instructionCid = exercisedEvent.contractId;
679
+ const transferInstructionEvents = await this.getEventsForArchive(exercisedEvent);
680
+ if (!transferInstructionEvents) {
681
+ return null;
682
+ }
683
+ const transferInstructionView = ensureInterfaceViewIsPresent(
684
+ transferInstructionEvents.created.createdEvent,
685
+ TRANSFER_INSTRUCTION_INTERFACE_ID
686
+ ).viewValue;
687
+ const multiStepCorrelationId = getCorrelationIdFromTransferInstruction(
688
+ instructionCid,
689
+ transferInstructionView.originalInstructionCid ?? null
690
+ );
691
+ const resultTag = exercisedEvent.exerciseResult?.output?.tag || void 0;
692
+ const currentTag = currentStatusFromChoiceOrResult(
693
+ exercisedEvent.choice,
694
+ resultTag
695
+ );
696
+ const transferInstruction = {
697
+ originalInstructionCid: transferInstructionView.originalInstructionCid,
698
+ multiStepCorrelationId,
699
+ transfer: transferInstructionView.transfer,
700
+ meta: transferInstructionView.meta,
701
+ status: {
702
+ before: transferInstructionView.status,
703
+ current: { tag: currentTag, value: {} }
704
+ }
705
+ };
706
+ const exerciseResultOutputTag = resultTag;
707
+ let result = null;
708
+ switch (exerciseResultOutputTag) {
709
+ case "TransferInstructionResult_Failed":
710
+ case "TransferInstructionResult_Pending":
711
+ result = await this.buildMergeSplit(
712
+ exercisedEvent,
713
+ tokenStandardChoice
714
+ );
715
+ break;
716
+ case "TransferInstructionResult_Completed":
717
+ result = await this.buildTransfer(
718
+ exercisedEvent,
719
+ tokenStandardChoice,
720
+ transferInstruction
721
+ );
722
+ break;
723
+ default:
724
+ throw new Error(
725
+ `Unknown TransferInstructionResult: ${exerciseResultOutputTag}`
726
+ );
727
+ }
728
+ return result && {
729
+ ...result,
730
+ transferInstruction
731
+ };
732
+ }
733
+ async buildBasic(exercisedEvent, type, tokenStandardChoice) {
734
+ const children = await this.getChildren(exercisedEvent);
735
+ const meta = mergeMetas(exercisedEvent);
736
+ const amountChanges = computeAmountChanges(children, meta, this.partyId);
737
+ const reason = getMetaKeyValue(ReasonMetaKey, meta);
738
+ return {
739
+ label: {
740
+ ...amountChanges,
741
+ type,
742
+ tokenStandardChoice,
743
+ reason,
744
+ meta
745
+ },
746
+ children,
747
+ transferInstruction: null
748
+ };
749
+ }
750
+ async getChildren(exercisedEvent) {
751
+ const mutatingResult = { creates: [], archives: [] };
752
+ const childrenEventsSlice = (this.transaction.events || []).map(getNodeIdAndEvent).filter(
753
+ ({ nodeId }) => nodeId > exercisedEvent.nodeId && nodeId <= exercisedEvent.lastDescendantNodeId
754
+ );
755
+ if (exercisedEvent.consuming && hasInterface(HOLDING_INTERFACE_ID, exercisedEvent)) {
756
+ const selfEvent = await this.getEventsForArchive(exercisedEvent);
757
+ if (selfEvent) {
758
+ const holdingView = ensureInterfaceViewIsPresent(
759
+ selfEvent.created.createdEvent,
760
+ HOLDING_INTERFACE_ID
761
+ ).viewValue;
762
+ mutatingResult.archives.push({
763
+ amount: holdingView.amount,
764
+ instrumentId: holdingView.instrumentId,
765
+ contractId: exercisedEvent.contractId,
766
+ owner: holdingView.owner,
767
+ meta: holdingView.meta,
768
+ lock: holdingView.lock
769
+ });
770
+ }
771
+ }
772
+ for (const {
773
+ createdEvent,
774
+ archivedEvent,
775
+ exercisedEvent: exercisedEvent2
776
+ } of childrenEventsSlice) {
777
+ if (createdEvent) {
778
+ const interfaceView = getInterfaceView(createdEvent);
779
+ if (interfaceView && matchInterfaceIds(
780
+ HOLDING_INTERFACE_ID,
781
+ interfaceView.interfaceId
782
+ )) {
783
+ const holdingView = interfaceView.viewValue;
784
+ mutatingResult.creates.push({
785
+ amount: holdingView.amount,
786
+ instrumentId: holdingView.instrumentId,
787
+ contractId: createdEvent.contractId,
788
+ owner: holdingView.owner,
789
+ meta: holdingView.meta,
790
+ lock: holdingView.lock
791
+ });
792
+ }
793
+ } else if (archivedEvent && hasInterface(HOLDING_INTERFACE_ID, archivedEvent) || exercisedEvent2 && exercisedEvent2.consuming && hasInterface(HOLDING_INTERFACE_ID, exercisedEvent2)) {
794
+ const contractEvents = await this.getEventsForArchive(
795
+ archivedEvent || exercisedEvent2
796
+ );
797
+ if (contractEvents) {
798
+ const holdingView = ensureInterfaceViewIsPresent(
799
+ contractEvents.created?.createdEvent,
800
+ HOLDING_INTERFACE_ID
801
+ ).viewValue;
802
+ mutatingResult.archives.push({
803
+ amount: holdingView.amount,
804
+ instrumentId: holdingView.instrumentId,
805
+ contractId: archivedEvent?.contractId || exercisedEvent2.contractId,
806
+ owner: holdingView.owner,
807
+ meta: holdingView.meta,
808
+ lock: holdingView.lock
809
+ });
810
+ }
811
+ }
812
+ }
813
+ return {
814
+ // remove transient contracts
815
+ creates: mutatingResult.creates.filter(
816
+ (create) => !mutatingResult.archives.some(
817
+ (archive) => create.contractId === archive.contractId
818
+ )
819
+ ),
820
+ archives: mutatingResult.archives.filter(
821
+ (archive) => !mutatingResult.creates.some(
822
+ (create) => create.contractId === archive.contractId
823
+ )
824
+ )
825
+ };
826
+ }
827
+ async getEventsForArchive(archivedEvent) {
828
+ if (!(archivedEvent.witnessParties || []).includes(this.partyId)) {
829
+ return null;
830
+ }
831
+ const basePayload = {
832
+ contractId: archivedEvent.contractId,
833
+ eventFormat: EventFilterBySetup({
834
+ interfaceIds: [
835
+ HOLDING_INTERFACE_ID,
836
+ TRANSFER_INSTRUCTION_INTERFACE_ID
837
+ ],
838
+ isMasterUser: this.isMasterUser,
839
+ partyId: this.partyId,
840
+ verbose: true
841
+ })
842
+ };
843
+ const payload = this.ledgerClient.getCurrentClientVersion() === "3.3" ? { ...basePayload, requestingParties: [] } : basePayload;
844
+ const events = await this.ledgerClient.postWithRetry("/v2/events/events-by-contract-id", payload).catch((err) => {
845
+ if (err.code === "CONTRACT_EVENTS_NOT_FOUND") {
846
+ return null;
847
+ } else {
848
+ throw err;
849
+ }
850
+ });
851
+ if (!events) {
852
+ return null;
853
+ }
854
+ const created = events.created;
855
+ const archived = events.archived;
856
+ if (!created || !archived) {
857
+ throw new Error(
858
+ `Archival of ${archivedEvent.contractId} does not have a corresponding create/archive event: ${JSON.stringify(
859
+ events
860
+ )}`
861
+ );
862
+ }
863
+ return { created, archived };
864
+ }
865
+ };
866
+ function isLeafEventNode(result) {
867
+ return !!result.event;
868
+ }
869
+ function getNodeIdAndEvent(event) {
870
+ if ("ExercisedEvent" in event) {
871
+ if (event.ExercisedEvent.choice === "Archive") {
872
+ return {
873
+ nodeId: event.ExercisedEvent.nodeId,
874
+ archivedEvent: event.ExercisedEvent
875
+ };
876
+ } else {
877
+ return {
878
+ nodeId: event.ExercisedEvent.nodeId,
879
+ exercisedEvent: event.ExercisedEvent
880
+ };
881
+ }
882
+ } else if ("CreatedEvent" in event) {
883
+ return {
884
+ nodeId: event.CreatedEvent.nodeId,
885
+ createdEvent: event.CreatedEvent
886
+ };
887
+ } else if ("ArchivedEvent" in event) {
888
+ return {
889
+ nodeId: event.ArchivedEvent.nodeId,
890
+ archivedEvent: event.ArchivedEvent
891
+ };
892
+ } else {
893
+ throw new Error(`Impossible event type: ${event}`);
894
+ }
895
+ }
896
+ function sumHoldingsChange(change, filter) {
897
+ return sumHoldings(
898
+ change.creates.filter((create) => filter(create.owner, create.lock))
899
+ ).minus(
900
+ sumHoldings(
901
+ change.archives.filter(
902
+ (archive) => filter(archive.owner, archive.lock)
903
+ )
904
+ )
905
+ );
906
+ }
907
+ function sumHoldings(holdings) {
908
+ if (holdings.length > 0) {
909
+ const instrumentId = holdings[0].instrumentId;
910
+ for (const holding of holdings) {
911
+ if (holding.instrumentId.admin !== instrumentId.admin || holding.instrumentId.id !== instrumentId.id) {
912
+ throw new Error(
913
+ `Attempted to call sumHoldings on heterogeneous instruments: ${JSON.stringify(instrumentId)} != ${JSON.stringify(holding.instrumentId)}`
914
+ );
915
+ }
916
+ }
917
+ }
918
+ return BigNumber.sum(
919
+ ...holdings.map((h) => h.amount).concat(["0"])
920
+ // avoid NaN
921
+ );
922
+ }
923
+ function computeAmountChanges(children, meta, partyId) {
924
+ const burnAmount = BigNumber(getMetaKeyValue(BurnedMetaKey, meta) || "0");
925
+ const partyHoldingAmountChange = sumHoldingsChange(
926
+ children,
927
+ (owner) => owner === partyId
928
+ );
929
+ const otherPartiesHoldingAmountChange = sumHoldingsChange(
930
+ children,
931
+ (owner) => owner !== partyId
932
+ );
933
+ const mintAmount = partyHoldingAmountChange.plus(burnAmount).plus(otherPartiesHoldingAmountChange);
934
+ return {
935
+ burnAmount: burnAmount.toString(),
936
+ mintAmount: mintAmount.toString()
937
+ };
938
+ }
939
+ function computeSummary(instrumentId, changes, partyId) {
940
+ const amountChange = sumHoldingsChange(
941
+ changes,
942
+ (owner) => owner === partyId
943
+ );
944
+ const outputAmount = sumHoldings(changes.creates);
945
+ const inputAmount = sumHoldings(changes.archives);
946
+ return {
947
+ instrumentId,
948
+ amountChange: amountChange.toString(),
949
+ numOutputs: changes.creates.length,
950
+ outputAmount: outputAmount.toString(),
951
+ numInputs: changes.archives.length,
952
+ inputAmount: inputAmount.toString()
953
+ };
954
+ }
955
+ function holdingsChangeByInstrument(changes) {
956
+ const map = new InstrumentMap();
957
+ for (const create of changes.creates) {
958
+ if (map.has(create.instrumentId)) {
959
+ map.get(create.instrumentId).creates.push(create);
960
+ } else {
961
+ map.set(create.instrumentId, { creates: [create], archives: [] });
962
+ }
963
+ }
964
+ for (const archive of changes.archives) {
965
+ if (map.has(archive.instrumentId)) {
966
+ map.get(archive.instrumentId).archives.push(archive);
967
+ } else {
968
+ map.set(archive.instrumentId, { creates: [], archives: [archive] });
969
+ }
970
+ }
971
+ return map;
972
+ }
973
+ function computeSummaries(changes, partyId) {
974
+ const byInstrument = holdingsChangeByInstrument(changes);
975
+ return [...byInstrument.entries()].map(
976
+ ([instrumentId, change]) => computeSummary(instrumentId, change, partyId)
977
+ );
978
+ }
979
+ function holdingChangesNonEmpty(event) {
980
+ return event.unlockedHoldingsChange.creates.length > 0 || event.unlockedHoldingsChange.archives.length > 0 || event.lockedHoldingsChange.creates.length > 0 || event.lockedHoldingsChange.archives.length > 0;
981
+ }
982
+ var emptyHoldingsChangeSummary = {
983
+ // This is obviously incorrect, but the field was introduced at the same
984
+ // time at which we introduced the more correct per-instrument summaries,
985
+ // so we know that old code couldn't use this (broken) field, and new code
986
+ // should use the correct summaries.
987
+ instrumentId: { admin: "", id: "" },
988
+ numInputs: 0,
989
+ numOutputs: 0,
990
+ inputAmount: "0",
991
+ outputAmount: "0",
992
+ amountChange: "0"
993
+ };
994
+
995
+ export { AllKnownMetaKeys, BurnedMetaKey, ReasonMetaKey, SenderMetaKey, TokenStandardTransactionInterfaces, TransactionParser, TxKindMetaKey, ensureInterfaceViewIsPresent, getInterfaceView, getKnownInterfaceView, getMetaKeyValue, hasInterface, matchInterfaceIds, mergeMetas, removeParsedMetaKeys, renderTransaction, splitInterfaceId };
996
+ //# sourceMappingURL=index.js.map
997
+ //# sourceMappingURL=index.js.map