@distrohelena/canton-explorer 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/public/assets/DebuggerView-VieEr0b3.js +2 -0
- package/dist/public/assets/index-BmT5Hrud.js +1 -0
- package/dist/public/assets/index-DnEheBxd.css +1 -0
- package/dist/public/assets/{editor.api-8w-mShFJ.js → monaco-runtime-DiIHw4v2.js} +1 -1
- package/dist/public/index.html +2 -2
- package/dist/src/api/nodes.controller.d.ts +2 -2
- package/dist/src/api/nodes.controller.js +20 -8
- package/dist/src/api/nodes.controller.js.map +1 -1
- package/dist/src/cache/node-cache.service.js +23 -9
- package/dist/src/cache/node-cache.service.js.map +1 -1
- package/dist/src/debugger/debugger.service.d.ts +1 -0
- package/dist/src/debugger/debugger.service.js +9 -0
- package/dist/src/debugger/debugger.service.js.map +1 -1
- package/dist/src/domain/node-health.js +1 -1
- package/dist/src/domain/node-health.js.map +1 -1
- package/dist/src/domain/node.types.d.ts +2 -0
- package/dist/src/grpc/daml-lf-archive.d.ts +2 -0
- package/dist/src/grpc/daml-lf-archive.js +104 -0
- package/dist/src/grpc/daml-lf-archive.js.map +1 -0
- package/dist/src/grpc/grpc-operations.service.d.ts +3 -0
- package/dist/src/grpc/grpc-operations.service.js +80 -28
- package/dist/src/grpc/grpc-operations.service.js.map +1 -1
- package/dist/src/pqs/pqs-summary.service.d.ts +21 -1
- package/dist/src/pqs/pqs-summary.service.js +492 -93
- package/dist/src/pqs/pqs-summary.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/public/assets/DebuggerView-DnuybIZF.js +0 -2
- package/dist/public/assets/index-Yd5H2RaX.js +0 -1
- package/dist/public/assets/index-fkZAWLbW.css +0 -1
- /package/dist/public/assets/{editor-C5uazxST.css → monaco-runtime-C5uazxST.css} +0 -0
|
@@ -28,6 +28,13 @@ const CANTON_COIN_TOKEN_ID = 'canton-coin';
|
|
|
28
28
|
const CANTON_COIN_TOKEN_NAME = 'Canton Coin';
|
|
29
29
|
const CANTON_COIN_TRANSFER_TEMPLATE_ID = 'Splice.AmuletTransferInstruction:AmuletTransferInstruction';
|
|
30
30
|
const CANTON_COIN_AMULET_TEMPLATE_ID = 'Splice.Amulet:Amulet';
|
|
31
|
+
const AMULET_RULES_TEMPLATE_ID = 'Splice.AmuletRules:AmuletRules';
|
|
32
|
+
const NATIVE_AMULET_SUPPORT_TEMPLATE_IDS = [
|
|
33
|
+
CANTON_COIN_AMULET_TEMPLATE_ID,
|
|
34
|
+
AMULET_RULES_TEMPLATE_ID,
|
|
35
|
+
'Splice.Amulet:ValidatorRight',
|
|
36
|
+
];
|
|
37
|
+
const NATIVE_AMULET_INTRINSIC_ID = 'Amulet';
|
|
31
38
|
const CIP56_HOLDING_TEMPLATE_ID = 'Splice.Api.Token.HoldingV1:Holding';
|
|
32
39
|
const CIP56_TRANSFER_TEMPLATE_ID = 'Splice.Api.Token.TransferInstructionV1:Transfer';
|
|
33
40
|
const CIP112_TEMPLATE_ID_LIKE_PATTERN = '%.CIP112:%';
|
|
@@ -855,13 +862,14 @@ function singleUpdateQuery(node, eventOffset) {
|
|
|
855
862
|
limit 1
|
|
856
863
|
`;
|
|
857
864
|
}
|
|
858
|
-
function
|
|
865
|
+
function updateEventsByUpdateIdsQuery(node, updateIds) {
|
|
859
866
|
const relations = pqsCoreRelations(node);
|
|
860
|
-
const
|
|
867
|
+
const quotedIds = updateIds.map((updateId) => `'${escapeSqlLiteral(updateId)}'`).join(', ');
|
|
861
868
|
const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
|
|
862
869
|
const exerciseTemplateId = contractTemplateIdentifierExpression('exercise_contract_tpe_row');
|
|
863
870
|
return `
|
|
864
871
|
select
|
|
872
|
+
update_id,
|
|
865
873
|
event_kind,
|
|
866
874
|
event_id,
|
|
867
875
|
contract_id,
|
|
@@ -875,6 +883,7 @@ function updateEventsQuery(node, updateId) {
|
|
|
875
883
|
raw
|
|
876
884
|
from (
|
|
877
885
|
select
|
|
886
|
+
tx.transaction_id::text as update_id,
|
|
878
887
|
'create'::text as event_kind,
|
|
879
888
|
event_row.event_id::text as event_id,
|
|
880
889
|
contract_row.contract_id::text as contract_id,
|
|
@@ -902,11 +911,12 @@ function updateEventsQuery(node, updateId) {
|
|
|
902
911
|
on contract_tpe_row.pk = contract_row.tpe_pk
|
|
903
912
|
left join ${relations.packages} package_row
|
|
904
913
|
on package_row.pk = contract_row.package_pk
|
|
905
|
-
where tx.transaction_id
|
|
914
|
+
where tx.transaction_id in (${quotedIds})
|
|
906
915
|
|
|
907
916
|
union all
|
|
908
917
|
|
|
909
918
|
select
|
|
919
|
+
tx.transaction_id::text as update_id,
|
|
910
920
|
case
|
|
911
921
|
when exercise_tpe_row.consuming then 'consuming_exercise'::text
|
|
912
922
|
else 'non_consuming_exercise'::text
|
|
@@ -940,11 +950,14 @@ function updateEventsQuery(node, updateId) {
|
|
|
940
950
|
on exercise_tpe_row.pk = exercise_row.tpe_pk
|
|
941
951
|
left join ${relations.packages} package_row
|
|
942
952
|
on package_row.pk = exercise_row.package_pk
|
|
943
|
-
where tx.transaction_id
|
|
953
|
+
where tx.transaction_id in (${quotedIds})
|
|
944
954
|
) update_events
|
|
945
|
-
order by event_id asc nulls last, event_kind asc, contract_id asc, template_id asc
|
|
955
|
+
order by update_id asc nulls last, event_id asc nulls last, event_kind asc, contract_id asc, template_id asc
|
|
946
956
|
`;
|
|
947
957
|
}
|
|
958
|
+
function updateEventsQuery(node, updateId) {
|
|
959
|
+
return updateEventsByUpdateIdsQuery(node, [updateId]);
|
|
960
|
+
}
|
|
948
961
|
function rewardCouponInstanceQuery(node, updateId) {
|
|
949
962
|
const relations = pqsCoreRelations(node);
|
|
950
963
|
const quotedId = `'${escapeSqlLiteral(normalizeByteaHex(updateId))}'`;
|
|
@@ -970,9 +983,11 @@ function rewardCouponInstanceQuery(node, updateId) {
|
|
|
970
983
|
limit 1
|
|
971
984
|
`;
|
|
972
985
|
}
|
|
973
|
-
function
|
|
986
|
+
function contractDetailsQuery(node, contractIds) {
|
|
974
987
|
const relations = pqsCoreRelations(node);
|
|
975
|
-
const
|
|
988
|
+
const quotedIds = contractIds
|
|
989
|
+
.map((contractId) => `'${escapeSqlLiteral(normalizeByteaHex(contractId))}'`)
|
|
990
|
+
.join(', ');
|
|
976
991
|
const contractTemplateId = contractTemplateIdentifierExpression('contract_tpe_row');
|
|
977
992
|
return `
|
|
978
993
|
with selected_contract as (
|
|
@@ -988,8 +1003,7 @@ function contractDetailQuery(node, contractId) {
|
|
|
988
1003
|
on contract_tpe_row.pk = contract_row.tpe_pk
|
|
989
1004
|
left join ${relations.packages} package_row
|
|
990
1005
|
on package_row.pk = contract_row.package_pk
|
|
991
|
-
where contract_row.contract_id
|
|
992
|
-
limit 1
|
|
1006
|
+
where contract_row.contract_id in (${quotedIds})
|
|
993
1007
|
)
|
|
994
1008
|
select
|
|
995
1009
|
contract_row.contract_id,
|
|
@@ -1009,6 +1023,9 @@ function contractDetailQuery(node, contractId) {
|
|
|
1009
1023
|
on archived_tx.ix = contract_row.archived_at_ix
|
|
1010
1024
|
`;
|
|
1011
1025
|
}
|
|
1026
|
+
function contractDetailQuery(node, contractId) {
|
|
1027
|
+
return contractDetailsQuery(node, [contractId]);
|
|
1028
|
+
}
|
|
1012
1029
|
function tokenRowsQuery(node, limit, templateIds, templatePatterns = []) {
|
|
1013
1030
|
const relations = pqsCoreRelations(node);
|
|
1014
1031
|
const normalizedLimit = Number.isFinite(limit) && Number(limit) > 0 ? Math.trunc(limit) : TOKEN_TRANSFER_CACHE_LIMIT;
|
|
@@ -1054,6 +1071,21 @@ function tokenRowsQuery(node, limit, templateIds, templatePatterns = []) {
|
|
|
1054
1071
|
limit ${normalizedLimit}
|
|
1055
1072
|
`;
|
|
1056
1073
|
}
|
|
1074
|
+
function nativeAmuletSupportQuery(node) {
|
|
1075
|
+
const relations = pqsCoreRelations(node);
|
|
1076
|
+
const templateIds = NATIVE_AMULET_SUPPORT_TEMPLATE_IDS
|
|
1077
|
+
.map((templateId) => `'${escapeSqlLiteral(templateId)}'`)
|
|
1078
|
+
.join(',\n ');
|
|
1079
|
+
const templateId = contractTemplateIdentifierExpression('contract_tpe_row');
|
|
1080
|
+
return `
|
|
1081
|
+
select ${templateId} as template_id
|
|
1082
|
+
from ${relations.contractTpe} contract_tpe_row
|
|
1083
|
+
where ${templateId} in (
|
|
1084
|
+
${templateIds}
|
|
1085
|
+
)
|
|
1086
|
+
limit 1
|
|
1087
|
+
`;
|
|
1088
|
+
}
|
|
1057
1089
|
function recentCip112MovementUpdateIdsQuery(node, limit) {
|
|
1058
1090
|
const relations = pqsCoreRelations(node);
|
|
1059
1091
|
const normalizedLimit = Number.isFinite(limit) && Number(limit) > 0 ? Math.trunc(limit) : TOKEN_TRANSFER_CACHE_LIMIT;
|
|
@@ -1205,12 +1237,19 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
1205
1237
|
const rawMetaRows = await this.queryRecentUpdateMetaRows(node, query, normalizedLimit, before, after, parties, templates, partyMode, hideSplice);
|
|
1206
1238
|
const hasMoreInQuery = rawMetaRows.length > normalizedLimit;
|
|
1207
1239
|
const trimmedMetaRows = rawMetaRows.slice(0, normalizedLimit);
|
|
1208
|
-
const orderedUpdates = (useAfterCursor ? [...trimmedMetaRows].reverse() : trimmedMetaRows).
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1240
|
+
const orderedUpdates = (useAfterCursor ? [...trimmedMetaRows].reverse() : trimmedMetaRows).flatMap((row) => {
|
|
1241
|
+
if (typeof row.update_id !== 'string') {
|
|
1242
|
+
return [];
|
|
1243
|
+
}
|
|
1244
|
+
return [
|
|
1245
|
+
{
|
|
1246
|
+
eventOffset: this.extractEventOffset(row),
|
|
1247
|
+
rawUpdateId: row.update_id,
|
|
1248
|
+
updateId: this.normalizeUpdateId(row.update_id),
|
|
1249
|
+
recordTime: row.record_time ?? null,
|
|
1250
|
+
},
|
|
1251
|
+
];
|
|
1252
|
+
});
|
|
1214
1253
|
if (orderedUpdates.length === 0) {
|
|
1215
1254
|
return {
|
|
1216
1255
|
nodeId: node.id,
|
|
@@ -1276,7 +1315,7 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
1276
1315
|
if (nodesToFetch.length === 0) {
|
|
1277
1316
|
break;
|
|
1278
1317
|
}
|
|
1279
|
-
const
|
|
1318
|
+
const settled = await Promise.allSettled(nodesToFetch.map((state) => this.fetchRecentUpdates(state.node, {
|
|
1280
1319
|
limit: pageSize,
|
|
1281
1320
|
parties,
|
|
1282
1321
|
templates,
|
|
@@ -1284,8 +1323,25 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
1284
1323
|
hideSplice,
|
|
1285
1324
|
before: state.nextBefore,
|
|
1286
1325
|
})));
|
|
1287
|
-
|
|
1326
|
+
const successfulResponses = [];
|
|
1327
|
+
settled.forEach((result, index) => {
|
|
1288
1328
|
const state = nodesToFetch[index];
|
|
1329
|
+
if (!state) {
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (result.status !== 'fulfilled') {
|
|
1333
|
+
state.exhausted = true;
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
successfulResponses.push({
|
|
1337
|
+
state,
|
|
1338
|
+
response: result.value,
|
|
1339
|
+
});
|
|
1340
|
+
});
|
|
1341
|
+
if (successfulResponses.length === 0) {
|
|
1342
|
+
throw settled.find((result) => result.status === 'rejected')?.reason;
|
|
1343
|
+
}
|
|
1344
|
+
successfulResponses.forEach(({ response, state }) => {
|
|
1289
1345
|
for (const update of response.updates) {
|
|
1290
1346
|
mergedUpdatesByKey.set(`${state.node.id}:${update.eventOffset}:${update.updateId}`, {
|
|
1291
1347
|
nodeId: state.node.id,
|
|
@@ -1398,12 +1454,17 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
1398
1454
|
}
|
|
1399
1455
|
}
|
|
1400
1456
|
const rawRows = Array.from(dedupedRows.values());
|
|
1401
|
-
const rawUpdateIds = rawRows
|
|
1457
|
+
const rawUpdateIds = rawRows
|
|
1458
|
+
.map((row) => row.update_id)
|
|
1459
|
+
.filter((updateId) => typeof updateId === 'string');
|
|
1402
1460
|
const partiesByUpdateId = rawUpdateIds.length > 0
|
|
1403
1461
|
? await this.fetchPartiesByUpdateId(node, client.query.bind(client), rawUpdateIds)
|
|
1404
1462
|
: new Map();
|
|
1405
1463
|
return rawRows
|
|
1406
1464
|
.map((row) => {
|
|
1465
|
+
if (typeof row.update_id !== 'string') {
|
|
1466
|
+
return null;
|
|
1467
|
+
}
|
|
1407
1468
|
const normalizedUpdateId = this.normalizeUpdateId(row.update_id);
|
|
1408
1469
|
const eventOffset = this.extractEventOffset(row);
|
|
1409
1470
|
const exact = this.isExactMatch(query, [eventOffset, normalizedUpdateId]);
|
|
@@ -1704,29 +1765,52 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
1704
1765
|
}
|
|
1705
1766
|
async fetchActiveParties(nodes) {
|
|
1706
1767
|
return {
|
|
1707
|
-
nodes: await Promise.all(nodes.map(async (node) => (
|
|
1768
|
+
nodes: await Promise.all(nodes.map(async (node) => this.buildActivePartiesEntry(node))),
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
async buildActivePartiesEntry(node) {
|
|
1772
|
+
try {
|
|
1773
|
+
return {
|
|
1708
1774
|
nodeId: node.id,
|
|
1709
1775
|
label: node.label,
|
|
1710
1776
|
mode: node.mode,
|
|
1711
1777
|
parties: await this.fetchActivePartiesForNode(node),
|
|
1712
|
-
|
|
1713
|
-
|
|
1778
|
+
activePartiesStatus: 'ok',
|
|
1779
|
+
activePartiesError: null,
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
catch (error) {
|
|
1783
|
+
return {
|
|
1784
|
+
nodeId: node.id,
|
|
1785
|
+
label: node.label,
|
|
1786
|
+
mode: node.mode,
|
|
1787
|
+
parties: [],
|
|
1788
|
+
activePartiesStatus: 'pqs_error',
|
|
1789
|
+
activePartiesError: error instanceof Error ? error.message : 'Unknown error',
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1714
1792
|
}
|
|
1715
1793
|
async fetchPartyDetail(nodes, partyId) {
|
|
1716
1794
|
const normalizedPartyId = this.normalizePartyIdentifier(partyId);
|
|
1717
1795
|
const grpcOperationsService = this.grpcOperationsService;
|
|
1718
|
-
const activePartiesByNode = await Promise.
|
|
1796
|
+
const activePartiesByNode = (await Promise.allSettled(nodes.map(async (node) => ({
|
|
1719
1797
|
node,
|
|
1720
1798
|
parties: await this.fetchActivePartiesForNode(node),
|
|
1721
|
-
})))
|
|
1722
|
-
|
|
1799
|
+
}))))
|
|
1800
|
+
.filter((result) => result.status === 'fulfilled')
|
|
1801
|
+
.map((result) => result.value);
|
|
1802
|
+
const recentUpdatesByNode = (await Promise.allSettled(nodes.map(async (node) => ({
|
|
1723
1803
|
node,
|
|
1724
1804
|
updates: await this.fetchPartyRecentUpdatesForNode(node, normalizedPartyId, 10),
|
|
1725
|
-
})))
|
|
1726
|
-
|
|
1805
|
+
}))))
|
|
1806
|
+
.filter((result) => result.status === 'fulfilled')
|
|
1807
|
+
.map((result) => result.value);
|
|
1808
|
+
const recentContractsByNode = (await Promise.allSettled(nodes.map(async (node) => ({
|
|
1727
1809
|
node,
|
|
1728
1810
|
contracts: await this.fetchPartyRecentContractsForNode(node, normalizedPartyId, 10),
|
|
1729
|
-
})))
|
|
1811
|
+
}))))
|
|
1812
|
+
.filter((result) => result.status === 'fulfilled')
|
|
1813
|
+
.map((result) => result.value);
|
|
1730
1814
|
const localPartiesByNode = grpcOperationsService
|
|
1731
1815
|
? await Promise.all(nodes.map(async (node) => {
|
|
1732
1816
|
try {
|
|
@@ -1834,10 +1918,12 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
1834
1918
|
async fetchNamespaceDetail(nodes, namespaceId) {
|
|
1835
1919
|
const normalizedNamespaceId = namespaceId.trim();
|
|
1836
1920
|
const grpcOperationsService = this.grpcOperationsService;
|
|
1837
|
-
const activePartiesByNode = await Promise.
|
|
1921
|
+
const activePartiesByNode = (await Promise.allSettled(nodes.map(async (node) => ({
|
|
1838
1922
|
node,
|
|
1839
1923
|
parties: (await this.fetchActivePartiesForNode(node)).filter((partyId) => this.extractNamespaceIdentifier(partyId) === normalizedNamespaceId),
|
|
1840
|
-
})))
|
|
1924
|
+
}))))
|
|
1925
|
+
.filter((result) => result.status === 'fulfilled')
|
|
1926
|
+
.map((result) => result.value);
|
|
1841
1927
|
const localPartiesByNode = grpcOperationsService
|
|
1842
1928
|
? await Promise.all(nodes.map(async (node) => {
|
|
1843
1929
|
try {
|
|
@@ -1930,7 +2016,9 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
1930
2016
|
async fetchNamespaceParties(nodes, namespaceId, options) {
|
|
1931
2017
|
const normalizedNamespaceId = namespaceId.trim();
|
|
1932
2018
|
const matchingParties = new Set();
|
|
1933
|
-
const activePartiesByNode = await Promise.
|
|
2019
|
+
const activePartiesByNode = (await Promise.allSettled(nodes.map(async (node) => (await this.fetchActivePartiesForNode(node)).filter((partyId) => this.extractNamespaceIdentifier(partyId) === normalizedNamespaceId))))
|
|
2020
|
+
.filter((result) => result.status === 'fulfilled')
|
|
2021
|
+
.map((result) => result.value);
|
|
1934
2022
|
for (const parties of activePartiesByNode) {
|
|
1935
2023
|
for (const partyId of parties) {
|
|
1936
2024
|
matchingParties.add(partyId);
|
|
@@ -2026,7 +2114,7 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2026
2114
|
if (nodesToFetch.length === 0) {
|
|
2027
2115
|
break;
|
|
2028
2116
|
}
|
|
2029
|
-
const
|
|
2117
|
+
const settled = await Promise.allSettled(nodesToFetch.map((state) => this.fetchNodeContracts(state.node, {
|
|
2030
2118
|
limit: pageSize,
|
|
2031
2119
|
before: state.nextBefore,
|
|
2032
2120
|
parties: activePartyFilters.length > 0 ? activePartyFilters : undefined,
|
|
@@ -2034,8 +2122,25 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2034
2122
|
partyMode,
|
|
2035
2123
|
hideSplice,
|
|
2036
2124
|
})));
|
|
2037
|
-
|
|
2125
|
+
const successfulResponses = [];
|
|
2126
|
+
settled.forEach((result, index) => {
|
|
2038
2127
|
const state = nodesToFetch[index];
|
|
2128
|
+
if (!state) {
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
if (result.status !== 'fulfilled') {
|
|
2132
|
+
state.exhausted = true;
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
successfulResponses.push({
|
|
2136
|
+
state,
|
|
2137
|
+
response: result.value,
|
|
2138
|
+
});
|
|
2139
|
+
});
|
|
2140
|
+
if (successfulResponses.length === 0) {
|
|
2141
|
+
throw settled.find((result) => result.status === 'rejected')?.reason;
|
|
2142
|
+
}
|
|
2143
|
+
successfulResponses.forEach(({ response, state }) => {
|
|
2039
2144
|
for (const contract of response.contracts) {
|
|
2040
2145
|
mergedContractsByKey.set(`${state.node.id}:${contract.contractId}`, {
|
|
2041
2146
|
nodeId: state.node.id,
|
|
@@ -2116,12 +2221,29 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2116
2221
|
if (nodesToFetch.length === 0) {
|
|
2117
2222
|
break;
|
|
2118
2223
|
}
|
|
2119
|
-
const
|
|
2224
|
+
const settled = await Promise.allSettled(nodesToFetch.map((state) => this.fetchPartyContractsForNode(state.node, normalizedPartyId, {
|
|
2120
2225
|
limit: pageSize,
|
|
2121
2226
|
before: state.nextBefore,
|
|
2122
2227
|
})));
|
|
2123
|
-
|
|
2228
|
+
const successfulResponses = [];
|
|
2229
|
+
settled.forEach((result, index) => {
|
|
2124
2230
|
const state = nodesToFetch[index];
|
|
2231
|
+
if (!state) {
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
if (result.status !== 'fulfilled') {
|
|
2235
|
+
state.exhausted = true;
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
successfulResponses.push({
|
|
2239
|
+
state,
|
|
2240
|
+
response: result.value,
|
|
2241
|
+
});
|
|
2242
|
+
});
|
|
2243
|
+
if (successfulResponses.length === 0) {
|
|
2244
|
+
throw settled.find((result) => result.status === 'rejected')?.reason;
|
|
2245
|
+
}
|
|
2246
|
+
successfulResponses.forEach(({ response, state }) => {
|
|
2125
2247
|
for (const contract of response.contracts) {
|
|
2126
2248
|
mergedContractsByKey.set(`${contract.nodeId}:${contract.contractId}`, contract);
|
|
2127
2249
|
}
|
|
@@ -2177,18 +2299,18 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2177
2299
|
return this.paginateTokens(filteredTokens.sort(compareGlobalTokens), limit, options);
|
|
2178
2300
|
}
|
|
2179
2301
|
async fetchLatestTokenTransfers(nodes, limit = 25, options) {
|
|
2180
|
-
const mergedTransfers = this.
|
|
2302
|
+
const mergedTransfers = this.filterTokenTransfers(await this.loadMergedTokenTransfers(nodes), options);
|
|
2181
2303
|
return this.paginateTokenTransfers(mergedTransfers, limit, options);
|
|
2182
2304
|
}
|
|
2183
2305
|
async fetchTokenTransfers(nodes, tokenId, limit = 25, options) {
|
|
2184
2306
|
const normalizedTokenId = this.normalizeTokenId(tokenId);
|
|
2185
2307
|
await this.findObservedToken(nodes, normalizedTokenId);
|
|
2186
|
-
const mergedTransfers = this.
|
|
2308
|
+
const mergedTransfers = this.filterTokenTransfers(await this.loadMergedTokenTransfers(nodes), options);
|
|
2187
2309
|
const directTransfers = mergedTransfers.filter((transfer) => transfer.tokenId === normalizedTokenId);
|
|
2188
2310
|
if (directTransfers.length > 0) {
|
|
2189
2311
|
return this.paginateTokenTransfers(directTransfers, limit, options);
|
|
2190
2312
|
}
|
|
2191
|
-
const recoveredTransfers = this.
|
|
2313
|
+
const recoveredTransfers = this.filterTokenTransfers(await this.recoverTokenTransfersFromActiveHolders(nodes, normalizedTokenId), options);
|
|
2192
2314
|
return this.paginateTokenTransfers(recoveredTransfers, limit, options);
|
|
2193
2315
|
}
|
|
2194
2316
|
async fetchTokenTransferDetail(nodes, transferId) {
|
|
@@ -2328,10 +2450,11 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2328
2450
|
node,
|
|
2329
2451
|
transfers: await this.loadCachedTokenTransfers(node),
|
|
2330
2452
|
})));
|
|
2331
|
-
const
|
|
2453
|
+
const successfulRefreshes = refreshResults.filter((result) => result.status === 'fulfilled');
|
|
2454
|
+
const successfulTransfers = successfulRefreshes
|
|
2332
2455
|
.filter((result) => result.status === 'fulfilled')
|
|
2333
2456
|
.flatMap((result) => result.value.transfers);
|
|
2334
|
-
if (
|
|
2457
|
+
if (successfulRefreshes.length === 0) {
|
|
2335
2458
|
const firstFailure = refreshResults.find((result) => result.status === 'rejected');
|
|
2336
2459
|
if (firstFailure) {
|
|
2337
2460
|
throw firstFailure.reason;
|
|
@@ -2351,9 +2474,9 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2351
2474
|
.filter((holder) => holder.tokenId === tokenId && holder.contractId !== null);
|
|
2352
2475
|
const recoveredTransfers = [];
|
|
2353
2476
|
const seenContracts = new Set();
|
|
2477
|
+
const contractIdsByNode = new Map();
|
|
2354
2478
|
for (const holder of candidateHolders) {
|
|
2355
|
-
|
|
2356
|
-
if (!node || !holder.contractId) {
|
|
2479
|
+
if (!nodeById.has(holder.nodeId) || !holder.contractId) {
|
|
2357
2480
|
continue;
|
|
2358
2481
|
}
|
|
2359
2482
|
const dedupeKey = `${holder.nodeId}\u0000${holder.contractId}`;
|
|
@@ -2361,19 +2484,38 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2361
2484
|
continue;
|
|
2362
2485
|
}
|
|
2363
2486
|
seenContracts.add(dedupeKey);
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2487
|
+
const nodeContractIds = contractIdsByNode.get(holder.nodeId);
|
|
2488
|
+
if (nodeContractIds) {
|
|
2489
|
+
nodeContractIds.push(holder.contractId);
|
|
2490
|
+
}
|
|
2491
|
+
else {
|
|
2492
|
+
contractIdsByNode.set(holder.nodeId, [holder.contractId]);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
for (const [nodeId, contractIds] of contractIdsByNode) {
|
|
2496
|
+
const node = nodeById.get(nodeId);
|
|
2497
|
+
if (!node) {
|
|
2368
2498
|
continue;
|
|
2369
2499
|
}
|
|
2370
|
-
const
|
|
2371
|
-
const
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2500
|
+
const client = this.clientFactory.getClient(node);
|
|
2501
|
+
const contractRows = await this.fetchContractDetailsByIds(node, client.query.bind(client), contractIds);
|
|
2502
|
+
const eventsByUpdateId = await this.fetchEventsByUpdateIds(node, client.query.bind(client), contractRows
|
|
2503
|
+
.map((contractRow) => contractRow.created_update_id)
|
|
2504
|
+
.filter((updateId) => typeof updateId === 'string'));
|
|
2505
|
+
for (const contractRow of contractRows) {
|
|
2506
|
+
if (typeof contractRow.created_update_id !== 'string') {
|
|
2507
|
+
continue;
|
|
2508
|
+
}
|
|
2509
|
+
const events = eventsByUpdateId.get(contractRow.created_update_id) ?? [];
|
|
2510
|
+
const inferredTransfers = this.extractCip112TokenMovements(node, {
|
|
2511
|
+
update_id: contractRow.created_update_id,
|
|
2512
|
+
event_offset: contractRow.created_event_offset,
|
|
2513
|
+
record_time: contractRow.created_record_time,
|
|
2514
|
+
}, events, {
|
|
2515
|
+
canonicalShareTokenId: tokenId,
|
|
2516
|
+
}).filter((transfer) => transfer.tokenId === tokenId);
|
|
2517
|
+
recoveredTransfers.push(...inferredTransfers);
|
|
2518
|
+
}
|
|
2377
2519
|
}
|
|
2378
2520
|
return this.mergeTokenTransfers(recoveredTransfers);
|
|
2379
2521
|
}
|
|
@@ -2412,13 +2554,15 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2412
2554
|
transfers: pagedTransfers,
|
|
2413
2555
|
};
|
|
2414
2556
|
}
|
|
2415
|
-
|
|
2557
|
+
filterTokenTransfers(transfers, options) {
|
|
2416
2558
|
const activeFromParties = normalizePartyFilters(options?.fromParties);
|
|
2417
2559
|
const activeToParties = normalizePartyFilters(options?.toParties);
|
|
2560
|
+
const activeMovementTypes = this.normalizeTokenTransferMovementTypeFilters(options?.movementTypes);
|
|
2418
2561
|
const amountGt = options?.amountGt?.trim() || null;
|
|
2419
2562
|
const amountLt = options?.amountLt?.trim() || null;
|
|
2420
2563
|
if (activeFromParties.length === 0 &&
|
|
2421
2564
|
activeToParties.length === 0 &&
|
|
2565
|
+
activeMovementTypes.length === 0 &&
|
|
2422
2566
|
amountGt === null &&
|
|
2423
2567
|
amountLt === null) {
|
|
2424
2568
|
return transfers;
|
|
@@ -2428,11 +2572,13 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2428
2572
|
(transfer.sender !== null && activeFromParties.includes(transfer.sender));
|
|
2429
2573
|
const matchesTo = activeToParties.length === 0 ||
|
|
2430
2574
|
(transfer.receiver !== null && activeToParties.includes(transfer.receiver));
|
|
2575
|
+
const matchesMovementType = activeMovementTypes.length === 0 ||
|
|
2576
|
+
activeMovementTypes.includes(this.effectiveTokenTransferMovementType(transfer).toLowerCase());
|
|
2431
2577
|
const matchesAmountGt = amountGt === null ||
|
|
2432
2578
|
(transfer.amount !== null && this.compareNumericStrings(transfer.amount, amountGt) > 0);
|
|
2433
2579
|
const matchesAmountLt = amountLt === null ||
|
|
2434
2580
|
(transfer.amount !== null && this.compareNumericStrings(transfer.amount, amountLt) < 0);
|
|
2435
|
-
return matchesFrom && matchesTo && matchesAmountGt && matchesAmountLt;
|
|
2581
|
+
return matchesFrom && matchesTo && matchesMovementType && matchesAmountGt && matchesAmountLt;
|
|
2436
2582
|
});
|
|
2437
2583
|
}
|
|
2438
2584
|
async fetchUpdateDetail(node, eventOffset) {
|
|
@@ -2493,20 +2639,27 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2493
2639
|
const client = this.clientFactory.getClient(node);
|
|
2494
2640
|
const result = await client.query(pqsPartyRecentUpdatesQuery(node, partyId, limit));
|
|
2495
2641
|
const rows = result.rows ?? [];
|
|
2496
|
-
const rawUpdateIds = rows
|
|
2642
|
+
const rawUpdateIds = rows
|
|
2643
|
+
.map((row) => row.update_id)
|
|
2644
|
+
.filter((updateId) => typeof updateId === 'string');
|
|
2497
2645
|
const partiesByUpdateId = rawUpdateIds.length > 0
|
|
2498
2646
|
? await this.fetchPartiesByUpdateId(node, client.query.bind(client), rawUpdateIds)
|
|
2499
2647
|
: new Map();
|
|
2500
|
-
return rows.
|
|
2648
|
+
return rows.flatMap((row) => {
|
|
2649
|
+
if (typeof row.update_id !== 'string') {
|
|
2650
|
+
return [];
|
|
2651
|
+
}
|
|
2501
2652
|
const updateId = this.normalizeUpdateId(row.update_id);
|
|
2502
|
-
return
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2653
|
+
return [
|
|
2654
|
+
{
|
|
2655
|
+
nodeId: node.id,
|
|
2656
|
+
label: node.label,
|
|
2657
|
+
eventOffset: this.extractEventOffset(row),
|
|
2658
|
+
updateId,
|
|
2659
|
+
recordTime: typeof row.record_time === 'string' ? row.record_time : null,
|
|
2660
|
+
parties: partiesByUpdateId.get(updateId) ?? [],
|
|
2661
|
+
},
|
|
2662
|
+
];
|
|
2510
2663
|
});
|
|
2511
2664
|
}
|
|
2512
2665
|
async fetchPartyRecentContractsForNode(node, partyId, limit) {
|
|
@@ -2586,16 +2739,15 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2586
2739
|
return cached.tokens;
|
|
2587
2740
|
}
|
|
2588
2741
|
const useGrpcHoldingViews = node.mode === 'pqs_with_grpc' && this.grpcOperationsService !== undefined;
|
|
2589
|
-
const [pqsTokens, grpcTokens] = await Promise.all([
|
|
2590
|
-
this.fetchObservedTokensForNode(node, TOKEN_TRANSFER_CACHE_LIMIT,
|
|
2591
|
-
includeCip112: !useGrpcHoldingViews,
|
|
2592
|
-
}),
|
|
2742
|
+
const [pqsTokens, grpcTokens, builtinTokens] = await Promise.all([
|
|
2743
|
+
this.fetchObservedTokensForNode(node, TOKEN_TRANSFER_CACHE_LIMIT),
|
|
2593
2744
|
useGrpcHoldingViews
|
|
2594
2745
|
? this.grpcOperationsService.fetchHoldingV2Tokens(node)
|
|
2595
2746
|
: Promise.resolve([]),
|
|
2747
|
+
this.fetchBuiltinTokensForNode(node),
|
|
2596
2748
|
]);
|
|
2597
2749
|
const dedupedTokens = new Map();
|
|
2598
|
-
for (const token of [...pqsTokens, ...grpcTokens]) {
|
|
2750
|
+
for (const token of [...pqsTokens, ...grpcTokens, ...builtinTokens]) {
|
|
2599
2751
|
if (!dedupedTokens.has(token.tokenId)) {
|
|
2600
2752
|
dedupedTokens.set(token.tokenId, token);
|
|
2601
2753
|
}
|
|
@@ -2607,6 +2759,31 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2607
2759
|
});
|
|
2608
2760
|
return tokens;
|
|
2609
2761
|
}
|
|
2762
|
+
async fetchBuiltinTokensForNode(node) {
|
|
2763
|
+
const client = this.clientFactory.getClient(node);
|
|
2764
|
+
const result = await client.query(nativeAmuletSupportQuery(node));
|
|
2765
|
+
const rows = Array.isArray(result.rows) ? result.rows : [];
|
|
2766
|
+
const hasNativeAmuletSupport = rows.some((row) => {
|
|
2767
|
+
if (!row || typeof row !== 'object') {
|
|
2768
|
+
return false;
|
|
2769
|
+
}
|
|
2770
|
+
const templateId = row.template_id;
|
|
2771
|
+
return (typeof templateId === 'string' &&
|
|
2772
|
+
NATIVE_AMULET_SUPPORT_TEMPLATE_IDS.includes(this.normalizeTemplateIdentifier(templateId)));
|
|
2773
|
+
});
|
|
2774
|
+
if (!hasNativeAmuletSupport) {
|
|
2775
|
+
return [];
|
|
2776
|
+
}
|
|
2777
|
+
return [
|
|
2778
|
+
{
|
|
2779
|
+
tokenId: CANTON_COIN_TOKEN_ID,
|
|
2780
|
+
name: CANTON_COIN_TOKEN_NAME,
|
|
2781
|
+
symbol: null,
|
|
2782
|
+
issuer: null,
|
|
2783
|
+
source: 'pqs',
|
|
2784
|
+
},
|
|
2785
|
+
];
|
|
2786
|
+
}
|
|
2610
2787
|
async fetchObservedTokensForNode(node, limit, options) {
|
|
2611
2788
|
const client = this.clientFactory.getClient(node);
|
|
2612
2789
|
const includeCip112 = options?.includeCip112 ?? true;
|
|
@@ -2792,17 +2969,93 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2792
2969
|
const result = await client.query(recentCip112MovementUpdateIdsQuery(node, limit));
|
|
2793
2970
|
const rows = result.rows ?? [];
|
|
2794
2971
|
const transfers = [];
|
|
2972
|
+
const rawUpdateIds = rows
|
|
2973
|
+
.map((row) => (typeof row.update_id === 'string' ? row.update_id : null))
|
|
2974
|
+
.filter((updateId) => Boolean(updateId && this.isHexTokenMovementUpdateId(updateId)));
|
|
2975
|
+
const eventsByUpdateId = await this.fetchEventsByUpdateIds(node, client.query.bind(client), rawUpdateIds);
|
|
2795
2976
|
for (const row of rows) {
|
|
2796
2977
|
const rawUpdateId = typeof row.update_id === 'string' ? row.update_id : null;
|
|
2797
2978
|
if (!rawUpdateId || !this.isHexTokenMovementUpdateId(rawUpdateId)) {
|
|
2798
2979
|
continue;
|
|
2799
2980
|
}
|
|
2800
|
-
const events =
|
|
2801
|
-
transfers.push(...this.
|
|
2981
|
+
const events = eventsByUpdateId.get(rawUpdateId) ?? [];
|
|
2982
|
+
transfers.push(...this.extractCip112TokenMovements(node, row, events));
|
|
2983
|
+
}
|
|
2984
|
+
return transfers;
|
|
2985
|
+
}
|
|
2986
|
+
extractCip112TokenMovements(node, update, events, options) {
|
|
2987
|
+
const standardTransfers = this.extractStandardCip112TokenMovements(node, update, events, options);
|
|
2988
|
+
if (standardTransfers.length > 0) {
|
|
2989
|
+
return standardTransfers;
|
|
2990
|
+
}
|
|
2991
|
+
return this.inferCip112TokenMovements(node, update, events, options);
|
|
2992
|
+
}
|
|
2993
|
+
extractStandardCip112TokenMovements(node, update, events, options) {
|
|
2994
|
+
const createTokensById = new Map();
|
|
2995
|
+
for (const event of events) {
|
|
2996
|
+
if (event.eventKind !== 'create' ||
|
|
2997
|
+
!this.isCip112TemplateId(event.templateId) ||
|
|
2998
|
+
event.createData?.status !== 'decoded') {
|
|
2999
|
+
continue;
|
|
3000
|
+
}
|
|
3001
|
+
const token = this.extractObservedTokenSummary(event.templateId, event.createData);
|
|
3002
|
+
if (token) {
|
|
3003
|
+
createTokensById.set(token.tokenId, token);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
const transfers = [];
|
|
3007
|
+
const eventLogExercises = events.filter((event) => event.eventKind === 'non_consuming_exercise' &&
|
|
3008
|
+
(event.choice === 'HoldingsChange' || event.choice === 'EventLog_HoldingsChange') &&
|
|
3009
|
+
event.exerciseData?.argument.status === 'decoded' &&
|
|
3010
|
+
this.isRecordValue(event.exerciseData.argument.value));
|
|
3011
|
+
for (const event of eventLogExercises) {
|
|
3012
|
+
const argument = event.exerciseData?.argument;
|
|
3013
|
+
if (!argument || argument.status !== 'decoded' || !this.isRecordValue(argument.value)) {
|
|
3014
|
+
continue;
|
|
3015
|
+
}
|
|
3016
|
+
const account = this.findRecordField(argument.value, 'account');
|
|
3017
|
+
const transferLegs = this.readListField(argument.value, 'transferLegSides');
|
|
3018
|
+
const admin = this.readScalarField(argument.value, 'admin');
|
|
3019
|
+
if (!account || !admin || transferLegs.length === 0) {
|
|
3020
|
+
continue;
|
|
3021
|
+
}
|
|
3022
|
+
for (const leg of transferLegs) {
|
|
3023
|
+
if (!this.isRecordValue(leg)) {
|
|
3024
|
+
continue;
|
|
3025
|
+
}
|
|
3026
|
+
const side = this.readConstructorField(leg, 'side');
|
|
3027
|
+
if (side !== 'SenderSide') {
|
|
3028
|
+
continue;
|
|
3029
|
+
}
|
|
3030
|
+
const transferLegId = this.readScalarField(leg, 'transferLegId');
|
|
3031
|
+
const amount = this.readScalarField(leg, 'amount');
|
|
3032
|
+
const instrumentId = this.readScalarField(leg, 'instrumentId');
|
|
3033
|
+
const otherSideAccount = this.findRecordField(leg, 'otherside');
|
|
3034
|
+
if (!transferLegId || !instrumentId || !otherSideAccount) {
|
|
3035
|
+
continue;
|
|
3036
|
+
}
|
|
3037
|
+
const token = this.resolveEventLogTokenSummary(admin, instrumentId, createTokensById, options?.canonicalShareTokenId ?? null);
|
|
3038
|
+
const movementType = this.classifyEventLogMovementType(account, otherSideAccount, admin, instrumentId);
|
|
3039
|
+
transfers.push({
|
|
3040
|
+
rowId: this.buildTokenTransferLegRowId(typeof update.update_id === 'string' ? this.normalizeUpdateId(update.update_id) : '', event.eventId, transferLegId, movementType),
|
|
3041
|
+
movementType,
|
|
3042
|
+
source: 'pqs',
|
|
3043
|
+
nodeId: node.id,
|
|
3044
|
+
label: node.label,
|
|
3045
|
+
tokenId: token.tokenId,
|
|
3046
|
+
tokenName: token.name,
|
|
3047
|
+
amount,
|
|
3048
|
+
sender: this.readAccountParty(account),
|
|
3049
|
+
receiver: this.readAccountParty(otherSideAccount),
|
|
3050
|
+
eventOffset: this.normalizeOptionalScalar(update.event_offset) ?? '',
|
|
3051
|
+
updateId: typeof update.update_id === 'string' ? this.normalizeUpdateId(update.update_id) : '',
|
|
3052
|
+
recordTime: typeof update.record_time === 'string' ? update.record_time : null,
|
|
3053
|
+
});
|
|
3054
|
+
}
|
|
2802
3055
|
}
|
|
2803
3056
|
return transfers;
|
|
2804
3057
|
}
|
|
2805
|
-
inferCip112TokenMovements(node, update, events) {
|
|
3058
|
+
inferCip112TokenMovements(node, update, events, options) {
|
|
2806
3059
|
const createEvents = events.filter((event) => event.eventKind === 'create' &&
|
|
2807
3060
|
this.isCip112TemplateId(event.templateId) &&
|
|
2808
3061
|
event.createData?.status === 'decoded' &&
|
|
@@ -2822,10 +3075,11 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2822
3075
|
? this.readScalarField(event.createData.value, 'owner')
|
|
2823
3076
|
: null)
|
|
2824
3077
|
.find((owner) => Boolean(owner));
|
|
2825
|
-
|
|
3078
|
+
const shouldEmitCreateMovements = transferExercises.length > 0 || (transferExercises.length === 0 && mintExercises.length === 0);
|
|
3079
|
+
if (shouldEmitCreateMovements) {
|
|
2826
3080
|
for (const event of createEvents) {
|
|
2827
3081
|
const movementType = 'Create';
|
|
2828
|
-
const transfer = this.buildInferredCip112Movement(node, update, event, movementType, !this.isShareLikeCip112Template(event.templateId) ? shareOwnerFallback : null);
|
|
3082
|
+
const transfer = this.buildInferredCip112Movement(node, update, event, movementType, !this.isShareLikeCip112Template(event.templateId) ? shareOwnerFallback : null, options?.canonicalShareTokenId);
|
|
2829
3083
|
if (transfer) {
|
|
2830
3084
|
transfers.push(transfer);
|
|
2831
3085
|
}
|
|
@@ -2844,7 +3098,7 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2844
3098
|
}
|
|
2845
3099
|
return transfers;
|
|
2846
3100
|
}
|
|
2847
|
-
buildInferredCip112Movement(node, update, event, movementType, receiverFallback = null) {
|
|
3101
|
+
buildInferredCip112Movement(node, update, event, movementType, receiverFallback = null, canonicalShareTokenId = null) {
|
|
2848
3102
|
if (event.createData?.status !== 'decoded') {
|
|
2849
3103
|
return null;
|
|
2850
3104
|
}
|
|
@@ -2856,13 +3110,16 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2856
3110
|
if (!token) {
|
|
2857
3111
|
return null;
|
|
2858
3112
|
}
|
|
3113
|
+
const tokenId = canonicalShareTokenId && this.isShareLikeCip112Template(event.templateId)
|
|
3114
|
+
? canonicalShareTokenId
|
|
3115
|
+
: token.tokenId;
|
|
2859
3116
|
return {
|
|
2860
3117
|
rowId: this.buildTokenMovementRowId(typeof update.update_id === 'string' ? update.update_id : this.normalizeUpdateId(''), event.eventId, event.templateId, movementType),
|
|
2861
3118
|
movementType,
|
|
2862
3119
|
source: INFERRED_HOLDING_V2_SOURCE,
|
|
2863
3120
|
nodeId: node.id,
|
|
2864
3121
|
label: node.label,
|
|
2865
|
-
tokenId
|
|
3122
|
+
tokenId,
|
|
2866
3123
|
tokenName: token.name,
|
|
2867
3124
|
amount: this.readScalarField(record, 'amount'),
|
|
2868
3125
|
sender: movementType === 'Mint' ? this.readScalarField(record, 'issuer') : null,
|
|
@@ -2964,6 +3221,17 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2964
3221
|
normalizeTokenId(tokenId) {
|
|
2965
3222
|
return tokenId.trim();
|
|
2966
3223
|
}
|
|
3224
|
+
normalizeTokenTransferMovementTypeFilters(movementTypes) {
|
|
3225
|
+
return Array.from(new Set((movementTypes ?? [])
|
|
3226
|
+
.map((movementType) => movementType.trim().toLowerCase())
|
|
3227
|
+
.filter((movementType) => movementType.length > 0)));
|
|
3228
|
+
}
|
|
3229
|
+
effectiveTokenTransferMovementType(transfer) {
|
|
3230
|
+
const normalizedMovementType = transfer.movementType?.trim();
|
|
3231
|
+
return normalizedMovementType && normalizedMovementType.length > 0
|
|
3232
|
+
? normalizedMovementType
|
|
3233
|
+
: 'Transfer';
|
|
3234
|
+
}
|
|
2967
3235
|
isCip112TemplateId(templateId) {
|
|
2968
3236
|
return typeof templateId === 'string' && templateId.includes('.CIP112:');
|
|
2969
3237
|
}
|
|
@@ -2981,6 +3249,33 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
2981
3249
|
buildTokenMovementRowId(updateId, eventId, templateId, movementType) {
|
|
2982
3250
|
return [updateId, eventId ?? '', templateId ?? '', movementType].join(':');
|
|
2983
3251
|
}
|
|
3252
|
+
buildTokenTransferLegRowId(updateId, eventId, transferLegId, movementType) {
|
|
3253
|
+
return [updateId, eventId ?? '', transferLegId, movementType].join(':');
|
|
3254
|
+
}
|
|
3255
|
+
resolveEventLogTokenSummary(admin, instrumentId, tokensById, canonicalShareTokenId) {
|
|
3256
|
+
const fallbackToken = this.buildObservedTokenSummary(instrumentId, admin, this.isNativeAmuletIntrinsicId(instrumentId) ? CANTON_COIN_TOKEN_NAME : instrumentId, null);
|
|
3257
|
+
const tokenId = canonicalShareTokenId && fallbackToken.tokenId === this.normalizeShareLikeIntrinsicTokenId(instrumentId)
|
|
3258
|
+
? canonicalShareTokenId
|
|
3259
|
+
: fallbackToken.tokenId;
|
|
3260
|
+
return tokensById.get(tokenId) ?? tokensById.get(fallbackToken.tokenId) ?? {
|
|
3261
|
+
...fallbackToken,
|
|
3262
|
+
tokenId,
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
classifyEventLogMovementType(senderAccount, receiverAccount, admin, instrumentId) {
|
|
3266
|
+
if (this.isSyntheticEventLogAccount(senderAccount, admin, `${instrumentId}:mint`)) {
|
|
3267
|
+
return 'Mint';
|
|
3268
|
+
}
|
|
3269
|
+
if (this.isSyntheticEventLogAccount(receiverAccount, admin, `${instrumentId}:burn`)) {
|
|
3270
|
+
return 'Burn';
|
|
3271
|
+
}
|
|
3272
|
+
return 'Transfer';
|
|
3273
|
+
}
|
|
3274
|
+
isSyntheticEventLogAccount(account, provider, expectedId) {
|
|
3275
|
+
return (this.readOptionalScalarField(account, 'owner') === null &&
|
|
3276
|
+
this.readOptionalScalarField(account, 'provider') === provider &&
|
|
3277
|
+
this.readScalarField(account, 'id') === expectedId);
|
|
3278
|
+
}
|
|
2984
3279
|
compareNumericStrings(left, right) {
|
|
2985
3280
|
const leftValue = left === null ? Number.NEGATIVE_INFINITY : Number(left);
|
|
2986
3281
|
const rightValue = right === null ? Number.NEGATIVE_INFINITY : Number(right);
|
|
@@ -3033,6 +3328,9 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
3033
3328
|
return negative ? -parsed : parsed;
|
|
3034
3329
|
}
|
|
3035
3330
|
normalizeUpdateId(updateId) {
|
|
3331
|
+
if (typeof updateId !== 'string') {
|
|
3332
|
+
return '';
|
|
3333
|
+
}
|
|
3036
3334
|
return updateId.startsWith('\\x') ? updateId.slice(2) : updateId;
|
|
3037
3335
|
}
|
|
3038
3336
|
extractTokenMovement(templateId, decoded) {
|
|
@@ -3085,14 +3383,7 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
3085
3383
|
return null;
|
|
3086
3384
|
}
|
|
3087
3385
|
const issuer = this.readNestedScalarField(decoded.value, ['instrumentId', 'admin']);
|
|
3088
|
-
|
|
3089
|
-
return {
|
|
3090
|
-
tokenId,
|
|
3091
|
-
name: this.readConfiguredDecodedTokenMetadata(decoded.value, 'name') ?? intrinsicId,
|
|
3092
|
-
symbol: this.readConfiguredDecodedTokenMetadata(decoded.value, 'symbol'),
|
|
3093
|
-
issuer,
|
|
3094
|
-
source: 'pqs',
|
|
3095
|
-
};
|
|
3386
|
+
return this.buildObservedTokenSummary(intrinsicId, issuer, this.readConfiguredDecodedTokenMetadata(decoded.value, 'name') ?? intrinsicId, this.readConfiguredDecodedTokenMetadata(decoded.value, 'symbol'));
|
|
3096
3387
|
}
|
|
3097
3388
|
extractObservedTokenSummary(templateId, decoded) {
|
|
3098
3389
|
if (!templateId) {
|
|
@@ -3123,14 +3414,25 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
3123
3414
|
?? this.readNestedScalarField(decoded.value, ['vaultIdentity', 'admin'])
|
|
3124
3415
|
?? this.readScalarField(decoded.value, 'vaultParty')
|
|
3125
3416
|
?? this.readScalarField(decoded.value, 'issuer');
|
|
3126
|
-
|
|
3417
|
+
return this.buildObservedTokenSummary(intrinsicId, issuer, this.readConfiguredDecodedTokenMetadata(decoded.value, 'name')
|
|
3418
|
+
?? this.readScalarField(decoded.value, 'name')
|
|
3419
|
+
?? instrumentIdText
|
|
3420
|
+
?? symbol
|
|
3421
|
+
?? intrinsicId, symbol);
|
|
3422
|
+
}
|
|
3423
|
+
buildObservedTokenSummary(intrinsicId, issuer, fallbackName, symbol) {
|
|
3424
|
+
if (this.isNativeAmuletIntrinsicId(intrinsicId)) {
|
|
3425
|
+
return {
|
|
3426
|
+
tokenId: CANTON_COIN_TOKEN_ID,
|
|
3427
|
+
name: CANTON_COIN_TOKEN_NAME,
|
|
3428
|
+
symbol: null,
|
|
3429
|
+
issuer: null,
|
|
3430
|
+
source: 'pqs',
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3127
3433
|
return {
|
|
3128
|
-
tokenId,
|
|
3129
|
-
name:
|
|
3130
|
-
?? this.readScalarField(decoded.value, 'name')
|
|
3131
|
-
?? instrumentIdText
|
|
3132
|
-
?? symbol
|
|
3133
|
-
?? intrinsicId,
|
|
3434
|
+
tokenId: this.buildObservedTokenId(intrinsicId, issuer),
|
|
3435
|
+
name: fallbackName,
|
|
3134
3436
|
symbol,
|
|
3135
3437
|
issuer,
|
|
3136
3438
|
source: 'pqs',
|
|
@@ -3144,6 +3446,9 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
3144
3446
|
}
|
|
3145
3447
|
return `${normalizedIssuer}::${normalizedIntrinsicId}`;
|
|
3146
3448
|
}
|
|
3449
|
+
isNativeAmuletIntrinsicId(intrinsicId) {
|
|
3450
|
+
return intrinsicId.trim() === NATIVE_AMULET_INTRINSIC_ID;
|
|
3451
|
+
}
|
|
3147
3452
|
normalizeShareLikeIntrinsicTokenId(intrinsicId) {
|
|
3148
3453
|
const normalizedIntrinsicId = intrinsicId.trim();
|
|
3149
3454
|
if (normalizedIntrinsicId.endsWith(':share')) {
|
|
@@ -3168,9 +3473,43 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
3168
3473
|
};
|
|
3169
3474
|
}
|
|
3170
3475
|
async fetchEventsByUpdateId(node, query, rawUpdateId) {
|
|
3171
|
-
const
|
|
3476
|
+
const eventsByUpdateId = await this.fetchEventsByUpdateIds(node, query, [rawUpdateId]);
|
|
3477
|
+
return eventsByUpdateId.get(rawUpdateId) ?? [];
|
|
3478
|
+
}
|
|
3479
|
+
async fetchEventsByUpdateIds(node, query, rawUpdateIds) {
|
|
3480
|
+
if (rawUpdateIds.length === 0) {
|
|
3481
|
+
return new Map();
|
|
3482
|
+
}
|
|
3483
|
+
const result = await query(updateEventsByUpdateIdsQuery(node, rawUpdateIds));
|
|
3172
3484
|
const rows = result.rows ?? [];
|
|
3173
|
-
|
|
3485
|
+
const onlyRequestedUpdateId = rawUpdateIds.length === 1 ? rawUpdateIds[0] : null;
|
|
3486
|
+
const normalizedRows = await Promise.all(rows.map(async (row) => ({
|
|
3487
|
+
updateId: typeof row.update_id === 'string'
|
|
3488
|
+
? row.update_id
|
|
3489
|
+
: onlyRequestedUpdateId,
|
|
3490
|
+
event: await this.normalizeEventRow(node, row),
|
|
3491
|
+
})));
|
|
3492
|
+
const eventsByUpdateId = new Map();
|
|
3493
|
+
for (const { updateId, event } of normalizedRows) {
|
|
3494
|
+
if (!updateId) {
|
|
3495
|
+
continue;
|
|
3496
|
+
}
|
|
3497
|
+
const existingEvents = eventsByUpdateId.get(updateId);
|
|
3498
|
+
if (existingEvents) {
|
|
3499
|
+
existingEvents.push(event);
|
|
3500
|
+
}
|
|
3501
|
+
else {
|
|
3502
|
+
eventsByUpdateId.set(updateId, [event]);
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
return eventsByUpdateId;
|
|
3506
|
+
}
|
|
3507
|
+
async fetchContractDetailsByIds(node, query, contractIds) {
|
|
3508
|
+
if (contractIds.length === 0) {
|
|
3509
|
+
return [];
|
|
3510
|
+
}
|
|
3511
|
+
const result = await query(contractDetailsQuery(node, contractIds));
|
|
3512
|
+
return result.rows ?? [];
|
|
3174
3513
|
}
|
|
3175
3514
|
shouldResolveRewardCoupon(events) {
|
|
3176
3515
|
return events.some((event) => event.choice === 'ReceiveSvRewardCoupon');
|
|
@@ -3467,6 +3806,66 @@ let PqsSummaryService = class PqsSummaryService {
|
|
|
3467
3806
|
}
|
|
3468
3807
|
return null;
|
|
3469
3808
|
}
|
|
3809
|
+
readOptionalScalarField(value, label) {
|
|
3810
|
+
const field = value.fields.find((candidate) => candidate.label === label)?.value;
|
|
3811
|
+
if (field === null || field === undefined) {
|
|
3812
|
+
return null;
|
|
3813
|
+
}
|
|
3814
|
+
if (typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') {
|
|
3815
|
+
return String(field);
|
|
3816
|
+
}
|
|
3817
|
+
if (typeof field === 'object' &&
|
|
3818
|
+
field !== null &&
|
|
3819
|
+
'kind' in field &&
|
|
3820
|
+
field.kind === 'contract_id') {
|
|
3821
|
+
return field.value;
|
|
3822
|
+
}
|
|
3823
|
+
if (typeof field !== 'object' || !('kind' in field) || field.kind !== 'optional') {
|
|
3824
|
+
return null;
|
|
3825
|
+
}
|
|
3826
|
+
const innerValue = field.value;
|
|
3827
|
+
if (typeof innerValue === 'string' ||
|
|
3828
|
+
typeof innerValue === 'number' ||
|
|
3829
|
+
typeof innerValue === 'boolean') {
|
|
3830
|
+
return String(innerValue);
|
|
3831
|
+
}
|
|
3832
|
+
if (typeof innerValue === 'object' &&
|
|
3833
|
+
innerValue !== null &&
|
|
3834
|
+
'kind' in innerValue &&
|
|
3835
|
+
innerValue.kind === 'contract_id') {
|
|
3836
|
+
return innerValue.value;
|
|
3837
|
+
}
|
|
3838
|
+
return null;
|
|
3839
|
+
}
|
|
3840
|
+
readConstructorField(value, label) {
|
|
3841
|
+
const field = value.fields.find((candidate) => candidate.label === label)?.value;
|
|
3842
|
+
if (field === null || field === undefined) {
|
|
3843
|
+
return null;
|
|
3844
|
+
}
|
|
3845
|
+
if (typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') {
|
|
3846
|
+
return String(field);
|
|
3847
|
+
}
|
|
3848
|
+
if (typeof field !== 'object' || !('kind' in field)) {
|
|
3849
|
+
return null;
|
|
3850
|
+
}
|
|
3851
|
+
if (field.kind === 'enum') {
|
|
3852
|
+
return field.constructor;
|
|
3853
|
+
}
|
|
3854
|
+
if (field.kind === 'variant') {
|
|
3855
|
+
return field.constructor;
|
|
3856
|
+
}
|
|
3857
|
+
return null;
|
|
3858
|
+
}
|
|
3859
|
+
readListField(value, label) {
|
|
3860
|
+
const field = value.fields.find((candidate) => candidate.label === label)?.value;
|
|
3861
|
+
if (!field || typeof field !== 'object' || !('kind' in field) || field.kind !== 'list') {
|
|
3862
|
+
return [];
|
|
3863
|
+
}
|
|
3864
|
+
return field.items;
|
|
3865
|
+
}
|
|
3866
|
+
readAccountParty(value) {
|
|
3867
|
+
return this.readOptionalScalarField(value, 'owner') ?? this.readOptionalScalarField(value, 'provider');
|
|
3868
|
+
}
|
|
3470
3869
|
readTextMapEntryField(value, path, key) {
|
|
3471
3870
|
let current = value;
|
|
3472
3871
|
for (const segment of path) {
|