@zktx.io/sui-mcp 0.5.0 → 0.6.1

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 (138) hide show
  1. package/README.md +21 -21
  2. package/dist/adapters/adapterPromptSurfaces.js +0 -20
  3. package/dist/adapters/deepbook/deepbookQuotePolicy.js +0 -4
  4. package/dist/adapters/deepbook/deepbookReviewEvidence.js +0 -9
  5. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +0 -9
  6. package/dist/adapters/intentPlanFactories.js +0 -6
  7. package/dist/core/action/contractNameRegistry.js +0 -53
  8. package/dist/core/action/forbiddenFields.js +0 -4
  9. package/dist/core/action/ptbVisualizationProducer.js +0 -3
  10. package/dist/core/action/receiptPtbGraph.js +0 -17
  11. package/dist/core/action/reviewTimeSimulationEvidence.js +0 -4
  12. package/dist/core/action/signableAdapterContract.js +0 -5
  13. package/dist/core/action/suiChainReceiptMapping.js +0 -18
  14. package/dist/core/action/suiChainReceiptReader.js +1 -21
  15. package/dist/core/action/suiChainReceiptVerifier.js +0 -10
  16. package/dist/core/action/transactionFunding.js +0 -7
  17. package/dist/core/action/types.js +0 -1
  18. package/dist/core/action/walletReviewContractAssembler.js +0 -2
  19. package/dist/core/activity/localDataService.js +65 -52
  20. package/dist/core/activity/localDataValidation.js +1 -3
  21. package/dist/core/activity/sqliteActivityStore.js +24 -24
  22. package/dist/core/activity/sqliteActivityStoreRows.js +0 -2
  23. package/dist/core/activity/sqliteActivityStoreSchema.js +81 -14
  24. package/dist/core/preferences/sqlitePreferencesRepository.js +5 -3
  25. package/dist/core/read/deepbookReadHelpers.js +0 -2
  26. package/dist/core/read/deepbookRegistry.js +0 -19
  27. package/dist/core/read/deepbookUsdcChartService.js +0 -2
  28. package/dist/core/read/readCardResult.js +0 -2
  29. package/dist/core/read/readService.js +0 -19
  30. package/dist/core/read/readServiceTypes.js +0 -2
  31. package/dist/core/session/cardSession.js +1 -5
  32. package/dist/core/session/cardSessionStore.js +54 -7
  33. package/dist/core/session/reviewValidity.js +0 -3
  34. package/dist/core/session/sessionRecordStore.js +0 -1
  35. package/dist/core/session/sessionStore.js +0 -32
  36. package/dist/core/session/settingsFileIdentity.js +9 -0
  37. package/dist/core/session/settingsFileProtocol.js +20 -0
  38. package/dist/core/session/settingsFileStore.js +337 -0
  39. package/dist/core/session/settingsInputError.js +32 -0
  40. package/dist/core/session/settingsOperation.js +220 -0
  41. package/dist/core/session/settingsPreparationSql.js +13 -0
  42. package/dist/core/session/settingsWorkflow.js +1183 -0
  43. package/dist/core/session/sqliteCardStore.js +0 -2
  44. package/dist/core/session/sqliteSessionStore.js +0 -51
  45. package/dist/core/session/sqliteSettingsWorkflowStore.js +453 -0
  46. package/dist/core/session/sqliteTransactionMaterialStore.js +0 -11
  47. package/dist/core/session/sqliteWalletWorkflowStore.js +160 -66
  48. package/dist/core/session/status.js +2 -4
  49. package/dist/core/session/transactionMaterialStore.js +0 -4
  50. package/dist/core/session/transactionRequest.js +0 -4
  51. package/dist/core/session/walletConnection.js +26 -10
  52. package/dist/core/session/walletWorkflow.js +70 -30
  53. package/dist/core/session/workflowState.js +14 -14
  54. package/dist/core/session/workflowView.js +18 -8
  55. package/dist/core/suiAddress.js +0 -5
  56. package/dist/core/suiEndpoint.js +0 -3
  57. package/dist/mcp/prompts.js +0 -12
  58. package/dist/mcp/responseGuidance.js +10 -5
  59. package/dist/mcp/schemas.js +0 -3
  60. package/dist/mcp/serverInfo.js +3 -4
  61. package/dist/mcp/toolNames.js +2 -2
  62. package/dist/mcp/tools/account/index.js +1 -19
  63. package/dist/mcp/tools/action/prepareSuiActionReview.js +0 -6
  64. package/dist/mcp/tools/read/readToolHelpers.js +5 -4
  65. package/dist/mcp/tools/read/serverStatusTools.js +0 -1
  66. package/dist/mcp/tools/session/executionResultTools.js +2 -2
  67. package/dist/mcp/tools/session/shared.js +1 -2
  68. package/dist/mcp/tools/session/statusTools.js +4 -5
  69. package/dist/mcp/tools/settings/index.js +8 -28
  70. package/dist/mcp-app/account.html +14 -14
  71. package/dist/mcp-app/account.notices.txt +1 -1
  72. package/dist/mcp-app/chart.html +9 -9
  73. package/dist/mcp-app/chart.notices.txt +1 -1
  74. package/dist/mcp-app/connect.html +14 -14
  75. package/dist/mcp-app/connect.notices.txt +1 -1
  76. package/dist/mcp-app/receipt.html +1298 -1463
  77. package/dist/mcp-app/receipt.notices.txt +226 -1204
  78. package/dist/mcp-app/review.html +1298 -1463
  79. package/dist/mcp-app/review.notices.txt +226 -1204
  80. package/dist/mcp-app/settings.html +3133 -0
  81. package/dist/mcp-app/settings.notices.txt +973 -0
  82. package/dist/mcp-ui/contracts.js +8 -2
  83. package/dist/mcp-ui/readCards.js +1 -1
  84. package/dist/mcp-ui/tools.js +34 -4
  85. package/dist/review-server/server.js +3 -51
  86. package/dist/runtime/application.js +41 -14
  87. package/dist/runtime/config.js +0 -2
  88. package/dist/runtime/localSettingsService.js +23 -2
  89. package/dist/runtime/logger.js +0 -2
  90. package/dist/runtime/reviewServerAcquire.js +1 -18
  91. package/dist/runtime/runtimeTermination.js +1 -4
  92. package/dist/runtime/shared/authenticatedFetch.js +2 -4
  93. package/dist/runtime/shared/control.js +4 -7
  94. package/dist/runtime/shared/dataAccess.js +0 -2
  95. package/dist/runtime/shared/http.js +14 -0
  96. package/dist/runtime/shared/mcpHttp.js +6 -6
  97. package/dist/runtime/shared/ownerLease.js +0 -4
  98. package/dist/runtime/shared/server.js +1 -2
  99. package/dist/runtime/shared/stdio.js +2 -5
  100. package/dist/runtime/smokeMainnetRead.js +0 -3
  101. package/dist/runtime/smokeReview.js +0 -4
  102. package/dist/runtime/start.js +0 -2
  103. package/dist/runtime/suiTransactionGraphqlMapping.js +0 -2
  104. package/dist/runtime/suiTransactionGraphqlQueries.js +0 -3
  105. package/dist/runtime/walletConnectConfig.js +0 -2
  106. package/dist/runtime/walletConnectStorage.js +2 -10
  107. package/dist/runtime/walletConnectTransport.js +0 -11
  108. package/dist/runtime/walletSdkChildRuntime.js +2 -6
  109. package/dist/runtime/walletSdkIpc.js +2 -1
  110. package/dist/runtime/walletSdkProcess.js +3 -8
  111. package/docs/AGENT_BEHAVIOR.md +45 -15
  112. package/docs/LOCAL_DB_ARCHITECTURE.md +145 -22
  113. package/docs/MCP_SETUP.md +99 -39
  114. package/docs/MCP_TOOLS.md +288 -24
  115. package/docs/SDK_API.md +63 -10
  116. package/docs/UTILITY_INDEX.md +9 -6
  117. package/docs/WALLET_CONNECTION.md +32 -10
  118. package/package.json +22 -8
  119. package/dist/core/activity/schemaVersion.js +0 -2
  120. package/dist/core/session/keyedRecordStore.js +0 -18
  121. package/dist/core/session/settingsSession.js +0 -1
  122. package/dist/core/session/settingsSessions.js +0 -45
  123. package/dist/review-app/brand-dark.svg +0 -1
  124. package/dist/review-app/brand-light.svg +0 -1
  125. package/dist/review-app/favicon.svg +0 -1
  126. package/dist/review-app/settings.css +0 -1
  127. package/dist/review-app/settings.js +0 -1
  128. package/dist/review-app/ui.css +0 -809
  129. package/dist/review-server/assets.js +0 -48
  130. package/dist/review-server/html.js +0 -45
  131. package/dist/review-server/http.js +0 -47
  132. package/dist/review-server/middleware/reviewToken.js +0 -7
  133. package/dist/review-server/settingsApi.js +0 -158
  134. package/docs/AGENT_DEVELOPMENT_POLICY.md +0 -815
  135. package/docs/FRONTEND_POLICY.md +0 -534
  136. package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +0 -166
  137. package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +0 -161
  138. package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +0 -259
package/README.md CHANGED
@@ -16,8 +16,8 @@ cause another signing or submission request. Request failures and unknown
16
16
  outcomes are separate from independently observed chain success/failure.
17
17
 
18
18
  Account, Receipt and Chart cards provide read-only evidence independently of
19
- signing. Connect and Review also run inside the MCP host. Settings currently
20
- uses a local token page. Human-readable review is not a guarantee of safety or
19
+ signing. Connect, Review and Settings also run inside the MCP host. Settings
20
+ provides local data controls and explicit connection-service recovery. Human-readable review is not a guarantee of safety or
21
21
  of complete interpretation of every transaction detail; the raw PTB structure
22
22
  and addresses remain inspectable.
23
23
 
@@ -27,13 +27,13 @@ Users can ask ordinary questions:
27
27
  - "How much are my USD-denominated assets together?"
28
28
  - "What is the shortfall?"
29
29
 
30
- This document describes `@zktx.io/sui-mcp@0.5.0`. Confirm that version is
30
+ This document describes `@zktx.io/sui-mcp@0.6.1`. Confirm that version is
31
31
  published before using npm installation commands. For a local build, follow
32
32
  [Developer Checkout Setup](docs/MCP_SETUP.md#developer-checkout-setup).
33
33
 
34
34
  ## Product Scope in Two Layers
35
35
 
36
- - **Implemented:** Sui mainnet read evidence, non-signable external proposal review, and account-bound DeepBook swap review using protocol-agnostic adapters. Five internal cards use the same SQLite state and permission model. The backend verifies stored material, ownership, quote/policy provenance, human-readable facts, simulation and PTB evidence before an explicit user request. WalletConnect carries bytes only between the backend and wallet; ordinary MCP responses expose facts, request status and observed execution results. Settings remains a local token page; an internal Settings card is not implemented.
36
+ - **Implemented:** Sui mainnet read evidence, non-signable external proposal review, and account-bound DeepBook swap review using protocol-agnostic adapters. Six internal cards use the same SQLite state and permission model. The backend verifies stored material, ownership, quote/policy provenance, human-readable facts, simulation and PTB evidence before an explicit user request. WalletConnect carries bytes only between the backend and wallet; ordinary MCP responses expose facts, request status and observed execution results. Settings owns endpoint and read-address controls, local backup/import/reset, and explicit connection-service recovery. Settings operations require scoped user confirmation; ordinary MCP calls only create the card or read settings.
37
37
  - **Never (permanently unsupported at every layer):** no private-key custody, no MCP or AI autonomous execution, no forwarding of opaque external transaction bytes to a wallet, no silent settlement-token or route choice, no fiat cash-out, no P&L, no peg guarantee.
38
38
 
39
39
  DeepBook provides the current scoped Sui conversion, price, and orderbook
@@ -191,20 +191,17 @@ install through your client's registry support or run `npx -y @zktx.io/sui-mcp`.
191
191
  For per-client configuration (Claude Code, Claude Desktop, Codex, Cursor) and
192
192
  running from a local checkout, see [docs/MCP_SETUP.md](docs/MCP_SETUP.md).
193
193
 
194
- ### Package and repository transition
194
+ ### Package identity and local data
195
195
 
196
- The package for this release is `@zktx.io/sui-mcp@0.5.0`, and its repository is
197
- `zktx-mcp/sui-mcp`. The previous package is `@zktx.io/say-ur-intent` (through
198
- 0.4.4). The older `@stelis/say-ur-intent@0.4.0` is also a separate package.
199
- Existing installations and MCP client registrations do not change names
200
- automatically. Before switching, confirm the new package with
201
- `npm view @zktx.io/sui-mcp@0.5.0 version`; renaming the repository does not publish
202
- an npm package or MCP Registry entry.
196
+ The npm package is `@zktx.io/sui-mcp`, and its repository is
197
+ `zktx-mcp/sui-mcp`. Confirm the requested version is available before changing
198
+ your client registration.
203
199
 
204
200
  Use the full scoped package name: the unscoped `sui-mcp` package is a different
205
- project. Keep the MCP registration name `sui-mcp`, without a suffix. This release
206
- starts with a new local data directory and does not import or
207
- convert data from the previous package. See the
201
+ project. Keep the MCP registration name `sui-mcp`, without a suffix. Compatible
202
+ updates preserve the current data directory. Incompatible databases and backups
203
+ are rejected without conversion, and files are left in place. Data from a
204
+ different package is not discovered or imported. See the
208
205
  [setup guide](docs/MCP_SETUP.md#switching-the-installed-package) for installation
209
206
  and shared-server restart instructions.
210
207
 
@@ -253,16 +250,19 @@ AI client answer behavior must be mirrored in runtime-facing instructions, resou
253
250
  - `docs/WALLET_CONNECTION.md`: Wallet connection reference: active read context, private SDK ownership and user-approved transaction requests.
254
251
  - `protocols/deepbook-v3.md`: Protocol reference only; use MCP tool responses and read.list_supported_protocols for current support.
255
252
  - `protocols/deepbook-margin.md`: Protocol reference only; no margin MCP read tools or signable actions are exposed in this release.
256
- - `docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md`: current-release question, tool-path, and standard-answer matrix for AI client release review.
257
- - `docs/golden-scenarios/BEHAVIOR_MATRIX.md`: broader behavior scenario matrix for supported and unsupported user prompts.
258
253
  - `docs/TRANSACTION_ACTIVITY_LOG.md`: transaction activity evidence, storage, scan, and summary boundaries.
259
254
  - `docs/UTILITY_INDEX.md`: manual maintainer and developer utilities. Utility rows are not MCP tools unless they explicitly name an MCP tool, and source-checkout scripts are not packaged product commands.
260
255
  - `docs/LOCAL_DB_ARCHITECTURE.md`: local SQLite storage boundaries for maintainers.
261
256
  - `docs/SDK_API.md`: pinned SDK API notes and source-verification boundaries.
262
- - `docs/FRONTEND_POLICY.md`: review-app frontend implementation policy for coding agents.
263
257
  - `docs/SIGNABLE_ADAPTER_CONTRACT.md`: wallet-review adapter and PTB visualization contract. It defines the pre-signing review evidence and commitment boundary; wallet signing requires a user card action and approval in the wallet; the backend alone handles bytes.
264
- - `AGENTS.md`: root repository development contract and non-negotiable product boundaries for coding agents working on this codebase.
265
- - `docs/AGENT_DEVELOPMENT_POLICY.md`: detailed binding development, review, documentation, source-of-truth, and completion policies for coding agents.
258
+
259
+ Contributor references live in the source repository and are not installed MCP
260
+ resources or product commands:
261
+
262
+ - [AGENTS.md](https://github.com/zktx-mcp/sui-mcp/blob/main/AGENTS.md): root repository development contract and non-negotiable product boundaries.
263
+ - [docs/AGENT_DEVELOPMENT_POLICY.md](https://github.com/zktx-mcp/sui-mcp/blob/main/docs/AGENT_DEVELOPMENT_POLICY.md): contributor development and review policy.
264
+ - [docs/FRONTEND_POLICY.md](https://github.com/zktx-mcp/sui-mcp/blob/main/docs/FRONTEND_POLICY.md): card frontend implementation policy.
265
+ - [docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md](https://github.com/zktx-mcp/sui-mcp/blob/main/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md) and [docs/golden-scenarios/BEHAVIOR_MATRIX.md](https://github.com/zktx-mcp/sui-mcp/blob/main/docs/golden-scenarios/BEHAVIOR_MATRIX.md): release-review scenarios; current tool responses define supported evidence.
266
266
 
267
267
  ## Contract Name Registry
268
268
 
@@ -272,7 +272,7 @@ safety, trust, route-quality, or signing-readiness signal, and only registered
272
272
  addresses are relabeled.
273
273
 
274
274
  Two pinned, context-aware registries in
275
- [`src/core/action/contractNameRegistry.ts`](src/core/action/contractNameRegistry.ts)
275
+ [`src/core/action/contractNameRegistry.ts`](https://github.com/zktx-mcp/sui-mcp/blob/main/src/core/action/contractNameRegistry.ts)
276
276
  drive this:
277
277
 
278
278
  - packages, relabeled only in `<address>::` path position — the DeepBook swap
@@ -1,18 +1,5 @@
1
1
  import { z } from "zod";
2
2
  import { DEEPBOOK_SWAP_ACTION_KIND, DEEPBOOK_SWAP_ADAPTER_ID } from "./deepbook/deepbookSwapIntent.js";
3
- /**
4
- * One slash-prompt surface contributed by a review adapter.
5
- *
6
- * Prompt names follow action-first naming: `<action>-<protocolSlug>`
7
- * (for example `swap-deep`). Users reach for the verb before the venue, so
8
- * action-first keeps autocomplete grouped by what the user wants to do. When
9
- * an action has exactly one registered protocol, the bare action name (for
10
- * example `swap`) is also registered as a shorthand.
11
- *
12
- * The surface carries only protocol-specific copy. Boundary language (no
13
- * signing data, no transaction bytes, local-review-only signing) is owned by
14
- * the platform and appended at registration time; adapters cannot weaken it.
15
- */
16
3
  export const adapterPromptSurfaceSchema = z
17
4
  .object({
18
5
  adapterId: z.string().min(1),
@@ -32,13 +19,6 @@ export const adapterPromptSurfaceSchema = z
32
19
  export function promptNameFor(surface) {
33
20
  return `${surface.action}-${surface.protocolSlug}`;
34
21
  }
35
- /**
36
- * Bare action prompts (`swap`) are always registered. With a single
37
- * registered protocol they go straight to that protocol; with several they
38
- * carry an optional `protocol` argument (completion suggests the slugs) and
39
- * instruct the model to ask the user which protocol to use - never to pick a
40
- * venue silently.
41
- */
42
22
  export function actionGroups(surfaces) {
43
23
  const byAction = new Map();
44
24
  for (const surface of surfaces) {
@@ -1,9 +1,5 @@
1
1
  import { parseDeepbookRawU64 } from "../../core/read/deepbookReadHelpers.js";
2
2
  export const DEEPBOOK_REVIEW_QUOTE_STALE_AFTER_MS = 30_000;
3
- // Same-machine pipelines capture `now` before the network quote fetch, so the
4
- // fetch timestamp can land a few hundred milliseconds "in the future". Treat
5
- // skew within this tolerance as a fresh quote (age 0) and record the skew;
6
- // anything beyond it is a real clock-integrity violation and refuses.
7
3
  export const DEEPBOOK_QUOTE_FUTURE_SKEW_TOLERANCE_MS = 5_000;
8
4
  export const DEEPBOOK_MAX_SLIPPAGE_BPS = 1000;
9
5
  export const DEEPBOOK_MIN_SLIPPAGE_BPS = 1;
@@ -116,8 +116,6 @@ export async function computeDeepbookSwapReviewEvidence(input) {
116
116
  asset: quotePolicyAmount(quote.rawQuote.directionalOutput).asset
117
117
  },
118
118
  protocolFee: quotePolicyAmount(quote.rawQuote.deepRequired),
119
- // The pipeline anchor `now` is captured before the network quote fetch,
120
- // so anchor derivedAt at the quote timestamp when the fetch lands later.
121
119
  derivedAt: new Date(Math.max(now.getTime(), Date.parse(quotePolicy.fetchedAt)))
122
120
  });
123
121
  }
@@ -132,10 +130,6 @@ export async function computeDeepbookSwapReviewEvidence(input) {
132
130
  transactionMaterialDigest: digestStage.evidence,
133
131
  swapQuotePolicy
134
132
  };
135
- // Render the PTB visualization as soon as material and digest exist, so the
136
- // review page can draw the transaction even when a later stage stops the
137
- // review. The artifact is commitment-bound at production and carries no
138
- // digest or bytes.
139
133
  let ptbVisualization;
140
134
  if (input.ptbVisualizationProducer) {
141
135
  const rendered = await input.ptbVisualizationProducer({
@@ -227,9 +221,6 @@ export async function computeDeepbookSwapReviewEvidence(input) {
227
221
  });
228
222
  if (!simulationStage.ok) {
229
223
  return {
230
- // Keep the public projection paired with the stored private evidence:
231
- // the human-readable review stays visible while simulation asks for a
232
- // refresh, and the store's projection binding stays satisfied.
233
224
  result: withPtb({
234
225
  ...simulationStage.result,
235
226
  humanReadableReview: publicHumanReadableReviewFromEvidence(humanReviewStage.evidence)
@@ -112,14 +112,7 @@ export function createDeepbookSwapTransactionMaterialProducer(options) {
112
112
  });
113
113
  const transaction = new Transaction();
114
114
  transaction.setSender(input.account);
115
- // The DeepBook SDK reserves a 0.25 SUI gas budget by default, which
116
- // blocks small wallets whose balance is close to the swap amount. A
117
- // mainnet DeepBook swap costs well under this explicit 0.05 SUI budget.
118
115
  transaction.setGasBudget(DEEPBOOK_SWAP_GAS_BUDGET_MIST);
119
- // input_coin fee mode swaps with an explicit zero DEEP coin; the Move
120
- // entry point then charges the taker fee in the source coin at the
121
- // protocol penalty. The explicit zero coin avoids SDK coin selection,
122
- // which cannot resolve a zero balance for accounts holding no DEEP.
123
116
  const inputFeeMode = input.quotePolicy.feeMode === "input_coin";
124
117
  const zeroDeepCoin = inputFeeMode
125
118
  ? transaction.moveCall({
@@ -245,8 +238,6 @@ function quotePolicyExpiresAt(policy) {
245
238
  return new Date(Date.parse(policy.fetchedAt) + policy.staleAfterMs);
246
239
  }
247
240
  function buildFailureMessage(error) {
248
- // The pinned SDK discards some gRPC error kinds when building a transaction.
249
- // Its error text cannot establish an account-balance or gas shortfall.
250
241
  if (error instanceof SimulationError && error.executionError) {
251
242
  return "DeepBook transaction was rejected during build-time simulation. Refresh the review or inspect its selected constraints. Nothing was signed or submitted.";
252
243
  }
@@ -1,11 +1,5 @@
1
1
  import { z } from "zod";
2
2
  import { createDeepbookSwapActionPlan, DEEPBOOK_SWAP_ACTION_KIND, DEEPBOOK_SWAP_ADAPTER_ID, DEEPBOOK_SWAP_PROTOCOL } from "./deepbook/deepbookSwapIntent.js";
3
- /**
4
- * Protocol-neutral swap intent. The optional `protocol` field carries the
5
- * protocol slug (same slug vocabulary as the adapter prompt surfaces). When
6
- * several protocols support the same action kind, the caller must name one -
7
- * the entry point never picks a venue silently.
8
- */
9
3
  export const swapIntentInputSchema = z.object({
10
4
  type: z.literal("swap"),
11
5
  from: z.object({
@@ -1,7 +1,6 @@
1
1
  import { mainnetPackageIds } from "@mysten/deepbook-v3";
2
2
  import { MOVE_STDLIB_ADDRESS, normalizeSuiAddress, SUI_CLOCK_OBJECT_ID, SUI_COIN_REGISTRY_OBJECT_ID, SUI_DENY_LIST_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_RANDOM_OBJECT_ID, SUI_SYSTEM_ADDRESS, SUI_SYSTEM_STATE_OBJECT_ID } from "@mysten/sui/utils";
3
3
  import { DEEPBOOK_SOURCE_FIELD_VALUES } from "../read/deepbookSourceOwners.js";
4
- /** Packages, relabeled only where they appear as a `<address>::...` path. */
5
4
  export const PACKAGE_NAME_REGISTRY = [
6
5
  {
7
6
  address: mainnetPackageIds.DEEPBOOK_PACKAGE_ID,
@@ -24,7 +23,6 @@ export const PACKAGE_NAME_REGISTRY = [
24
23
  source: "sui_sdk_system_address"
25
24
  }
26
25
  ];
27
- /** Well-known shared system objects, relabeled only as a bare object id. */
28
26
  export const OBJECT_NAME_REGISTRY = [
29
27
  {
30
28
  address: SUI_SYSTEM_STATE_OBJECT_ID,
@@ -52,20 +50,11 @@ export const OBJECT_NAME_REGISTRY = [
52
50
  source: "sui_sdk_coin_registry_object_id"
53
51
  },
54
52
  {
55
- // Address-based balances (coin reservation / fast path) reference the
56
- // accumulator root object. @mysten/sui defines this id only internally as
57
- // normalizeSuiAddress("0xacc") (not an exported constant), so mirror it here.
58
53
  address: normalizeSuiAddress("0xacc"),
59
54
  name: "AccumulatorRoot",
60
55
  source: "sui_accumulator_root_object_id_0xacc"
61
56
  }
62
57
  ];
63
- /**
64
- * Normalize a Sui address or package id to `0x` + 64 lowercase hex, or return
65
- * undefined when the input is not a well-formed Sui address. Short forms are
66
- * left-padded with zeros so different-width forms of the same address compare
67
- * equal.
68
- */
69
58
  function normalizeContractAddress(value) {
70
59
  const lower = value.trim().toLowerCase();
71
60
  const body = lower.startsWith("0x") ? lower.slice(2) : lower;
@@ -82,63 +71,21 @@ function buildNameMap(entries) {
82
71
  }
83
72
  const PACKAGE_NAME_BY_ADDRESS = buildNameMap(PACKAGE_NAME_REGISTRY);
84
73
  const OBJECT_NAME_BY_ADDRESS = buildNameMap(OBJECT_NAME_REGISTRY);
85
- /**
86
- * Replace registered addresses in PTB Mermaid label text with their display
87
- * names. Mermaid node ids are synthetic (`command0`, `input0`, ...), so an
88
- * address only appears inside quoted label text, and matching is context-aware:
89
- *
90
- * - A package is relabeled only where it is a path prefix (`<address>::`), the
91
- * form a MoveCall target or type path takes — never a bare object id.
92
- * - A well-known object is relabeled only where it is a bare id (not followed by
93
- * `::`), the form an object input takes — never a package path.
94
- *
95
- * Unknown addresses (no registered name) are shortened for the display graph
96
- * (leading zeros collapsed, long ids middle-elided); the full address stays in
97
- * the artifact's raw `text` stream for audit and copy. Matching uses the full
98
- * `0x` + 64-hex address form the PTB renderer (`@zktx.io/ptb-model`) emits; the
99
- * registry keys are normalized to that same form.
100
- *
101
- * Registered package names may be Move Registry names like `@deepbook/core`;
102
- * Mermaid v11 reads a literal `@` as node/edge metadata syntax even inside a
103
- * quoted label and crashes the renderer, so an inserted `@` is written as the
104
- * decimal entity `#64;`, which renders as `@` without a literal `@`. The copyable
105
- * Mermaid source keeps raw addresses (it is built from the unmodified text), so
106
- * this only affects the rendered graph.
107
- */
108
74
  export function applyContractNamesToMermaid(mermaidText) {
109
75
  let text = mermaidText;
110
- // Packages: only in path position (`<address>::...`).
111
76
  for (const [address, name] of PACKAGE_NAME_BY_ADDRESS) {
112
77
  text = text.split(`${address}::`).join(`${mermaidSafeName(name)}::`);
113
78
  }
114
- // Objects: only as a bare id (anything but a `::` path prefix).
115
79
  for (const [address, name] of OBJECT_NAME_BY_ADDRESS) {
116
80
  const safe = mermaidSafeName(name);
117
81
  text = text.replace(new RegExp(`${address}(?!::)`, "g"), () => safe);
118
82
  }
119
- // Any address still present (no registered name) is shortened for the display
120
- // graph; its `::module::name` suffix, if any, is left intact. The full address
121
- // is untouched in the raw `text` stream the artifact keeps for audit and copy.
122
83
  text = text.replace(/0x[0-9a-f]{40,64}(?![0-9a-f])/gi, (address) => shortenAddressForDisplay(address));
123
84
  return text;
124
85
  }
125
- /**
126
- * Escape characters that break Mermaid label parsing. A literal `@` triggers
127
- * Mermaid v11 node/edge metadata syntax (even inside quotes) and throws, so it
128
- * is written as the `#64;` decimal entity, which renders as `@`. The other
129
- * registered label characters (letters, `/`, `-`, `_`, `.`, `:`) are valid in
130
- * Mermaid label text.
131
- */
132
86
  function mermaidSafeName(name) {
133
87
  return name.replace(/@/g, "#64;");
134
88
  }
135
- /**
136
- * Shorten an unregistered full-length address for the display graph: collapse
137
- * leading zeros so low ids read compactly (`0x...0002` -> `0x2`), and middle-elide
138
- * a genuinely long id (`0x123456...cdef`). Display only — the raw `text` stream
139
- * keeps the full address for audit and copy. Uses ASCII `...` (a Mermaid-valid
140
- * label character) rather than a unicode ellipsis.
141
- */
142
89
  function shortenAddressForDisplay(address) {
143
90
  const body = address.slice(2).replace(/^0+/, "");
144
91
  if (body.length === 0) {
@@ -27,10 +27,6 @@ export function findForbiddenMcpFields(value, path = "$") {
27
27
  return Object.entries(value).flatMap(([key, nested]) => {
28
28
  const normalized = key.toLowerCase();
29
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
30
  const current = isForbiddenFieldKey(normalized) ? [currentPath] : [];
35
31
  return current.concat(findForbiddenMcpFields(nested, currentPath));
36
32
  });
@@ -45,9 +45,6 @@ export async function producePtbVisualizationArtifact(input) {
45
45
  },
46
46
  mermaid: {
47
47
  diagramType: "flowchart",
48
- // text keeps raw package addresses (truth/audit and copyable source);
49
- // namedText relabels registered packages with their Move Registry name for
50
- // the default graph, with a review-page toggle back to raw addresses.
51
48
  text: mermaidText,
52
49
  namedText: applyContractNamesToMermaid(mermaidText)
53
50
  },
@@ -1,25 +1,8 @@
1
1
  import { rawTransactionToIR, transactionIRToMermaid } from "@zktx.io/ptb-model";
2
2
  import { applyContractNamesToMermaid } from "./contractNameRegistry.js";
3
- // Produce a left-to-right PTB Mermaid graph for a PUBLIC receipt straight from the
4
- // already-fetched on-chain transaction's inputs and commands. Unlike the
5
- // signing-flow `producePtbVisualizationArtifact`, this needs no stored material and
6
- // no digest commitment — the transaction is the chain's own record, read by digest.
7
- // It uses the same `@zktx.io/ptb-model` pipeline (LR, matching the review page), so
8
- // the receipt graph and the review graph render identically.
9
- //
10
- // The same contract name registry the review graph uses is applied here, so
11
- // registered packages/objects show their display name (@deepbook/core, Clock, …)
12
- // and every other long address is shortened for the graph; the full ids stay in
13
- // the receipt's Inputs / Move calls / Object changes sections and Markdown.
14
- //
15
- // Returns undefined when the model cannot render the transaction (e.g. a
16
- // non-programmable system transaction); the page then shows a placeholder card in
17
- // the same slot rather than collapsing the layout.
18
3
  export function receiptPtbMermaid(transactionData) {
19
4
  try {
20
5
  const ir = rawTransactionToIR({ inputs: transactionData.inputs, commands: transactionData.commands });
21
- // Emit both the raw-address graph and the registry-named graph so the receipt and
22
- // the review render the same toggleable graph through the shared component.
23
6
  const text = transactionIRToMermaid(ir, { direction: "LR" });
24
7
  return text.trim().length > 0 ? { text, namedText: applyContractNamesToMermaid(text) } : undefined;
25
8
  }
@@ -207,8 +207,6 @@ export function createReviewTimeSimulationProducer(options) {
207
207
  const result = await options.client.transactionExecutionService.simulateTransaction({
208
208
  transaction: { bcs: { value: material.transactionBytes } },
209
209
  checks: GrpcTypes.SimulateTransactionRequest_TransactionChecks.ENABLED,
210
- // With false, the node may inject mock gas for empty payments. A fully
211
- // specified BCS transaction stays immutable: verify returned bytes below.
212
210
  doGasSelection: true,
213
211
  readMask: { paths: ["transaction.transaction.bcs", "transaction.effects", "transaction.balance_changes",
214
212
  "transaction.objects.objects.object_id", "transaction.objects.objects.object_type"] }
@@ -354,8 +352,6 @@ function createReviewTimeSimulationEvidenceFromTransaction(input) {
354
352
  transaction: summarizeSimulatedTransaction(Transaction.from(returnedBytes).getData(), input.transactionMaterial.account, input.chainIdentifier)
355
353
  });
356
354
  }
357
- // Preserve the established simulation labels while reading the SDK's public
358
- // protobuf enums. Missing state is malformed evidence, not a guessed state.
359
355
  function simulationEnum(values, value) {
360
356
  if (value === undefined || values[value] === undefined)
361
357
  throw new Error("simulation object state is missing");
@@ -4,7 +4,6 @@ import { SUI_TYPE_ARG } from "@mysten/sui/utils";
4
4
  import { makeCanonicalRawU64StringSchema } from "../numeric/rawU64.js";
5
5
  import { suiAddressStringSchema, suiTransactionDigestSchema } from "../suiAddress.js";
6
6
  export const REVIEW_TIME_SIMULATION_PROVIDER = "client.transactionExecutionService.simulateTransaction";
7
- // Stored public history may retain the previous provider; new review evidence uses the provider above.
8
7
  export const reviewSimulationProviderSchema = z.enum(["client.core.simulateTransaction", REVIEW_TIME_SIMULATION_PROVIDER]);
9
8
  export const TRANSACTION_REVIEW_SCHEMA_VERSION = 1;
10
9
  export const PTB_VISUALIZATION_CONTRACT_VERSION = "ptb-visualization-contract-alpha-2026-05-25";
@@ -44,7 +43,6 @@ export const PTB_VISUALIZATION_REQUIRED_UNSUPPORTED_USES = [
44
43
  "payment_execution_readiness",
45
44
  "route_recommendation"
46
45
  ];
47
- // The generic review contract must not load a protocol registry to name Sui gas.
48
46
  export const SUI_GAS_COIN_TYPE = SUI_TYPE_ARG;
49
47
  export const SUI_GAS_RAW_UNIT = "MIST";
50
48
  const EXPIRY_TIMESTAMP_SOURCE_FIELDS = ["checkedAt", "expiresAt"];
@@ -955,9 +953,6 @@ export const transactionReviewDataSchema = z.object({
955
953
  requireEqual(ctx, value.simulation.failureReason, simulationClaim.failureReason, ["simulation", "failureReason"], "simulation.failureReason");
956
954
  }
957
955
  }).superRefine((value, ctx) => {
958
- // Contract invariant: human review, review-time simulation, and transaction
959
- // material commitment must all reference the same Sui transaction digest.
960
- // Reuse requireEqual so commitment checks match the evidence-claim bindings.
961
956
  const handoffDigest = value.reviewedTransactionDigest;
962
957
  requireEqual(ctx, value.humanReadableReview.transactionDigest, handoffDigest, ["humanReadableReview", "transactionDigest"], "humanReadableReview.transactionDigest");
963
958
  requireEqual(ctx, value.simulation.transactionDigest, handoffDigest, ["simulation", "transactionDigest"], "simulation.transactionDigest");
@@ -1,10 +1,4 @@
1
1
  import { parseSuiAddress } from "../suiAddress.js";
2
- // Shared, pure mappers from the SDK getTransaction result to chain-receipt facts.
3
- // Both the session-bound verifier (suiChainReceiptVerifier.ts) and the public
4
- // receipt reader (suiChainReceiptReader.ts) use these so the effects-status,
5
- // Move-call, and object-type shapes are mapped in one place. Balance changes are
6
- // not here because the verifier filters them to the reviewed account while the
7
- // public reader keeps them all, so each owns its own balance mapper.
8
2
  export function effectsStatusFromSdk(status) {
9
3
  if (status.success) {
10
4
  return { success: true };
@@ -52,12 +46,6 @@ export function objectTypesFromSdk(objectTypes) {
52
46
  return [normalizedObjectId, objectType];
53
47
  }));
54
48
  }
55
- // Fail-closed address parsing for receipt facts. Both the verifier and the
56
- // public reader turn a raw chain address into a receipt address ONLY through
57
- // this helper, so neither path can silently drop or null out a balance-change
58
- // address or sender when the SDK payload is not the expected shape. A malformed
59
- // address throws; callers map the throw to a fail-closed receipt outcome rather
60
- // than returning partial facts.
61
49
  export function requireReceiptAddress(rawAddress, errorMessage) {
62
50
  const address = rawAddress === null || rawAddress === undefined ? undefined : parseSuiAddress(rawAddress);
63
51
  if (!address) {
@@ -65,9 +53,6 @@ export function requireReceiptAddress(rawAddress, errorMessage) {
65
53
  }
66
54
  return address;
67
55
  }
68
- // Shared error classification for the getTransaction read, used by both the
69
- // verifier and the public reader so a "transaction not found" is recognized one
70
- // way everywhere.
71
56
  export function isReceiptNotFoundError(error) {
72
57
  const code = typeof error === "object" && error !== null
73
58
  ? (error.code ?? error.status)
@@ -75,9 +60,6 @@ export function isReceiptNotFoundError(error) {
75
60
  if (code === "NOT_FOUND" || code === "NotFound" || code === "NOT_FOUND_ERROR" || code === 5) {
76
61
  return true;
77
62
  }
78
- // Only genuine "this digest does not exist / is not indexed" signals map to
79
- // not_found. Availability/outage phrasing (e.g. "service not available") is a
80
- // provider failure and must stay an `unavailable` (502), not a 404.
81
63
  return /\b(not found|not indexed|could not find|unable to find|no transaction)\b/i.test(receiptErrorMessage(error, ""));
82
64
  }
83
65
  export function receiptErrorMessage(error, fallback) {
@@ -42,20 +42,10 @@ export async function readPublicChainReceipt(options, input) {
42
42
  if (!transactionData || !effects || !transaction.balanceChanges || !transaction.objectTypes) {
43
43
  return { status: "unavailable", message: "Sui mainnet receipt is missing required fields." };
44
44
  }
45
- // Build receipt facts fail-closed: any malformed field (sender, Move calls,
46
- // balance-change address, object id) throws inside this block and maps to an
47
- // `unavailable` outcome, so the public receipt is never returned with silently
48
- // dropped or nulled facts and the mappers can never crash the request. The PTB
49
- // graph is best-effort and falls back to undefined (placeholder shown), so it is
50
- // produced outside this block and never fails the receipt.
51
45
  let receipt;
52
46
  try {
53
47
  receipt = {
54
48
  txDigest: transaction.digest,
55
- // A present-but-unparseable sender is a malformed payload and fails closed.
56
- // An absent sender is legitimate for some system transactions, so it maps
57
- // to undefined. The account-bound verifier instead requires a sender
58
- // because it must match the reviewed account.
59
49
  sender: transactionData.sender === null || transactionData.sender === undefined
60
50
  ? undefined
61
51
  : requireReceiptAddress(transactionData.sender, "Sui mainnet receipt has an unparseable sender address."),
@@ -77,9 +67,6 @@ export async function readPublicChainReceipt(options, input) {
77
67
  message: receiptErrorMessage(error, "Sui mainnet receipt could not be read as expected facts.")
78
68
  };
79
69
  }
80
- // Best-effort decimals enrichment: resolved outside the fail-closed block so a
81
- // coin-metadata miss or outage leaves the balance change on its raw amount and
82
- // never turns a readable receipt into an error.
83
70
  if (options.resolveCoinUnit && receipt.balanceChanges.length > 0) {
84
71
  receipt = { ...receipt, balanceChanges: await withCoinDecimals(receipt.balanceChanges, options.resolveCoinUnit) };
85
72
  }
@@ -92,7 +79,6 @@ async function withCoinDecimals(changes, resolveCoinUnit) {
92
79
  units.set(coinType, await resolveCoinUnit(coinType));
93
80
  }
94
81
  catch {
95
- // Best-effort only: leave the coin without decimals.
96
82
  }
97
83
  }));
98
84
  return changes.map((change) => {
@@ -143,13 +129,10 @@ function firstPaymentObjectId(gasData) {
143
129
  if (!readCoinReservation(ref.digest))
144
130
  return ref.objectId;
145
131
  }
146
- catch { /* Invalid refs do not establish gas object facts. */ }
132
+ catch { }
147
133
  }
148
134
  return undefined;
149
135
  }
150
- // PTB inputs, simplified to a kind and (for object inputs) an object id. The exact
151
- // SDK enum shape is read defensively so a shape change degrades to `unknown`
152
- // rather than throwing.
153
136
  function inputsFromSdk(inputs) {
154
137
  return inputs.map((raw, index) => {
155
138
  const input = asRecord(raw);
@@ -195,9 +178,6 @@ function ptbGraphFromTransaction(transactionData) {
195
178
  function asRecord(value) {
196
179
  return typeof value === "object" && value !== null ? value : undefined;
197
180
  }
198
- // A pure input's bytes arrive base64-encoded; surface them as 0x-hex (the
199
- // convention used elsewhere in the UI). Returns undefined for an absent or
200
- // non-string value so the input simply carries no bytes.
201
181
  function pureBytesToHex(value) {
202
182
  if (typeof value !== "string" || value.length === 0) {
203
183
  return undefined;
@@ -4,14 +4,7 @@ import { normalizeCoinType } from "../read/coinMetadata.js";
4
4
  import { parseSuiAddress, suiTransactionDigestSchema } from "../suiAddress.js";
5
5
  import { effectsStatusFromSdk, isReceiptNotFoundError, objectTypesFromSdk, packageCallsFromTransaction, receiptErrorMessage, requireReceiptAddress } from "./suiChainReceiptMapping.js";
6
6
  export const SUI_CHAIN_RECEIPT_GET_TRANSACTION_INCLUDE = includeObjectFromFields(SUI_CHAIN_RECEIPT_REQUIRED_INCLUDE);
7
- // Bounded per-read wait for the known digest to become visible on this node.
8
- // This existing 12-second source-read window is independent of the backend's
9
- // 10-minute initial observation deadline. A missing receipt does not establish
10
- // whether a prior submit (including one with a lost reply) executed.
11
7
  export const SUI_CHAIN_RECEIPT_WAIT_TIMEOUT_MS = 12_000;
12
- // waitForTransaction aborts with a DOMException named "TimeoutError" when its wait window elapses.
13
- // Treat that exactly like not_found — the transaction is simply not readable here yet — so the
14
- // session stays pending for a retry rather than being marked a verification failure.
15
8
  function isWaitTimeoutError(error) {
16
9
  return typeof error === "object" && error !== null && error.name === "TimeoutError";
17
10
  }
@@ -36,9 +29,6 @@ export async function verifySuiChainReceipt(options, input) {
36
29
  }
37
30
  let result;
38
31
  try {
39
- // The signed transaction was just submitted: it is on chain but may not yet be queryable on
40
- // this server's fullnode (checkpoint-propagation lag). Wait for it instead of returning a
41
- // premature not_found, so a single review-page poll resolves straight to the final receipt.
42
32
  result = await options.client.core.waitForTransaction({
43
33
  digest: parsedInput.txDigest,
44
34
  include: SUI_CHAIN_RECEIPT_GET_TRANSACTION_INCLUDE,
@@ -34,7 +34,6 @@ export const transactionFundingDescriptionSchema = z.object({
34
34
  expiration
35
35
  }).strict();
36
36
  const addressBalance = z.object({ owner: address, coinType,
37
- // A holding can exceed the u64 limit of an individual transaction input.
38
37
  balanceRaw: z.string().regex(/^(0|[1-9][0-9]*)$/), requiredRaw: z.string().regex(/^(0|[1-9][0-9]*)$/)
39
38
  }).strict();
40
39
  export const transactionFundingEvidenceSchema = transactionFundingDescriptionSchema.extend({
@@ -54,11 +53,6 @@ export class TransactionFundingError extends Error {
54
53
  this.kind = kind;
55
54
  }
56
55
  }
57
- // Reservation-format portions are adapted from Mysten Labs (Apache-2.0).
58
- // Copyright (c) Mysten Labs, Inc. See LICENSES/@mysten-sui-2.17.0-Apache-2.0.txt.
59
- // Sui mainnet-v1.72.5 coin_reservation.rs and @mysten/sui 2.17.0
60
- // utils/coin-reservation.ts define this layout. The SDK helper is not exported.
61
- // Decode only: coin selection and reservation creation remain SDK/node work.
62
56
  export function readCoinReservation(digest) {
63
57
  const bytes = fromBase58(digest);
64
58
  if (bytes.length !== 32)
@@ -131,7 +125,6 @@ export function describeTransactionFunding(data, account, chainIdentifier) {
131
125
  }
132
126
  else if (value.Epoch !== undefined) {
133
127
  const epoch = value.Epoch;
134
- // The pinned SDK's BCS Epoch decoder returns a number, unlike ValidDuring.
135
128
  if (typeof epoch === "number" && (!Number.isSafeInteger(epoch) || epoch < 0)) {
136
129
  throw new Error("Transaction expiration epoch must be a non-negative safe integer");
137
130
  }
@@ -25,7 +25,6 @@ export const REFRESH_REASONS = [
25
25
  export const REVIEW_PREPARATION_STATUSES = ["proposed", "awaiting_wallet", "wallet_connected", "ready_for_wallet_review", "refresh_required", "blocked", "expired"];
26
26
  export const REVIEW_MATERIAL_DERIVED_FIELDS = ["humanReadableReview", "simulation", "transactionReviewData",
27
27
  "ptbVisualization", "assetFlowActual", "beforeAfterBalance"];
28
- // Display wording for the three computed review states, not wallet authority.
29
28
  export const REVIEW_UI_LABELS = {
30
29
  ready_for_wallet_review: "Ready for your review",
31
30
  refresh_required: "Review needs updating",
@@ -105,8 +105,6 @@ export function mapReviewTimeSimulationGasToContractDraft(evidenceInput, gasObje
105
105
  const gasAsset = { coinType: SUI_GAS_COIN_TYPE };
106
106
  const gasUsedSourceId = "review_time_simulation_gas_used_source";
107
107
  const gasUsedClaimId = "review_time_simulation_gas_used_claim";
108
- // A storage rebate can make net gas negative. Keep the verified budget and
109
- // simulation components; do not invent an unsigned zero or a spending claim.
110
108
  if (gasUsed >= 0n) {
111
109
  sourceReferences.push(adapterSourceOfTruthSchema.parse({
112
110
  id: gasUsedSourceId,