@distrohelena/canton-explorer 0.1.0 → 0.1.2

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.
Files changed (50) hide show
  1. package/README.md +26 -0
  2. package/config/nodes.example.json +5 -1
  3. package/dist/public/assets/DebuggerView-Cf8sZ-NE.js +2 -0
  4. package/dist/public/assets/css.worker-BKx7BC4h.js +89 -0
  5. package/dist/public/assets/editor-C5uazxST.css +1 -0
  6. package/dist/public/assets/editor.api-8w-mShFJ.js +820 -0
  7. package/dist/public/assets/editor.worker-DQJwLooG.js +26 -0
  8. package/dist/public/assets/html.worker-ChRLLxCa.js +502 -0
  9. package/dist/public/assets/index-C6ucHbtz.js +1 -0
  10. package/dist/public/assets/index-D9pVnHtz.css +1 -0
  11. package/dist/public/assets/json.worker-DR52qCex.js +58 -0
  12. package/dist/public/assets/ts.worker-Cqvg7802.js +67734 -0
  13. package/dist/public/index.html +2 -2
  14. package/dist/src/api/debugger.controller.d.ts +16 -0
  15. package/dist/src/api/debugger.controller.js +105 -0
  16. package/dist/src/api/debugger.controller.js.map +1 -0
  17. package/dist/src/app-bootstrap.d.ts +1 -1
  18. package/dist/src/app-bootstrap.js +6 -2
  19. package/dist/src/app-bootstrap.js.map +1 -1
  20. package/dist/src/app.module.js +4 -1
  21. package/dist/src/app.module.js.map +1 -1
  22. package/dist/src/config/node-config.schema.d.ts +11 -0
  23. package/dist/src/config/node-config.schema.js +8 -0
  24. package/dist/src/config/node-config.schema.js.map +1 -1
  25. package/dist/src/config/node-config.service.d.ts +2 -1
  26. package/dist/src/config/node-config.service.js +3 -0
  27. package/dist/src/config/node-config.service.js.map +1 -1
  28. package/dist/src/debugger/debugger.service.d.ts +158 -0
  29. package/dist/src/debugger/debugger.service.js +746 -0
  30. package/dist/src/debugger/debugger.service.js.map +1 -0
  31. package/dist/src/domain/node.types.d.ts +1 -1
  32. package/dist/src/packages/daml-decoder.types.d.ts +8 -1
  33. package/dist/src/packages/daml-value-decoder.service.js.map +1 -1
  34. package/dist/src/packages/package-cache.service.js +103 -7
  35. package/dist/src/packages/package-cache.service.js.map +1 -1
  36. package/dist/src/packages/package-registry.service.js.map +1 -1
  37. package/dist/src/packages/package-sync.service.js +31 -3
  38. package/dist/src/packages/package-sync.service.js.map +1 -1
  39. package/dist/src/packages/pqs-package.service.js +15 -80
  40. package/dist/src/packages/pqs-package.service.js.map +1 -1
  41. package/dist/src/pqs/pqs-schema.d.ts +3 -0
  42. package/dist/src/pqs/pqs-schema.js +32 -0
  43. package/dist/src/pqs/pqs-schema.js.map +1 -0
  44. package/dist/src/pqs/pqs-summary.service.d.ts +3 -3
  45. package/dist/src/pqs/pqs-summary.service.js +619 -1414
  46. package/dist/src/pqs/pqs-summary.service.js.map +1 -1
  47. package/dist/tsconfig.build.tsbuildinfo +1 -1
  48. package/package.json +35 -31
  49. package/dist/public/assets/index-NfJYgVwb.css +0 -1
  50. package/dist/public/assets/index-ybPCCjei.js +0 -1
@@ -22,6 +22,7 @@ const package_cache_service_1 = require("../packages/package-cache.service");
22
22
  const package_registry_service_1 = require("../packages/package-registry.service");
23
23
  const grpc_operations_service_1 = require("../grpc/grpc-operations.service");
24
24
  const package_sync_service_1 = require("../packages/package-sync.service");
25
+ const pqs_schema_1 = require("./pqs-schema");
25
26
  const SEARCH_GROUP_LIMIT = 25;
26
27
  const CANTON_COIN_TOKEN_ID = 'canton-coin';
27
28
  const CANTON_COIN_TOKEN_NAME = 'Canton Coin';
@@ -62,18 +63,33 @@ const TOKEN_TRANSFER_TEMPLATE_IDS = [
62
63
  const INFERRED_HOLDING_V2_SOURCE = 'pqs_inferred_holding_v2';
63
64
  const TOKEN_TRANSFER_CACHE_TTL_MS = 5 * 60 * 1000;
64
65
  const TOKEN_TRANSFER_CACHE_LIMIT = 250;
65
- const ACTIVE_QUERY = `
66
- select
67
- current_database() as pqs_database,
68
- count(*)::int as active_contract_count,
69
- max(created_at_offset) as latest_offset,
70
- max(created_effective_at)::text as latest_event_at,
71
- (
72
- select count(*)::int
73
- from participant.lapi_update_meta
74
- ) as total_update_count
75
- from active()
76
- `;
66
+ function pqsCoreRelations(node) {
67
+ const qualifiedNode = {
68
+ ...node,
69
+ pqs: {
70
+ connectionUriEnv: node.pqs?.connectionUriEnv ?? '',
71
+ schema: node.pqs?.schema ?? 'public',
72
+ },
73
+ };
74
+ return {
75
+ contracts: (0, pqs_schema_1.qualifyPqsRelation)(qualifiedNode, '__contracts'),
76
+ contractTpe: (0, pqs_schema_1.qualifyPqsRelation)(qualifiedNode, '__contract_tpe'),
77
+ exercises: (0, pqs_schema_1.qualifyPqsRelation)(qualifiedNode, '__exercises'),
78
+ exerciseTpe: (0, pqs_schema_1.qualifyPqsRelation)(qualifiedNode, '__exercise_tpe'),
79
+ events: (0, pqs_schema_1.qualifyPqsRelation)(qualifiedNode, '__events'),
80
+ packages: (0, pqs_schema_1.qualifyPqsRelation)(qualifiedNode, '__packages'),
81
+ transactions: (0, pqs_schema_1.qualifyPqsRelation)(qualifiedNode, '__transactions'),
82
+ };
83
+ }
84
+ function isoUtcTimestampExpression(expression) {
85
+ return `to_char(${expression} at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`;
86
+ }
87
+ function contractTemplateIdentifierExpression(alias) {
88
+ return `case
89
+ when ${alias}.module_name is null or ${alias}.entity_name is null then null
90
+ else ${alias}.module_name || ':' || ${alias}.entity_name
91
+ end`;
92
+ }
77
93
  function compareGlobalMergedUpdates(left, right) {
78
94
  const leftRecordTimeMs = Date.parse(left.recordTime ?? '');
79
95
  const rightRecordTimeMs = Date.parse(right.recordTime ?? '');
@@ -360,48 +376,6 @@ function compareSearchPackageNames(left, right) {
360
376
  }
361
377
  return left.name.localeCompare(right.name);
362
378
  }
363
- const PARTICIPANT_FALLBACK_QUERY = `
364
- select
365
- current_database() as pqs_database,
366
- (
367
- select count(*)::int
368
- from participant.par_active_contracts
369
- ) as active_contract_count,
370
- (
371
- select max(event_offset)::text
372
- from participant.lapi_update_meta
373
- ) as latest_offset,
374
- (
375
- select to_char(
376
- to_timestamp(max(record_time) / 1000000.0) at time zone 'UTC',
377
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
378
- )
379
- from participant.lapi_update_meta
380
- ) as latest_event_at,
381
- (
382
- select count(*)::int
383
- from participant.lapi_update_meta
384
- ) as total_update_count
385
- `;
386
- function activityBucketsQuery(days, bucketMinutes) {
387
- const normalizedDays = Number.isFinite(days) && days > 0 ? Math.trunc(days) : 30;
388
- const normalizedBucketMinutes = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.trunc(bucketMinutes) : 15;
389
- const bucketSeconds = normalizedBucketMinutes * 60;
390
- const minRecordTimeMicros = Math.floor((Date.now() - normalizedDays * 24 * 60 * 60 * 1000) * 1000);
391
- return `
392
- select
393
- to_char(
394
- to_timestamp(floor((record_time / 1000000.0) / ${bucketSeconds}) * ${bucketSeconds}) at time zone 'UTC',
395
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
396
- ) as bucket_timestamp,
397
- count(*)::int as activity_value,
398
- max(event_offset)::text as latest_offset
399
- from participant.lapi_update_meta
400
- where record_time >= ${minRecordTimeMicros}
401
- group by 1
402
- order by 1 asc
403
- `;
404
- }
405
379
  function normalizePartyFilters(parties) {
406
380
  if (!parties) {
407
381
  return [];
@@ -440,139 +414,108 @@ function normalizeTokenIssuerFilters(values) {
440
414
  .map((value) => value.trim())
441
415
  .filter((value) => value.length > 0)));
442
416
  }
443
- function buildUpdatePartyExistsCondition(partyId) {
444
- const createMatch = partyWitnessArrayMatchCondition('create_event.tree_event_witnesses', partyId);
445
- const consumingMatch = partyWitnessArrayMatchCondition('exercise_event.tree_event_witnesses', partyId);
446
- const nonConsumingMatch = partyWitnessArrayMatchCondition('exercise_event.tree_event_witnesses', partyId);
447
- return `(
448
- exists (
449
- select 1
450
- from participant.lapi_events_create create_event
451
- where create_event.update_id = update_meta.update_id
452
- and ${createMatch}
453
- )
454
- or exists (
455
- select 1
456
- from participant.lapi_events_consuming_exercise exercise_event
457
- where exercise_event.update_id = update_meta.update_id
458
- and ${consumingMatch}
459
- )
460
- or exists (
461
- select 1
462
- from participant.lapi_events_non_consuming_exercise exercise_event
463
- where exercise_event.update_id = update_meta.update_id
464
- and ${nonConsumingMatch}
465
- )
466
- )`;
417
+ function summaryQuery(node) {
418
+ const relations = pqsCoreRelations(node);
419
+ return `
420
+ select
421
+ current_database() as pqs_database,
422
+ (
423
+ select count(*)::int
424
+ from ${relations.contracts} contract_row
425
+ where contract_row.archived_at_ix is null
426
+ ) as active_contract_count,
427
+ (
428
+ select max(tx.offset)::text
429
+ from ${relations.transactions} tx
430
+ ) as latest_offset,
431
+ (
432
+ select ${isoUtcTimestampExpression('max(tx.effective_at)')}
433
+ from ${relations.transactions} tx
434
+ ) as latest_event_at,
435
+ (
436
+ select count(*)::int
437
+ from ${relations.transactions} tx
438
+ ) as total_update_count
439
+ `;
440
+ }
441
+ function pqsActivityBucketsQuery(node, days, bucketMinutes) {
442
+ const relations = pqsCoreRelations(node);
443
+ const normalizedDays = Number.isFinite(days) && days > 0 ? Math.trunc(days) : 30;
444
+ const normalizedBucketMinutes = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.trunc(bucketMinutes) : 15;
445
+ const bucketSeconds = normalizedBucketMinutes * 60;
446
+ const minEffectiveAt = new Date(Date.now() - normalizedDays * 24 * 60 * 60 * 1000).toISOString();
447
+ return `
448
+ select
449
+ to_char(
450
+ to_timestamp(floor(extract(epoch from tx.effective_at) / ${bucketSeconds}) * ${bucketSeconds}) at time zone 'UTC',
451
+ 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
452
+ ) as bucket_timestamp,
453
+ count(*)::int as activity_value,
454
+ max(tx.offset)::text as latest_offset
455
+ from ${relations.transactions} tx
456
+ where tx.effective_at >= '${escapeSqlLiteral(minEffectiveAt)}'::timestamptz
457
+ group by 1
458
+ order by 1 asc
459
+ `;
467
460
  }
468
- function buildNormalizedUpdatePartyExistsCondition(partyId) {
469
- const partyMatch = partyScalarMatchCondition('party_string.external_string', partyId);
461
+ function buildPqsUpdatePartyExistsCondition(node, partyId) {
462
+ const relations = pqsCoreRelations(node);
463
+ const contractMatch = partyWitnessArrayMatchCondition('contract_row.witnesses', partyId);
464
+ const exerciseMatch = partyWitnessArrayMatchCondition('exercise_row.witnesses', partyId);
470
465
  return `(
471
466
  exists (
472
467
  select 1
473
- from participant.lapi_events_activate_contract activate_event
474
- join participant.lapi_filter_activate_witness witness_filter
475
- on witness_filter.event_sequential_id = activate_event.event_sequential_id
476
- join participant.lapi_string_interning party_string
477
- on party_string.internal_id = witness_filter.party_id
478
- where activate_event.update_id = update_meta.update_id
479
- and ${partyMatch}
468
+ from ${relations.contracts} contract_row
469
+ where (contract_row.created_at_ix = tx.ix or contract_row.archived_at_ix = tx.ix)
470
+ and ${contractMatch}
480
471
  )
481
472
  or exists (
482
473
  select 1
483
- from participant.lapi_events_deactivate_contract deactivate_event
484
- join participant.lapi_filter_deactivate_witness witness_filter
485
- on witness_filter.event_sequential_id = deactivate_event.event_sequential_id
486
- join participant.lapi_string_interning party_string
487
- on party_string.internal_id = witness_filter.party_id
488
- where deactivate_event.update_id = update_meta.update_id
489
- and ${partyMatch}
490
- )
491
- or exists (
492
- select 1
493
- from participant.lapi_events_various_witnessed various_event
494
- join participant.lapi_filter_various_witness witness_filter
495
- on witness_filter.event_sequential_id = various_event.event_sequential_id
496
- join participant.lapi_string_interning party_string
497
- on party_string.internal_id = witness_filter.party_id
498
- where various_event.update_id = update_meta.update_id
499
- and ${partyMatch}
500
- )
501
- )`;
502
- }
503
- function buildUpdateTemplateExistsCondition(templateId) {
504
- const normalizedTemplateId = normalizeTemplateFilterValue(templateId);
505
- const quotedTemplateId = `'${escapeSqlLiteral(normalizedTemplateId)}'`;
506
- return `(
507
- exists (
508
- select 1
509
- from (
510
- select regexp_replace(coalesce(create_event.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id
511
- from participant.lapi_events_create create_event
512
- where create_event.update_id = update_meta.update_id
513
-
514
- union all
515
-
516
- select regexp_replace(coalesce(exercise_event.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id
517
- from participant.lapi_events_consuming_exercise exercise_event
518
- where exercise_event.update_id = update_meta.update_id
519
-
520
- union all
521
-
522
- select regexp_replace(coalesce(exercise_event.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id
523
- from participant.lapi_events_non_consuming_exercise exercise_event
524
- where exercise_event.update_id = update_meta.update_id
525
- ) update_event_templates
526
- where update_event_templates.template_id = ${quotedTemplateId}
474
+ from ${relations.exercises} exercise_row
475
+ where exercise_row.exercised_at_ix = tx.ix
476
+ and ${exerciseMatch}
527
477
  )
528
478
  )`;
529
479
  }
530
- function buildNormalizedUpdateTemplateExistsCondition(templateId) {
480
+ function buildPqsUpdateTemplateExistsCondition(node, templateId) {
481
+ const relations = pqsCoreRelations(node);
531
482
  const normalizedTemplateId = normalizeTemplateFilterValue(templateId);
532
483
  const quotedTemplateId = `'${escapeSqlLiteral(normalizedTemplateId)}'`;
484
+ const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
485
+ const exerciseTemplateId = contractTemplateIdentifierExpression('exercise_contract_tpe_row');
533
486
  return `(
534
487
  exists (
535
488
  select 1
536
489
  from (
537
- select regexp_replace(coalesce(contract.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id
538
- from participant.lapi_events_activate_contract activate_event
539
- left join participant.par_contracts contract
540
- on contract.internal_contract_id = activate_event.internal_contract_id
541
- where activate_event.update_id = update_meta.update_id
542
-
543
- union all
544
-
545
- select regexp_replace(coalesce(contract.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id
546
- from participant.lapi_events_various_witnessed various_event
547
- left join participant.par_contracts contract
548
- on contract.internal_contract_id = various_event.internal_contract_id
549
- where various_event.update_id = update_meta.update_id
550
- and various_event.event_type = 6
490
+ select ${contractTemplateId} as template_id
491
+ from ${relations.contracts} contract_row
492
+ join ${relations.contractTpe} contract_tpe_row
493
+ on contract_tpe_row.pk = contract_row.tpe_pk
494
+ where contract_row.created_at_ix = tx.ix
551
495
 
552
496
  union all
553
497
 
554
- select regexp_replace(coalesce(template_string.external_string, ''), '^t\\\\|#[^:]+:', '') as template_id
555
- from participant.lapi_events_deactivate_contract deactivate_event
556
- left join participant.lapi_string_interning template_string
557
- on template_string.internal_id = deactivate_event.template_id
558
- where deactivate_event.update_id = update_meta.update_id
498
+ select ${contractTemplateId} as template_id
499
+ from ${relations.contracts} contract_row
500
+ join ${relations.contractTpe} contract_tpe_row
501
+ on contract_tpe_row.pk = contract_row.tpe_pk
502
+ where contract_row.archived_at_ix = tx.ix
559
503
 
560
504
  union all
561
505
 
562
- select regexp_replace(coalesce(template_string.external_string, ''), '^t\\\\|#[^:]+:', '') as template_id
563
- from participant.lapi_events_various_witnessed various_event
564
- left join participant.lapi_string_interning template_string
565
- on template_string.internal_id = various_event.template_id
566
- where various_event.update_id = update_meta.update_id
567
- and various_event.event_type in (5, 7)
506
+ select ${exerciseTemplateId} as template_id
507
+ from ${relations.exercises} exercise_row
508
+ join ${relations.contractTpe} exercise_contract_tpe_row
509
+ on exercise_contract_tpe_row.pk = exercise_row.contract_tpe_pk
510
+ where exercise_row.exercised_at_ix = tx.ix
568
511
  ) update_event_templates
569
512
  where update_event_templates.template_id = ${quotedTemplateId}
570
513
  )
571
514
  )`;
572
515
  }
573
- function buildUpdatesFilterClause(parties, templates, partyMode) {
574
- const partyConditions = normalizePartyFilters(parties).map((party) => buildUpdatePartyExistsCondition(party));
575
- const templateConditions = normalizeTemplateFilters(templates).map((templateId) => buildUpdateTemplateExistsCondition(templateId));
516
+ function buildPqsUpdatesFilterClause(node, parties, templates, partyMode) {
517
+ const partyConditions = normalizePartyFilters(parties).map((party) => buildPqsUpdatePartyExistsCondition(node, party));
518
+ const templateConditions = normalizeTemplateFilters(templates).map((templateId) => buildPqsUpdateTemplateExistsCondition(node, templateId));
576
519
  const groups = [];
577
520
  if (partyConditions.length > 0) {
578
521
  const partyJoiner = normalizePartyFilterMode(partyMode) === 'and' ? '\n and ' : '\n or ';
@@ -590,103 +533,55 @@ function buildUpdatesFilterClause(parties, templates, partyMode) {
590
533
  }
591
534
  return groups.length === 1 ? groups[0] : `(\n ${groups.join('\n and ')}\n )`;
592
535
  }
593
- function buildNormalizedUpdatesFilterClause(parties, templates, partyMode) {
594
- const partyConditions = normalizePartyFilters(parties).map((party) => buildNormalizedUpdatePartyExistsCondition(party));
595
- const templateConditions = normalizeTemplateFilters(templates).map((templateId) => buildNormalizedUpdateTemplateExistsCondition(templateId));
596
- const groups = [];
597
- if (partyConditions.length > 0) {
598
- const partyJoiner = normalizePartyFilterMode(partyMode) === 'and' ? '\n and ' : '\n or ';
599
- groups.push(partyConditions.length === 1
600
- ? partyConditions[0]
601
- : `(\n ${partyConditions.join(partyJoiner)}\n )`);
602
- }
603
- if (templateConditions.length > 0) {
604
- groups.push(templateConditions.length === 1
605
- ? templateConditions[0]
606
- : `(\n ${templateConditions.join('\n or ')}\n )`);
607
- }
608
- if (groups.length === 0) {
609
- return null;
610
- }
611
- return groups.length === 1 ? groups[0] : `(\n ${groups.join('\n and ')}\n )`;
612
- }
613
- function recentUpdatesQuery(limit, before, after, parties, templates, partyMode, hideSplice) {
614
- const normalizedBefore = normalizeEventOffsetCursor(before);
615
- const normalizedAfter = normalizeEventOffsetCursor(after);
616
- const filterClause = buildUpdatesFilterClause(parties, templates, partyMode);
617
- const hideSpliceClause = hideSplice ? buildHideSpliceOffsetsClause() : null;
618
- const queryLimit = limit + 1;
619
- const afterFilters = [
620
- normalizedAfter ? `update_meta.event_offset::numeric > ${normalizedAfter}` : null,
621
- filterClause,
622
- hideSpliceClause,
623
- ].filter((value) => Boolean(value));
624
- const olderFilters = [
625
- normalizedBefore ? `update_meta.event_offset::numeric < ${normalizedBefore}` : null,
626
- filterClause,
627
- hideSpliceClause,
628
- ].filter((value) => Boolean(value));
629
- if (normalizedAfter && !normalizedBefore) {
630
- const whereClause = afterFilters.length > 0 ? `where ${afterFilters.join('\n and ')}` : '';
631
- return `
632
- select
633
- update_meta.update_id::text as update_id,
634
- update_meta.event_offset::text as event_offset,
635
- to_char(
636
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
637
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
638
- ) as record_time
639
- from participant.lapi_update_meta update_meta
640
- ${whereClause}
641
- order by update_meta.event_offset::numeric asc
642
- limit ${queryLimit}
643
- `;
644
- }
645
- const whereClause = olderFilters.length > 0 ? `where ${olderFilters.join('\n and ')}` : '';
536
+ function buildPqsHideSpliceUpdatesClause(node) {
537
+ const relations = pqsCoreRelations(node);
538
+ const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
539
+ const exerciseTemplateId = contractTemplateIdentifierExpression('exercise_contract_tpe_row');
646
540
  return `
647
- select
648
- update_meta.update_id::text as update_id,
649
- update_meta.event_offset::text as event_offset,
650
- to_char(
651
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
652
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
653
- ) as record_time
654
- from participant.lapi_update_meta update_meta
655
- ${whereClause}
656
- order by update_meta.event_offset::numeric desc
657
- limit ${queryLimit}
658
- `;
659
- }
660
- function searchUpdatesQuery(searchQuery, limit) {
661
- const quotedQuery = escapeSqlLiteral(searchQuery);
662
- return `
663
- select
664
- update_meta.update_id::text as update_id,
665
- update_meta.event_offset::text as event_offset,
666
- to_char(
667
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
668
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
669
- ) as record_time
670
- from participant.lapi_update_meta update_meta
671
- where update_meta.event_offset::text like '${quotedQuery}%'
672
- or regexp_replace(update_meta.update_id::text, '^\\\\x', '') like '${quotedQuery}%'
673
- order by update_meta.record_time desc, update_meta.event_offset::numeric desc
674
- limit ${limit}
541
+ exists (
542
+ select 1
543
+ from (
544
+ select ${contractTemplateId} as template_id
545
+ from ${relations.contracts} contract_row
546
+ join ${relations.contractTpe} contract_tpe_row
547
+ on contract_tpe_row.pk = contract_row.tpe_pk
548
+ where contract_row.created_at_ix = tx.ix
549
+
550
+ union all
551
+
552
+ select ${contractTemplateId} as template_id
553
+ from ${relations.contracts} contract_row
554
+ join ${relations.contractTpe} contract_tpe_row
555
+ on contract_tpe_row.pk = contract_row.tpe_pk
556
+ where contract_row.archived_at_ix = tx.ix
557
+
558
+ union all
559
+
560
+ select ${exerciseTemplateId} as template_id
561
+ from ${relations.exercises} exercise_row
562
+ join ${relations.contractTpe} exercise_contract_tpe_row
563
+ on exercise_contract_tpe_row.pk = exercise_row.contract_tpe_pk
564
+ where exercise_row.exercised_at_ix = tx.ix
565
+ ) update_event_templates
566
+ where update_event_templates.template_id is null
567
+ or update_event_templates.template_id not like 'Splice.%'
568
+ )
675
569
  `;
676
570
  }
677
- function normalizedRecentUpdatesQuery(limit, before, after, parties, templates, partyMode, hideSplice) {
571
+ function pqsRecentUpdatesQuery(node, limit, before, after, parties, templates, partyMode, hideSplice) {
572
+ const relations = pqsCoreRelations(node);
678
573
  const normalizedBefore = normalizeEventOffsetCursor(before);
679
574
  const normalizedAfter = normalizeEventOffsetCursor(after);
680
- const filterClause = buildNormalizedUpdatesFilterClause(parties, templates, partyMode);
681
- const hideSpliceClause = hideSplice ? buildNormalizedHideSpliceOffsetsClause() : null;
575
+ const filterClause = buildPqsUpdatesFilterClause(node, parties, templates, partyMode);
576
+ const hideSpliceClause = hideSplice ? buildPqsHideSpliceUpdatesClause(node) : null;
682
577
  const queryLimit = limit + 1;
683
578
  const afterFilters = [
684
- normalizedAfter ? `update_meta.event_offset::numeric > ${normalizedAfter}` : null,
579
+ normalizedAfter ? `tx.offset > ${normalizedAfter}` : null,
685
580
  filterClause,
686
581
  hideSpliceClause,
687
582
  ].filter((value) => Boolean(value));
688
583
  const olderFilters = [
689
- normalizedBefore ? `update_meta.event_offset::numeric < ${normalizedBefore}` : null,
584
+ normalizedBefore ? `tx.offset < ${normalizedBefore}` : null,
690
585
  filterClause,
691
586
  hideSpliceClause,
692
587
  ].filter((value) => Boolean(value));
@@ -694,483 +589,277 @@ function normalizedRecentUpdatesQuery(limit, before, after, parties, templates,
694
589
  const whereClause = afterFilters.length > 0 ? `where ${afterFilters.join('\n and ')}` : '';
695
590
  return `
696
591
  select
697
- encode(update_meta.update_id, 'hex') as update_id,
698
- update_meta.event_offset::text as event_offset,
699
- to_char(
700
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
701
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
702
- ) as record_time
703
- from participant.lapi_update_meta update_meta
592
+ tx.transaction_id::text as update_id,
593
+ tx.offset::text as event_offset,
594
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time
595
+ from ${relations.transactions} tx
704
596
  ${whereClause}
705
- order by update_meta.event_offset::numeric asc
597
+ order by tx.offset asc
706
598
  limit ${queryLimit}
707
599
  `;
708
600
  }
709
601
  const whereClause = olderFilters.length > 0 ? `where ${olderFilters.join('\n and ')}` : '';
710
602
  return `
711
603
  select
712
- encode(update_meta.update_id, 'hex') as update_id,
713
- update_meta.event_offset::text as event_offset,
714
- to_char(
715
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
716
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
717
- ) as record_time
718
- from participant.lapi_update_meta update_meta
604
+ tx.transaction_id::text as update_id,
605
+ tx.offset::text as event_offset,
606
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time
607
+ from ${relations.transactions} tx
719
608
  ${whereClause}
720
- order by update_meta.event_offset::numeric desc
609
+ order by tx.offset desc
721
610
  limit ${queryLimit}
722
611
  `;
723
612
  }
724
- function normalizedSearchUpdatesQuery(searchQuery, limit) {
613
+ function pqsSearchUpdatesQuery(node, searchQuery, limit) {
614
+ const relations = pqsCoreRelations(node);
725
615
  const quotedQuery = escapeSqlLiteral(searchQuery);
726
616
  return `
727
617
  select
728
- encode(update_meta.update_id, 'hex') as update_id,
729
- update_meta.event_offset::text as event_offset,
730
- to_char(
731
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
732
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
733
- ) as record_time
734
- from participant.lapi_update_meta update_meta
735
- where update_meta.event_offset::text like '${quotedQuery}%'
736
- or encode(update_meta.update_id, 'hex') like '${quotedQuery}%'
737
- order by update_meta.record_time desc, update_meta.event_offset::numeric desc
618
+ tx.transaction_id::text as update_id,
619
+ tx.offset::text as event_offset,
620
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time
621
+ from ${relations.transactions} tx
622
+ where tx.offset::text like '${quotedQuery}%'
623
+ or tx.transaction_id like '${quotedQuery}%'
624
+ order by tx.effective_at desc nulls last, tx.offset desc
738
625
  limit ${limit}
739
626
  `;
740
627
  }
741
- function buildHideSpliceOffsetsClause() {
742
- return `
743
- exists (
744
- select 1
745
- from (
746
- select create_event.template_id::text as template_id
747
- from participant.lapi_events_create create_event
748
- where create_event.update_id::text = update_meta.update_id::text
749
-
750
- union all
751
-
752
- select exercise_event.template_id::text as template_id
753
- from participant.lapi_events_consuming_exercise exercise_event
754
- where exercise_event.update_id::text = update_meta.update_id::text
755
-
756
- union all
757
-
758
- select exercise_event.template_id::text as template_id
759
- from participant.lapi_events_non_consuming_exercise exercise_event
760
- where exercise_event.update_id::text = update_meta.update_id::text
761
- ) update_event_templates
762
- where update_event_templates.template_id is null
763
- or update_event_templates.template_id not like 'Splice.%'
764
- )
765
- `;
766
- }
767
- function buildNormalizedHideSpliceOffsetsClause() {
768
- return `
769
- exists (
770
- select 1
771
- from (
772
- select regexp_replace(coalesce(contract.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id
773
- from participant.lapi_events_activate_contract activate_event
774
- left join participant.par_contracts contract
775
- on contract.internal_contract_id = activate_event.internal_contract_id
776
- where encode(activate_event.update_id, 'hex') = encode(update_meta.update_id, 'hex')
777
-
778
- union all
779
-
780
- select regexp_replace(coalesce(contract.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id
781
- from participant.lapi_events_various_witnessed various_event
782
- left join participant.par_contracts contract
783
- on contract.internal_contract_id = various_event.internal_contract_id
784
- where encode(various_event.update_id, 'hex') = encode(update_meta.update_id, 'hex')
785
- and various_event.event_type = 6
786
-
787
- union all
788
-
789
- select regexp_replace(coalesce(template_string.external_string, ''), '^t\\\\|#[^:]+:', '') as template_id
790
- from participant.lapi_events_deactivate_contract deactivate_event
791
- left join participant.lapi_string_interning template_string
792
- on template_string.internal_id = deactivate_event.template_id
793
- where encode(deactivate_event.update_id, 'hex') = encode(update_meta.update_id, 'hex')
794
-
795
- union all
796
-
797
- select regexp_replace(coalesce(template_string.external_string, ''), '^t\\\\|#[^:]+:', '') as template_id
798
- from participant.lapi_events_various_witnessed various_event
799
- left join participant.lapi_string_interning template_string
800
- on template_string.internal_id = various_event.template_id
801
- where encode(various_event.update_id, 'hex') = encode(update_meta.update_id, 'hex')
802
- and various_event.event_type in (5, 7)
803
- ) update_event_templates
804
- where update_event_templates.template_id = ''
805
- or update_event_templates.template_id not like 'Splice.%'
806
- )
807
- `;
808
- }
809
- function buildQuotedPartyIdentifiers(partyId) {
810
- const trimmed = partyId.trim();
811
- if (!trimmed) {
812
- return [];
813
- }
814
- const normalized = trimmed.replace(/^p\|/, '');
815
- const identifiers = new Set([trimmed, normalized]);
816
- if (normalized) {
817
- identifiers.add(`p|${normalized}`);
818
- }
819
- return Array.from(identifiers).map((identifier) => `'${escapeSqlLiteral(identifier)}'`);
820
- }
821
- function partyWitnessArrayMatchCondition(arrayExpression, partyId) {
822
- const quotedIdentifiers = buildQuotedPartyIdentifiers(partyId);
823
- if (quotedIdentifiers.length === 0) {
824
- return 'false';
825
- }
826
- return `array[${quotedIdentifiers.join(', ')}]::text[] && ${arrayExpression}::text[]`;
827
- }
828
- function partyScalarMatchCondition(columnExpression, partyId) {
829
- const quotedIdentifiers = buildQuotedPartyIdentifiers(partyId);
830
- if (quotedIdentifiers.length === 0) {
831
- return 'false';
832
- }
833
- return `${columnExpression} in (${quotedIdentifiers.join(', ')})`;
834
- }
835
- function partyRecentUpdatesQuery(partyId, limit) {
836
- const witnessMatch = partyWitnessArrayMatchCondition('create_event.tree_event_witnesses', partyId);
837
- const consumingWitnessMatch = partyWitnessArrayMatchCondition('exercise_event.tree_event_witnesses', partyId);
838
- const nonConsumingWitnessMatch = partyWitnessArrayMatchCondition('exercise_event.tree_event_witnesses', partyId);
839
- return `
840
- select
841
- update_meta.update_id::text as update_id,
842
- update_meta.event_offset::text as event_offset,
843
- to_char(
844
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
845
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
846
- ) as record_time
847
- from participant.lapi_update_meta update_meta
848
- where update_meta.update_id::text in (
849
- select create_event.update_id::text
850
- from participant.lapi_events_create create_event
851
- where ${witnessMatch}
852
-
853
- union
854
-
855
- select exercise_event.update_id::text
856
- from participant.lapi_events_consuming_exercise exercise_event
857
- where ${consumingWitnessMatch}
858
-
859
- union
860
-
861
- select exercise_event.update_id::text
862
- from participant.lapi_events_non_consuming_exercise exercise_event
863
- where ${nonConsumingWitnessMatch}
864
- )
865
- order by update_meta.record_time desc
866
- limit ${limit}
867
- `;
868
- }
869
- function normalizedPartyRecentUpdatesQuery(partyId, limit) {
870
- const partyMatch = partyScalarMatchCondition('party_string.external_string', partyId);
871
- return `
872
- with matching_updates as (
873
- select distinct activate_event.update_id
874
- from participant.lapi_events_activate_contract activate_event
875
- join participant.lapi_filter_activate_witness witness_filter
876
- on witness_filter.event_sequential_id = activate_event.event_sequential_id
877
- join participant.lapi_string_interning party_string
878
- on party_string.internal_id = witness_filter.party_id
879
- where ${partyMatch}
880
-
881
- union
882
-
883
- select distinct deactivate_event.update_id
884
- from participant.lapi_events_deactivate_contract deactivate_event
885
- join participant.lapi_filter_deactivate_witness witness_filter
886
- on witness_filter.event_sequential_id = deactivate_event.event_sequential_id
887
- join participant.lapi_string_interning party_string
888
- on party_string.internal_id = witness_filter.party_id
889
- where ${partyMatch}
890
-
891
- union
892
-
893
- select distinct various_event.update_id
894
- from participant.lapi_events_various_witnessed various_event
895
- join participant.lapi_filter_various_witness witness_filter
896
- on witness_filter.event_sequential_id = various_event.event_sequential_id
897
- join participant.lapi_string_interning party_string
898
- on party_string.internal_id = witness_filter.party_id
899
- where ${partyMatch}
900
- )
901
- select
902
- encode(update_meta.update_id, 'hex') as update_id,
903
- update_meta.event_offset::text as event_offset,
904
- to_char(
905
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
906
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
907
- ) as record_time
908
- from participant.lapi_update_meta update_meta
909
- join matching_updates
910
- on matching_updates.update_id = update_meta.update_id
911
- order by update_meta.record_time desc
912
- limit ${limit}
913
- `;
914
- }
915
- function recentUpdatePartiesQuery(updateIds) {
628
+ function pqsRecentUpdatePartiesQuery(node, updateIds) {
629
+ const relations = pqsCoreRelations(node);
916
630
  const quotedIds = updateIds.map((updateId) => `'${escapeSqlLiteral(updateId)}'`).join(', ');
917
631
  return `
918
- select
919
- update_id::text as update_id,
920
- array_agg(distinct party order by party) as parties
921
- from (
922
- select
923
- update_id,
924
- unnest(tree_event_witnesses) as party
925
- from participant.lapi_events_create
926
- where update_id in (${quotedIds})
927
-
928
- union
929
-
930
- select
931
- update_id,
932
- unnest(tree_event_witnesses) as party
933
- from participant.lapi_events_consuming_exercise
934
- where update_id in (${quotedIds})
935
-
936
- union
937
-
938
- select
939
- update_id,
940
- unnest(tree_event_witnesses) as party
941
- from participant.lapi_events_non_consuming_exercise
942
- where update_id in (${quotedIds})
943
- ) update_parties
944
- group by update_id
945
- `;
946
- }
947
- function normalizedRecentUpdatePartiesQuery(updateIds) {
948
- const quotedIds = updateIds
949
- .map((updateId) => `'${escapeSqlLiteral(normalizeByteaHex(updateId))}'`)
950
- .join(', ');
951
- return `
952
632
  select
953
633
  update_id,
954
634
  array_agg(distinct party order by party) as parties
955
635
  from (
956
636
  select
957
- encode(activate_event.update_id, 'hex') as update_id,
958
- party_string.external_string as party
959
- from participant.lapi_events_activate_contract activate_event
960
- join participant.lapi_filter_activate_witness witness_filter
961
- on witness_filter.event_sequential_id = activate_event.event_sequential_id
962
- join participant.lapi_string_interning party_string
963
- on party_string.internal_id = witness_filter.party_id
964
- where encode(activate_event.update_id, 'hex') in (${quotedIds})
637
+ tx.transaction_id::text as update_id,
638
+ unnest(contract_row.witnesses) as party
639
+ from ${relations.transactions} tx
640
+ join ${relations.contracts} contract_row
641
+ on contract_row.created_at_ix = tx.ix
642
+ where tx.transaction_id in (${quotedIds})
965
643
 
966
644
  union
967
645
 
968
646
  select
969
- encode(deactivate_event.update_id, 'hex') as update_id,
970
- party_string.external_string as party
971
- from participant.lapi_events_deactivate_contract deactivate_event
972
- join participant.lapi_filter_deactivate_witness witness_filter
973
- on witness_filter.event_sequential_id = deactivate_event.event_sequential_id
974
- join participant.lapi_string_interning party_string
975
- on party_string.internal_id = witness_filter.party_id
976
- where encode(deactivate_event.update_id, 'hex') in (${quotedIds})
647
+ tx.transaction_id::text as update_id,
648
+ unnest(contract_row.witnesses) as party
649
+ from ${relations.transactions} tx
650
+ join ${relations.contracts} contract_row
651
+ on contract_row.archived_at_ix = tx.ix
652
+ where tx.transaction_id in (${quotedIds})
977
653
 
978
654
  union
979
655
 
980
656
  select
981
- encode(various_event.update_id, 'hex') as update_id,
982
- party_string.external_string as party
983
- from participant.lapi_events_various_witnessed various_event
984
- join participant.lapi_filter_various_witness witness_filter
985
- on witness_filter.event_sequential_id = various_event.event_sequential_id
986
- join participant.lapi_string_interning party_string
987
- on party_string.internal_id = witness_filter.party_id
988
- where encode(various_event.update_id, 'hex') in (${quotedIds})
657
+ tx.transaction_id::text as update_id,
658
+ unnest(exercise_row.witnesses) as party
659
+ from ${relations.transactions} tx
660
+ join ${relations.exercises} exercise_row
661
+ on exercise_row.exercised_at_ix = tx.ix
662
+ where tx.transaction_id in (${quotedIds})
989
663
  ) update_parties
990
664
  group by update_id
991
665
  `;
992
666
  }
993
- function activePartiesQuery() {
667
+ function pqsActivePartiesQuery(node) {
668
+ const relations = pqsCoreRelations(node);
994
669
  return `
995
670
  select
996
671
  array_agg(distinct party order by party) as parties
997
672
  from (
998
- select unnest(tree_event_witnesses) as party
999
- from participant.lapi_events_create
673
+ select unnest(contract_row.witnesses) as party
674
+ from ${relations.contracts} contract_row
1000
675
 
1001
676
  union
1002
677
 
1003
- select unnest(tree_event_witnesses) as party
1004
- from participant.lapi_events_consuming_exercise
1005
-
1006
- union
1007
-
1008
- select unnest(tree_event_witnesses) as party
1009
- from participant.lapi_events_non_consuming_exercise
678
+ select unnest(exercise_row.witnesses) as party
679
+ from ${relations.exercises} exercise_row
1010
680
  ) observed_parties
1011
681
  `;
1012
682
  }
1013
- function normalizedActivePartiesQuery() {
683
+ function pqsPartyRecentUpdatesQuery(node, partyId, limit) {
684
+ const relations = pqsCoreRelations(node);
685
+ const contractMatch = partyWitnessArrayMatchCondition('contract_row.witnesses', partyId);
686
+ const exerciseMatch = partyWitnessArrayMatchCondition('exercise_row.witnesses', partyId);
1014
687
  return `
1015
688
  select
1016
- array_agg(distinct party order by party) as parties
1017
- from (
1018
- select party_string.external_string as party
1019
- from participant.lapi_filter_activate_witness witness_filter
1020
- join participant.lapi_string_interning party_string
1021
- on party_string.internal_id = witness_filter.party_id
1022
-
1023
- union
1024
-
1025
- select party_string.external_string as party
1026
- from participant.lapi_filter_deactivate_witness witness_filter
1027
- join participant.lapi_string_interning party_string
1028
- on party_string.internal_id = witness_filter.party_id
1029
-
1030
- union
1031
-
1032
- select party_string.external_string as party
1033
- from participant.lapi_filter_various_witness witness_filter
1034
- join participant.lapi_string_interning party_string
1035
- on party_string.internal_id = witness_filter.party_id
1036
- ) observed_parties
689
+ tx.transaction_id::text as update_id,
690
+ tx.offset::text as event_offset,
691
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time
692
+ from ${relations.transactions} tx
693
+ where exists (
694
+ select 1
695
+ from ${relations.contracts} contract_row
696
+ where (contract_row.created_at_ix = tx.ix or contract_row.archived_at_ix = tx.ix)
697
+ and ${contractMatch}
698
+ )
699
+ or exists (
700
+ select 1
701
+ from ${relations.exercises} exercise_row
702
+ where exercise_row.exercised_at_ix = tx.ix
703
+ and ${exerciseMatch}
704
+ )
705
+ order by tx.offset desc
706
+ limit ${limit}
1037
707
  `;
1038
708
  }
1039
- function partyRecentContractsQuery(partyId, limit, before, after) {
1040
- const witnessMatch = partyWitnessArrayMatchCondition('create_event.tree_event_witnesses', partyId);
709
+ function buildPqsActiveContractsFilterClause(parties, templates, partyMode, hideSplice) {
710
+ const partyConditions = normalizePartyFilters(parties).map((party) => partyWitnessArrayMatchCondition('contract_row.witnesses', party));
711
+ const templateExpression = contractTemplateIdentifierExpression('contract_tpe_row');
712
+ const templateConditions = normalizeTemplateFilters(templates).map((templateId) => {
713
+ const quotedTemplateId = `'${escapeSqlLiteral(templateId)}'`;
714
+ return `${templateExpression} = ${quotedTemplateId}`;
715
+ });
716
+ const groups = [];
717
+ if (partyConditions.length > 0) {
718
+ const partyJoiner = normalizePartyFilterMode(partyMode) === 'and' ? '\n and ' : '\n or ';
719
+ groups.push(partyConditions.length === 1
720
+ ? partyConditions[0]
721
+ : `(\n ${partyConditions.join(partyJoiner)}\n )`);
722
+ }
723
+ if (templateConditions.length > 0) {
724
+ groups.push(templateConditions.length === 1
725
+ ? templateConditions[0]
726
+ : `(\n ${templateConditions.join('\n or ')}\n )`);
727
+ }
728
+ if (hideSplice) {
729
+ groups.push(`${templateExpression} not like 'Splice.%'`);
730
+ }
731
+ if (groups.length === 0) {
732
+ return null;
733
+ }
734
+ return groups.length === 1 ? groups[0] : `(\n ${groups.join('\n and ')}\n )`;
735
+ }
736
+ function pqsActiveContractsQuery(node, limit, before, after, parties, templates, partyMode, hideSplice) {
737
+ const relations = pqsCoreRelations(node);
1041
738
  const normalizedBefore = normalizeEventOffsetCursor(before);
1042
739
  const normalizedAfter = normalizeEventOffsetCursor(after);
1043
740
  const queryLimit = limit + 1;
1044
- const afterClause = normalizedAfter ? `and create_event.event_offset::numeric > ${normalizedAfter}` : '';
1045
- const beforeClause = normalizedBefore ? `and create_event.event_offset::numeric < ${normalizedBefore}` : '';
1046
- if (normalizedAfter && !normalizedBefore) {
1047
- return `
1048
- select
1049
- create_event.contract_id::text as contract_id,
1050
- create_event.template_id::text as template_id,
1051
- null::text as package_id,
1052
- to_char(
1053
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
1054
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1055
- ) as record_time,
1056
- create_event.event_offset::text as created_event_offset
1057
- from participant.lapi_events_create create_event
1058
- join participant.lapi_update_meta update_meta
1059
- on update_meta.update_id = create_event.update_id
1060
- where ${witnessMatch}
1061
- ${afterClause}
1062
- order by create_event.event_offset::numeric asc
1063
- limit ${queryLimit}
1064
- `;
1065
- }
741
+ const useAfterCursor = Boolean(normalizedAfter && !normalizedBefore);
742
+ const cursorFilter = useAfterCursor
743
+ ? `tx.offset > ${normalizedAfter}`
744
+ : normalizedBefore
745
+ ? `tx.offset < ${normalizedBefore}`
746
+ : null;
747
+ const orderDirection = useAfterCursor ? 'asc' : 'desc';
748
+ const filterClause = buildPqsActiveContractsFilterClause(parties, templates, partyMode, hideSplice);
749
+ const whereConditions = [
750
+ 'contract_row.archived_at_ix is null',
751
+ cursorFilter,
752
+ filterClause,
753
+ ].filter((value) => Boolean(value));
754
+ const whereClause = whereConditions.length > 0 ? `where ${whereConditions.join('\n and ')}` : '';
1066
755
  return `
1067
756
  select
1068
- create_event.contract_id::text as contract_id,
1069
- create_event.template_id::text as template_id,
1070
- null::text as package_id,
1071
- to_char(
1072
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
1073
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1074
- ) as record_time
1075
- ,
1076
- create_event.event_offset::text as created_event_offset
1077
- from participant.lapi_events_create create_event
1078
- join participant.lapi_update_meta update_meta
1079
- on update_meta.update_id = create_event.update_id
1080
- where ${witnessMatch}
1081
- ${beforeClause}
1082
- order by create_event.event_offset::numeric desc
757
+ contract_row.contract_id::text as contract_id,
758
+ ${contractTemplateIdentifierExpression('contract_tpe_row')} as template_id,
759
+ ${isoUtcTimestampExpression('tx.effective_at')} as created_record_time,
760
+ tx.offset::text as created_event_offset
761
+ from ${relations.contracts} contract_row
762
+ join ${relations.contractTpe} contract_tpe_row
763
+ on contract_tpe_row.pk = contract_row.tpe_pk
764
+ join ${relations.transactions} tx
765
+ on tx.ix = contract_row.created_at_ix
766
+ ${whereClause}
767
+ order by tx.offset ${orderDirection}
1083
768
  limit ${queryLimit}
1084
769
  `;
1085
770
  }
1086
- function normalizedPartyRecentContractsQuery(partyId, limit, before, after) {
1087
- const partyMatch = partyScalarMatchCondition('party_string.external_string', partyId);
771
+ function pqsSearchContractsQuery(node, searchQuery, limit) {
772
+ const relations = pqsCoreRelations(node);
773
+ const quotedQuery = escapeSqlLiteral(searchQuery);
774
+ return `
775
+ select
776
+ contract_row.contract_id::text as contract_id,
777
+ ${contractTemplateIdentifierExpression('contract_tpe_row')} as template_id,
778
+ ${isoUtcTimestampExpression('tx.effective_at')} as created_record_time
779
+ from ${relations.contracts} contract_row
780
+ join ${relations.contractTpe} contract_tpe_row
781
+ on contract_tpe_row.pk = contract_row.tpe_pk
782
+ join ${relations.transactions} tx
783
+ on tx.ix = contract_row.created_at_ix
784
+ where contract_row.contract_id like '${quotedQuery}%'
785
+ and contract_row.archived_at_ix is null
786
+ order by tx.effective_at desc nulls last, contract_row.contract_id asc
787
+ limit ${limit}
788
+ `;
789
+ }
790
+ function pqsPartyRecentContractsQuery(node, partyId, limit, before, after) {
791
+ const relations = pqsCoreRelations(node);
792
+ const witnessMatch = partyWitnessArrayMatchCondition('contract_row.witnesses', partyId);
1088
793
  const normalizedBefore = normalizeEventOffsetCursor(before);
1089
794
  const normalizedAfter = normalizeEventOffsetCursor(after);
1090
795
  const queryLimit = limit + 1;
1091
- const afterFilter = normalizedAfter && !normalizedBefore
1092
- ? `where created_event_offset::numeric > ${normalizedAfter}`
796
+ const sortDirection = normalizedAfter && !normalizedBefore ? 'asc' : 'desc';
797
+ const cursorClause = normalizedAfter && !normalizedBefore
798
+ ? `and tx.offset > ${normalizedAfter}`
1093
799
  : normalizedBefore
1094
- ? `where created_event_offset::numeric < ${normalizedBefore}`
800
+ ? `and tx.offset < ${normalizedBefore}`
1095
801
  : '';
1096
- const sortDirection = normalizedAfter && !normalizedBefore ? 'asc' : 'desc';
1097
802
  return `
1098
803
  select
1099
- contract_id,
1100
- template_id,
1101
- package_id,
1102
- record_time,
1103
- created_event_offset
1104
- from (
1105
- select
1106
- encode(contract.contract_id, 'hex') as contract_id,
1107
- contract.template_id::text as template_id,
1108
- package_string.external_string as package_id,
1109
- to_char(
1110
- to_timestamp(activate_event.record_time / 1000000.0) at time zone 'UTC',
1111
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1112
- ) as record_time,
1113
- activate_event.event_sequential_id as sort_event_sequential_id,
1114
- activate_event.event_offset::text as created_event_offset
1115
- from participant.lapi_events_activate_contract activate_event
1116
- join participant.lapi_filter_activate_witness witness_filter
1117
- on witness_filter.event_sequential_id = activate_event.event_sequential_id
1118
- join participant.lapi_string_interning party_string
1119
- on party_string.internal_id = witness_filter.party_id
1120
- left join participant.par_contracts contract
1121
- on contract.internal_contract_id = activate_event.internal_contract_id
1122
- left join participant.lapi_string_interning package_string
1123
- on package_string.internal_id = activate_event.representative_package_id
1124
- where ${partyMatch}
1125
-
1126
- union all
1127
-
1128
- select
1129
- encode(contract.contract_id, 'hex') as contract_id,
1130
- contract.template_id::text as template_id,
1131
- package_string.external_string as package_id,
1132
- to_char(
1133
- to_timestamp(various_event.record_time / 1000000.0) at time zone 'UTC',
1134
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1135
- ) as record_time,
1136
- various_event.event_sequential_id as sort_event_sequential_id,
1137
- various_event.event_offset::text as created_event_offset
1138
- from participant.lapi_events_various_witnessed various_event
1139
- join participant.lapi_filter_various_witness witness_filter
1140
- on witness_filter.event_sequential_id = various_event.event_sequential_id
1141
- join participant.lapi_string_interning party_string
1142
- on party_string.internal_id = witness_filter.party_id
1143
- left join participant.par_contracts contract
1144
- on contract.internal_contract_id = various_event.internal_contract_id
1145
- left join participant.lapi_string_interning package_string
1146
- on package_string.internal_id = various_event.package_id
1147
- where ${partyMatch}
1148
- and various_event.event_type = 6
1149
- ) recent_contracts
1150
- ${afterFilter}
1151
- order by created_event_offset::numeric ${sortDirection}
804
+ contract_row.contract_id::text as contract_id,
805
+ ${contractTemplateIdentifierExpression('contract_tpe_row')} as template_id,
806
+ contract_row.creation_package_id::text as package_id,
807
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time,
808
+ tx.offset::text as created_event_offset
809
+ from ${relations.contracts} contract_row
810
+ join ${relations.contractTpe} contract_tpe_row
811
+ on contract_tpe_row.pk = contract_row.tpe_pk
812
+ join ${relations.transactions} tx
813
+ on tx.ix = contract_row.created_at_ix
814
+ where ${witnessMatch}
815
+ ${cursorClause}
816
+ order by tx.offset ${sortDirection}
1152
817
  limit ${queryLimit}
1153
818
  `;
1154
819
  }
1155
- function singleUpdateQuery(eventOffset) {
820
+ function buildQuotedPartyIdentifiers(partyId) {
821
+ const trimmed = partyId.trim();
822
+ if (!trimmed) {
823
+ return [];
824
+ }
825
+ const normalized = trimmed.replace(/^p\|/, '');
826
+ const identifiers = new Set([trimmed, normalized]);
827
+ if (normalized) {
828
+ identifiers.add(`p|${normalized}`);
829
+ }
830
+ return Array.from(identifiers).map((identifier) => `'${escapeSqlLiteral(identifier)}'`);
831
+ }
832
+ function partyWitnessArrayMatchCondition(arrayExpression, partyId) {
833
+ const quotedIdentifiers = buildQuotedPartyIdentifiers(partyId);
834
+ if (quotedIdentifiers.length === 0) {
835
+ return 'false';
836
+ }
837
+ return `array[${quotedIdentifiers.join(', ')}]::text[] && ${arrayExpression}::text[]`;
838
+ }
839
+ function singleUpdateQuery(node, eventOffset) {
840
+ const relations = pqsCoreRelations(node);
1156
841
  const quotedOffset = `'${escapeSqlLiteral(eventOffset)}'`;
1157
842
  return `
1158
843
  select
1159
- update_meta.update_id::text as update_id,
1160
- update_meta.event_offset::text as event_offset,
1161
- to_char(
1162
- to_timestamp(update_meta.record_time / 1000000.0) at time zone 'UTC',
1163
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1164
- ) as record_time_iso,
1165
- to_jsonb(update_meta) as meta
1166
- from participant.lapi_update_meta update_meta
1167
- where update_meta.event_offset::text = ${quotedOffset}
1168
- order by update_meta.record_time desc
844
+ tx.transaction_id::text as update_id,
845
+ tx.offset::text as event_offset,
846
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time_iso,
847
+ jsonb_build_object(
848
+ 'update_id', tx.transaction_id::text,
849
+ 'event_offset', tx.offset::text,
850
+ 'record_time', ${isoUtcTimestampExpression('tx.effective_at')}
851
+ ) as meta
852
+ from ${relations.transactions} tx
853
+ where tx.offset::text = ${quotedOffset}
854
+ order by tx.offset desc
1169
855
  limit 1
1170
856
  `;
1171
857
  }
1172
- function updateEventsQuery(updateId) {
858
+ function updateEventsQuery(node, updateId) {
859
+ const relations = pqsCoreRelations(node);
1173
860
  const quotedId = `'${escapeSqlLiteral(updateId)}'`;
861
+ const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
862
+ const exerciseTemplateId = contractTemplateIdentifierExpression('exercise_contract_tpe_row');
1174
863
  return `
1175
864
  select
1176
865
  event_kind,
@@ -1187,393 +876,149 @@ function updateEventsQuery(updateId) {
1187
876
  from (
1188
877
  select
1189
878
  'create'::text as event_kind,
1190
- create_event.event_id::text as event_id,
1191
- create_event.contract_id::text as contract_id,
1192
- create_event.template_id::text as template_id,
1193
- null::text as package_id,
1194
- null::text as choice,
1195
- create_event.tree_event_witnesses as witnesses,
1196
- null::bytea as contract_instance,
1197
- null::bytea as exercise_argument,
1198
- null::bytea as exercise_result,
1199
- to_jsonb(create_event) as raw
1200
- from participant.lapi_events_create create_event
1201
- where create_event.update_id::text = ${quotedId}
1202
-
1203
- union all
1204
-
1205
- select
1206
- 'consuming_exercise'::text as event_kind,
1207
- exercise_event.event_id::text as event_id,
1208
- exercise_event.contract_id::text as contract_id,
1209
- exercise_event.template_id::text as template_id,
1210
- null::text as package_id,
1211
- exercise_event.choice::text as choice,
1212
- exercise_event.tree_event_witnesses as witnesses,
1213
- null::bytea as contract_instance,
1214
- null::bytea as exercise_argument,
1215
- null::bytea as exercise_result,
1216
- to_jsonb(exercise_event) as raw
1217
- from participant.lapi_events_consuming_exercise exercise_event
1218
- where exercise_event.update_id::text = ${quotedId}
1219
-
1220
- union all
1221
-
1222
- select
1223
- 'non_consuming_exercise'::text as event_kind,
1224
- exercise_event.event_id::text as event_id,
1225
- exercise_event.contract_id::text as contract_id,
1226
- exercise_event.template_id::text as template_id,
1227
- null::text as package_id,
1228
- exercise_event.choice::text as choice,
1229
- exercise_event.tree_event_witnesses as witnesses,
1230
- null::bytea as contract_instance,
1231
- null::bytea as exercise_argument,
1232
- null::bytea as exercise_result,
1233
- to_jsonb(exercise_event) as raw
1234
- from participant.lapi_events_non_consuming_exercise exercise_event
1235
- where exercise_event.update_id::text = ${quotedId}
1236
- ) update_events
1237
- order by event_id asc, event_kind asc, contract_id asc, template_id asc
1238
- `;
1239
- }
1240
- function normalizedUpdateEventsQuery(updateId) {
1241
- const quotedId = `'${escapeSqlLiteral(normalizeByteaHex(updateId))}'`;
1242
- return `
1243
- with activate_witnesses as (
1244
- select
1245
- witness_filter.event_sequential_id,
1246
- array_agg(distinct party_string.external_string order by party_string.external_string) as witnesses
1247
- from participant.lapi_filter_activate_witness witness_filter
1248
- join participant.lapi_string_interning party_string
1249
- on party_string.internal_id = witness_filter.party_id
1250
- group by witness_filter.event_sequential_id
1251
- ),
1252
- deactivate_witnesses as (
1253
- select
1254
- witness_filter.event_sequential_id,
1255
- array_agg(distinct party_string.external_string order by party_string.external_string) as witnesses
1256
- from participant.lapi_filter_deactivate_witness witness_filter
1257
- join participant.lapi_string_interning party_string
1258
- on party_string.internal_id = witness_filter.party_id
1259
- group by witness_filter.event_sequential_id
1260
- ),
1261
- various_witnesses as (
1262
- select
1263
- witness_filter.event_sequential_id,
1264
- array_agg(distinct party_string.external_string order by party_string.external_string) as witnesses
1265
- from participant.lapi_filter_various_witness witness_filter
1266
- join participant.lapi_string_interning party_string
1267
- on party_string.internal_id = witness_filter.party_id
1268
- group by witness_filter.event_sequential_id
1269
- )
1270
- select
1271
- event_kind,
1272
- event_id,
1273
- contract_id,
1274
- template_id,
1275
- package_id,
1276
- choice,
1277
- witnesses,
1278
- contract_instance,
1279
- exercise_argument,
1280
- exercise_result,
1281
- raw
1282
- from (
1283
- select
1284
- 'create'::text as event_kind,
1285
- '#0:' || activate_event.node_id::text as event_id,
1286
- encode(contract.contract_id, 'hex') as contract_id,
1287
- contract.template_id::text as template_id,
1288
- package_string.external_string as package_id,
1289
- null::text as choice,
1290
- coalesce(activate_witnesses.witnesses, array[]::text[]) as witnesses,
1291
- contract.instance as contract_instance,
1292
- null::bytea as exercise_argument,
1293
- null::bytea as exercise_result,
1294
- jsonb_build_object(
1295
- 'source_table', 'participant.lapi_events_activate_contract',
1296
- 'update_id', encode(activate_event.update_id, 'hex'),
1297
- 'event_offset', activate_event.event_offset,
1298
- 'event_type', activate_event.event_type,
1299
- 'event_sequential_id', activate_event.event_sequential_id,
1300
- 'node_id', activate_event.node_id,
1301
- 'contract_id', encode(contract.contract_id, 'hex'),
1302
- 'template_id', contract.template_id,
1303
- 'package_id', package_string.external_string
1304
- ) as raw,
1305
- activate_event.event_sequential_id as sort_event_sequential_id
1306
- from participant.lapi_events_activate_contract activate_event
1307
- left join participant.par_contracts contract
1308
- on contract.internal_contract_id = activate_event.internal_contract_id
1309
- left join participant.lapi_string_interning package_string
1310
- on package_string.internal_id = activate_event.representative_package_id
1311
- left join activate_witnesses
1312
- on activate_witnesses.event_sequential_id = activate_event.event_sequential_id
1313
- where encode(activate_event.update_id, 'hex') = ${quotedId}
1314
-
1315
- union all
1316
-
1317
- select
1318
- 'create'::text as event_kind,
1319
- '#0:' || various_event.node_id::text as event_id,
1320
- encode(contract.contract_id, 'hex') as contract_id,
1321
- contract.template_id::text as template_id,
1322
- package_string.external_string as package_id,
879
+ event_row.event_id::text as event_id,
880
+ contract_row.contract_id::text as contract_id,
881
+ ${contractTemplateId} as template_id,
882
+ coalesce(contract_row.creation_package_id, package_row.id)::text as package_id,
1323
883
  null::text as choice,
1324
- coalesce(various_witnesses.witnesses, array[]::text[]) as witnesses,
1325
- contract.instance as contract_instance,
1326
- null::bytea as exercise_argument,
1327
- null::bytea as exercise_result,
1328
- jsonb_build_object(
1329
- 'source_table', 'participant.lapi_events_various_witnessed',
1330
- 'update_id', encode(various_event.update_id, 'hex'),
1331
- 'event_offset', various_event.event_offset,
1332
- 'event_type', various_event.event_type,
1333
- 'event_sequential_id', various_event.event_sequential_id,
1334
- 'node_id', various_event.node_id,
1335
- 'contract_id', encode(contract.contract_id, 'hex'),
1336
- 'template_id', contract.template_id,
1337
- 'package_id', package_string.external_string
1338
- ) as raw,
1339
- various_event.event_sequential_id as sort_event_sequential_id
1340
- from participant.lapi_events_various_witnessed various_event
1341
- left join participant.par_contracts contract
1342
- on contract.internal_contract_id = various_event.internal_contract_id
1343
- left join participant.lapi_string_interning package_string
1344
- on package_string.internal_id = various_event.package_id
1345
- left join various_witnesses
1346
- on various_witnesses.event_sequential_id = various_event.event_sequential_id
1347
- where encode(various_event.update_id, 'hex') = ${quotedId}
1348
- and various_event.event_type = 6
1349
-
1350
- union all
1351
-
1352
- select
1353
- 'consuming_exercise'::text as event_kind,
1354
- '#0:' || deactivate_event.node_id::text as event_id,
1355
- encode(deactivate_event.contract_id, 'hex') as contract_id,
1356
- template_string.external_string as template_id,
1357
- package_string.external_string as package_id,
1358
- choice_string.external_string as choice,
1359
- coalesce(deactivate_witnesses.witnesses, array[]::text[]) as witnesses,
1360
- null::bytea as contract_instance,
1361
- deactivate_event.exercise_argument as exercise_argument,
1362
- deactivate_event.exercise_result as exercise_result,
884
+ contract_row.witnesses as witnesses,
885
+ contract_row.payload as contract_instance,
886
+ null::jsonb as exercise_argument,
887
+ null::jsonb as exercise_result,
1363
888
  jsonb_build_object(
1364
- 'source_table', 'participant.lapi_events_deactivate_contract',
1365
- 'update_id', encode(deactivate_event.update_id, 'hex'),
1366
- 'event_offset', deactivate_event.event_offset,
1367
- 'event_type', deactivate_event.event_type,
1368
- 'event_sequential_id', deactivate_event.event_sequential_id,
1369
- 'node_id', deactivate_event.node_id,
1370
- 'contract_id', encode(deactivate_event.contract_id, 'hex'),
1371
- 'template_id', template_string.external_string,
1372
- 'package_id', package_string.external_string,
1373
- 'choice', choice_string.external_string
1374
- ) as raw,
1375
- deactivate_event.event_sequential_id as sort_event_sequential_id
1376
- from participant.lapi_events_deactivate_contract deactivate_event
1377
- left join participant.lapi_string_interning template_string
1378
- on template_string.internal_id = deactivate_event.template_id
1379
- left join participant.lapi_string_interning package_string
1380
- on package_string.internal_id = deactivate_event.package_id
1381
- left join participant.lapi_string_interning choice_string
1382
- on choice_string.internal_id = deactivate_event.exercise_choice
1383
- left join deactivate_witnesses
1384
- on deactivate_witnesses.event_sequential_id = deactivate_event.event_sequential_id
1385
- where encode(deactivate_event.update_id, 'hex') = ${quotedId}
889
+ 'source_table', '__contracts',
890
+ 'transaction_id', tx.transaction_id::text,
891
+ 'event_offset', tx.offset::text,
892
+ 'event_id', event_row.event_id,
893
+ 'contract_id', contract_row.contract_id,
894
+ 'template_id', ${contractTemplateId}
895
+ ) as raw
896
+ from ${relations.transactions} tx
897
+ join ${relations.contracts} contract_row
898
+ on contract_row.created_at_ix = tx.ix
899
+ left join ${relations.events} event_row
900
+ on event_row.pk = contract_row.create_event_pk
901
+ left join ${relations.contractTpe} contract_tpe_row
902
+ on contract_tpe_row.pk = contract_row.tpe_pk
903
+ left join ${relations.packages} package_row
904
+ on package_row.pk = contract_row.package_pk
905
+ where tx.transaction_id = ${quotedId}
1386
906
 
1387
907
  union all
1388
908
 
1389
909
  select
1390
910
  case
1391
- when various_event.consuming then 'consuming_exercise'::text
911
+ when exercise_tpe_row.consuming then 'consuming_exercise'::text
1392
912
  else 'non_consuming_exercise'::text
1393
913
  end as event_kind,
1394
- '#0:' || various_event.node_id::text as event_id,
1395
- encode(various_event.contract_id, 'hex') as contract_id,
1396
- template_string.external_string as template_id,
1397
- package_string.external_string as package_id,
1398
- choice_string.external_string as choice,
1399
- coalesce(various_witnesses.witnesses, array[]::text[]) as witnesses,
1400
- null::bytea as contract_instance,
1401
- various_event.exercise_argument as exercise_argument,
1402
- various_event.exercise_result as exercise_result,
914
+ event_row.event_id::text as event_id,
915
+ exercise_row.contract_id::text as contract_id,
916
+ ${exerciseTemplateId} as template_id,
917
+ package_row.id::text as package_id,
918
+ exercise_tpe_row.choice::text as choice,
919
+ exercise_row.witnesses as witnesses,
920
+ null::jsonb as contract_instance,
921
+ exercise_row.argument as exercise_argument,
922
+ exercise_row.result as exercise_result,
1403
923
  jsonb_build_object(
1404
- 'source_table', 'participant.lapi_events_various_witnessed',
1405
- 'update_id', encode(various_event.update_id, 'hex'),
1406
- 'event_offset', various_event.event_offset,
1407
- 'event_type', various_event.event_type,
1408
- 'event_sequential_id', various_event.event_sequential_id,
1409
- 'node_id', various_event.node_id,
1410
- 'consuming', various_event.consuming,
1411
- 'contract_id', encode(various_event.contract_id, 'hex'),
1412
- 'template_id', template_string.external_string,
1413
- 'package_id', package_string.external_string,
1414
- 'choice', choice_string.external_string
1415
- ) as raw,
1416
- various_event.event_sequential_id as sort_event_sequential_id
1417
- from participant.lapi_events_various_witnessed various_event
1418
- left join participant.lapi_string_interning template_string
1419
- on template_string.internal_id = various_event.template_id
1420
- left join participant.lapi_string_interning package_string
1421
- on package_string.internal_id = various_event.package_id
1422
- left join participant.lapi_string_interning choice_string
1423
- on choice_string.internal_id = various_event.exercise_choice
1424
- left join various_witnesses
1425
- on various_witnesses.event_sequential_id = various_event.event_sequential_id
1426
- where encode(various_event.update_id, 'hex') = ${quotedId}
1427
- and various_event.event_type in (5, 7)
924
+ 'source_table', '__exercises',
925
+ 'transaction_id', tx.transaction_id::text,
926
+ 'event_offset', tx.offset::text,
927
+ 'event_id', event_row.event_id,
928
+ 'contract_id', exercise_row.contract_id,
929
+ 'template_id', ${exerciseTemplateId},
930
+ 'choice', exercise_tpe_row.choice
931
+ ) as raw
932
+ from ${relations.transactions} tx
933
+ join ${relations.exercises} exercise_row
934
+ on exercise_row.exercised_at_ix = tx.ix
935
+ left join ${relations.events} event_row
936
+ on event_row.pk = exercise_row.exercise_event_pk
937
+ left join ${relations.contractTpe} exercise_contract_tpe_row
938
+ on exercise_contract_tpe_row.pk = exercise_row.contract_tpe_pk
939
+ left join ${relations.exerciseTpe} exercise_tpe_row
940
+ on exercise_tpe_row.pk = exercise_row.tpe_pk
941
+ left join ${relations.packages} package_row
942
+ on package_row.pk = exercise_row.package_pk
943
+ where tx.transaction_id = ${quotedId}
1428
944
  ) update_events
1429
- order by sort_event_sequential_id asc, event_kind asc
945
+ order by event_id asc nulls last, event_kind asc, contract_id asc, template_id asc
1430
946
  `;
1431
947
  }
1432
- function rewardCouponInstanceQuery(updateId) {
948
+ function rewardCouponInstanceQuery(node, updateId) {
949
+ const relations = pqsCoreRelations(node);
1433
950
  const quotedId = `'${escapeSqlLiteral(normalizeByteaHex(updateId))}'`;
951
+ const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
1434
952
  return `
1435
953
  select
1436
954
  coupon_contract_id,
1437
955
  contract_instance
1438
956
  from (
1439
957
  select
1440
- encode(contract.contract_id, 'hex') as coupon_contract_id,
1441
- contract.instance as contract_instance,
1442
- activate_event.event_sequential_id as sort_event_sequential_id
1443
- from participant.lapi_events_activate_contract activate_event
1444
- join participant.par_contracts contract
1445
- on contract.internal_contract_id = activate_event.internal_contract_id
1446
- where encode(activate_event.update_id, 'hex') = ${quotedId}
1447
- and contract.template_id = 'Splice.Amulet:SvRewardCoupon'
1448
-
1449
- union all
1450
-
1451
- select
1452
- encode(contract.contract_id, 'hex') as coupon_contract_id,
1453
- contract.instance as contract_instance,
1454
- various_event.event_sequential_id as sort_event_sequential_id
1455
- from participant.lapi_events_various_witnessed various_event
1456
- join participant.par_contracts contract
1457
- on contract.internal_contract_id = various_event.internal_contract_id
1458
- where encode(various_event.update_id, 'hex') = ${quotedId}
1459
- and various_event.event_type = 6
1460
- and contract.template_id = 'Splice.Amulet:SvRewardCoupon'
958
+ contract_row.contract_id::text as coupon_contract_id,
959
+ contract_row.payload as contract_instance,
960
+ coalesce(contract_row.create_event_pk, 0) as sort_event_pk
961
+ from ${relations.transactions} tx
962
+ join ${relations.contracts} contract_row
963
+ on contract_row.created_at_ix = tx.ix
964
+ join ${relations.contractTpe} contract_tpe_row
965
+ on contract_tpe_row.pk = contract_row.tpe_pk
966
+ where tx.transaction_id = ${quotedId}
967
+ and ${contractTemplateId} = 'Splice.Amulet:SvRewardCoupon'
1461
968
  ) reward_coupon_events
1462
- order by sort_event_sequential_id desc
969
+ order by sort_event_pk desc
1463
970
  limit 1
1464
971
  `;
1465
972
  }
1466
- function contractDetailQuery(contractId) {
973
+ function contractDetailQuery(node, contractId) {
974
+ const relations = pqsCoreRelations(node);
1467
975
  const quotedId = `'${escapeSqlLiteral(normalizeByteaHex(contractId))}'`;
976
+ const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
1468
977
  return `
1469
- with contract_row as (
978
+ with selected_contract as (
1470
979
  select
1471
- internal_contract_id,
1472
- encode(contract_id, 'hex') as contract_id,
1473
- package_id,
1474
- template_id,
1475
- instance
1476
- from participant.par_contracts
1477
- where encode(contract_id, 'hex') = ${quotedId}
980
+ contract_row.contract_id::text as contract_id,
981
+ ${contractTemplateId} as template_id,
982
+ coalesce(contract_row.creation_package_id, package_row.id)::text as package_id,
983
+ contract_row.payload as contract_instance,
984
+ contract_row.created_at_ix,
985
+ contract_row.archived_at_ix
986
+ from ${relations.contracts} contract_row
987
+ left join ${relations.contractTpe} contract_tpe_row
988
+ on contract_tpe_row.pk = contract_row.tpe_pk
989
+ left join ${relations.packages} package_row
990
+ on package_row.pk = contract_row.package_pk
991
+ where contract_row.contract_id = ${quotedId}
1478
992
  limit 1
1479
- ),
1480
- create_events as (
1481
- select
1482
- encode(activate_event.update_id, 'hex') as update_id,
1483
- activate_event.event_offset::text as event_offset,
1484
- to_char(
1485
- to_timestamp(activate_event.record_time / 1000000.0) at time zone 'UTC',
1486
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1487
- ) as record_time,
1488
- activate_event.event_sequential_id
1489
- from participant.lapi_events_activate_contract activate_event
1490
- join contract_row contract_row
1491
- on contract_row.internal_contract_id = activate_event.internal_contract_id
1492
-
1493
- union all
1494
-
1495
- select
1496
- encode(various_event.update_id, 'hex') as update_id,
1497
- various_event.event_offset::text as event_offset,
1498
- to_char(
1499
- to_timestamp(various_event.record_time / 1000000.0) at time zone 'UTC',
1500
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1501
- ) as record_time,
1502
- various_event.event_sequential_id
1503
- from participant.lapi_events_various_witnessed various_event
1504
- join contract_row contract_row
1505
- on contract_row.internal_contract_id = various_event.internal_contract_id
1506
- where various_event.event_type = 6
1507
- ),
1508
- archive_events as (
1509
- select
1510
- encode(deactivate_event.update_id, 'hex') as update_id,
1511
- deactivate_event.event_offset::text as event_offset,
1512
- to_char(
1513
- to_timestamp(deactivate_event.record_time / 1000000.0) at time zone 'UTC',
1514
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1515
- ) as record_time,
1516
- deactivate_event.event_sequential_id
1517
- from participant.lapi_events_deactivate_contract deactivate_event
1518
- join contract_row contract_row
1519
- on contract_row.internal_contract_id = deactivate_event.internal_contract_id
1520
-
1521
- union all
1522
-
1523
- select
1524
- encode(various_event.update_id, 'hex') as update_id,
1525
- various_event.event_offset::text as event_offset,
1526
- to_char(
1527
- to_timestamp(various_event.record_time / 1000000.0) at time zone 'UTC',
1528
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1529
- ) as record_time,
1530
- various_event.event_sequential_id
1531
- from participant.lapi_events_various_witnessed various_event
1532
- join contract_row contract_row
1533
- on contract_row.internal_contract_id = various_event.internal_contract_id
1534
- where various_event.event_type in (5, 7)
1535
- and various_event.consuming
1536
993
  )
1537
994
  select
1538
995
  contract_row.contract_id,
1539
996
  contract_row.template_id,
1540
997
  contract_row.package_id,
1541
- contract_row.instance as contract_instance,
1542
- created_event.update_id as created_update_id,
1543
- created_event.event_offset as created_event_offset,
1544
- created_event.record_time as created_record_time,
1545
- archived_event.update_id as archived_update_id,
1546
- archived_event.event_offset as archived_event_offset,
1547
- archived_event.record_time as archived_record_time
1548
- from contract_row
1549
- left join lateral (
1550
- select
1551
- update_id,
1552
- event_offset,
1553
- record_time
1554
- from create_events
1555
- order by event_sequential_id asc
1556
- limit 1
1557
- ) created_event on true
1558
- left join lateral (
1559
- select
1560
- update_id,
1561
- event_offset,
1562
- record_time
1563
- from archive_events
1564
- order by event_sequential_id asc
1565
- limit 1
1566
- ) archived_event on true
998
+ contract_row.contract_instance,
999
+ created_tx.transaction_id::text as created_update_id,
1000
+ created_tx.offset::text as created_event_offset,
1001
+ ${isoUtcTimestampExpression('created_tx.effective_at')} as created_record_time,
1002
+ archived_tx.transaction_id::text as archived_update_id,
1003
+ archived_tx.offset::text as archived_event_offset,
1004
+ ${isoUtcTimestampExpression('archived_tx.effective_at')} as archived_record_time
1005
+ from selected_contract contract_row
1006
+ left join ${relations.transactions} created_tx
1007
+ on created_tx.ix = contract_row.created_at_ix
1008
+ left join ${relations.transactions} archived_tx
1009
+ on archived_tx.ix = contract_row.archived_at_ix
1567
1010
  `;
1568
1011
  }
1569
- function tokenRowsQuery(limit, templateIds, templatePatterns = []) {
1012
+ function tokenRowsQuery(node, limit, templateIds, templatePatterns = []) {
1013
+ const relations = pqsCoreRelations(node);
1570
1014
  const normalizedLimit = Number.isFinite(limit) && Number(limit) > 0 ? Math.trunc(limit) : TOKEN_TRANSFER_CACHE_LIMIT;
1571
1015
  const quotedTemplateIds = templateIds
1572
1016
  .map((templateId) => `'${escapeSqlLiteral(templateId)}'`)
1573
1017
  .join(',\n ');
1574
- const patternClauses = templatePatterns.map((pattern) => `contract.template_id::text like '${escapeSqlLiteral(pattern)}'`);
1018
+ const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
1019
+ const patternClauses = templatePatterns.map((pattern) => `${contractTemplateId} like '${escapeSqlLiteral(pattern)}'`);
1575
1020
  const templateFilterClause = [
1576
- quotedTemplateIds ? `contract.template_id in (\n ${quotedTemplateIds}\n )` : null,
1021
+ quotedTemplateIds ? `${contractTemplateId} in (\n ${quotedTemplateIds}\n )` : null,
1577
1022
  ...patternClauses,
1578
1023
  ]
1579
1024
  .filter((clause) => clause !== null)
@@ -1589,100 +1034,70 @@ function tokenRowsQuery(limit, templateIds, templatePatterns = []) {
1589
1034
  contract_instance
1590
1035
  from (
1591
1036
  select
1592
- encode(contract.contract_id, 'hex') as contract_id,
1593
- encode(activate_event.update_id, 'hex') as update_id,
1594
- activate_event.event_offset::text as event_offset,
1595
- to_char(
1596
- to_timestamp(activate_event.record_time / 1000000.0) at time zone 'UTC',
1597
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1598
- ) as record_time,
1599
- contract.template_id::text as template_id,
1600
- package_string.external_string as package_id,
1601
- contract.instance as contract_instance
1602
- from participant.lapi_events_activate_contract activate_event
1603
- join participant.par_contracts contract
1604
- on contract.internal_contract_id = activate_event.internal_contract_id
1605
- left join participant.lapi_string_interning package_string
1606
- on package_string.internal_id = activate_event.representative_package_id
1037
+ contract_row.contract_id::text as contract_id,
1038
+ tx.transaction_id::text as update_id,
1039
+ tx.offset::text as event_offset,
1040
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time,
1041
+ ${contractTemplateId} as template_id,
1042
+ coalesce(contract_row.creation_package_id, package_row.id)::text as package_id,
1043
+ contract_row.payload as contract_instance
1044
+ from ${relations.transactions} tx
1045
+ join ${relations.contracts} contract_row
1046
+ on contract_row.created_at_ix = tx.ix
1047
+ join ${relations.contractTpe} contract_tpe_row
1048
+ on contract_tpe_row.pk = contract_row.tpe_pk
1049
+ left join ${relations.packages} package_row
1050
+ on package_row.pk = contract_row.package_pk
1607
1051
  where ${templateFilterClause}
1608
-
1609
- union all
1610
-
1611
- select
1612
- encode(contract.contract_id, 'hex') as contract_id,
1613
- encode(various_event.update_id, 'hex') as update_id,
1614
- various_event.event_offset::text as event_offset,
1615
- to_char(
1616
- to_timestamp(various_event.record_time / 1000000.0) at time zone 'UTC',
1617
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1618
- ) as record_time,
1619
- contract.template_id::text as template_id,
1620
- package_string.external_string as package_id,
1621
- contract.instance as contract_instance
1622
- from participant.lapi_events_various_witnessed various_event
1623
- join participant.par_contracts contract
1624
- on contract.internal_contract_id = various_event.internal_contract_id
1625
- left join participant.lapi_string_interning package_string
1626
- on package_string.internal_id = various_event.package_id
1627
- where various_event.event_type = 6
1628
- and (${templateFilterClause})
1629
1052
  ) token_transfer_rows
1630
1053
  order by event_offset::numeric desc
1631
1054
  limit ${normalizedLimit}
1632
1055
  `;
1633
1056
  }
1634
- function recentCip112MovementUpdateIdsQuery(limit) {
1057
+ function recentCip112MovementUpdateIdsQuery(node, limit) {
1058
+ const relations = pqsCoreRelations(node);
1635
1059
  const normalizedLimit = Number.isFinite(limit) && Number(limit) > 0 ? Math.trunc(limit) : TOKEN_TRANSFER_CACHE_LIMIT;
1060
+ const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
1061
+ const exerciseTemplateId = contractTemplateIdentifierExpression('exercise_contract_tpe_row');
1636
1062
  return `
1637
1063
  /* cip112_movement_update_ids */
1638
1064
  with relevant_updates as (
1639
1065
  select
1640
- encode(activate_event.update_id, 'hex') as update_id,
1641
- activate_event.event_offset::text as event_offset,
1642
- to_char(
1643
- to_timestamp(activate_event.record_time / 1000000.0) at time zone 'UTC',
1644
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1645
- ) as record_time
1646
- from participant.lapi_events_activate_contract activate_event
1647
- join participant.par_contracts contract
1648
- on contract.internal_contract_id = activate_event.internal_contract_id
1649
- where contract.template_id::text like '${escapeSqlLiteral(CIP112_TEMPLATE_ID_LIKE_PATTERN)}'
1066
+ tx.transaction_id::text as update_id,
1067
+ tx.offset::text as event_offset,
1068
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time
1069
+ from ${relations.transactions} tx
1070
+ join ${relations.contracts} contract_row
1071
+ on contract_row.created_at_ix = tx.ix
1072
+ join ${relations.contractTpe} contract_tpe_row
1073
+ on contract_tpe_row.pk = contract_row.tpe_pk
1074
+ where ${contractTemplateId} like '${escapeSqlLiteral(CIP112_TEMPLATE_ID_LIKE_PATTERN)}'
1650
1075
 
1651
1076
  union all
1652
1077
 
1653
1078
  select
1654
- encode(deactivate_event.update_id, 'hex') as update_id,
1655
- deactivate_event.event_offset::text as event_offset,
1656
- to_char(
1657
- to_timestamp(deactivate_event.record_time / 1000000.0) at time zone 'UTC',
1658
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1659
- ) as record_time
1660
- from participant.lapi_events_deactivate_contract deactivate_event
1661
- left join participant.lapi_string_interning template_string
1662
- on template_string.internal_id = deactivate_event.template_id
1663
- where template_string.external_string like '${escapeSqlLiteral(CIP112_TEMPLATE_ID_LIKE_PATTERN)}'
1079
+ tx.transaction_id::text as update_id,
1080
+ tx.offset::text as event_offset,
1081
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time
1082
+ from ${relations.transactions} tx
1083
+ join ${relations.contracts} contract_row
1084
+ on contract_row.archived_at_ix = tx.ix
1085
+ join ${relations.contractTpe} contract_tpe_row
1086
+ on contract_tpe_row.pk = contract_row.tpe_pk
1087
+ where ${contractTemplateId} like '${escapeSqlLiteral(CIP112_TEMPLATE_ID_LIKE_PATTERN)}'
1664
1088
 
1665
1089
  union all
1666
1090
 
1667
1091
  select
1668
- encode(various_event.update_id, 'hex') as update_id,
1669
- various_event.event_offset::text as event_offset,
1670
- to_char(
1671
- to_timestamp(various_event.record_time / 1000000.0) at time zone 'UTC',
1672
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1673
- ) as record_time
1674
- from participant.lapi_events_various_witnessed various_event
1675
- left join participant.par_contracts contract
1676
- on contract.internal_contract_id = various_event.internal_contract_id
1677
- left join participant.lapi_string_interning template_string
1678
- on template_string.internal_id = various_event.template_id
1679
- where (
1680
- various_event.event_type = 6
1681
- and contract.template_id::text like '${escapeSqlLiteral(CIP112_TEMPLATE_ID_LIKE_PATTERN)}'
1682
- ) or (
1683
- various_event.event_type in (5, 7)
1684
- and template_string.external_string like '${escapeSqlLiteral(CIP112_TEMPLATE_ID_LIKE_PATTERN)}'
1685
- )
1092
+ tx.transaction_id::text as update_id,
1093
+ tx.offset::text as event_offset,
1094
+ ${isoUtcTimestampExpression('tx.effective_at')} as record_time
1095
+ from ${relations.transactions} tx
1096
+ join ${relations.exercises} exercise_row
1097
+ on exercise_row.exercised_at_ix = tx.ix
1098
+ join ${relations.contractTpe} exercise_contract_tpe_row
1099
+ on exercise_contract_tpe_row.pk = exercise_row.contract_tpe_pk
1100
+ where ${exerciseTemplateId} like '${escapeSqlLiteral(CIP112_TEMPLATE_ID_LIKE_PATTERN)}'
1686
1101
  )
1687
1102
  select
1688
1103
  update_id,
@@ -1694,203 +1109,6 @@ function recentCip112MovementUpdateIdsQuery(limit) {
1694
1109
  limit ${normalizedLimit}
1695
1110
  `;
1696
1111
  }
1697
- function buildActiveContractsFilterClause(parties, templates, partyMode, hideSplice) {
1698
- const partyConditions = normalizePartyFilters(parties).map((party) => partyWitnessArrayMatchCondition('create_events.witnesses', party));
1699
- const templateExpression = "regexp_replace(coalesce(contract.template_id::text, ''), '^t\\\\|#[^:]+:', '')";
1700
- const templateConditions = normalizeTemplateFilters(templates).map((templateId) => {
1701
- const quotedTemplateId = `'${escapeSqlLiteral(templateId)}'`;
1702
- return `${templateExpression} = ${quotedTemplateId}`;
1703
- });
1704
- const groups = [];
1705
- if (partyConditions.length > 0) {
1706
- const partyJoiner = normalizePartyFilterMode(partyMode) === 'and' ? '\n and ' : '\n or ';
1707
- groups.push(partyConditions.length === 1
1708
- ? partyConditions[0]
1709
- : `(\n ${partyConditions.join(partyJoiner)}\n )`);
1710
- }
1711
- if (templateConditions.length > 0) {
1712
- groups.push(templateConditions.length === 1
1713
- ? templateConditions[0]
1714
- : `(\n ${templateConditions.join('\n or ')}\n )`);
1715
- }
1716
- if (hideSplice) {
1717
- groups.push(`${templateExpression} not like 'Splice.%'`);
1718
- }
1719
- if (groups.length === 0) {
1720
- return null;
1721
- }
1722
- return groups.length === 1 ? groups[0] : `(\n ${groups.join('\n and ')}\n )`;
1723
- }
1724
- function activeContractsQuery(limit, before, after, parties, templates, partyMode, hideSplice) {
1725
- const normalizedBefore = normalizeEventOffsetCursor(before);
1726
- const normalizedAfter = normalizeEventOffsetCursor(after);
1727
- const queryLimit = limit + 1;
1728
- const useAfterCursor = Boolean(normalizedAfter && !normalizedBefore);
1729
- const cursorFilter = useAfterCursor
1730
- ? `create_event_offset::numeric > ${normalizedAfter}`
1731
- : normalizedBefore
1732
- ? `create_event_offset::numeric < ${normalizedBefore}`
1733
- : null;
1734
- const orderDirection = useAfterCursor ? 'asc' : 'desc';
1735
- const filterClause = buildActiveContractsFilterClause(parties, templates, partyMode, hideSplice);
1736
- const whereConditions = [
1737
- `not exists (
1738
- select 1
1739
- from archived_contracts archived_contract
1740
- where archived_contract.internal_contract_id = create_events.internal_contract_id
1741
- )`,
1742
- cursorFilter,
1743
- filterClause,
1744
- ].filter((value) => Boolean(value));
1745
- const whereClause = whereConditions.length > 0 ? `where ${whereConditions.join('\n and ')}` : '';
1746
- return `
1747
- with activate_witnesses as (
1748
- select
1749
- witness_filter.event_sequential_id,
1750
- array_agg(distinct party_string.external_string order by party_string.external_string) as witnesses
1751
- from participant.lapi_filter_activate_witness witness_filter
1752
- join participant.lapi_string_interning party_string
1753
- on party_string.internal_id = witness_filter.party_id
1754
- group by witness_filter.event_sequential_id
1755
- ),
1756
- various_witnesses as (
1757
- select
1758
- witness_filter.event_sequential_id,
1759
- array_agg(distinct party_string.external_string order by party_string.external_string) as witnesses
1760
- from participant.lapi_filter_various_witness witness_filter
1761
- join participant.lapi_string_interning party_string
1762
- on party_string.internal_id = witness_filter.party_id
1763
- group by witness_filter.event_sequential_id
1764
- ),
1765
- create_events as (
1766
- select
1767
- activate_event.internal_contract_id,
1768
- activate_event.event_offset::text as create_event_offset,
1769
- to_char(
1770
- to_timestamp(activate_event.record_time / 1000000.0) at time zone 'UTC',
1771
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1772
- ) as created_record_time,
1773
- coalesce(activate_witnesses.witnesses, array[]::text[]) as witnesses
1774
- from participant.lapi_events_activate_contract activate_event
1775
- left join activate_witnesses
1776
- on activate_witnesses.event_sequential_id = activate_event.event_sequential_id
1777
-
1778
- union all
1779
-
1780
- select
1781
- various_event.internal_contract_id,
1782
- various_event.event_offset::text as create_event_offset,
1783
- to_char(
1784
- to_timestamp(various_event.record_time / 1000000.0) at time zone 'UTC',
1785
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1786
- ) as created_record_time,
1787
- coalesce(various_witnesses.witnesses, array[]::text[]) as witnesses
1788
- from participant.lapi_events_various_witnessed various_event
1789
- left join various_witnesses
1790
- on various_witnesses.event_sequential_id = various_event.event_sequential_id
1791
- where various_event.event_type = 6
1792
- ),
1793
- archived_contracts as (
1794
- select distinct deactivate_event.internal_contract_id
1795
- from participant.lapi_events_deactivate_contract deactivate_event
1796
-
1797
- union
1798
-
1799
- select distinct various_event.internal_contract_id
1800
- from participant.lapi_events_various_witnessed various_event
1801
- where various_event.event_type in (5, 7)
1802
- and various_event.consuming
1803
- )
1804
- select
1805
- encode(contract.contract_id, 'hex') as contract_id,
1806
- regexp_replace(coalesce(contract.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id,
1807
- create_events.created_record_time,
1808
- create_events.create_event_offset as created_event_offset
1809
- from create_events
1810
- join participant.par_contracts contract
1811
- on contract.internal_contract_id = create_events.internal_contract_id
1812
- ${whereClause}
1813
- order by create_event_offset::numeric ${orderDirection}
1814
- limit ${queryLimit}
1815
- `;
1816
- }
1817
- function searchContractsQuery(searchQuery, limit) {
1818
- const quotedQuery = escapeSqlLiteral(searchQuery);
1819
- return `
1820
- with activate_witnesses as (
1821
- select
1822
- witness_filter.event_sequential_id,
1823
- array_agg(distinct party_string.external_string order by party_string.external_string) as witnesses
1824
- from participant.lapi_filter_activate_witness witness_filter
1825
- join participant.lapi_string_interning party_string
1826
- on party_string.internal_id = witness_filter.party_id
1827
- group by witness_filter.event_sequential_id
1828
- ),
1829
- various_witnesses as (
1830
- select
1831
- witness_filter.event_sequential_id,
1832
- array_agg(distinct party_string.external_string order by party_string.external_string) as witnesses
1833
- from participant.lapi_filter_various_witness witness_filter
1834
- join participant.lapi_string_interning party_string
1835
- on party_string.internal_id = witness_filter.party_id
1836
- group by witness_filter.event_sequential_id
1837
- ),
1838
- create_events as (
1839
- select
1840
- activate_event.internal_contract_id,
1841
- activate_event.event_offset::text as create_event_offset,
1842
- to_char(
1843
- to_timestamp(activate_event.record_time / 1000000.0) at time zone 'UTC',
1844
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1845
- ) as created_record_time,
1846
- coalesce(activate_witnesses.witnesses, array[]::text[]) as witnesses
1847
- from participant.lapi_events_activate_contract activate_event
1848
- left join activate_witnesses
1849
- on activate_witnesses.event_sequential_id = activate_event.event_sequential_id
1850
-
1851
- union all
1852
-
1853
- select
1854
- various_event.internal_contract_id,
1855
- various_event.event_offset::text as create_event_offset,
1856
- to_char(
1857
- to_timestamp(various_event.record_time / 1000000.0) at time zone 'UTC',
1858
- 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
1859
- ) as created_record_time,
1860
- coalesce(various_witnesses.witnesses, array[]::text[]) as witnesses
1861
- from participant.lapi_events_various_witnessed various_event
1862
- left join various_witnesses
1863
- on various_witnesses.event_sequential_id = various_event.event_sequential_id
1864
- where various_event.event_type = 6
1865
- ),
1866
- archived_contracts as (
1867
- select distinct deactivate_event.internal_contract_id
1868
- from participant.lapi_events_deactivate_contract deactivate_event
1869
-
1870
- union
1871
-
1872
- select distinct various_event.internal_contract_id
1873
- from participant.lapi_events_various_witnessed various_event
1874
- where various_event.event_type in (5, 7)
1875
- and various_event.consuming
1876
- )
1877
- select
1878
- encode(contract.contract_id, 'hex') as contract_id,
1879
- regexp_replace(coalesce(contract.template_id::text, ''), '^t\\\\|#[^:]+:', '') as template_id,
1880
- create_events.created_record_time
1881
- from create_events
1882
- join participant.par_contracts contract
1883
- on contract.internal_contract_id = create_events.internal_contract_id
1884
- where encode(contract.contract_id, 'hex') like '${quotedQuery}%'
1885
- and not exists (
1886
- select 1
1887
- from archived_contracts archived_contract
1888
- where archived_contract.internal_contract_id = create_events.internal_contract_id
1889
- )
1890
- order by create_events.created_record_time desc nulls last, contract_id asc
1891
- limit ${limit}
1892
- `;
1893
- }
1894
1112
  function escapeSqlLiteral(value) {
1895
1113
  return value.replaceAll("'", "''");
1896
1114
  }
@@ -1929,7 +1147,7 @@ let PqsSummaryService = class PqsSummaryService {
1929
1147
  }
1930
1148
  async fetchSummary(node) {
1931
1149
  const client = this.clientFactory.getClient(node);
1932
- const result = await this.querySummary(client.query.bind(client));
1150
+ const result = await client.query(summaryQuery(node));
1933
1151
  const row = result.rows[0] ?? this.defaultRow();
1934
1152
  return {
1935
1153
  ledgerLabel: node.ledgerLabel ?? node.label,
@@ -1984,7 +1202,7 @@ let PqsSummaryService = class PqsSummaryService {
1984
1202
  const hideSplice = typeof options === 'object' ? options.hideSplice === true : false;
1985
1203
  const useAfterCursor = Boolean(after && !before);
1986
1204
  const query = client.query.bind(client);
1987
- const rawMetaRows = await this.queryRecentUpdateMetaRows(query, normalizedLimit, before, after, parties, templates, partyMode, hideSplice);
1205
+ const rawMetaRows = await this.queryRecentUpdateMetaRows(node, query, normalizedLimit, before, after, parties, templates, partyMode, hideSplice);
1988
1206
  const hasMoreInQuery = rawMetaRows.length > normalizedLimit;
1989
1207
  const trimmedMetaRows = rawMetaRows.slice(0, normalizedLimit);
1990
1208
  const orderedUpdates = (useAfterCursor ? [...trimmedMetaRows].reverse() : trimmedMetaRows).map((row) => ({
@@ -2003,7 +1221,7 @@ let PqsSummaryService = class PqsSummaryService {
2003
1221
  updates: [],
2004
1222
  };
2005
1223
  }
2006
- const partiesByUpdateId = await this.fetchPartiesByUpdateId(query, orderedUpdates.map((update) => update.rawUpdateId));
1224
+ const partiesByUpdateId = await this.fetchPartiesByUpdateId(node, query, orderedUpdates.map((update) => update.rawUpdateId));
2007
1225
  return {
2008
1226
  nodeId: node.id,
2009
1227
  label: node.label,
@@ -2171,7 +1389,7 @@ let PqsSummaryService = class PqsSummaryService {
2171
1389
  }
2172
1390
  async searchUpdatesForNode(node, query, limit) {
2173
1391
  const client = this.clientFactory.getClient(node);
2174
- const rows = await this.querySearchUpdateMetaRows(client.query.bind(client), query, limit);
1392
+ const rows = await this.querySearchUpdateMetaRows(node, client.query.bind(client), query, limit);
2175
1393
  const dedupedRows = new Map();
2176
1394
  for (const row of rows) {
2177
1395
  const eventOffset = this.extractEventOffset(row);
@@ -2182,7 +1400,7 @@ let PqsSummaryService = class PqsSummaryService {
2182
1400
  const rawRows = Array.from(dedupedRows.values());
2183
1401
  const rawUpdateIds = rawRows.map((row) => row.update_id);
2184
1402
  const partiesByUpdateId = rawUpdateIds.length > 0
2185
- ? await this.fetchPartiesByUpdateId(client.query.bind(client), rawUpdateIds)
1403
+ ? await this.fetchPartiesByUpdateId(node, client.query.bind(client), rawUpdateIds)
2186
1404
  : new Map();
2187
1405
  return rawRows
2188
1406
  .map((row) => {
@@ -2230,7 +1448,7 @@ let PqsSummaryService = class PqsSummaryService {
2230
1448
  }
2231
1449
  async searchContractsForNode(node, query, limit) {
2232
1450
  const client = this.clientFactory.getClient(node);
2233
- const result = await client.query(searchContractsQuery(query, limit));
1451
+ const result = await client.query(pqsSearchContractsQuery(node, query, limit));
2234
1452
  const rows = result.rows ?? [];
2235
1453
  return rows
2236
1454
  .filter((row) => this.hasPrefixMatch(query, [row.contract_id]))
@@ -2331,31 +1549,13 @@ let PqsSummaryService = class PqsSummaryService {
2331
1549
  }),
2332
1550
  };
2333
1551
  }
2334
- async querySearchUpdateMetaRows(queryFn, searchQuery, limit) {
2335
- try {
2336
- const result = await queryFn(searchUpdatesQuery(searchQuery, limit));
2337
- return result.rows ?? [];
2338
- }
2339
- catch (error) {
2340
- if (!this.shouldFallbackToNormalizedEventTables(error)) {
2341
- throw error;
2342
- }
2343
- }
2344
- const fallbackResult = await queryFn(normalizedSearchUpdatesQuery(searchQuery, limit));
2345
- return fallbackResult.rows ?? [];
1552
+ async querySearchUpdateMetaRows(node, queryFn, searchQuery, limit) {
1553
+ const result = await queryFn(pqsSearchUpdatesQuery(node, searchQuery, limit));
1554
+ return result.rows ?? [];
2346
1555
  }
2347
- async queryRecentUpdateMetaRows(query, limit, before, after, parties, templates, mode, hideSplice) {
2348
- try {
2349
- const result = await query(recentUpdatesQuery(limit, before, after, parties, templates, mode, hideSplice));
2350
- return result.rows ?? [];
2351
- }
2352
- catch (error) {
2353
- if (!this.shouldFallbackToNormalizedEventTables(error)) {
2354
- throw error;
2355
- }
2356
- }
2357
- const fallbackResult = await query(normalizedRecentUpdatesQuery(limit, before, after, parties, templates, mode, hideSplice));
2358
- return fallbackResult.rows ?? [];
1556
+ async queryRecentUpdateMetaRows(node, query, limit, before, after, parties, templates, mode, hideSplice) {
1557
+ const result = await query(pqsRecentUpdatesQuery(node, limit, before, after, parties, templates, mode, hideSplice));
1558
+ return result.rows ?? [];
2359
1559
  }
2360
1560
  async fetchNodeContracts(node, options) {
2361
1561
  const client = this.clientFactory.getClient(node);
@@ -2369,7 +1569,7 @@ let PqsSummaryService = class PqsSummaryService {
2369
1569
  const partyMode = options?.partyMode;
2370
1570
  const hideSplice = options?.hideSplice === true;
2371
1571
  const useAfterCursor = Boolean(after && !before);
2372
- const result = await client.query(activeContractsQuery(normalizedLimit, before, after, parties, templates, partyMode, hideSplice));
1572
+ const result = await client.query(pqsActiveContractsQuery(node, normalizedLimit, before, after, parties, templates, partyMode, hideSplice));
2373
1573
  const rawRows = result.rows ?? [];
2374
1574
  const hasMoreInQuery = rawRows.length > normalizedLimit;
2375
1575
  const trimmedRows = rawRows.slice(0, normalizedLimit);
@@ -2404,7 +1604,7 @@ let PqsSummaryService = class PqsSummaryService {
2404
1604
  }
2405
1605
  async fetchActivityBuckets(node, days = 30, bucketMinutes = 15) {
2406
1606
  const client = this.clientFactory.getClient(node);
2407
- const result = await client.query(activityBucketsQuery(days, bucketMinutes));
1607
+ const result = await client.query(pqsActivityBucketsQuery(node, days, bucketMinutes));
2408
1608
  const rows = result.rows ?? [];
2409
1609
  return rows
2410
1610
  .filter((row) => typeof row.bucket_timestamp === 'string')
@@ -2435,7 +1635,7 @@ let PqsSummaryService = class PqsSummaryService {
2435
1635
  version: metadata.version,
2436
1636
  uploadedAt: metadata.uploadedAt,
2437
1637
  packageSize: metadata.packageSize,
2438
- status: inspection.reason === 'missing_package' ? 'missing_package' : 'invalid_package',
1638
+ status: inspection.reason === 'missing_package' ? 'not_available' : 'invalid_package',
2439
1639
  seenOnNodes,
2440
1640
  moduleCount: 0,
2441
1641
  templateCount: 0,
@@ -3162,7 +2362,7 @@ let PqsSummaryService = class PqsSummaryService {
3162
2362
  }
3163
2363
  seenContracts.add(dedupeKey);
3164
2364
  const client = this.clientFactory.getClient(node);
3165
- const contractResult = await client.query(contractDetailQuery(holder.contractId));
2365
+ const contractResult = await client.query(contractDetailQuery(node, holder.contractId));
3166
2366
  const contractRow = contractResult.rows[0];
3167
2367
  if (!contractRow || typeof contractRow.created_update_id !== 'string') {
3168
2368
  continue;
@@ -3237,7 +2437,7 @@ let PqsSummaryService = class PqsSummaryService {
3237
2437
  }
3238
2438
  async fetchUpdateDetail(node, eventOffset) {
3239
2439
  const client = this.clientFactory.getClient(node);
3240
- const detailResult = await client.query(singleUpdateQuery(eventOffset));
2440
+ const detailResult = await client.query(singleUpdateQuery(node, eventOffset));
3241
2441
  const detailRows = detailResult.rows ?? [];
3242
2442
  const detailRow = detailRows[0];
3243
2443
  if (!detailRow) {
@@ -3246,10 +2446,10 @@ let PqsSummaryService = class PqsSummaryService {
3246
2446
  const rawUpdateId = this.extractRawUpdateId(detailRow);
3247
2447
  const matchedEventOffset = this.extractEventOffset(detailRow);
3248
2448
  const canonicalUpdateId = this.normalizeUpdateId(rawUpdateId);
3249
- const partiesByUpdateId = await this.fetchPartiesByUpdateId(client.query.bind(client), [rawUpdateId]);
2449
+ const partiesByUpdateId = await this.fetchPartiesByUpdateId(node, client.query.bind(client), [rawUpdateId]);
3250
2450
  const events = await this.fetchEventsByUpdateId(node, client.query.bind(client), rawUpdateId);
3251
2451
  const exerciseData = this.shouldResolveRewardCoupon(events)
3252
- ? await this.fetchRewardCouponDetails(client.query.bind(client), rawUpdateId)
2452
+ ? await this.fetchRewardCouponDetails(node, client.query.bind(client), rawUpdateId)
3253
2453
  : null;
3254
2454
  return {
3255
2455
  nodeId: node.id,
@@ -3264,7 +2464,7 @@ let PqsSummaryService = class PqsSummaryService {
3264
2464
  }
3265
2465
  async fetchContractDetail(node, contractId) {
3266
2466
  const client = this.clientFactory.getClient(node);
3267
- const result = await client.query(contractDetailQuery(contractId));
2467
+ const result = await client.query(contractDetailQuery(node, contractId));
3268
2468
  const row = result.rows[0];
3269
2469
  if (!row) {
3270
2470
  throw new Error('Contract not found');
@@ -3289,34 +2489,13 @@ let PqsSummaryService = class PqsSummaryService {
3289
2489
  contractData: await this.decodeContractData(node, packageId, templateId, row.contract_instance),
3290
2490
  };
3291
2491
  }
3292
- async querySummary(query) {
3293
- try {
3294
- return await query(ACTIVE_QUERY);
3295
- }
3296
- catch (error) {
3297
- if (!this.shouldFallbackToParticipantTables(error)) {
3298
- throw error;
3299
- }
3300
- }
3301
- return query(PARTICIPANT_FALLBACK_QUERY);
3302
- }
3303
2492
  async fetchPartyRecentUpdatesForNode(node, partyId, limit) {
3304
2493
  const client = this.clientFactory.getClient(node);
3305
- let rows = [];
3306
- try {
3307
- const result = await client.query(partyRecentUpdatesQuery(partyId, limit));
3308
- rows = result.rows ?? [];
3309
- }
3310
- catch (error) {
3311
- if (!this.shouldFallbackToNormalizedEventTables(error)) {
3312
- throw error;
3313
- }
3314
- const result = await client.query(normalizedPartyRecentUpdatesQuery(partyId, limit));
3315
- rows = result.rows ?? [];
3316
- }
2494
+ const result = await client.query(pqsPartyRecentUpdatesQuery(node, partyId, limit));
2495
+ const rows = result.rows ?? [];
3317
2496
  const rawUpdateIds = rows.map((row) => row.update_id);
3318
2497
  const partiesByUpdateId = rawUpdateIds.length > 0
3319
- ? await this.fetchPartiesByUpdateId(client.query.bind(client), rawUpdateIds)
2498
+ ? await this.fetchPartiesByUpdateId(node, client.query.bind(client), rawUpdateIds)
3320
2499
  : new Map();
3321
2500
  return rows.map((row) => {
3322
2501
  const updateId = this.normalizeUpdateId(row.update_id);
@@ -3342,18 +2521,8 @@ let PqsSummaryService = class PqsSummaryService {
3342
2521
  const before = options?.before;
3343
2522
  const after = options?.after;
3344
2523
  const useAfterCursor = Boolean(after && !before);
3345
- let rows = [];
3346
- try {
3347
- const result = await client.query(partyRecentContractsQuery(partyId, normalizedLimit, before, after));
3348
- rows = result.rows ?? [];
3349
- }
3350
- catch (error) {
3351
- if (!this.shouldFallbackToNormalizedEventTables(error)) {
3352
- throw error;
3353
- }
3354
- const result = await client.query(normalizedPartyRecentContractsQuery(partyId, normalizedLimit, before, after));
3355
- rows = result.rows ?? [];
3356
- }
2524
+ const result = await client.query(pqsPartyRecentContractsQuery(node, partyId, normalizedLimit, before, after));
2525
+ const rows = result.rows ?? [];
3357
2526
  const hasMoreInQuery = rows.length > normalizedLimit;
3358
2527
  const trimmedRows = rows.slice(0, normalizedLimit);
3359
2528
  const orderedContracts = (useAfterCursor ? [...trimmedRows].reverse() : trimmedRows)
@@ -3389,58 +2558,24 @@ let PqsSummaryService = class PqsSummaryService {
3389
2558
  contracts: orderedContracts.map(({ createdEventOffset: _, ...contract }) => contract),
3390
2559
  };
3391
2560
  }
3392
- shouldFallbackToParticipantTables(error) {
3393
- if (!(error instanceof Error)) {
3394
- return false;
3395
- }
3396
- const code = error.code;
3397
- return code === '42883' || error.message.includes('function active() does not exist');
3398
- }
3399
- shouldFallbackToNormalizedEventTables(error) {
3400
- if (!(error instanceof Error)) {
3401
- return false;
3402
- }
3403
- const code = error.code;
3404
- return code === '42P01' || error.message.includes('does not exist');
3405
- }
3406
2561
  async fetchActivePartiesForNode(node) {
3407
2562
  const client = this.clientFactory.getClient(node);
3408
- try {
3409
- const result = await client.query(activePartiesQuery());
3410
- const row = result.rows[0];
3411
- return this.normalizeParties(row?.parties ?? null);
3412
- }
3413
- catch (error) {
3414
- if (!this.shouldFallbackToNormalizedEventTables(error)) {
3415
- throw error;
3416
- }
3417
- }
3418
- const fallbackResult = await client.query(normalizedActivePartiesQuery());
3419
- const fallbackRow = fallbackResult.rows[0];
3420
- return this.normalizeParties(fallbackRow?.parties ?? null);
2563
+ const result = await client.query(pqsActivePartiesQuery(node));
2564
+ const row = result.rows[0];
2565
+ return this.normalizeParties(row?.parties ?? null);
3421
2566
  }
3422
- async fetchPartiesByUpdateId(query, updateIds) {
2567
+ async fetchPartiesByUpdateId(node, query, updateIds) {
2568
+ if (updateIds.length === 0) {
2569
+ return new Map();
2570
+ }
3423
2571
  try {
3424
- const result = await query(recentUpdatePartiesQuery(updateIds));
2572
+ const result = await query(pqsRecentUpdatePartiesQuery(node, updateIds));
3425
2573
  const rows = result.rows ?? [];
3426
2574
  return new Map(rows.map((row) => [
3427
2575
  this.normalizeUpdateId(row.update_id),
3428
2576
  this.normalizeParties(row.parties),
3429
2577
  ]));
3430
2578
  }
3431
- catch (error) {
3432
- if (!this.shouldFallbackToNormalizedEventTables(error)) {
3433
- return new Map();
3434
- }
3435
- }
3436
- try {
3437
- const fallbackResult = await query(normalizedRecentUpdatePartiesQuery(updateIds));
3438
- const fallbackRows = fallbackResult.rows ?? [];
3439
- return new Map(fallbackRows.map((row) => [
3440
- this.normalizeUpdateId(row.update_id),
3441
- this.normalizeParties(row.parties),
3442
- ]));
3443
- }
3444
2579
  catch {
3445
2580
  return new Map();
3446
2581
  }
@@ -3475,7 +2610,7 @@ let PqsSummaryService = class PqsSummaryService {
3475
2610
  async fetchObservedTokensForNode(node, limit, options) {
3476
2611
  const client = this.clientFactory.getClient(node);
3477
2612
  const includeCip112 = options?.includeCip112 ?? true;
3478
- const result = await client.query(tokenRowsQuery(limit, includeCip112 ? TOKEN_DISCOVERY_TEMPLATE_IDS : TOKEN_DISCOVERY_NON_CIP112_TEMPLATE_IDS, includeCip112 ? TOKEN_DISCOVERY_TEMPLATE_PATTERNS : TOKEN_DISCOVERY_NON_CIP112_TEMPLATE_PATTERNS));
2613
+ const result = await client.query(tokenRowsQuery(node, limit, includeCip112 ? TOKEN_DISCOVERY_TEMPLATE_IDS : TOKEN_DISCOVERY_NON_CIP112_TEMPLATE_IDS, includeCip112 ? TOKEN_DISCOVERY_TEMPLATE_PATTERNS : TOKEN_DISCOVERY_NON_CIP112_TEMPLATE_PATTERNS));
3479
2614
  const rows = result.rows ?? [];
3480
2615
  const dedupedTokens = new Map();
3481
2616
  for (const row of rows) {
@@ -3558,7 +2693,7 @@ let PqsSummaryService = class PqsSummaryService {
3558
2693
  async fetchTokenHoldersForNode(node, limit, options) {
3559
2694
  const client = this.clientFactory.getClient(node);
3560
2695
  const includeCip112 = options?.includeCip112 ?? true;
3561
- const result = await client.query(tokenRowsQuery(limit, includeCip112 ? TOKEN_HOLDER_TEMPLATE_IDS : TOKEN_HOLDER_NON_CIP112_TEMPLATE_IDS, includeCip112 ? TOKEN_HOLDER_TEMPLATE_PATTERNS : TOKEN_HOLDER_NON_CIP112_TEMPLATE_PATTERNS));
2696
+ const result = await client.query(tokenRowsQuery(node, limit, includeCip112 ? TOKEN_HOLDER_TEMPLATE_IDS : TOKEN_HOLDER_NON_CIP112_TEMPLATE_IDS, includeCip112 ? TOKEN_HOLDER_TEMPLATE_PATTERNS : TOKEN_HOLDER_NON_CIP112_TEMPLATE_PATTERNS));
3562
2697
  const rows = result.rows ?? [];
3563
2698
  const holders = [];
3564
2699
  for (const row of rows) {
@@ -3620,7 +2755,7 @@ let PqsSummaryService = class PqsSummaryService {
3620
2755
  }
3621
2756
  async fetchTokenTransfersForNode(node, limit) {
3622
2757
  const client = this.clientFactory.getClient(node);
3623
- const result = await client.query(tokenRowsQuery(limit, TOKEN_TRANSFER_TEMPLATE_IDS));
2758
+ const result = await client.query(tokenRowsQuery(node, limit, TOKEN_TRANSFER_TEMPLATE_IDS));
3624
2759
  const rows = result.rows ?? [];
3625
2760
  const transfers = [];
3626
2761
  for (const row of rows) {
@@ -3654,7 +2789,7 @@ let PqsSummaryService = class PqsSummaryService {
3654
2789
  }
3655
2790
  async fetchInferredCip112TransfersForNode(node, limit) {
3656
2791
  const client = this.clientFactory.getClient(node);
3657
- const result = await client.query(recentCip112MovementUpdateIdsQuery(limit));
2792
+ const result = await client.query(recentCip112MovementUpdateIdsQuery(node, limit));
3658
2793
  const rows = result.rows ?? [];
3659
2794
  const transfers = [];
3660
2795
  for (const row of rows) {
@@ -3701,7 +2836,7 @@ let PqsSummaryService = class PqsSummaryService {
3701
2836
  if (this.isShareLikeCip112Template(event.templateId)) {
3702
2837
  continue;
3703
2838
  }
3704
- const transfer = this.buildInferredCip112Movement(node, update, event, 'Create');
2839
+ const transfer = this.buildInferredCip112Movement(node, update, event, 'Mint');
3705
2840
  if (transfer) {
3706
2841
  transfers.push(transfer);
3707
2842
  }
@@ -3730,7 +2865,7 @@ let PqsSummaryService = class PqsSummaryService {
3730
2865
  tokenId: token.tokenId,
3731
2866
  tokenName: token.name,
3732
2867
  amount: this.readScalarField(record, 'amount'),
3733
- sender: this.readScalarField(record, 'issuer'),
2868
+ sender: movementType === 'Mint' ? this.readScalarField(record, 'issuer') : null,
3734
2869
  receiver: this.readScalarField(record, 'owner') ?? receiverFallback,
3735
2870
  eventOffset: this.normalizeOptionalScalar(update.event_offset) ?? '',
3736
2871
  updateId: typeof update.update_id === 'string' ? this.normalizeUpdateId(update.update_id) : '',
@@ -4033,31 +3168,21 @@ let PqsSummaryService = class PqsSummaryService {
4033
3168
  };
4034
3169
  }
4035
3170
  async fetchEventsByUpdateId(node, query, rawUpdateId) {
4036
- try {
4037
- const result = await query(updateEventsQuery(rawUpdateId));
4038
- const rows = result.rows ?? [];
4039
- return Promise.all(rows.map((row) => this.normalizeEventRow(node, row)));
4040
- }
4041
- catch (error) {
4042
- if (!this.shouldFallbackToNormalizedEventTables(error)) {
4043
- throw error;
4044
- }
4045
- }
4046
- const fallbackResult = await query(normalizedUpdateEventsQuery(rawUpdateId));
4047
- const rows = fallbackResult.rows ?? [];
3171
+ const result = await query(updateEventsQuery(node, rawUpdateId));
3172
+ const rows = result.rows ?? [];
4048
3173
  return Promise.all(rows.map((row) => this.normalizeEventRow(node, row)));
4049
3174
  }
4050
3175
  shouldResolveRewardCoupon(events) {
4051
3176
  return events.some((event) => event.choice === 'ReceiveSvRewardCoupon');
4052
3177
  }
4053
- async fetchRewardCouponDetails(query, rawUpdateId) {
3178
+ async fetchRewardCouponDetails(node, query, rawUpdateId) {
4054
3179
  try {
4055
- const result = await query(rewardCouponInstanceQuery(rawUpdateId));
3180
+ const result = await query(rewardCouponInstanceQuery(node, rawUpdateId));
4056
3181
  const row = result.rows[0];
4057
- if (!row || !Buffer.isBuffer(row.contract_instance)) {
3182
+ if (!row) {
4058
3183
  return null;
4059
3184
  }
4060
- const decoded = this.decodeRewardCouponContractInstance(row.contract_instance);
3185
+ const decoded = await this.decodeContractData(node, null, 'Splice.Amulet:SvRewardCoupon', row.contract_instance);
4061
3186
  if (!decoded || decoded.status !== 'decoded' || !this.isRecordValue(decoded.value)) {
4062
3187
  return null;
4063
3188
  }
@@ -4195,13 +3320,19 @@ let PqsSummaryService = class PqsSummaryService {
4195
3320
  };
4196
3321
  }
4197
3322
  async decodeContractData(node, packageId, templateId, contractInstance) {
4198
- if (!Buffer.isBuffer(contractInstance) || !templateId) {
3323
+ if (!templateId || contractInstance === null || contractInstance === undefined) {
4199
3324
  return null;
4200
3325
  }
4201
- if (templateId === 'Splice.Amulet:SvRewardCoupon') {
3326
+ if (!Buffer.isBuffer(contractInstance)) {
3327
+ const decodedJson = this.decodePqsJsonData(contractInstance);
3328
+ if (decodedJson) {
3329
+ return decodedJson;
3330
+ }
3331
+ }
3332
+ if (templateId === 'Splice.Amulet:SvRewardCoupon' && Buffer.isBuffer(contractInstance)) {
4202
3333
  return this.decodeRewardCouponContractInstance(contractInstance);
4203
3334
  }
4204
- if (!this.damlValueDecoder) {
3335
+ if (!Buffer.isBuffer(contractInstance) || !this.damlValueDecoder) {
4205
3336
  return null;
4206
3337
  }
4207
3338
  const initialDecode = await this.damlValueDecoder.decodeContractInstance({
@@ -4212,11 +3343,32 @@ let PqsSummaryService = class PqsSummaryService {
4212
3343
  return this.retryContractDecodeAfterPackageRefresh(node, packageId, templateId, contractInstance, initialDecode);
4213
3344
  }
4214
3345
  async decodeExerciseData(node, input) {
3346
+ if (!Buffer.isBuffer(input.exerciseArgument) && !Buffer.isBuffer(input.exerciseResult)) {
3347
+ const argument = this.decodePqsJsonData(input.exerciseArgument);
3348
+ const result = this.decodePqsJsonData(input.exerciseResult);
3349
+ if (argument || result) {
3350
+ return {
3351
+ argument: argument ?? { status: 'not_available' },
3352
+ result: result ?? { status: 'not_available' },
3353
+ };
3354
+ }
3355
+ }
4215
3356
  if (!this.damlValueDecoder) {
4216
3357
  return null;
4217
3358
  }
4218
- const initialDecode = await this.damlValueDecoder.decodeExerciseValue(input);
4219
- return this.retryExerciseDecodeAfterPackageRefresh(node, input, initialDecode);
3359
+ if ((input.exerciseArgument !== null && input.exerciseArgument !== undefined
3360
+ && !Buffer.isBuffer(input.exerciseArgument))
3361
+ || (input.exerciseResult !== null && input.exerciseResult !== undefined
3362
+ && !Buffer.isBuffer(input.exerciseResult))) {
3363
+ return null;
3364
+ }
3365
+ const decoderInput = {
3366
+ ...input,
3367
+ exerciseArgument: (input.exerciseArgument ?? null),
3368
+ exerciseResult: (input.exerciseResult ?? null),
3369
+ };
3370
+ const initialDecode = await this.damlValueDecoder.decodeExerciseValue(decoderInput);
3371
+ return this.retryExerciseDecodeAfterPackageRefresh(node, decoderInput, initialDecode);
4220
3372
  }
4221
3373
  async retryContractDecodeAfterPackageRefresh(node, packageId, templateId, contractInstance, initialDecode) {
4222
3374
  const reason = initialDecode?.status === 'invalid_data' ? initialDecode.reason : null;
@@ -4327,10 +3479,15 @@ let PqsSummaryService = class PqsSummaryService {
4327
3479
  }
4328
3480
  current = field;
4329
3481
  }
4330
- if (!current ||
4331
- typeof current !== 'object' ||
4332
- !('kind' in current) ||
4333
- current.kind !== 'text_map') {
3482
+ if (current && this.isRecordValue(current)) {
3483
+ const recordEntry = current.fields.find((candidate) => candidate.label === key)?.value;
3484
+ if (typeof recordEntry === 'string'
3485
+ || typeof recordEntry === 'number'
3486
+ || typeof recordEntry === 'boolean') {
3487
+ return String(recordEntry);
3488
+ }
3489
+ }
3490
+ if (!current || typeof current !== 'object' || !('kind' in current) || current.kind !== 'text_map') {
4334
3491
  return null;
4335
3492
  }
4336
3493
  const entry = current.entries.find((candidate) => candidate.key === key)?.value;
@@ -4348,6 +3505,54 @@ let PqsSummaryService = class PqsSummaryService {
4348
3505
  }
4349
3506
  return null;
4350
3507
  }
3508
+ decodePqsJsonData(value) {
3509
+ const decoded = this.decodePqsJsonValue(value);
3510
+ return decoded === null
3511
+ ? null
3512
+ : {
3513
+ status: 'decoded',
3514
+ value: decoded,
3515
+ };
3516
+ }
3517
+ decodePqsJsonValue(value) {
3518
+ if (value === null || value === undefined) {
3519
+ return null;
3520
+ }
3521
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
3522
+ return value;
3523
+ }
3524
+ if (Array.isArray(value)) {
3525
+ return {
3526
+ kind: 'list',
3527
+ items: value
3528
+ .map((item) => this.decodePqsJsonValue(item))
3529
+ .filter((item) => item !== null),
3530
+ };
3531
+ }
3532
+ if (!this.isPlainJsonObject(value)) {
3533
+ return null;
3534
+ }
3535
+ const entries = Object.entries(value);
3536
+ if (entries.length === 0) {
3537
+ return { kind: 'unit' };
3538
+ }
3539
+ if (entries.length === 1
3540
+ && entries[0][0] === 'number'
3541
+ && (typeof entries[0][1] === 'string' || typeof entries[0][1] === 'number')) {
3542
+ const numericValue = Number(entries[0][1]);
3543
+ return Number.isFinite(numericValue) ? numericValue : String(entries[0][1]);
3544
+ }
3545
+ return {
3546
+ kind: 'record',
3547
+ fields: entries.map(([label, fieldValue]) => ({
3548
+ label,
3549
+ value: this.decodePqsJsonValue(fieldValue) ?? { kind: 'unit' },
3550
+ })),
3551
+ };
3552
+ }
3553
+ isPlainJsonObject(value) {
3554
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3555
+ }
4351
3556
  getFirstLengthDelimitedField(message, fieldNumber) {
4352
3557
  return this.parseProtobufFields(message).find((field) => field.fieldNumber === fieldNumber && field.buffer)?.buffer ?? null;
4353
3558
  }