@zktx.io/sui-mcp 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES/@mysten-bcs-2.0.5-Apache-2.0.txt +205 -0
  3. package/LICENSES/@mysten-sui-2.17.0-Apache-2.0.txt +201 -0
  4. package/LICENSES/@mysten-utils-0.3.3-Apache-2.0.txt +205 -0
  5. package/LICENSES/fancy-canvas-2.1.0-MIT.txt +7 -0
  6. package/LICENSES/lightweight-charts-5.2.0-NOTICE.txt +2 -0
  7. package/README.md +288 -0
  8. package/dist/adapters/adapterLifecycleValidators.js +5 -0
  9. package/dist/adapters/adapterPromptSurfaces.js +60 -0
  10. package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +178 -0
  11. package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
  12. package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
  13. package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
  14. package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
  15. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +254 -0
  16. package/dist/adapters/intentPlanFactories.js +51 -0
  17. package/dist/adapters/reviewAdapters.js +47 -0
  18. package/dist/core/action/adapterLifecycleValidation.js +12 -0
  19. package/dist/core/action/contractNameRegistry.js +151 -0
  20. package/dist/core/action/forbiddenFields.js +43 -0
  21. package/dist/core/action/humanReadableReviewEvidence.js +193 -0
  22. package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
  23. package/dist/core/action/ptbVisualizationProducer.js +71 -0
  24. package/dist/core/action/receiptPtbGraph.js +29 -0
  25. package/dist/core/action/reviewCheckResults.js +6 -0
  26. package/dist/core/action/reviewStateValidation.js +11 -0
  27. package/dist/core/action/reviewTimeSimulationEvidence.js +470 -0
  28. package/dist/core/action/schemas.js +491 -0
  29. package/dist/core/action/signableAdapterContract.js +998 -0
  30. package/dist/core/action/suiChainReceiptEvidence.js +106 -0
  31. package/dist/core/action/suiChainReceiptMapping.js +107 -0
  32. package/dist/core/action/suiChainReceiptReader.js +211 -0
  33. package/dist/core/action/suiChainReceiptVerifier.js +178 -0
  34. package/dist/core/action/swapHumanReadableReviewProjection.js +130 -0
  35. package/dist/core/action/swapQuotePolicyEvidence.js +271 -0
  36. package/dist/core/action/transactionFunding.js +244 -0
  37. package/dist/core/action/transactionObjectOwnershipEvidence.js +256 -0
  38. package/dist/core/action/transactionObjectOwnershipProducer.js +323 -0
  39. package/dist/core/action/types.js +33 -0
  40. package/dist/core/action/walletReviewContractAssembler.js +286 -0
  41. package/dist/core/activity/accountAssetTimeline.js +157 -0
  42. package/dist/core/activity/accountAssetTimelineUsdcReferences.js +120 -0
  43. package/dist/core/activity/activityStore.js +16 -0
  44. package/dist/core/activity/externalActivityCoverage.js +76 -0
  45. package/dist/core/activity/externalActivityTransactionStream.js +82 -0
  46. package/dist/core/activity/localDataService.js +296 -0
  47. package/dist/core/activity/localDataTypes.js +11 -0
  48. package/dist/core/activity/localDataValidation.js +439 -0
  49. package/dist/core/activity/schemaVersion.js +2 -0
  50. package/dist/core/activity/sqliteActivityStore.js +1068 -0
  51. package/dist/core/activity/sqliteActivityStoreRows.js +389 -0
  52. package/dist/core/activity/sqliteActivityStoreSchema.js +367 -0
  53. package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
  54. package/dist/core/activity/suiFunctionTarget.js +43 -0
  55. package/dist/core/activity/transactionActivityAccountEffects.js +186 -0
  56. package/dist/core/activity/transactionActivityAnalysis.js +295 -0
  57. package/dist/core/activity/transactionActivityClassifier.js +306 -0
  58. package/dist/core/activity/transactionActivityDetails.js +229 -0
  59. package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
  60. package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
  61. package/dist/core/activity/transactionActivityService.js +379 -0
  62. package/dist/core/activity/transactionActivityTypes.js +18 -0
  63. package/dist/core/eventlog/sink.js +35 -0
  64. package/dist/core/evidence/settlementFamilies.js +85 -0
  65. package/dist/core/evidence/userAnswerUse.js +1 -0
  66. package/dist/core/numeric/rawU64.js +63 -0
  67. package/dist/core/preferences/preferencesStore.js +26 -0
  68. package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
  69. package/dist/core/proposal/externalProposalReview.js +347 -0
  70. package/dist/core/proposal/schemas.js +208 -0
  71. package/dist/core/proposal/types.js +35 -0
  72. package/dist/core/read/amounts.js +14 -0
  73. package/dist/core/read/coinMetadata.js +60 -0
  74. package/dist/core/read/deepbookOfficialIndexerSource.js +373 -0
  75. package/dist/core/read/deepbookRawQuoteClient.js +86 -0
  76. package/dist/core/read/deepbookReadHelpers.js +321 -0
  77. package/dist/core/read/deepbookRegistry.js +157 -0
  78. package/dist/core/read/deepbookSourceOwners.js +159 -0
  79. package/dist/core/read/deepbookUsdcChartService.js +342 -0
  80. package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
  81. package/dist/core/read/readCardInputs.js +19 -0
  82. package/dist/core/read/readCardResult.js +40 -0
  83. package/dist/core/read/readResponseGuidance.js +402 -0
  84. package/dist/core/read/readService.js +1639 -0
  85. package/dist/core/read/readServiceTypes.js +88 -0
  86. package/dist/core/read/settlementParityFormatting.js +82 -0
  87. package/dist/core/read/walletReadHelpers.js +99 -0
  88. package/dist/core/review/reviewChecks.js +45 -0
  89. package/dist/core/review/reviewComputation.js +38 -0
  90. package/dist/core/review/reviewComputationResult.js +87 -0
  91. package/dist/core/session/cardSession.js +31 -0
  92. package/dist/core/session/cardSessionStore.js +204 -0
  93. package/dist/core/session/keyedRecordStore.js +18 -0
  94. package/dist/core/session/liveReviewSessionContract.js +3 -0
  95. package/dist/core/session/localSession.js +31 -0
  96. package/dist/core/session/privateReviewArtifacts.js +75 -0
  97. package/dist/core/session/reviewValidity.js +49 -0
  98. package/dist/core/session/sessionErrors.js +9 -0
  99. package/dist/core/session/sessionRecordStore.js +40 -0
  100. package/dist/core/session/sessionStore.js +655 -0
  101. package/dist/core/session/settingsSession.js +1 -0
  102. package/dist/core/session/settingsSessions.js +45 -0
  103. package/dist/core/session/sqliteCardStore.js +88 -0
  104. package/dist/core/session/sqliteSessionStore.js +213 -0
  105. package/dist/core/session/sqliteTransactionMaterialStore.js +64 -0
  106. package/dist/core/session/sqliteWalletWorkflowStore.js +821 -0
  107. package/dist/core/session/status.js +60 -0
  108. package/dist/core/session/transactionMaterialStore.js +221 -0
  109. package/dist/core/session/transactionRequest.js +72 -0
  110. package/dist/core/session/wait.js +79 -0
  111. package/dist/core/session/walletConnection.js +92 -0
  112. package/dist/core/session/walletRuntime.js +38 -0
  113. package/dist/core/session/walletWorkflow.js +608 -0
  114. package/dist/core/session/workflowState.js +80 -0
  115. package/dist/core/session/workflowView.js +150 -0
  116. package/dist/core/suiAddress.js +18 -0
  117. package/dist/core/suiEndpoint.js +72 -0
  118. package/dist/mcp/activeAccountResponse.js +24 -0
  119. package/dist/mcp/prompts.js +146 -0
  120. package/dist/mcp/registerTool.js +19 -0
  121. package/dist/mcp/resources.js +72 -0
  122. package/dist/mcp/responseGuidance.js +326 -0
  123. package/dist/mcp/result.js +17 -0
  124. package/dist/mcp/schemas.js +11 -0
  125. package/dist/mcp/server.js +32 -0
  126. package/dist/mcp/serverInfo.js +131 -0
  127. package/dist/mcp/toolErrors.js +118 -0
  128. package/dist/mcp/toolNames.js +58 -0
  129. package/dist/mcp/tools/account/index.js +44 -0
  130. package/dist/mcp/tools/action/prepareSuiActionReview.js +126 -0
  131. package/dist/mcp/tools/read/commonSchemas.js +43 -0
  132. package/dist/mcp/tools/read/deepbookReadTools.js +686 -0
  133. package/dist/mcp/tools/read/index.js +14 -0
  134. package/dist/mcp/tools/read/readToolHelpers.js +97 -0
  135. package/dist/mcp/tools/read/reviewActivityTools.js +179 -0
  136. package/dist/mcp/tools/read/serverStatusTools.js +100 -0
  137. package/dist/mcp/tools/read/transactionActivityOutput.js +371 -0
  138. package/dist/mcp/tools/read/transactionActivityTools.js +858 -0
  139. package/dist/mcp/tools/read/walletReadTools.js +734 -0
  140. package/dist/mcp/tools/session/executionResultTools.js +44 -0
  141. package/dist/mcp/tools/session/index.js +8 -0
  142. package/dist/mcp/tools/session/shared.js +36 -0
  143. package/dist/mcp/tools/session/statusTools.js +69 -0
  144. package/dist/mcp/tools/session/walletConnectionTools.js +69 -0
  145. package/dist/mcp/tools/settings/index.js +66 -0
  146. package/dist/mcp-app/account.html +3133 -0
  147. package/dist/mcp-app/account.notices.txt +973 -0
  148. package/dist/mcp-app/chart.html +3354 -0
  149. package/dist/mcp-app/chart.notices.txt +1194 -0
  150. package/dist/mcp-app/connect.html +3179 -0
  151. package/dist/mcp-app/connect.notices.txt +1012 -0
  152. package/dist/mcp-app/receipt.html +8430 -0
  153. package/dist/mcp-app/receipt.notices.txt +3549 -0
  154. package/dist/mcp-app/review.html +8430 -0
  155. package/dist/mcp-app/review.notices.txt +3549 -0
  156. package/dist/mcp-ui/contracts.js +31 -0
  157. package/dist/mcp-ui/readCards.js +28 -0
  158. package/dist/mcp-ui/tools.js +134 -0
  159. package/dist/review-app/brand-dark.svg +1 -0
  160. package/dist/review-app/brand-light.svg +1 -0
  161. package/dist/review-app/favicon.svg +1 -0
  162. package/dist/review-app/settings.css +1 -0
  163. package/dist/review-app/settings.js +1 -0
  164. package/dist/review-app/ui.css +809 -0
  165. package/dist/review-server/assets.js +48 -0
  166. package/dist/review-server/html.js +45 -0
  167. package/dist/review-server/http.js +47 -0
  168. package/dist/review-server/middleware/hostOrigin.js +48 -0
  169. package/dist/review-server/middleware/reviewToken.js +7 -0
  170. package/dist/review-server/reviewServerPolicy.js +1 -0
  171. package/dist/review-server/server.js +72 -0
  172. package/dist/review-server/settingsApi.js +158 -0
  173. package/dist/runtime/application.js +203 -0
  174. package/dist/runtime/config.js +97 -0
  175. package/dist/runtime/localSettingsService.js +198 -0
  176. package/dist/runtime/logger.js +66 -0
  177. package/dist/runtime/reviewDependencies.js +58 -0
  178. package/dist/runtime/reviewServerAcquire.js +93 -0
  179. package/dist/runtime/runtimeTermination.js +60 -0
  180. package/dist/runtime/shared/authenticatedFetch.js +127 -0
  181. package/dist/runtime/shared/control.js +93 -0
  182. package/dist/runtime/shared/dataAccess.js +66 -0
  183. package/dist/runtime/shared/mcpHttp.js +84 -0
  184. package/dist/runtime/shared/ownerLease.js +50 -0
  185. package/dist/runtime/shared/server.js +98 -0
  186. package/dist/runtime/shared/stdio.js +113 -0
  187. package/dist/runtime/smokeMainnetRead.js +391 -0
  188. package/dist/runtime/smokeMainnetReadAssertions.js +306 -0
  189. package/dist/runtime/smokeReview.js +40 -0
  190. package/dist/runtime/start.js +90 -0
  191. package/dist/runtime/suiEndpoint.js +97 -0
  192. package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
  193. package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
  194. package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
  195. package/dist/runtime/walletConnectConfig.js +3 -0
  196. package/dist/runtime/walletConnectStorage.js +99 -0
  197. package/dist/runtime/walletConnectTransport.js +128 -0
  198. package/dist/runtime/walletSdkChild.js +3 -0
  199. package/dist/runtime/walletSdkChildRuntime.js +160 -0
  200. package/dist/runtime/walletSdkIpc.js +26 -0
  201. package/dist/runtime/walletSdkProcess.js +358 -0
  202. package/docs/AGENT_BEHAVIOR.md +524 -0
  203. package/docs/AGENT_DEVELOPMENT_POLICY.md +815 -0
  204. package/docs/FRONTEND_POLICY.md +534 -0
  205. package/docs/LOCAL_DB_ARCHITECTURE.md +381 -0
  206. package/docs/MCP_SETUP.md +742 -0
  207. package/docs/MCP_TOOLS.md +1318 -0
  208. package/docs/SDK_API.md +206 -0
  209. package/docs/SIGNABLE_ADAPTER_CONTRACT.md +396 -0
  210. package/docs/TRANSACTION_ACTIVITY_LOG.md +194 -0
  211. package/docs/UTILITY_INDEX.md +275 -0
  212. package/docs/WALLET_CONNECTION.md +238 -0
  213. package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +166 -0
  214. package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +161 -0
  215. package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +259 -0
  216. package/package.json +79 -0
  217. package/protocols/deepbook-margin.md +15 -0
  218. package/protocols/deepbook-v3.md +24 -0
@@ -0,0 +1,295 @@
1
+ import { compactExternalActivityTransactionDetails, suiGasCostFact } from "./transactionActivityDetails.js";
2
+ export const SUI_ACTIVITY_ANALYSIS_MOVE_CALL_TARGET_LIMIT = 20;
3
+ export const SUI_ACTIVITY_ANALYSIS_PROTOCOL_LIMIT = 20;
4
+ export const SUI_ACTIVITY_ANALYSIS_COIN_FLOW_LIMIT = 50;
5
+ export const SUI_ACTIVITY_ANALYSIS_EVENT_TYPE_LIMIT = 20;
6
+ export const SUI_ACTIVITY_ANALYSIS_FAILURE_LIMIT = 20;
7
+ export const suiActivityAnalysisLimitations = [
8
+ "details_missing",
9
+ "protocol_labels_absent",
10
+ "failed_without_error_detail",
11
+ "detail_truncated",
12
+ "window_incomplete",
13
+ "window_latest_only",
14
+ "ordering_unverified",
15
+ "stored_scan_incomplete",
16
+ "empty_result",
17
+ "analysis_rows_truncated",
18
+ "move_call_targets_truncated",
19
+ "protocols_truncated",
20
+ "coin_flows_truncated",
21
+ "event_types_truncated",
22
+ "failures_truncated"
23
+ ];
24
+ export function buildSuiActivityAnalysis(transactions, context = {}) {
25
+ const limitations = new Set();
26
+ if (transactions.length === 0) {
27
+ limitations.add("empty_result");
28
+ }
29
+ if (context.windowComplete === false) {
30
+ limitations.add("window_incomplete");
31
+ }
32
+ else if (context.windowComplete === null) {
33
+ limitations.add("window_latest_only");
34
+ }
35
+ if (context.orderingVerified === false) {
36
+ limitations.add("ordering_unverified");
37
+ }
38
+ if (context.truncated === true) {
39
+ limitations.add("analysis_rows_truncated");
40
+ }
41
+ const statusCounts = zeroStatusCounts();
42
+ const relationshipCounts = zeroRelationshipCounts();
43
+ let earliestTimestamp;
44
+ let latestTimestamp;
45
+ let earliestCheckpoint;
46
+ let latestCheckpoint;
47
+ const moveCallCounts = new Map();
48
+ const protocolCounts = new Map();
49
+ const coinFlows = new Map();
50
+ const objectChanges = { created: 0, mutated: 0, deleted: 0 };
51
+ const eventTypeCounts = new Map();
52
+ let gasTransactionCount = 0;
53
+ let gasNetCostRaw = 0n;
54
+ const failures = new Map();
55
+ for (const transaction of transactions) {
56
+ statusCounts[transaction.status] += 1;
57
+ const relationship = transaction.relationship ?? context.relationship;
58
+ if (relationship !== undefined) {
59
+ relationshipCounts[relationship] += 1;
60
+ }
61
+ earliestTimestamp = earlierIso(earliestTimestamp, transaction.timestamp);
62
+ latestTimestamp = laterIso(latestTimestamp, transaction.timestamp);
63
+ earliestCheckpoint = earlierCheckpoint(earliestCheckpoint, transaction.checkpoint);
64
+ latestCheckpoint = laterCheckpoint(latestCheckpoint, transaction.checkpoint);
65
+ if (transaction.lastScanIncompleteReason !== undefined) {
66
+ limitations.add("stored_scan_incomplete");
67
+ }
68
+ const compact = compactFacts(transaction);
69
+ if (compact === undefined) {
70
+ limitations.add("details_missing");
71
+ if (transaction.status === "failure") {
72
+ limitations.add("failed_without_error_detail");
73
+ }
74
+ continue;
75
+ }
76
+ if (compact.protocolMatches === undefined || compact.protocolMatches.length === 0) {
77
+ limitations.add("protocol_labels_absent");
78
+ }
79
+ if (compact.detailTruncated) {
80
+ limitations.add("detail_truncated");
81
+ }
82
+ for (const target of compact.moveCallTargets) {
83
+ incrementMap(moveCallCounts, target);
84
+ }
85
+ const protocolsInTransaction = new Set();
86
+ for (const match of compact.protocolMatches ?? []) {
87
+ if (protocolsInTransaction.has(match.protocolId)) {
88
+ continue;
89
+ }
90
+ protocolsInTransaction.add(match.protocolId);
91
+ const existing = protocolCounts.get(match.protocolId);
92
+ protocolCounts.set(match.protocolId, {
93
+ displayName: existing?.displayName ?? match.displayName,
94
+ count: (existing?.count ?? 0) + 1
95
+ });
96
+ }
97
+ for (const change of compact.balanceChanges) {
98
+ const entry = coinFlows.get(change.coinType) ?? {
99
+ increaseRaw: 0n,
100
+ decreaseRaw: 0n,
101
+ netRaw: 0n,
102
+ digests: new Set()
103
+ };
104
+ const count = BigInt(change.count ?? 1);
105
+ const amount = BigInt(change.amountRaw) * count;
106
+ entry.netRaw += amount;
107
+ if (change.direction === "increase") {
108
+ entry.increaseRaw += absBigInt(amount);
109
+ }
110
+ else if (change.direction === "decrease") {
111
+ entry.decreaseRaw += absBigInt(amount);
112
+ }
113
+ entry.digests.add(transaction.digest);
114
+ coinFlows.set(change.coinType, entry);
115
+ }
116
+ objectChanges.created += compact.objectChangeCounts.created;
117
+ objectChanges.mutated += compact.objectChangeCounts.mutated;
118
+ objectChanges.deleted += compact.objectChangeCounts.deleted;
119
+ for (const eventType of compact.eventTypes) {
120
+ incrementMap(eventTypeCounts, eventType);
121
+ }
122
+ if (compact.gasNetCostRaw !== undefined) {
123
+ gasTransactionCount += 1;
124
+ gasNetCostRaw += BigInt(compact.gasNetCostRaw);
125
+ }
126
+ if (transaction.status === "failure" && compact.executionError === undefined) {
127
+ limitations.add("failed_without_error_detail");
128
+ }
129
+ if (compact.executionError !== undefined) {
130
+ const key = failureKey(compact.executionError);
131
+ const serialized = JSON.stringify(key);
132
+ const existing = failures.get(serialized);
133
+ failures.set(serialized, { key, count: (existing?.count ?? 0) + 1 });
134
+ }
135
+ }
136
+ const moveCallTargets = topCountRows(moveCallCounts, SUI_ACTIVITY_ANALYSIS_MOVE_CALL_TARGET_LIMIT)
137
+ .map((row) => ({ target: row.key, count: row.count }));
138
+ if (moveCallCounts.size > SUI_ACTIVITY_ANALYSIS_MOVE_CALL_TARGET_LIMIT) {
139
+ limitations.add("move_call_targets_truncated");
140
+ }
141
+ const protocols = [...protocolCounts.entries()]
142
+ .map(([protocolId, value]) => ({ protocolId, ...value }))
143
+ .sort(compareProtocolRows)
144
+ .slice(0, SUI_ACTIVITY_ANALYSIS_PROTOCOL_LIMIT);
145
+ if (protocolCounts.size > SUI_ACTIVITY_ANALYSIS_PROTOCOL_LIMIT) {
146
+ limitations.add("protocols_truncated");
147
+ }
148
+ const coinFlowRows = [...coinFlows.entries()]
149
+ .map(([coinType, value]) => ({
150
+ coinType,
151
+ increaseRaw: value.increaseRaw.toString(),
152
+ decreaseRaw: value.decreaseRaw.toString(),
153
+ netRaw: value.netRaw.toString(),
154
+ transactionCount: value.digests.size
155
+ }))
156
+ .sort(compareCoinFlowRows)
157
+ .slice(0, SUI_ACTIVITY_ANALYSIS_COIN_FLOW_LIMIT);
158
+ if (coinFlows.size > SUI_ACTIVITY_ANALYSIS_COIN_FLOW_LIMIT) {
159
+ limitations.add("coin_flows_truncated");
160
+ }
161
+ const eventTypes = topCountRows(eventTypeCounts, SUI_ACTIVITY_ANALYSIS_EVENT_TYPE_LIMIT)
162
+ .map((row) => ({ eventType: row.key, count: row.count }));
163
+ if (eventTypeCounts.size > SUI_ACTIVITY_ANALYSIS_EVENT_TYPE_LIMIT) {
164
+ limitations.add("event_types_truncated");
165
+ }
166
+ const failureRows = [...failures.values()]
167
+ .sort(compareFailureRows)
168
+ .slice(0, SUI_ACTIVITY_ANALYSIS_FAILURE_LIMIT)
169
+ .map(({ key, count }) => ({ ...key, count }));
170
+ if (failures.size > SUI_ACTIVITY_ANALYSIS_FAILURE_LIMIT) {
171
+ limitations.add("failures_truncated");
172
+ }
173
+ const overviewSummary = context.summary;
174
+ const overviewEarliestTimestamp = overviewSummary?.earliestTimestamp ?? earliestTimestamp;
175
+ const overviewLatestTimestamp = overviewSummary?.latestTimestamp ?? latestTimestamp;
176
+ return {
177
+ overview: {
178
+ transactionCount: overviewSummary?.transactionCount ?? transactions.length,
179
+ analyzedTransactionCount: transactions.length,
180
+ statusCounts: overviewSummary?.statusCounts ?? statusCounts,
181
+ relationshipCounts: overviewSummary?.relationshipCounts ?? relationshipCounts,
182
+ ...(overviewEarliestTimestamp === undefined ? {} : { earliestTimestamp: overviewEarliestTimestamp }),
183
+ ...(overviewLatestTimestamp === undefined ? {} : { latestTimestamp: overviewLatestTimestamp }),
184
+ ...(earliestCheckpoint === undefined ? {} : { earliestCheckpoint }),
185
+ ...(latestCheckpoint === undefined ? {} : { latestCheckpoint })
186
+ },
187
+ moveCallTargets,
188
+ protocols,
189
+ coinFlows: coinFlowRows,
190
+ objectChanges,
191
+ eventTypes,
192
+ ...(gasTransactionCount === 0
193
+ ? {}
194
+ : {
195
+ gas: {
196
+ transactionCount: gasTransactionCount,
197
+ netGasCostRaw: gasNetCostRaw.toString(),
198
+ netGasCost: suiGasCostFact(gasNetCostRaw.toString())
199
+ }
200
+ }),
201
+ failures: failureRows,
202
+ limitations: [...limitations].sort()
203
+ };
204
+ }
205
+ function compactFacts(transaction) {
206
+ if (transaction.compact !== undefined) {
207
+ return transaction.compact;
208
+ }
209
+ return transaction.details === undefined
210
+ ? undefined
211
+ : compactExternalActivityTransactionDetails(transaction.details);
212
+ }
213
+ function zeroStatusCounts() {
214
+ return { success: 0, failure: 0, unknown: 0 };
215
+ }
216
+ function zeroRelationshipCounts() {
217
+ return { affected: 0, sent: 0 };
218
+ }
219
+ function incrementMap(map, key) {
220
+ map.set(key, (map.get(key) ?? 0) + 1);
221
+ }
222
+ function topCountRows(map, limit) {
223
+ return [...map.entries()]
224
+ .map(([key, count]) => ({ key, count }))
225
+ .sort(compareCountRows)
226
+ .slice(0, limit);
227
+ }
228
+ function compareCountRows(a, b) {
229
+ return b.count - a.count || compareAscii(a.key, b.key);
230
+ }
231
+ function compareProtocolRows(a, b) {
232
+ return b.count - a.count || compareAscii(a.protocolId, b.protocolId);
233
+ }
234
+ function compareCoinFlowRows(a, b) {
235
+ return b.transactionCount - a.transactionCount || compareAscii(a.coinType, b.coinType);
236
+ }
237
+ function compareFailureRows(a, b) {
238
+ return b.count - a.count || compareAscii(failureSortKey(a.key), failureSortKey(b.key));
239
+ }
240
+ function failureKey(error) {
241
+ return {
242
+ message: error.message,
243
+ ...(error.abortCodeRaw === undefined ? {} : { abortCodeRaw: error.abortCodeRaw }),
244
+ ...(error.package === undefined ? {} : { package: error.package }),
245
+ ...(error.module === undefined ? {} : { module: error.module }),
246
+ ...(error.function === undefined ? {} : { function: error.function })
247
+ };
248
+ }
249
+ function failureSortKey(key) {
250
+ return [
251
+ key.message,
252
+ key.abortCodeRaw ?? "",
253
+ key.package ?? "",
254
+ key.module ?? "",
255
+ key.function ?? ""
256
+ ].join("\u0000");
257
+ }
258
+ function earlierIso(current, candidate) {
259
+ if (candidate === undefined)
260
+ return current;
261
+ if (current === undefined)
262
+ return candidate;
263
+ return candidate < current ? candidate : current;
264
+ }
265
+ function laterIso(current, candidate) {
266
+ if (candidate === undefined)
267
+ return current;
268
+ if (current === undefined)
269
+ return candidate;
270
+ return candidate > current ? candidate : current;
271
+ }
272
+ function earlierCheckpoint(current, candidate) {
273
+ if (candidate === undefined)
274
+ return current;
275
+ if (current === undefined)
276
+ return candidate;
277
+ return BigInt(candidate) < BigInt(current) ? candidate : current;
278
+ }
279
+ function laterCheckpoint(current, candidate) {
280
+ if (candidate === undefined)
281
+ return current;
282
+ if (current === undefined)
283
+ return candidate;
284
+ return BigInt(candidate) > BigInt(current) ? candidate : current;
285
+ }
286
+ function absBigInt(value) {
287
+ return value < 0n ? -value : value;
288
+ }
289
+ function compareAscii(a, b) {
290
+ if (a < b)
291
+ return -1;
292
+ if (a > b)
293
+ return 1;
294
+ return 0;
295
+ }
@@ -0,0 +1,306 @@
1
+ import { z } from "zod";
2
+ import { parseSuiAddress } from "../suiAddress.js";
3
+ import { SUI_DEFI_ACTIVITY_PROTOCOL_RULES, protocolActivityPrimaryActions, protocolPackageRuleAllows } from "./transactionActivityProtocolRules.js";
4
+ export const SUI_DEFI_ACTIVITY_CLASSIFIER_VERSION = "sui_defi_activity_v0_4";
5
+ const protocolActivityPrimaryActionSchema = z.enum(protocolActivityPrimaryActions);
6
+ const protocolMatchBasisSchema = z.enum([
7
+ "direct_move_call",
8
+ "event_type",
9
+ "object_type",
10
+ "shared_object"
11
+ ]);
12
+ const primaryActionPriority = {
13
+ order: 0,
14
+ swap: 0,
15
+ liquidity: 0,
16
+ lending: 0,
17
+ fee_or_reward: 1,
18
+ admin_or_versioning: 2,
19
+ unknown: 3
20
+ };
21
+ export const protocolActivityClassifierMatchSchema = z.object({
22
+ classifierVersion: z.literal(SUI_DEFI_ACTIVITY_CLASSIFIER_VERSION),
23
+ protocolId: z.string(),
24
+ displayName: z.string(),
25
+ activityCategory: z.string(),
26
+ primaryAction: protocolActivityPrimaryActionSchema,
27
+ matchBasis: protocolMatchBasisSchema,
28
+ evidence: z.array(z.discriminatedUnion("kind", [
29
+ z.object({
30
+ kind: z.literal("moveCall"),
31
+ package: z.string(),
32
+ packageSource: z.string().optional(),
33
+ mvrName: z.string().optional(),
34
+ module: z.string(),
35
+ function: z.string(),
36
+ commandIndex: z.number().int().nonnegative()
37
+ }).strict(),
38
+ z.object({
39
+ kind: z.literal("eventType"),
40
+ package: z.string().optional(),
41
+ packageSource: z.string().optional(),
42
+ mvrName: z.string().optional(),
43
+ eventType: z.string(),
44
+ sequenceNumber: z.string().optional()
45
+ }).strict(),
46
+ z.object({
47
+ kind: z.literal("objectType"),
48
+ objectId: z.string(),
49
+ changeKind: z.string(),
50
+ package: z.string().optional(),
51
+ packageSource: z.string().optional(),
52
+ mvrName: z.string().optional(),
53
+ type: z.string()
54
+ }).strict(),
55
+ z.object({
56
+ kind: z.literal("sharedObject"),
57
+ objectId: z.string(),
58
+ label: z.string()
59
+ }).strict()
60
+ ])).min(1),
61
+ relatedProtocols: z.array(z.object({
62
+ protocolId: z.string(),
63
+ reason: z.string()
64
+ }).strict()),
65
+ limitations: z.array(z.string())
66
+ }).strict();
67
+ export function classifySuiDeFiActivity(details) {
68
+ const matches = SUI_DEFI_ACTIVITY_PROTOCOL_RULES.flatMap((rule) => matchProtocol(rule, details));
69
+ const deepTradeDirect = matches.some((match) => match.protocolId === "deeptrade-core" && match.matchBasis === "direct_move_call");
70
+ const deepBookMatch = matches.find((match) => match.protocolId === "deepbook-v3");
71
+ const resolved = matches
72
+ .filter((match) => !(deepTradeDirect && match.protocolId === "deepbook-v3"))
73
+ .map((match) => {
74
+ if (match.protocolId === "deeptrade-core" && deepTradeDirect && deepBookMatch) {
75
+ return {
76
+ ...match,
77
+ relatedProtocols: [
78
+ ...match.relatedProtocols,
79
+ {
80
+ protocolId: "deepbook-v3",
81
+ reason: "deeptrade_wrapper_touched_deepbook_evidence"
82
+ }
83
+ ]
84
+ };
85
+ }
86
+ return match;
87
+ });
88
+ return resolved.sort((a, b) => {
89
+ const priority = matchBasisPriority(a.matchBasis) - matchBasisPriority(b.matchBasis);
90
+ if (priority !== 0) {
91
+ return priority;
92
+ }
93
+ return compareAscii(a.protocolId, b.protocolId);
94
+ });
95
+ }
96
+ function matchProtocol(rule, details) {
97
+ const evidence = [];
98
+ const limitationSet = new Set();
99
+ let matchBasis;
100
+ let primaryAction = "unknown";
101
+ for (const call of details.moveCalls) {
102
+ const packageId = normalizedPackageFromMoveCall(call);
103
+ const packageRule = packageId === undefined ? undefined : rule.packages[packageId];
104
+ if (packageRule === undefined || !protocolPackageRuleAllows(packageRule, "moveCall")) {
105
+ continue;
106
+ }
107
+ evidence.push({
108
+ kind: "moveCall",
109
+ package: call.package,
110
+ ...packageEvidenceFields(packageRule),
111
+ module: call.module,
112
+ function: call.function,
113
+ commandIndex: call.commandIndex
114
+ });
115
+ matchBasis = selectPreferredMatchBasis(matchBasis, "direct_move_call");
116
+ primaryAction = strongestPrimaryAction(primaryAction, rule.actionForMoveCall?.(call) ?? "unknown");
117
+ if (packageRule.limitation !== undefined) {
118
+ limitationSet.add(packageRule.limitation);
119
+ }
120
+ }
121
+ for (const event of details.events) {
122
+ const packageId = normalizedPackageFromEvent(event);
123
+ const packageRule = packageId === undefined ? undefined : rule.packages[packageId];
124
+ if (packageId !== undefined
125
+ && packageRule !== undefined
126
+ && protocolPackageRuleAllows(packageRule, "eventType")
127
+ && event.eventType !== undefined) {
128
+ evidence.push({
129
+ kind: "eventType",
130
+ package: packageId,
131
+ ...packageEvidenceFields(packageRule),
132
+ eventType: event.eventType,
133
+ ...(event.sequenceNumber === undefined ? {} : { sequenceNumber: event.sequenceNumber })
134
+ });
135
+ matchBasis = selectPreferredMatchBasis(matchBasis, "event_type");
136
+ }
137
+ }
138
+ for (const change of details.objectChanges) {
139
+ for (const type of uniqueStrings([change.inputType, change.outputType].flatMap((value) => value === undefined ? [] : [value]))) {
140
+ const packageId = normalizedPackageFromType(type);
141
+ const packageRule = packageId === undefined ? undefined : rule.packages[packageId];
142
+ if (packageId !== undefined
143
+ && packageRule !== undefined
144
+ && protocolPackageRuleAllows(packageRule, "objectType")) {
145
+ evidence.push({
146
+ kind: "objectType",
147
+ objectId: change.objectId,
148
+ changeKind: change.changeKind,
149
+ package: packageId,
150
+ ...packageEvidenceFields(packageRule),
151
+ type
152
+ });
153
+ matchBasis = selectPreferredMatchBasis(matchBasis, "object_type");
154
+ }
155
+ }
156
+ const objectId = parseSuiAddress(change.objectId);
157
+ const sharedObjectLabel = objectId === undefined ? undefined : rule.sharedObjects[objectId];
158
+ if (sharedObjectLabel !== undefined) {
159
+ evidence.push({
160
+ kind: "sharedObject",
161
+ objectId: change.objectId,
162
+ label: sharedObjectLabel
163
+ });
164
+ matchBasis = selectPreferredMatchBasis(matchBasis, "shared_object");
165
+ }
166
+ }
167
+ if (evidence.length === 0 || matchBasis === undefined) {
168
+ return [];
169
+ }
170
+ addTruncationLimitations(details, limitationSet);
171
+ if (matchBasis === "shared_object") {
172
+ limitationSet.add("shared_object_match_does_not_prove_wallet_position");
173
+ }
174
+ if (matchBasis === "event_type" || matchBasis === "object_type") {
175
+ limitationSet.add("no_direct_move_call_match");
176
+ }
177
+ limitationSet.add("transaction_activity_label_only");
178
+ limitationSet.add("not_position_inventory_or_pnl_or_signing");
179
+ return [{
180
+ classifierVersion: SUI_DEFI_ACTIVITY_CLASSIFIER_VERSION,
181
+ protocolId: rule.protocolId,
182
+ displayName: rule.displayName,
183
+ activityCategory: rule.activityCategory,
184
+ primaryAction,
185
+ matchBasis,
186
+ evidence: uniqueEvidence(evidence),
187
+ relatedProtocols: [],
188
+ limitations: [...limitationSet].sort(compareAscii)
189
+ }];
190
+ }
191
+ function addTruncationLimitations(details, limitations) {
192
+ if (details.truncation.moveCalls) {
193
+ limitations.add("move_call_details_truncated");
194
+ }
195
+ if (details.truncation.objectChanges) {
196
+ limitations.add("object_change_details_truncated");
197
+ }
198
+ if (details.truncation.events) {
199
+ limitations.add("event_details_truncated");
200
+ }
201
+ }
202
+ function selectPreferredMatchBasis(current, candidate) {
203
+ return current === undefined || matchBasisPriority(candidate) < matchBasisPriority(current)
204
+ ? candidate
205
+ : current;
206
+ }
207
+ function matchBasisPriority(matchBasis) {
208
+ switch (matchBasis) {
209
+ case "direct_move_call":
210
+ return 0;
211
+ case "event_type":
212
+ return 1;
213
+ case "object_type":
214
+ return 2;
215
+ case "shared_object":
216
+ return 3;
217
+ }
218
+ }
219
+ function strongestPrimaryAction(current, candidate) {
220
+ const priority = primaryActionPriority[candidate] - primaryActionPriority[current];
221
+ if (priority !== 0) {
222
+ return priority < 0 ? candidate : current;
223
+ }
224
+ return current === "unknown" ? candidate : current;
225
+ }
226
+ function normalizedPackageFromMoveCall(call) {
227
+ return parseSuiAddress(call.package) ?? normalizedPackageFromType(call.target);
228
+ }
229
+ function normalizedPackageFromEvent(event) {
230
+ if (event.package !== undefined) {
231
+ return parseSuiAddress(event.package);
232
+ }
233
+ return event.eventType === undefined ? undefined : normalizedPackageFromType(event.eventType);
234
+ }
235
+ function normalizedPackageFromType(type) {
236
+ const [packageCandidate] = type.split("::");
237
+ return packageCandidate === undefined ? undefined : parseSuiAddress(packageCandidate);
238
+ }
239
+ function packageEvidenceFields(packageRule) {
240
+ return {
241
+ packageSource: packageRule.source,
242
+ ...(packageRule.mvrName === undefined ? {} : { mvrName: packageRule.mvrName })
243
+ };
244
+ }
245
+ function uniqueEvidence(evidence) {
246
+ const seen = new Set();
247
+ const unique = [];
248
+ for (const item of evidence) {
249
+ const key = evidenceKey(item);
250
+ if (!seen.has(key)) {
251
+ seen.add(key);
252
+ unique.push(item);
253
+ }
254
+ }
255
+ return unique;
256
+ }
257
+ function evidenceKey(evidence) {
258
+ switch (evidence.kind) {
259
+ case "moveCall":
260
+ return keyParts([
261
+ evidence.kind,
262
+ evidence.package,
263
+ evidence.packageSource,
264
+ evidence.mvrName,
265
+ evidence.module,
266
+ evidence.function,
267
+ String(evidence.commandIndex)
268
+ ]);
269
+ case "eventType":
270
+ return keyParts([
271
+ evidence.kind,
272
+ evidence.package,
273
+ evidence.packageSource,
274
+ evidence.mvrName,
275
+ evidence.eventType,
276
+ evidence.sequenceNumber
277
+ ]);
278
+ case "objectType":
279
+ return keyParts([
280
+ evidence.kind,
281
+ evidence.objectId,
282
+ evidence.changeKind,
283
+ evidence.package,
284
+ evidence.packageSource,
285
+ evidence.mvrName,
286
+ evidence.type
287
+ ]);
288
+ case "sharedObject":
289
+ return keyParts([evidence.kind, evidence.objectId, evidence.label]);
290
+ }
291
+ }
292
+ function keyParts(values) {
293
+ return values.map((value) => value ?? "").join("\u001f");
294
+ }
295
+ function uniqueStrings(values) {
296
+ return [...new Set(values)];
297
+ }
298
+ function compareAscii(a, b) {
299
+ if (a < b) {
300
+ return -1;
301
+ }
302
+ if (a > b) {
303
+ return 1;
304
+ }
305
+ return 0;
306
+ }