@stelis/say-ur-intent 0.0.0 → 0.0.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 (234) hide show
  1. package/README.md +4 -39
  2. package/dist/adapters/adapterLifecycleValidators.js +7 -0
  3. package/dist/adapters/adapterPromptSurfaces.js +71 -0
  4. package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +175 -0
  5. package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
  6. package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
  7. package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
  8. package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
  9. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +269 -0
  10. package/dist/adapters/flowx/flowxSwapHumanReviewProducer.js +176 -0
  11. package/dist/adapters/flowx/flowxSwapIntent.js +79 -0
  12. package/dist/adapters/flowx/flowxSwapQuotePolicy.js +104 -0
  13. package/dist/adapters/flowx/flowxSwapReviewEvidence.js +468 -0
  14. package/dist/adapters/flowx/flowxSwapReviewLifecycle.js +85 -0
  15. package/dist/adapters/flowx/flowxSwapTransactionMaterialProducer.js +362 -0
  16. package/dist/adapters/intentPlanFactories.js +59 -0
  17. package/dist/adapters/reviewAdapters.js +81 -0
  18. package/dist/core/action/adapterLifecycleValidation.js +12 -0
  19. package/dist/core/action/forbiddenFields.js +43 -0
  20. package/dist/core/action/humanReadableReviewEvidence.js +203 -0
  21. package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
  22. package/dist/core/action/ptbVisualizationProducer.js +66 -0
  23. package/dist/core/action/reviewCheckResults.js +6 -0
  24. package/dist/core/action/reviewStateValidation.js +11 -0
  25. package/dist/core/action/reviewTimeSimulationEvidence.js +471 -0
  26. package/dist/core/action/schemas.js +529 -0
  27. package/dist/core/action/signableAdapterContract.js +993 -0
  28. package/dist/core/action/swapHumanReadableReviewProjection.js +124 -0
  29. package/dist/core/action/swapQuotePolicyEvidence.js +278 -0
  30. package/dist/core/action/transactionObjectOwnershipEvidence.js +247 -0
  31. package/dist/core/action/transactionObjectOwnershipProducer.js +329 -0
  32. package/dist/core/action/types.js +35 -0
  33. package/dist/core/action/walletReviewContractAssembler.js +282 -0
  34. package/dist/core/activity/activityStore.js +15 -0
  35. package/dist/core/activity/localDataService.js +258 -0
  36. package/dist/core/activity/localDataTypes.js +11 -0
  37. package/dist/core/activity/localDataValidation.js +396 -0
  38. package/dist/core/activity/schemaVersion.js +1 -0
  39. package/dist/core/activity/sqliteActivityStore.js +820 -0
  40. package/dist/core/activity/sqliteActivityStoreRows.js +430 -0
  41. package/dist/core/activity/sqliteActivityStoreSchema.js +258 -0
  42. package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
  43. package/dist/core/activity/suiFunctionTarget.js +43 -0
  44. package/dist/core/activity/transactionActivityAccountEffects.js +189 -0
  45. package/dist/core/activity/transactionActivityAnalysis.js +295 -0
  46. package/dist/core/activity/transactionActivityClassifier.js +306 -0
  47. package/dist/core/activity/transactionActivityDetails.js +229 -0
  48. package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
  49. package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
  50. package/dist/core/activity/transactionActivityService.js +379 -0
  51. package/dist/core/activity/transactionActivityTypes.js +18 -0
  52. package/dist/core/eventlog/sink.js +35 -0
  53. package/dist/core/evidence/settlementFamilies.js +87 -0
  54. package/dist/core/evidence/userAnswerUse.js +1 -0
  55. package/dist/core/numeric/rawU64.js +63 -0
  56. package/dist/core/preferences/preferencesStore.js +26 -0
  57. package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
  58. package/dist/core/proposal/externalProposalReview.js +347 -0
  59. package/dist/core/proposal/schemas.js +208 -0
  60. package/dist/core/proposal/types.js +35 -0
  61. package/dist/core/read/amounts.js +14 -0
  62. package/dist/core/read/coinMetadata.js +60 -0
  63. package/dist/core/read/deepbookRawQuoteClient.js +86 -0
  64. package/dist/core/read/deepbookReadHelpers.js +265 -0
  65. package/dist/core/read/deepbookRegistry.js +133 -0
  66. package/dist/core/read/flowxQuoteClient.js +117 -0
  67. package/dist/core/read/flowxReadHelpers.js +145 -0
  68. package/dist/core/read/flowxRegistry.js +174 -0
  69. package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
  70. package/dist/core/read/readResponseGuidance.js +451 -0
  71. package/dist/core/read/readService.js +1164 -0
  72. package/dist/core/read/readServiceTypes.js +59 -0
  73. package/dist/core/read/settlementParityFormatting.js +82 -0
  74. package/dist/core/read/walletReadHelpers.js +99 -0
  75. package/dist/core/review/reviewChecks.js +54 -0
  76. package/dist/core/review/reviewComputation.js +38 -0
  77. package/dist/core/review/reviewComputationResult.js +87 -0
  78. package/dist/core/session/localSession.js +31 -0
  79. package/dist/core/session/privateReviewArtifacts.js +73 -0
  80. package/dist/core/session/sessionErrors.js +9 -0
  81. package/dist/core/session/sessionStore.js +821 -0
  82. package/dist/core/session/settingsSession.js +1 -0
  83. package/dist/core/session/settingsSessions.js +43 -0
  84. package/dist/core/session/status.js +86 -0
  85. package/dist/core/session/transactionMaterialStore.js +205 -0
  86. package/dist/core/session/wait.js +102 -0
  87. package/dist/core/session/walletIdentity.js +103 -0
  88. package/dist/core/session/walletIdentitySessions.js +189 -0
  89. package/dist/core/suiAddress.js +18 -0
  90. package/dist/core/suiEndpoint.js +72 -0
  91. package/dist/mcp/activeAccountResponse.js +24 -0
  92. package/dist/mcp/prompts.js +146 -0
  93. package/dist/mcp/registerTool.js +19 -0
  94. package/dist/mcp/resources.js +72 -0
  95. package/dist/mcp/responseGuidance.js +381 -0
  96. package/dist/mcp/result.js +17 -0
  97. package/dist/mcp/schemas.js +8 -0
  98. package/dist/mcp/server.js +30 -0
  99. package/dist/mcp/serverInfo.js +123 -0
  100. package/dist/mcp/toolErrors.js +105 -0
  101. package/dist/mcp/toolNames.js +50 -0
  102. package/dist/mcp/tools/account/index.js +44 -0
  103. package/dist/mcp/tools/action/prepareSuiActionReview.js +120 -0
  104. package/dist/mcp/tools/read/commonSchemas.js +43 -0
  105. package/dist/mcp/tools/read/deepbookReadTools.js +453 -0
  106. package/dist/mcp/tools/read/flowxReadTools.js +135 -0
  107. package/dist/mcp/tools/read/index.js +16 -0
  108. package/dist/mcp/tools/read/readToolHelpers.js +68 -0
  109. package/dist/mcp/tools/read/reviewActivityTools.js +176 -0
  110. package/dist/mcp/tools/read/serverStatusTools.js +103 -0
  111. package/dist/mcp/tools/read/transactionActivityOutput.js +300 -0
  112. package/dist/mcp/tools/read/transactionActivityTools.js +544 -0
  113. package/dist/mcp/tools/read/walletReadTools.js +733 -0
  114. package/dist/mcp/tools/session/executionResultTools.js +92 -0
  115. package/dist/mcp/tools/session/index.js +8 -0
  116. package/dist/mcp/tools/session/shared.js +79 -0
  117. package/dist/mcp/tools/session/statusTools.js +134 -0
  118. package/dist/mcp/tools/session/walletIdentityTools.js +119 -0
  119. package/dist/mcp/tools/settings/index.js +64 -0
  120. package/dist/review-app/analysis.css +1 -0
  121. package/dist/review-app/analysis.js +1 -0
  122. package/dist/review-app/arc-BjIacwQm.js +1 -0
  123. package/dist/review-app/architecture-U656AL7Q-aSB9x1OK.js +1 -0
  124. package/dist/review-app/architectureDiagram-VXUJARFQ-C5W6re2I.js +36 -0
  125. package/dist/review-app/array-BmXUUrU6.js +1 -0
  126. package/dist/review-app/blockDiagram-VD42YOAC-20MLNcUm.js +122 -0
  127. package/dist/review-app/c4Diagram-YG6GDRKO-BZXRrcck.js +10 -0
  128. package/dist/review-app/channel-lk2p_CUu.js +1 -0
  129. package/dist/review-app/chunk-4BX2VUAB-BPITOdjX.js +1 -0
  130. package/dist/review-app/chunk-55IACEB6-Dz-pyw5k.js +1 -0
  131. package/dist/review-app/chunk-76Q3JFCE-cK_X1P_l.js +1 -0
  132. package/dist/review-app/chunk-ABZYJK2D-Dt4W53JI.js +81 -0
  133. package/dist/review-app/chunk-ATLVNIR6-fZHLXURb.js +1 -0
  134. package/dist/review-app/chunk-B4BG7PRW-BbgcjusC.js +165 -0
  135. package/dist/review-app/chunk-BJD4TVEz.js +1 -0
  136. package/dist/review-app/chunk-CVBHYZKI-CViawAKX.js +1 -0
  137. package/dist/review-app/chunk-DI55MBZ5-C5aoul-d.js +220 -0
  138. package/dist/review-app/chunk-FMBD7UC4-Chxmw62A.js +15 -0
  139. package/dist/review-app/chunk-FPAJGGOC-DDHjQ09H.js +80 -0
  140. package/dist/review-app/chunk-FWNWRKHM-CVVQUptk.js +1 -0
  141. package/dist/review-app/chunk-HN2XXSSU-yzNpjaSZ.js +1 -0
  142. package/dist/review-app/chunk-JA3XYJ7Z-C5ZJdU01.js +70 -0
  143. package/dist/review-app/chunk-JZLCHNYA-BBST4Cnk.js +54 -0
  144. package/dist/review-app/chunk-LBM3YZW2-CdwAPuHr.js +1 -0
  145. package/dist/review-app/chunk-LHMN2FUI-BtB5uDcp.js +1 -0
  146. package/dist/review-app/chunk-O7ZBX7Z2-pxdK4Sa3.js +1 -0
  147. package/dist/review-app/chunk-QN33PNHL-CbVv3uGK.js +1 -0
  148. package/dist/review-app/chunk-QXUST7PY-DKM2-t2c.js +7 -0
  149. package/dist/review-app/chunk-QZHKN3VN-C5ni2pN_.js +1 -0
  150. package/dist/review-app/chunk-S3R3BYOJ-BWvOhDs0.js +2 -0
  151. package/dist/review-app/chunk-S6J4BHB3-D9Fk0YeD.js +1 -0
  152. package/dist/review-app/chunk-T53DSG4Q-C1qEyzyV.js +1 -0
  153. package/dist/review-app/chunk-TZMSLE5B-B--7eU69.js +1 -0
  154. package/dist/review-app/classDiagram-2ON5EDUG-DlL1m2bp.js +1 -0
  155. package/dist/review-app/classDiagram-v2-WZHVMYZB-FXRskT1j.js +1 -0
  156. package/dist/review-app/clone-BZZb7gpZ.js +1 -0
  157. package/dist/review-app/cose-bilkent-S5V4N54A-CRIb8XEO.js +1 -0
  158. package/dist/review-app/cytoscape.esm-C7jYqDP5.js +321 -0
  159. package/dist/review-app/dagre-6UL2VRFP-FNCAXbdE.js +4 -0
  160. package/dist/review-app/dagre-Be46QtUd.js +1 -0
  161. package/dist/review-app/defaultLocale-BaWNtAUL.js +1 -0
  162. package/dist/review-app/diagram-PSM6KHXK-ylLWjiNM.js +24 -0
  163. package/dist/review-app/diagram-QEK2KX5R-BCDcESxs.js +43 -0
  164. package/dist/review-app/diagram-S2PKOQOG-Vdrc-vrO.js +24 -0
  165. package/dist/review-app/dist-WPc74x_f.js +1 -0
  166. package/dist/review-app/erDiagram-Q2GNP2WA-E5ZsUbDF.js +60 -0
  167. package/dist/review-app/flatten-DHf9IeNI.js +1 -0
  168. package/dist/review-app/flowDiagram-NV44I4VS-DBSQuj6x.js +162 -0
  169. package/dist/review-app/ganttDiagram-LVOFAZNH-CKUOsqwl.js +267 -0
  170. package/dist/review-app/gitGraph-F6HP7TQM-DsAD6qK1.js +1 -0
  171. package/dist/review-app/gitGraphDiagram-NY62KEGX-BCeIMWdl.js +65 -0
  172. package/dist/review-app/graphlib-CiX5CXxR.js +1 -0
  173. package/dist/review-app/http-DMvwuuFk.js +1 -0
  174. package/dist/review-app/identity-DY8PXc6t.js +1 -0
  175. package/dist/review-app/info-NVLQJR56-Dlx1nZic.js +1 -0
  176. package/dist/review-app/infoDiagram-F6ZHWCRC-CAuANIrz.js +2 -0
  177. package/dist/review-app/init-BvqephKz.js +1 -0
  178. package/dist/review-app/journeyDiagram-XKPGCS4Q-C-Z9phnx.js +139 -0
  179. package/dist/review-app/kanban-definition-3W4ZIXB7-DufgZABq.js +89 -0
  180. package/dist/review-app/katex-B-Z-NXXN.js +257 -0
  181. package/dist/review-app/line-DiIv3Jgw.js +1 -0
  182. package/dist/review-app/linear-Cv-UPvo1.js +1 -0
  183. package/dist/review-app/math-kmyYrkHL.js +1 -0
  184. package/dist/review-app/mermaid-parser.core-DkwUYTPl.js +4 -0
  185. package/dist/review-app/mindmap-definition-VGOIOE7T-TM_CqdmV.js +68 -0
  186. package/dist/review-app/ordinal-BliTlkoG.js +1 -0
  187. package/dist/review-app/packet-BFZMPI3H-DqbnU92v.js +1 -0
  188. package/dist/review-app/path-AEo9W6mQ.js +1 -0
  189. package/dist/review-app/pie-7BOR55EZ-LJzaLkgr.js +1 -0
  190. package/dist/review-app/pieDiagram-ADFJNKIX-BAs8OfRS.js +30 -0
  191. package/dist/review-app/quadrantDiagram-AYHSOK5B-CyUDZP5S.js +7 -0
  192. package/dist/review-app/radar-NHE76QYJ-DBpHc8_Y.js +1 -0
  193. package/dist/review-app/reduce-B-HuPpdd.js +1 -0
  194. package/dist/review-app/requirementDiagram-UZGBJVZJ-BEHix78P.js +64 -0
  195. package/dist/review-app/review.css +1 -0
  196. package/dist/review-app/review.js +43 -0
  197. package/dist/review-app/sankeyDiagram-TZEHDZUN-B2bKbmsm.js +10 -0
  198. package/dist/review-app/sequenceDiagram-WL72ISMW-DVLOORFJ.js +145 -0
  199. package/dist/review-app/settings.css +1 -0
  200. package/dist/review-app/settings.js +1 -0
  201. package/dist/review-app/src-Buml7cM5.js +1 -0
  202. package/dist/review-app/stateDiagram-FKZM4ZOC-sFGGp2kV.js +1 -0
  203. package/dist/review-app/stateDiagram-v2-4FDKWEC3-BHfCF4dX.js +1 -0
  204. package/dist/review-app/timeline-definition-IT6M3QCI-BESnBijC.js +61 -0
  205. package/dist/review-app/treemap-KMMF4GRG-wnVLBDeQ.js +1 -0
  206. package/dist/review-app/walletStatus-CcojOdGy.js +7 -0
  207. package/dist/review-app/xychartDiagram-PRI3JC2R-BGWVfCx4.js +7 -0
  208. package/dist/review-server/assets.js +48 -0
  209. package/dist/review-server/html.js +66 -0
  210. package/dist/review-server/http.js +47 -0
  211. package/dist/review-server/middleware/hostOrigin.js +48 -0
  212. package/dist/review-server/middleware/reviewToken.js +7 -0
  213. package/dist/review-server/reviewServerPolicy.js +10 -0
  214. package/dist/review-server/server.js +568 -0
  215. package/dist/review-server/settingsApi.js +182 -0
  216. package/dist/review-server/walletIdentityResponse.js +13 -0
  217. package/dist/runtime/config.js +103 -0
  218. package/dist/runtime/localSettingsService.js +198 -0
  219. package/dist/runtime/logger.js +50 -0
  220. package/dist/runtime/reviewServerAcquire.js +128 -0
  221. package/dist/runtime/smokeMainnetRead.js +529 -0
  222. package/dist/runtime/smokeMainnetReadAssertions.js +308 -0
  223. package/dist/runtime/start.js +295 -0
  224. package/dist/runtime/suiEndpoint.js +97 -0
  225. package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
  226. package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
  227. package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
  228. package/docs/AGENT_BEHAVIOR.md +1 -1
  229. package/docs/AGENT_DEVELOPMENT_POLICY.md +20 -0
  230. package/docs/FRONTEND_POLICY.md +4 -3
  231. package/docs/MCP_SETUP.md +59 -7
  232. package/docs/MCP_TOOLS.md +1 -1
  233. package/docs/SDK_API.md +5 -1
  234. package/package.json +3 -2
@@ -0,0 +1,43 @@
1
+ import { isValidSuiAddress, normalizeSuiAddress } from "@mysten/sui/utils";
2
+ import { parseSuiAddress } from "../suiAddress.js";
3
+ import { TransactionActivityError } from "./transactionActivityTypes.js";
4
+ const MOVE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
+ export function parseSuiFunctionTarget(value) {
6
+ if (value.length === 0 || value.trim() !== value || value.includes("<") || value.includes(">")) {
7
+ throw invalidFunctionTarget();
8
+ }
9
+ const parts = value.split("::");
10
+ if (parts.length !== 3) {
11
+ throw invalidFunctionTarget();
12
+ }
13
+ const [packageId, moduleName, functionName] = parts;
14
+ const normalizedPackage = parseFunctionPackageAddress(packageId);
15
+ if (normalizedPackage === undefined ||
16
+ !MOVE_IDENTIFIER_PATTERN.test(moduleName) ||
17
+ !MOVE_IDENTIFIER_PATTERN.test(functionName)) {
18
+ throw invalidFunctionTarget();
19
+ }
20
+ return {
21
+ package: normalizedPackage,
22
+ module: moduleName,
23
+ function: functionName,
24
+ target: `${normalizedPackage}::${moduleName}::${functionName}`
25
+ };
26
+ }
27
+ function parseFunctionPackageAddress(value) {
28
+ const normalized = parseSuiAddress(value);
29
+ if (normalized !== undefined) {
30
+ return normalized;
31
+ }
32
+ if (!/^0x[0-9a-fA-F]+$/.test(value)) {
33
+ return undefined;
34
+ }
35
+ const normalizedShort = normalizeSuiAddress(value);
36
+ return isValidSuiAddress(normalizedShort) ? normalizedShort : undefined;
37
+ }
38
+ function invalidFunctionTarget() {
39
+ return new TransactionActivityError("input_invalid", "Invalid Sui function target", {
40
+ field: "function",
41
+ reason: "invalid_function_target"
42
+ });
43
+ }
@@ -0,0 +1,189 @@
1
+ import { parseSuiAddress } from "../suiAddress.js";
2
+ export function attachRequestedAccountEffects(transactions, account) {
3
+ return transactions.map((transaction) => ({
4
+ ...transaction,
5
+ accountEffects: requestedAccountEffectsForTransaction(transaction, account)
6
+ }));
7
+ }
8
+ export function buildRequestedAccountActivity(input) {
9
+ const coinFlows = new Map();
10
+ let sentCount = 0;
11
+ let affectedOnlyCount = 0;
12
+ let completeness = "complete";
13
+ for (const transaction of input.transactions) {
14
+ const effects = transaction.accountEffects ?? requestedAccountEffectsForTransaction(transaction, input.account);
15
+ if (effects.sentByAccount) {
16
+ sentCount += 1;
17
+ }
18
+ else {
19
+ affectedOnlyCount += 1;
20
+ }
21
+ completeness = mergeCompleteness(completeness, effects.balanceChangeCompleteness);
22
+ for (const change of effects.balanceChanges) {
23
+ const entry = coinFlows.get(change.coinType) ?? {
24
+ increaseRaw: 0n,
25
+ decreaseRaw: 0n,
26
+ netRaw: 0n,
27
+ digests: new Set()
28
+ };
29
+ const amount = BigInt(change.amountRaw);
30
+ entry.netRaw += amount;
31
+ if (amount > 0n) {
32
+ entry.increaseRaw += amount;
33
+ }
34
+ else if (amount < 0n) {
35
+ entry.decreaseRaw += -amount;
36
+ }
37
+ entry.digests.add(transaction.digest);
38
+ coinFlows.set(change.coinType, entry);
39
+ }
40
+ }
41
+ return {
42
+ account: input.account,
43
+ relationship: input.relationship,
44
+ sentCount,
45
+ affectedOnlyCount,
46
+ balanceChangeCompleteness: completeness,
47
+ coinFlows: [...coinFlows.entries()]
48
+ .map(([coinType, value]) => ({
49
+ coinType,
50
+ increaseRaw: value.increaseRaw.toString(),
51
+ decreaseRaw: value.decreaseRaw.toString(),
52
+ netRaw: value.netRaw.toString(),
53
+ transactionCount: value.digests.size
54
+ }))
55
+ .sort(compareCoinFlowRows)
56
+ };
57
+ }
58
+ export function requestedAccountEffectsForTransaction(transaction, account) {
59
+ const sentByAccount = addressMatches(transaction.sender, account);
60
+ if (transaction.details === undefined) {
61
+ return {
62
+ account,
63
+ scope: "requested_account",
64
+ role: sentByAccount ? "sender" : "affected_only",
65
+ sentByAccount,
66
+ balanceChangeEvidence: "account_balance_changes_unavailable",
67
+ accountBalanceChangeAbsenceProven: false,
68
+ accountBalanceChangeInferencePolicy: "do_not_infer_from_transaction_context",
69
+ balanceChangeCompleteness: "unavailable",
70
+ balanceChanges: [],
71
+ coinFlows: [],
72
+ limitations: ["transaction_details_unavailable"]
73
+ };
74
+ }
75
+ const balanceChanges = transaction.details.balanceChanges
76
+ .filter((change) => addressMatches(change.owner, account))
77
+ .map((change) => ({
78
+ index: change.index,
79
+ coinType: change.coinType,
80
+ amountRaw: change.amountRaw,
81
+ direction: change.direction
82
+ }));
83
+ const balanceChangeCompleteness = transaction.details.truncation.balanceChanges ? "truncated" : "complete";
84
+ const balanceChangeEvidence = balanceChangeEvidenceFor(balanceChangeCompleteness, balanceChanges);
85
+ const accountBalanceChangeAbsenceProven = balanceChangeCompleteness === "complete" && balanceChanges.length === 0;
86
+ return {
87
+ account,
88
+ scope: "requested_account",
89
+ role: sentByAccount ? "sender" : "affected_only",
90
+ sentByAccount,
91
+ balanceChangeEvidence,
92
+ accountBalanceChangeAbsenceProven,
93
+ accountBalanceChangeInferencePolicy: accountBalanceChangeInferencePolicyFor(balanceChangeEvidence, accountBalanceChangeAbsenceProven),
94
+ balanceChangeCompleteness,
95
+ balanceChanges,
96
+ coinFlows: coinFlowsForBalanceChanges(balanceChanges),
97
+ limitations: limitationsForCompleteness(balanceChangeCompleteness)
98
+ };
99
+ }
100
+ function accountBalanceChangeInferencePolicyFor(evidence, absenceProven) {
101
+ if (evidence === "account_balance_changes_returned") {
102
+ return "use_returned_account_balance_changes";
103
+ }
104
+ if (evidence === "no_account_balance_changes_returned" && absenceProven) {
105
+ return "account_absence_proven_by_complete_details";
106
+ }
107
+ return "do_not_infer_from_transaction_context";
108
+ }
109
+ function balanceChangeEvidenceFor(completeness, balanceChanges) {
110
+ if (completeness === "truncated") {
111
+ return "incomplete_account_balance_changes";
112
+ }
113
+ if (completeness === "unavailable") {
114
+ return "account_balance_changes_unavailable";
115
+ }
116
+ return balanceChanges.length === 0
117
+ ? "no_account_balance_changes_returned"
118
+ : "account_balance_changes_returned";
119
+ }
120
+ function coinFlowsForBalanceChanges(balanceChanges) {
121
+ const flows = new Map();
122
+ for (const change of balanceChanges) {
123
+ const amount = BigInt(change.amountRaw);
124
+ const entry = flows.get(change.coinType) ?? {
125
+ increaseRaw: "0",
126
+ decreaseRaw: "0",
127
+ netRaw: "0",
128
+ increaseRawBigInt: 0n,
129
+ decreaseRawBigInt: 0n,
130
+ netRawBigInt: 0n
131
+ };
132
+ entry.netRawBigInt += amount;
133
+ if (amount > 0n) {
134
+ entry.increaseRawBigInt += amount;
135
+ }
136
+ else if (amount < 0n) {
137
+ entry.decreaseRawBigInt += -amount;
138
+ }
139
+ flows.set(change.coinType, entry);
140
+ }
141
+ return [...flows.entries()]
142
+ .map(([coinType, value]) => ({
143
+ coinType,
144
+ increaseRaw: value.increaseRawBigInt.toString(),
145
+ decreaseRaw: value.decreaseRawBigInt.toString(),
146
+ netRaw: value.netRawBigInt.toString()
147
+ }))
148
+ .sort((a, b) => {
149
+ const absoluteNetA = absBigInt(BigInt(a.netRaw));
150
+ const absoluteNetB = absBigInt(BigInt(b.netRaw));
151
+ if (absoluteNetA !== absoluteNetB) {
152
+ return absoluteNetA > absoluteNetB ? -1 : 1;
153
+ }
154
+ return a.coinType.localeCompare(b.coinType);
155
+ });
156
+ }
157
+ function limitationsForCompleteness(completeness) {
158
+ if (completeness === "truncated") {
159
+ return ["provider_balance_changes_truncated"];
160
+ }
161
+ if (completeness === "unavailable") {
162
+ return ["transaction_details_unavailable"];
163
+ }
164
+ return [];
165
+ }
166
+ function mergeCompleteness(current, next) {
167
+ if (current === "truncated" || next === "truncated") {
168
+ return "truncated";
169
+ }
170
+ if (current === "unavailable" || next === "unavailable") {
171
+ return "unavailable";
172
+ }
173
+ return "complete";
174
+ }
175
+ function compareCoinFlowRows(a, b) {
176
+ const absoluteNetA = absBigInt(BigInt(a.netRaw));
177
+ const absoluteNetB = absBigInt(BigInt(b.netRaw));
178
+ if (absoluteNetA !== absoluteNetB) {
179
+ return absoluteNetA > absoluteNetB ? -1 : 1;
180
+ }
181
+ return a.coinType.localeCompare(b.coinType);
182
+ }
183
+ function absBigInt(value) {
184
+ return value < 0n ? -value : value;
185
+ }
186
+ function addressMatches(candidate, account) {
187
+ const normalized = candidate === undefined ? undefined : parseSuiAddress(candidate);
188
+ return normalized !== undefined && normalized === account;
189
+ }
@@ -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
+ }