@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,362 @@
1
+ import { Trade } from "@flowx-finance/sdk";
2
+ import { bcs } from "@mysten/sui/bcs";
3
+ import { Transaction } from "@mysten/sui/transactions";
4
+ import { fromBase64, normalizeSuiObjectId } from "@mysten/sui/utils";
5
+ import { FLOWX_CLMM_MAINNET } from "../../core/read/flowxRegistry.js";
6
+ import { LocalTransactionMaterialStoreError } from "../../core/session/transactionMaterialStore.js";
7
+ import { suiTransactionDigestSchema } from "../../core/suiAddress.js";
8
+ import { failReviewCheck, passReviewCheck } from "../../core/review/reviewComputationResult.js";
9
+ const FLOWX_SWAP_GAS_BUDGET_MIST = 50000000n;
10
+ const SUI_COIN_TYPE = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI";
11
+ const SUI_CLOCK_OBJECT_ID = "0x0000000000000000000000000000000000000000000000000000000000000006";
12
+ const MOVE_STDLIB_PACKAGE_ID = "0x0000000000000000000000000000000000000000000000000000000000000001";
13
+ const SUI_FRAMEWORK_PACKAGE_ID = "0x0000000000000000000000000000000000000000000000000000000000000002";
14
+ export function createFlowxSwapTransactionMaterialProducer(options) {
15
+ const buildBytes = options.buildSwapTransactionBytes ?? sdkTradeBytesBuilder(options.client);
16
+ return async (input) => {
17
+ if (options.network !== "mainnet" || options.chainIdentifier !== options.expectedChainIdentifier) {
18
+ return {
19
+ status: "blocked",
20
+ blockedReason: "network_mismatch",
21
+ checks: [
22
+ failReviewCheck("flowx_transaction_material_network_mismatch", "Transaction material network", "FlowX transaction material build requires a verified Sui mainnet gRPC endpoint and matching mainnet chain identifier.", "network")
23
+ ]
24
+ };
25
+ }
26
+ if (input.quoteEvidence.swapXToY !== input.quotePolicy.swapXToY) {
27
+ return {
28
+ status: "blocked",
29
+ blockedReason: "amount_mismatch",
30
+ checks: [
31
+ failReviewCheck("flowx_transaction_material_direction_mismatch", "Transaction material quote policy", "FlowX transaction material build requires route quote evidence and quote policy to describe the same swap direction.", "adapter")
32
+ ]
33
+ };
34
+ }
35
+ const quoteExpiresAt = new Date(Date.parse(input.quotePolicy.fetchedAt) + input.quotePolicy.staleAfterMs);
36
+ if (quoteExpiresAt.getTime() <= input.now.getTime()) {
37
+ return {
38
+ status: "refresh_required",
39
+ refreshReason: "quote_stale",
40
+ checks: [
41
+ failReviewCheck("flowx_transaction_material_quote_expired", "Transaction material quote policy", "FlowX transaction material was not built because the quote policy expired before build.", "quote")
42
+ ]
43
+ };
44
+ }
45
+ try {
46
+ const sourceAmountRaw = BigInt(input.quotePolicy.sourceAmountRaw);
47
+ const source = input.pairEvidence.source;
48
+ const requirements = source.coinType === SUI_COIN_TYPE
49
+ ? [{ symbol: source.symbol, coinType: SUI_COIN_TYPE, decimals: source.decimals, requiredRaw: sourceAmountRaw + FLOWX_SWAP_GAS_BUDGET_MIST }]
50
+ : [
51
+ { symbol: source.symbol, coinType: source.coinType, decimals: source.decimals, requiredRaw: sourceAmountRaw },
52
+ { symbol: "SUI", coinType: SUI_COIN_TYPE, decimals: 9, requiredRaw: FLOWX_SWAP_GAS_BUDGET_MIST }
53
+ ];
54
+ for (const requirement of requirements) {
55
+ const balance = await options.client.core.getBalance({
56
+ owner: input.account,
57
+ coinType: requirement.coinType
58
+ });
59
+ const heldRaw = BigInt(balance.balance.balance);
60
+ if (heldRaw < requirement.requiredRaw) {
61
+ const isGasOnly = requirement.coinType === SUI_COIN_TYPE && source.coinType !== SUI_COIN_TYPE;
62
+ return {
63
+ status: "blocked",
64
+ blockedReason: "insufficient_balance",
65
+ checks: [
66
+ failReviewCheck("flowx_transaction_material_build_failed", "Transaction material build", `Insufficient balance: this swap needs ${displayRaw(requirement.requiredRaw, requirement.decimals)} ${requirement.symbol}${isGasOnly ? " for gas" : requirement.coinType === SUI_COIN_TYPE ? " (amount + gas)" : ""}, but the account holds ${displayRaw(heldRaw, requirement.decimals)} ${requirement.symbol}. Nothing was signed and no funds moved.`, "adapter")
67
+ ]
68
+ };
69
+ }
70
+ }
71
+ const transactionBytes = await buildBytes({
72
+ account: input.account,
73
+ quotePolicy: input.quotePolicy,
74
+ sdkRoutes: input.quoteEvidence.sdkRoutes
75
+ });
76
+ const verification = await verifyFlowxSwapMaterialBytes({
77
+ transactionBytes,
78
+ quotePolicy: input.quotePolicy
79
+ });
80
+ if (verification.status === "failed") {
81
+ return {
82
+ status: "blocked",
83
+ blockedReason: verification.blockedReason,
84
+ checks: [
85
+ failReviewCheck("flowx_transaction_material_bytes_verification_failed", "Transaction material verification", verification.message, "adapter")
86
+ ]
87
+ };
88
+ }
89
+ options.materialStore.deleteReviewSessionTransactionMaterials(input.reviewSessionId);
90
+ const handle = options.materialStore.recordTransactionMaterial({
91
+ reviewSessionId: input.reviewSessionId,
92
+ planId: input.plan.id,
93
+ account: input.account,
94
+ kind: "flowx_swap_transaction_data",
95
+ source: "say_ur_intent_built",
96
+ transactionBytes,
97
+ expiresAt: quoteExpiresAt,
98
+ redactedDiagnostics: {
99
+ protocol: input.plan.protocol,
100
+ adapterId: input.plan.adapterId,
101
+ actionKind: input.plan.actionKind,
102
+ poolKeys: input.quoteEvidence.pools.map((pool) => pool.poolKey),
103
+ swapXToY: input.quotePolicy.swapXToY,
104
+ quoteFetchedAt: input.quotePolicy.fetchedAt,
105
+ quoteExpiresAt: quoteExpiresAt.toISOString(),
106
+ sourceAmountRaw: input.quotePolicy.sourceAmountRaw,
107
+ expectedOutRaw: input.quotePolicy.expectedOutRaw,
108
+ minOutRaw: input.quotePolicy.minOutRaw,
109
+ routerSlippageUnits: input.quotePolicy.routerSlippageUnits,
110
+ deadlineMsEpoch: input.quotePolicy.deadlineMsEpoch
111
+ }
112
+ }, input.now);
113
+ return {
114
+ status: "completed",
115
+ evidence: handle,
116
+ checks: [
117
+ passReviewCheck("flowx_transaction_material_built", "Transaction material build", "Built account-bound FlowX swap transaction material locally from the quoted route and verified inside the bytes that every Move call targets pinned FlowX packages, every shared object is a pinned FlowX config object, and the router arguments carry the policy expected-output, slippage, and deadline. The unsigned bytes stay only in the local material store until quote expiry.", "adapter")
118
+ ]
119
+ };
120
+ }
121
+ catch (error) {
122
+ const blockedReason = blockedReasonForBuildError(error);
123
+ return {
124
+ status: "blocked",
125
+ blockedReason,
126
+ checks: [
127
+ failReviewCheck("flowx_transaction_material_build_failed", "Transaction material build", buildFailureMessage(blockedReason), "adapter")
128
+ ]
129
+ };
130
+ }
131
+ };
132
+ }
133
+ export function createFlowxSwapTransactionMaterialDigestProducer(options) {
134
+ return async (input) => {
135
+ const material = options.materialStore.getTransactionMaterial(input.materialHandle, input.now);
136
+ if (!material) {
137
+ return {
138
+ status: "refresh_required",
139
+ refreshReason: "quote_stale",
140
+ checks: [
141
+ failReviewCheck("flowx_transaction_material_digest_unavailable", "Transaction material digest", "FlowX transaction material digest was not computed because the stored local material was unavailable or expired; refresh the review evidence before continuing.", "adapter")
142
+ ]
143
+ };
144
+ }
145
+ let transactionDigest;
146
+ try {
147
+ transactionDigest = await Transaction.from(material.transactionBytes).getDigest();
148
+ }
149
+ catch {
150
+ return {
151
+ status: "blocked",
152
+ blockedReason: "object_resolution_failed",
153
+ checks: [
154
+ failReviewCheck("flowx_transaction_material_digest_failed", "Transaction material digest", "FlowX transaction material digest could not be derived from the stored local transaction bytes.", "adapter")
155
+ ]
156
+ };
157
+ }
158
+ const parsedDigest = suiTransactionDigestSchema.safeParse(transactionDigest);
159
+ if (!parsedDigest.success) {
160
+ return {
161
+ status: "blocked",
162
+ blockedReason: "object_resolution_failed",
163
+ checks: [
164
+ failReviewCheck("flowx_transaction_material_digest_invalid", "Transaction material digest", "FlowX transaction material digest did not match the pinned Sui SDK transaction digest format.", "adapter")
165
+ ]
166
+ };
167
+ }
168
+ return {
169
+ status: "completed",
170
+ evidence: {
171
+ materialId: material.materialId,
172
+ reviewSessionId: material.reviewSessionId,
173
+ planId: material.planId,
174
+ account: material.account,
175
+ kind: material.kind,
176
+ source: material.source,
177
+ digestKind: "sui_transaction_digest",
178
+ transactionDigest: parsedDigest.data,
179
+ computedAt: input.now.toISOString(),
180
+ expiresAt: material.expiresAt
181
+ },
182
+ checks: [
183
+ passReviewCheck("flowx_transaction_material_digest_commitment", "Transaction material digest", "Derived a Sui transaction digest from the stored local unsigned transaction material. The digest and bytes remain internal until later review stages bind object ownership, human-readable review, and simulation evidence.", "adapter")
184
+ ]
185
+ };
186
+ };
187
+ }
188
+ function sdkTradeBytesBuilder(client) {
189
+ return async ({ account, quotePolicy, sdkRoutes }) => {
190
+ const trade = new Trade({
191
+ network: "mainnet",
192
+ sender: account,
193
+ recipient: account,
194
+ amountIn: quotePolicy.sourceAmountRaw,
195
+ amountOut: quotePolicy.expectedOutRaw,
196
+ slippage: quotePolicy.routerSlippageUnits,
197
+ deadline: quotePolicy.deadlineMsEpoch,
198
+ // The route entities come from the same quoter response that produced
199
+ // the validated quote evidence; the byte verification below re-checks
200
+ // every pinned target and policy number inside the built bytes.
201
+ routes: sdkRoutes
202
+ });
203
+ const transaction = await trade.buildTransaction({ client });
204
+ transaction.setSenderIfNotSet(account);
205
+ transaction.setGasBudget(FLOWX_SWAP_GAS_BUDGET_MIST);
206
+ return transaction.build({ client });
207
+ };
208
+ }
209
+ /**
210
+ * Re-derive the safety-relevant facts from the built transaction bytes and
211
+ * compare them with the pinned registry and the derived quote policy. The
212
+ * bytes are the only authority: a mismatch blocks the review.
213
+ */
214
+ export async function verifyFlowxSwapMaterialBytes(input) {
215
+ let data;
216
+ try {
217
+ data = Transaction.from(input.transactionBytes).getData();
218
+ }
219
+ catch {
220
+ return {
221
+ status: "failed",
222
+ blockedReason: "object_resolution_failed",
223
+ message: "FlowX transaction material bytes could not be parsed as a Sui transaction."
224
+ };
225
+ }
226
+ const allowedPackages = new Set([
227
+ MOVE_STDLIB_PACKAGE_ID,
228
+ SUI_FRAMEWORK_PACKAGE_ID,
229
+ FLOWX_CLMM_MAINNET.universalRouter.packageId,
230
+ FLOWX_CLMM_MAINNET.universalRouter.wrappedRouterPackageId
231
+ ].map((id) => normalizeSuiObjectId(id)));
232
+ const allowedSharedObjects = new Set([
233
+ SUI_CLOCK_OBJECT_ID,
234
+ FLOWX_CLMM_MAINNET.universalRouter.treasuryObjectId,
235
+ FLOWX_CLMM_MAINNET.universalRouter.tradeIdTrackerObjectId,
236
+ FLOWX_CLMM_MAINNET.universalRouter.partnerRegistryObjectId,
237
+ FLOWX_CLMM_MAINNET.universalRouter.versionedObjectId,
238
+ FLOWX_CLMM_MAINNET.poolRegistry.objectId,
239
+ FLOWX_CLMM_MAINNET.versioned.objectId
240
+ ].map((id) => normalizeSuiObjectId(id)));
241
+ let routerBuildCall;
242
+ for (const command of data.commands) {
243
+ if (command.$kind !== "MoveCall" || !command.MoveCall) {
244
+ continue;
245
+ }
246
+ const moveCall = command.MoveCall;
247
+ const packageId = normalizeSuiObjectId(moveCall.package);
248
+ if (!allowedPackages.has(packageId)) {
249
+ return {
250
+ status: "failed",
251
+ blockedReason: "object_resolution_failed",
252
+ message: `FlowX transaction material calls a package outside the pinned FlowX set: ${moveCall.package}::${moveCall.module}::${moveCall.function}.`
253
+ };
254
+ }
255
+ if (packageId === normalizeSuiObjectId(FLOWX_CLMM_MAINNET.universalRouter.packageId) &&
256
+ moveCall.module === "universal_router" &&
257
+ moveCall.function === "build") {
258
+ if (routerBuildCall !== undefined) {
259
+ return {
260
+ status: "failed",
261
+ blockedReason: "object_resolution_failed",
262
+ message: "FlowX transaction material contains more than one universal_router::build call."
263
+ };
264
+ }
265
+ routerBuildCall = { arguments: moveCall.arguments };
266
+ }
267
+ }
268
+ if (!routerBuildCall) {
269
+ return {
270
+ status: "failed",
271
+ blockedReason: "object_resolution_failed",
272
+ message: "FlowX transaction material does not contain the universal_router::build call."
273
+ };
274
+ }
275
+ for (const inputEntry of data.inputs) {
276
+ if (inputEntry.$kind !== "Object" || !inputEntry.Object) {
277
+ continue;
278
+ }
279
+ const objectInput = inputEntry.Object;
280
+ if (objectInput.$kind === "SharedObject" && objectInput.SharedObject) {
281
+ const objectId = normalizeSuiObjectId(objectInput.SharedObject.objectId);
282
+ if (!allowedSharedObjects.has(objectId)) {
283
+ return {
284
+ status: "failed",
285
+ blockedReason: "object_resolution_failed",
286
+ message: `FlowX transaction material references a shared object outside the pinned FlowX set: ${objectId}.`
287
+ };
288
+ }
289
+ }
290
+ }
291
+ const expectations = [
292
+ { label: "expected output", argumentIndex: 4, expected: BigInt(input.quotePolicy.expectedOutRaw) },
293
+ { label: "slippage", argumentIndex: 5, expected: BigInt(input.quotePolicy.routerSlippageUnits) },
294
+ { label: "deadline", argumentIndex: 6, expected: BigInt(input.quotePolicy.deadlineMsEpoch) }
295
+ ];
296
+ for (const expectation of expectations) {
297
+ const actual = pureU64ArgumentValue(data, routerBuildCall.arguments, expectation.argumentIndex);
298
+ if (actual === undefined) {
299
+ return {
300
+ status: "failed",
301
+ blockedReason: "amount_mismatch",
302
+ message: `FlowX transaction material router ${expectation.label} argument is not a pure u64 input.`
303
+ };
304
+ }
305
+ if (actual !== expectation.expected) {
306
+ return {
307
+ status: "failed",
308
+ blockedReason: "amount_mismatch",
309
+ message: `FlowX transaction material router ${expectation.label} is ${actual}, but the derived quote policy requires ${expectation.expected}.`
310
+ };
311
+ }
312
+ }
313
+ return { status: "ok" };
314
+ }
315
+ function pureU64ArgumentValue(data, callArguments, argumentIndex) {
316
+ const argument = callArguments[argumentIndex];
317
+ if (typeof argument !== "object" || argument === null) {
318
+ return undefined;
319
+ }
320
+ const argumentRecord = argument;
321
+ if (argumentRecord.$kind !== "Input" || typeof argumentRecord.Input !== "number") {
322
+ return undefined;
323
+ }
324
+ const inputEntry = data.inputs[argumentRecord.Input];
325
+ if (!inputEntry || inputEntry.$kind !== "Pure" || !inputEntry.Pure) {
326
+ return undefined;
327
+ }
328
+ try {
329
+ return BigInt(bcs.U64.parse(fromBase64(inputEntry.Pure.bytes)));
330
+ }
331
+ catch {
332
+ return undefined;
333
+ }
334
+ }
335
+ function displayRaw(raw, decimals) {
336
+ const base = 10n ** BigInt(decimals);
337
+ const whole = raw / base;
338
+ const fraction = (raw % base).toString().padStart(decimals, "0").replace(/0+$/, "");
339
+ return fraction ? `${whole}.${fraction}` : whole.toString();
340
+ }
341
+ function blockedReasonForBuildError(error) {
342
+ if (error instanceof LocalTransactionMaterialStoreError) {
343
+ return "object_resolution_failed";
344
+ }
345
+ const message = error instanceof Error ? error.message : String(error);
346
+ if (/insufficient ?coin ?balance|insufficient balance/i.test(message)) {
347
+ return "insufficient_balance";
348
+ }
349
+ if (/gas/i.test(message) && /insufficient|no valid|payment|budget/i.test(message)) {
350
+ return "insufficient_gas";
351
+ }
352
+ return "object_resolution_failed";
353
+ }
354
+ function buildFailureMessage(blockedReason) {
355
+ if (blockedReason === "insufficient_balance") {
356
+ return "FlowX transaction material build failed before wallet handoff because the account does not have enough source or fee assets.";
357
+ }
358
+ if (blockedReason === "insufficient_gas") {
359
+ return "FlowX transaction material build failed before wallet handoff because a usable gas payment could not be resolved.";
360
+ }
361
+ return "FlowX transaction material build failed before wallet handoff because required account-bound objects could not be resolved.";
362
+ }
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+ import { createDeepbookSwapActionPlan, DEEPBOOK_SWAP_ACTION_KIND, DEEPBOOK_SWAP_ADAPTER_ID, DEEPBOOK_SWAP_PROTOCOL } from "./deepbook/deepbookSwapIntent.js";
3
+ import { createFlowxSwapActionPlan, FLOWX_SWAP_ACTION_KIND, FLOWX_SWAP_ADAPTER_ID, FLOWX_SWAP_PROTOCOL } from "./flowx/flowxSwapIntent.js";
4
+ /**
5
+ * Protocol-neutral swap intent. The optional `protocol` field carries the
6
+ * protocol slug (same slug vocabulary as the adapter prompt surfaces). When
7
+ * several protocols support the same action kind, the caller must name one -
8
+ * the entry point never picks a venue silently.
9
+ */
10
+ export const swapIntentInputSchema = z.object({
11
+ type: z.literal("swap"),
12
+ from: z.object({
13
+ symbol: z.string().min(1),
14
+ amount: z.string().min(1)
15
+ }),
16
+ to: z.object({
17
+ symbol: z.string().min(1)
18
+ }),
19
+ maxSlippageBps: z.number().int().min(1).max(1000),
20
+ protocol: z
21
+ .string()
22
+ .min(1)
23
+ .optional()
24
+ .describe("Protocol slug, required only when several protocols support the action")
25
+ });
26
+ export const INTENT_PLAN_FACTORIES = [
27
+ {
28
+ adapterId: DEEPBOOK_SWAP_ADAPTER_ID,
29
+ actionKind: DEEPBOOK_SWAP_ACTION_KIND,
30
+ protocolSlug: "deep",
31
+ protocol: DEEPBOOK_SWAP_PROTOCOL,
32
+ createPlan: (intent, now) => createDeepbookSwapActionPlan({ ...intent, type: "swap" }, now)
33
+ },
34
+ {
35
+ adapterId: FLOWX_SWAP_ADAPTER_ID,
36
+ actionKind: FLOWX_SWAP_ACTION_KIND,
37
+ protocolSlug: "flowx",
38
+ protocol: FLOWX_SWAP_PROTOCOL,
39
+ createPlan: (intent, now) => createFlowxSwapActionPlan({ ...intent, type: "swap" }, now)
40
+ }
41
+ ];
42
+ export function resolveIntentPlanFactory(factories, actionKind, protocolSlug) {
43
+ const group = factories.filter((factory) => factory.actionKind === actionKind);
44
+ if (group.length === 0) {
45
+ return { status: "unsupported_action", actionKind };
46
+ }
47
+ const available = group.map((factory) => factory.protocolSlug);
48
+ if (protocolSlug !== undefined) {
49
+ const factory = group.find((candidate) => candidate.protocolSlug === protocolSlug.trim());
50
+ if (!factory) {
51
+ return { status: "unknown_protocol", protocolSlug, available };
52
+ }
53
+ return { status: "resolved", factory };
54
+ }
55
+ if (group.length === 1 && group[0]) {
56
+ return { status: "resolved", factory: group[0] };
57
+ }
58
+ return { status: "protocol_choice_required", available };
59
+ }
@@ -0,0 +1,81 @@
1
+ import { computeDeepbookSwapReviewEvidence } from "./deepbook/deepbookReviewEvidence.js";
2
+ import { DEEPBOOK_SWAP_ACTION_KIND, DEEPBOOK_SWAP_ADAPTER_ID, DEEPBOOK_SWAP_PROTOCOL, isDeepbookSwapActionPlanIdentity } from "./deepbook/deepbookSwapIntent.js";
3
+ import { DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGE_CATALOG_ID } from "./deepbook/deepbookReviewLifecycle.js";
4
+ import { computeFlowxSwapReviewEvidence } from "./flowx/flowxSwapReviewEvidence.js";
5
+ import { FLOWX_SWAP_ACTION_KIND, FLOWX_SWAP_ADAPTER_ID, FLOWX_SWAP_PROTOCOL, isFlowxSwapActionPlanIdentity } from "./flowx/flowxSwapIntent.js";
6
+ import { FLOWX_SWAP_REVIEW_LIFECYCLE_STAGE_CATALOG_ID } from "./flowx/flowxSwapReviewLifecycle.js";
7
+ import { unsupportedDeepbookSwapPlanIdentityCheck, unsupportedFlowxSwapPlanIdentityCheck } from "../core/review/reviewChecks.js";
8
+ import { blockedReviewResult } from "../core/review/reviewComputationResult.js";
9
+ export function buildSupportedReviewAdapterDescriptors(wiring) {
10
+ const descriptors = [
11
+ {
12
+ adapterId: DEEPBOOK_SWAP_ADAPTER_ID,
13
+ protocol: DEEPBOOK_SWAP_PROTOCOL,
14
+ actionKind: DEEPBOOK_SWAP_ACTION_KIND,
15
+ stageCatalogId: DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGE_CATALOG_ID,
16
+ computeReview: deepbookSwapEvidenceComputer(wiring.deepbook)
17
+ }
18
+ ];
19
+ if (wiring.flowx) {
20
+ descriptors.push({
21
+ adapterId: FLOWX_SWAP_ADAPTER_ID,
22
+ protocol: FLOWX_SWAP_PROTOCOL,
23
+ actionKind: FLOWX_SWAP_ACTION_KIND,
24
+ stageCatalogId: FLOWX_SWAP_REVIEW_LIFECYCLE_STAGE_CATALOG_ID,
25
+ computeReview: flowxSwapEvidenceComputer(wiring.flowx)
26
+ });
27
+ }
28
+ return descriptors;
29
+ }
30
+ function deepbookSwapEvidenceComputer(wiring) {
31
+ return async (input) => {
32
+ if (!isDeepbookSwapActionPlanIdentity(input.plan)) {
33
+ return {
34
+ result: blockedReviewResult("unsupported_action", [unsupportedDeepbookSwapPlanIdentityCheck()])
35
+ };
36
+ }
37
+ return computeDeepbookSwapReviewEvidence({
38
+ reviewSessionId: input.reviewSessionId,
39
+ plan: input.plan,
40
+ account: input.account,
41
+ now: input.now,
42
+ quoteSource: wiring.deepbookQuoteSource,
43
+ deepBalanceSource: wiring.deepbookDeepBalanceSource,
44
+ transactionMaterialProducer: wiring.deepbookTransactionMaterialProducer,
45
+ transactionMaterialDigestProducer: wiring.deepbookTransactionMaterialDigestProducer,
46
+ transactionObjectOwnershipProducer: wiring.transactionObjectOwnershipProducer,
47
+ humanReadableReviewProducer: wiring.deepbookHumanReadableReviewProducer,
48
+ reviewTimeSimulationProducer: wiring.reviewTimeSimulationProducer,
49
+ ptbVisualizationProducer: wiring.ptbVisualizationProducer
50
+ });
51
+ };
52
+ }
53
+ function flowxSwapEvidenceComputer(wiring) {
54
+ return async (input) => {
55
+ if (!isFlowxSwapActionPlanIdentity(input.plan)) {
56
+ return {
57
+ result: blockedReviewResult("unsupported_action", [unsupportedFlowxSwapPlanIdentityCheck()])
58
+ };
59
+ }
60
+ return computeFlowxSwapReviewEvidence({
61
+ reviewSessionId: input.reviewSessionId,
62
+ plan: input.plan,
63
+ account: input.account,
64
+ now: input.now,
65
+ quoteSource: wiring.flowxQuoteSource,
66
+ transactionMaterialProducer: wiring.flowxTransactionMaterialProducer,
67
+ transactionMaterialDigestProducer: wiring.flowxTransactionMaterialDigestProducer,
68
+ transactionObjectOwnershipProducer: wiring.transactionObjectOwnershipProducer,
69
+ humanReadableReviewProducer: wiring.flowxHumanReadableReviewProducer,
70
+ reviewTimeSimulationProducer: wiring.reviewTimeSimulationProducer,
71
+ ptbVisualizationProducer: wiring.ptbVisualizationProducer
72
+ });
73
+ };
74
+ }
75
+ export function buildSupportedReviewAdapters(wiring) {
76
+ const adapters = {};
77
+ for (const descriptor of buildSupportedReviewAdapterDescriptors(wiring)) {
78
+ adapters[descriptor.adapterId] = descriptor.computeReview;
79
+ }
80
+ return adapters;
81
+ }
@@ -0,0 +1,12 @@
1
+ export function createAdapterLifecycleValidator(validatorsByStageCatalogId) {
2
+ return (lifecycle) => {
3
+ const validator = validatorsByStageCatalogId[lifecycle.stageCatalogId];
4
+ if (!validator) {
5
+ throw new Error(`Unsupported adapter lifecycle stage catalog: ${lifecycle.stageCatalogId}`);
6
+ }
7
+ validator(lifecycle);
8
+ };
9
+ }
10
+ export const rejectAdapterLifecycle = (lifecycle) => {
11
+ throw new Error(`Adapter lifecycle is not accepted here: ${lifecycle.stageCatalogId}`);
12
+ };
@@ -0,0 +1,43 @@
1
+ const FORBIDDEN_EDGE_TERMS = [
2
+ "bytes",
3
+ "signature",
4
+ "sessiontoken",
5
+ "reviewtoken",
6
+ "wallettoken",
7
+ "fragmenttoken",
8
+ "tokenhash",
9
+ "privatekey",
10
+ "secretkey",
11
+ "seed",
12
+ "mnemonic"
13
+ ];
14
+ const FORBIDDEN_PREFIX_OR_SUFFIX_TERMS = ["serialized", "signable"];
15
+ function isForbiddenFieldKey(key) {
16
+ const normalized = key.toLowerCase();
17
+ return (FORBIDDEN_EDGE_TERMS.some((term) => normalized === term || normalized.endsWith(term)) ||
18
+ FORBIDDEN_PREFIX_OR_SUFFIX_TERMS.some((term) => normalized === term || normalized.startsWith(term) || normalized.endsWith(term)));
19
+ }
20
+ export function findForbiddenMcpFields(value, path = "$") {
21
+ if (value === null || typeof value !== "object") {
22
+ return [];
23
+ }
24
+ if (Array.isArray(value)) {
25
+ return value.flatMap((item, index) => findForbiddenMcpFields(item, `${path}[${index}]`));
26
+ }
27
+ return Object.entries(value).flatMap(([key, nested]) => {
28
+ const normalized = key.toLowerCase();
29
+ const currentPath = `${path}.${key}`;
30
+ // Deliberately conservative: MCP tool output and stored evidence should not
31
+ // carry raw byte, signing, token, signature, seed, or key material under
32
+ // direct prefix/suffix key names. Generic domain keys such as tokenSymbol
33
+ // and bytesPerSecond remain valid.
34
+ const current = isForbiddenFieldKey(normalized) ? [currentPath] : [];
35
+ return current.concat(findForbiddenMcpFields(nested, currentPath));
36
+ });
37
+ }
38
+ export function assertNoForbiddenMcpFields(value) {
39
+ const forbidden = findForbiddenMcpFields(value);
40
+ if (forbidden.length > 0) {
41
+ throw new Error(`MCP payload contains forbidden field(s): ${forbidden.join(", ")}`);
42
+ }
43
+ }