@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,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_3";
5
+ const protocolActivityPrimaryActionSchema = z.enum(protocolActivityPrimaryActions);
6
+ const protocolActivityConfidenceSchema = 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
+ confidence: protocolActivityConfidenceSchema,
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.confidence === "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 = confidenceRank(a.confidence) - confidenceRank(b.confidence);
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 confidence;
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
+ confidence = strongestConfidence(confidence, "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
+ confidence = strongestConfidence(confidence, "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
+ confidence = strongestConfidence(confidence, "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
+ confidence = strongestConfidence(confidence, "shared_object");
165
+ }
166
+ }
167
+ if (evidence.length === 0 || confidence === undefined) {
168
+ return [];
169
+ }
170
+ addTruncationLimitations(details, limitationSet);
171
+ if (confidence === "shared_object") {
172
+ limitationSet.add("shared_object_match_does_not_prove_wallet_position");
173
+ }
174
+ if (confidence === "event_type" || confidence === "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
+ confidence,
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 strongestConfidence(current, candidate) {
203
+ return current === undefined || confidenceRank(candidate) < confidenceRank(current)
204
+ ? candidate
205
+ : current;
206
+ }
207
+ function confidenceRank(confidence) {
208
+ switch (confidence) {
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
+ }
@@ -0,0 +1,229 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { MIST_PER_SUI, SUI_DECIMALS } from "@mysten/sui/utils";
3
+ import { z } from "zod";
4
+ import { parseSuiAddress } from "../suiAddress.js";
5
+ import { classifySuiDeFiActivity, protocolActivityClassifierMatchSchema } from "./transactionActivityClassifier.js";
6
+ export const EXTERNAL_ACTIVITY_TRANSACTION_DETAIL_JSON_MAX_BYTES = 64 * 1024;
7
+ export const SUI_GAS_UNIT_SOURCE = "@mysten/sui MIST_PER_SUI";
8
+ export const externalActivityGasCostFactSchema = z.object({
9
+ netCostRaw: z.string().regex(/^-?\d+$/),
10
+ rawUnit: z.literal("MIST"),
11
+ display: z.string().regex(/^-?\d+(?:\.\d+)?$/),
12
+ displayUnit: z.literal("SUI"),
13
+ decimals: z.literal(SUI_DECIMALS),
14
+ unitSource: z.literal(SUI_GAS_UNIT_SOURCE)
15
+ }).strict();
16
+ export const externalActivityTransactionDetailSchema = z.object({
17
+ transactionKind: z.string().optional(),
18
+ moveCalls: z.array(z.object({
19
+ commandIndex: z.number().int().nonnegative(),
20
+ package: z.string(),
21
+ module: z.string(),
22
+ function: z.string(),
23
+ target: z.string()
24
+ }).strict()),
25
+ balanceChanges: z.array(z.object({
26
+ index: z.number().int().nonnegative(),
27
+ owner: z.string().optional(),
28
+ coinType: z.string(),
29
+ amountRaw: z.string().regex(/^-?\d+$/),
30
+ direction: z.enum(["increase", "decrease", "zero"])
31
+ }).strict()),
32
+ objectChanges: z.array(z.object({
33
+ index: z.number().int().nonnegative(),
34
+ objectId: z.string(),
35
+ changeKind: z.enum(["created", "mutated", "deleted"]),
36
+ inputType: z.string().optional(),
37
+ outputType: z.string().optional()
38
+ }).strict()),
39
+ events: z.array(z.object({
40
+ sequenceNumber: z.string().regex(/^\d+$/),
41
+ sender: z.string().optional(),
42
+ package: z.string().optional(),
43
+ module: z.string().optional(),
44
+ eventType: z.string().optional()
45
+ }).strict()),
46
+ gas: z.object({
47
+ gasObjectId: z.string().optional(),
48
+ computationCostRaw: z.string().regex(/^\d+$/).optional(),
49
+ storageCostRaw: z.string().regex(/^\d+$/).optional(),
50
+ storageRebateRaw: z.string().regex(/^\d+$/).optional(),
51
+ nonRefundableStorageFeeRaw: z.string().regex(/^\d+$/).optional(),
52
+ netGasCostRaw: z.string().regex(/^-?\d+$/).optional()
53
+ }).strict().optional(),
54
+ executionError: z.object({
55
+ message: z.string(),
56
+ abortCodeRaw: z.string().regex(/^\d+$/).optional(),
57
+ identifier: z.string().optional(),
58
+ instructionOffset: z.number().int().nonnegative().optional(),
59
+ sourceLineNumber: z.number().int().nonnegative().optional(),
60
+ package: z.string().optional(),
61
+ module: z.string().optional(),
62
+ function: z.string().optional()
63
+ }).strict().optional(),
64
+ truncation: z.object({
65
+ moveCalls: z.boolean(),
66
+ balanceChanges: z.boolean(),
67
+ objectChanges: z.boolean(),
68
+ events: z.boolean()
69
+ }).strict()
70
+ }).strict();
71
+ export const externalActivityTransactionCompactFactsSchema = z.object({
72
+ factScope: z.literal("transaction"),
73
+ requestedAccountScoped: z.literal(false),
74
+ moveCallTargets: z.array(z.string()),
75
+ balanceChanges: z.array(z.object({
76
+ coinType: z.string(),
77
+ amountRaw: z.string().regex(/^-?\d+$/),
78
+ direction: z.enum(["increase", "decrease", "zero"]),
79
+ count: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional()
80
+ }).strict()),
81
+ objectChangeCounts: z.object({
82
+ created: z.number().int().nonnegative(),
83
+ mutated: z.number().int().nonnegative(),
84
+ deleted: z.number().int().nonnegative()
85
+ }).strict(),
86
+ eventTypes: z.array(z.string()),
87
+ gasCost: externalActivityGasCostFactSchema.optional(),
88
+ gasNetCostRaw: z.string().regex(/^-?\d+$/).optional(),
89
+ executionError: z.object({
90
+ message: z.string(),
91
+ abortCodeRaw: z.string().regex(/^\d+$/).optional(),
92
+ package: z.string().optional(),
93
+ module: z.string().optional(),
94
+ function: z.string().optional()
95
+ }).strict().optional(),
96
+ detailTruncated: z.boolean(),
97
+ protocolMatches: z.array(protocolActivityClassifierMatchSchema).optional()
98
+ }).strict();
99
+ export function externalActivityTransactionDetailJsonByteLength(value) {
100
+ return Buffer.byteLength(value, "utf8");
101
+ }
102
+ export function sanitizeExternalActivityTransactionDetailsForKnownAccount(details, storageAccount) {
103
+ return {
104
+ ...details,
105
+ balanceChanges: details.balanceChanges.map((change) => {
106
+ if (addressMatches(change.owner, storageAccount)) {
107
+ return { ...change, owner: storageAccount };
108
+ }
109
+ const { owner: _owner, ...withoutOwner } = change;
110
+ return withoutOwner;
111
+ }),
112
+ events: details.events.map((event) => {
113
+ if (addressMatches(event.sender, storageAccount)) {
114
+ return { ...event, sender: storageAccount };
115
+ }
116
+ const { sender: _sender, ...withoutSender } = event;
117
+ return withoutSender;
118
+ })
119
+ };
120
+ }
121
+ export function externalActivityTransactionTouchesAccount(transaction, account) {
122
+ return externalActivityTransactionBalanceOwners(transaction).includes(account);
123
+ }
124
+ export function externalActivityTransactionBalanceOwners(transaction) {
125
+ const owners = transaction.details?.balanceChanges.flatMap((change) => {
126
+ const owner = change.owner === undefined ? undefined : parseSuiAddress(change.owner);
127
+ return owner === undefined ? [] : [owner];
128
+ }) ?? [];
129
+ return [...new Set(owners)];
130
+ }
131
+ export function externalActivityTransactionDetailsReferenceOnlyAccount(details, account) {
132
+ return details.balanceChanges.every((change) => change.owner === undefined || addressMatches(change.owner, account))
133
+ && details.events.every((event) => event.sender === undefined || addressMatches(event.sender, account));
134
+ }
135
+ export function compactExternalActivityTransactionDetails(details) {
136
+ const objectChangeCounts = {
137
+ created: 0,
138
+ mutated: 0,
139
+ deleted: 0
140
+ };
141
+ for (const change of details.objectChanges) {
142
+ objectChangeCounts[change.changeKind] += 1;
143
+ }
144
+ const executionError = details.executionError === undefined
145
+ ? undefined
146
+ : {
147
+ message: details.executionError.message,
148
+ ...(details.executionError.abortCodeRaw === undefined ? {} : { abortCodeRaw: details.executionError.abortCodeRaw }),
149
+ ...(details.executionError.package === undefined ? {} : { package: details.executionError.package }),
150
+ ...(details.executionError.module === undefined ? {} : { module: details.executionError.module }),
151
+ ...(details.executionError.function === undefined ? {} : { function: details.executionError.function })
152
+ };
153
+ const protocolMatches = classifySuiDeFiActivity(details);
154
+ return {
155
+ factScope: "transaction",
156
+ requestedAccountScoped: false,
157
+ moveCallTargets: uniqueStrings(details.moveCalls.map((call) => call.target)),
158
+ balanceChanges: compactBalanceChanges(details.balanceChanges),
159
+ objectChangeCounts,
160
+ eventTypes: uniqueStrings(details.events.flatMap((event) => event.eventType === undefined ? [] : [event.eventType])),
161
+ ...(details.gas?.netGasCostRaw === undefined ? {} : { gasCost: suiGasCostFact(details.gas.netGasCostRaw) }),
162
+ ...(details.gas?.netGasCostRaw === undefined ? {} : { gasNetCostRaw: details.gas.netGasCostRaw }),
163
+ ...(executionError === undefined ? {} : { executionError }),
164
+ detailTruncated: details.truncation.moveCalls
165
+ || details.truncation.balanceChanges
166
+ || details.truncation.objectChanges
167
+ || details.truncation.events,
168
+ ...(protocolMatches.length === 0 ? {} : { protocolMatches })
169
+ };
170
+ }
171
+ export function suiGasCostFact(netCostRaw) {
172
+ return {
173
+ netCostRaw,
174
+ rawUnit: "MIST",
175
+ display: formatSignedMistAsSui(netCostRaw),
176
+ displayUnit: "SUI",
177
+ decimals: SUI_DECIMALS,
178
+ unitSource: SUI_GAS_UNIT_SOURCE
179
+ };
180
+ }
181
+ function formatSignedMistAsSui(raw) {
182
+ if (!/^-?\d+$/.test(raw)) {
183
+ throw new Error("gas raw amount must be a signed integer string");
184
+ }
185
+ const negative = raw.startsWith("-");
186
+ const unsignedRaw = negative ? raw.slice(1) : raw;
187
+ const amount = BigInt(unsignedRaw);
188
+ const whole = amount / MIST_PER_SUI;
189
+ const fractional = amount % MIST_PER_SUI;
190
+ const sign = negative && amount !== 0n ? "-" : "";
191
+ if (fractional === 0n) {
192
+ return `${sign}${whole.toString()}`;
193
+ }
194
+ const fractionalText = fractional
195
+ .toString()
196
+ .padStart(SUI_DECIMALS, "0")
197
+ .replace(/0+$/, "");
198
+ return `${sign}${whole.toString()}.${fractionalText}`;
199
+ }
200
+ function compactBalanceChanges(balanceChanges) {
201
+ const compacted = new Map();
202
+ for (const change of balanceChanges) {
203
+ const key = `${change.coinType}\n${change.amountRaw}\n${change.direction}`;
204
+ const existing = compacted.get(key);
205
+ if (existing === undefined) {
206
+ compacted.set(key, {
207
+ coinType: change.coinType,
208
+ amountRaw: change.amountRaw,
209
+ direction: change.direction,
210
+ count: 1
211
+ });
212
+ continue;
213
+ }
214
+ existing.count += 1;
215
+ }
216
+ return [...compacted.values()].map((change) => ({
217
+ coinType: change.coinType,
218
+ amountRaw: change.amountRaw,
219
+ direction: change.direction,
220
+ ...(change.count === 1 ? {} : { count: change.count })
221
+ }));
222
+ }
223
+ function uniqueStrings(values) {
224
+ return [...new Set(values)];
225
+ }
226
+ function addressMatches(candidate, account) {
227
+ const normalized = candidate === undefined ? undefined : parseSuiAddress(candidate);
228
+ return normalized !== undefined && normalized === account;
229
+ }