@zktx.io/sui-mcp 0.6.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.
- package/README.md +18 -18
- package/dist/adapters/adapterPromptSurfaces.js +0 -20
- package/dist/adapters/deepbook/deepbookQuotePolicy.js +0 -4
- package/dist/adapters/deepbook/deepbookReviewEvidence.js +0 -9
- package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +0 -9
- package/dist/adapters/intentPlanFactories.js +0 -6
- package/dist/core/action/contractNameRegistry.js +0 -53
- package/dist/core/action/forbiddenFields.js +0 -4
- package/dist/core/action/ptbVisualizationProducer.js +0 -3
- package/dist/core/action/receiptPtbGraph.js +0 -17
- package/dist/core/action/reviewTimeSimulationEvidence.js +0 -4
- package/dist/core/action/signableAdapterContract.js +0 -5
- package/dist/core/action/suiChainReceiptMapping.js +0 -18
- package/dist/core/action/suiChainReceiptReader.js +1 -21
- package/dist/core/action/suiChainReceiptVerifier.js +0 -10
- package/dist/core/action/transactionFunding.js +0 -7
- package/dist/core/action/types.js +0 -1
- package/dist/core/action/walletReviewContractAssembler.js +0 -2
- package/dist/core/activity/localDataService.js +0 -6
- package/dist/core/activity/sqliteActivityStore.js +0 -8
- package/dist/core/activity/sqliteActivityStoreRows.js +0 -2
- package/dist/core/activity/sqliteActivityStoreSchema.js +0 -4
- package/dist/core/read/deepbookReadHelpers.js +0 -2
- package/dist/core/read/deepbookRegistry.js +0 -19
- package/dist/core/read/deepbookUsdcChartService.js +0 -2
- package/dist/core/read/readCardResult.js +0 -2
- package/dist/core/read/readService.js +0 -19
- package/dist/core/read/readServiceTypes.js +0 -2
- package/dist/core/session/cardSession.js +0 -4
- package/dist/core/session/cardSessionStore.js +1 -4
- package/dist/core/session/reviewValidity.js +0 -3
- package/dist/core/session/sessionRecordStore.js +0 -1
- package/dist/core/session/sessionStore.js +0 -15
- package/dist/core/session/settingsFileIdentity.js +0 -2
- package/dist/core/session/settingsFileProtocol.js +0 -5
- package/dist/core/session/settingsFileStore.js +1 -10
- package/dist/core/session/settingsInputError.js +0 -2
- package/dist/core/session/settingsOperation.js +0 -4
- package/dist/core/session/settingsPreparationSql.js +0 -2
- package/dist/core/session/settingsWorkflow.js +1 -37
- package/dist/core/session/sqliteCardStore.js +0 -2
- package/dist/core/session/sqliteSessionStore.js +0 -11
- package/dist/core/session/sqliteSettingsWorkflowStore.js +0 -10
- package/dist/core/session/sqliteTransactionMaterialStore.js +0 -11
- package/dist/core/session/sqliteWalletWorkflowStore.js +0 -29
- package/dist/core/session/status.js +0 -2
- package/dist/core/session/transactionMaterialStore.js +0 -4
- package/dist/core/session/transactionRequest.js +0 -4
- package/dist/core/session/walletConnection.js +0 -3
- package/dist/core/session/walletWorkflow.js +3 -21
- package/dist/core/session/workflowState.js +0 -8
- package/dist/core/session/workflowView.js +0 -6
- package/dist/core/suiAddress.js +0 -5
- package/dist/core/suiEndpoint.js +0 -3
- package/dist/mcp/prompts.js +0 -12
- package/dist/mcp/schemas.js +0 -3
- package/dist/mcp/tools/action/prepareSuiActionReview.js +0 -6
- package/dist/mcp/tools/read/serverStatusTools.js +0 -1
- package/dist/mcp/tools/session/shared.js +0 -2
- package/dist/mcp/tools/session/statusTools.js +0 -2
- package/dist/mcp-app/account.html +1 -1
- package/dist/mcp-app/chart.html +6 -6
- package/dist/mcp-app/connect.html +6 -6
- package/dist/mcp-app/receipt.html +298 -298
- package/dist/mcp-app/review.html +237 -237
- package/dist/mcp-app/settings.html +1 -1
- package/dist/mcp-ui/tools.js +3 -4
- package/dist/review-server/server.js +0 -2
- package/dist/runtime/application.js +1 -1
- package/dist/runtime/config.js +0 -2
- package/dist/runtime/localSettingsService.js +0 -3
- package/dist/runtime/logger.js +0 -2
- package/dist/runtime/reviewServerAcquire.js +1 -18
- package/dist/runtime/runtimeTermination.js +1 -4
- package/dist/runtime/shared/authenticatedFetch.js +0 -6
- package/dist/runtime/shared/control.js +0 -2
- package/dist/runtime/shared/dataAccess.js +0 -2
- package/dist/runtime/shared/mcpHttp.js +0 -5
- package/dist/runtime/shared/ownerLease.js +0 -4
- package/dist/runtime/shared/server.js +0 -1
- package/dist/runtime/shared/stdio.js +2 -5
- package/dist/runtime/smokeMainnetRead.js +0 -2
- package/dist/runtime/smokeReview.js +0 -4
- package/dist/runtime/start.js +0 -2
- package/dist/runtime/suiTransactionGraphqlMapping.js +0 -2
- package/dist/runtime/suiTransactionGraphqlQueries.js +0 -3
- package/dist/runtime/walletConnectConfig.js +0 -2
- package/dist/runtime/walletConnectStorage.js +0 -6
- package/dist/runtime/walletConnectTransport.js +0 -11
- package/dist/runtime/walletSdkChildRuntime.js +0 -5
- package/dist/runtime/walletSdkProcess.js +0 -6
- package/docs/AGENT_BEHAVIOR.md +1 -5
- package/docs/LOCAL_DB_ARCHITECTURE.md +3 -3
- package/docs/MCP_SETUP.md +14 -14
- package/docs/MCP_TOOLS.md +15 -0
- package/docs/SDK_API.md +12 -0
- package/docs/WALLET_CONNECTION.md +2 -1
- package/package.json +10 -2
- package/docs/AGENT_DEVELOPMENT_POLICY.md +0 -819
- package/docs/FRONTEND_POLICY.md +0 -611
- package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +0 -166
- package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +0 -161
- package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +0 -259
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ 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.6.
|
|
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
|
|
|
@@ -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
|
|
194
|
+
### Package identity and local data
|
|
195
195
|
|
|
196
|
-
The package
|
|
197
|
-
`zktx-mcp/sui-mcp`.
|
|
198
|
-
|
|
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.6.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.
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
265
|
-
|
|
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
|
-
// Set the product's explicit 0.05 SUI spending cap before the SDK's
|
|
116
|
-
// setGasBudgetIfNotSet fallback (0.25 SUI). This is not a cost estimate;
|
|
117
|
-
// funding and checks-enabled simulation must validate each transaction.
|
|
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 {
|
|
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,
|
|
@@ -83,8 +83,6 @@ export class SqliteLocalDataService {
|
|
|
83
83
|
this.notifyDataReplaced();
|
|
84
84
|
return result;
|
|
85
85
|
}
|
|
86
|
-
// The caller commits this primitive with the admitted Settings card/result.
|
|
87
|
-
// It contains no async gap or post-commit reconstruction of its own authority.
|
|
88
86
|
applyValidatedReplacement(envelope, now, preserveCardId) {
|
|
89
87
|
this.options.advanceRequestDeadlines(now);
|
|
90
88
|
this.assertReplacementAllowed(now);
|
|
@@ -114,9 +112,6 @@ export class SqliteLocalDataService {
|
|
|
114
112
|
resetLocalDataTables(preserveCardId) {
|
|
115
113
|
this.db.prepare(`UPDATE settings_files SET invalidated=1 WHERE role='upload' OR ${SETTINGS_DOWNLOAD_FILE_SQL}`).run();
|
|
116
114
|
if (preserveCardId) {
|
|
117
|
-
// Deleted cards cannot report or retry their cleanup. Transfer cleanup
|
|
118
|
-
// responsibility while preserving file identities and public backup paths.
|
|
119
|
-
// Uploads remain invalidated and never replace the selected input.
|
|
120
115
|
const operation = this.db.prepare("SELECT operation_id FROM settings_operations WHERE card_id=?").get(preserveCardId);
|
|
121
116
|
if (!operation)
|
|
122
117
|
throw new LocalDataError("internal_error", "The replacement result identity is unavailable.");
|
|
@@ -273,7 +268,6 @@ export class SqliteLocalDataService {
|
|
|
273
268
|
}
|
|
274
269
|
}
|
|
275
270
|
}
|
|
276
|
-
// Preparation and final replacement use the same rule without starting wallet I/O.
|
|
277
271
|
assertReplacementAllowed(now = this.now()) {
|
|
278
272
|
if (hasUnsettledWalletWork(this.db, now))
|
|
279
273
|
throw new LocalDataError("input_invalid", "A wallet request is still unsettled.", { reason: "wallet_request_unsettled" });
|
|
@@ -30,14 +30,11 @@ export { ActivityStoreError };
|
|
|
30
30
|
const ACTIVE_ACCOUNT_SINGLETON_ID = 1;
|
|
31
31
|
export const DATA_DIR_ENV = "SUI_MCP_DATA_DIR";
|
|
32
32
|
export const ACTIVITY_DATABASE_FILENAME = "sui-mcp.sqlite";
|
|
33
|
-
// Best-effort permission hardening. The owner-only data directory is the primary
|
|
34
|
-
// protection; failures (e.g. on Windows, which ignores POSIX modes) are non-fatal.
|
|
35
33
|
function restrictPathPermissions(path, mode) {
|
|
36
34
|
try {
|
|
37
35
|
chmodSync(path, mode);
|
|
38
36
|
}
|
|
39
37
|
catch {
|
|
40
|
-
// Non-fatal: the 0700 directory remains the primary protection.
|
|
41
38
|
}
|
|
42
39
|
}
|
|
43
40
|
export class SqliteActivityStore {
|
|
@@ -71,10 +68,6 @@ export class SqliteActivityStore {
|
|
|
71
68
|
this.db.close();
|
|
72
69
|
throw error;
|
|
73
70
|
}
|
|
74
|
-
// This database persists unsigned transaction material (Option B), so restrict it to
|
|
75
|
-
// the owner. The 0700 directory is the primary protection (it also covers the WAL/SHM
|
|
76
|
-
// sidecars and blocks other OS users); the 0600 file is belt-and-suspenders. Best-effort
|
|
77
|
-
// because some platforms (e.g. Windows) ignore POSIX modes.
|
|
78
71
|
restrictPathPermissions(dataDirectory, 0o700);
|
|
79
72
|
restrictPathPermissions(options.databasePath, 0o600);
|
|
80
73
|
}
|
|
@@ -362,7 +355,6 @@ export class SqliteActivityStore {
|
|
|
362
355
|
(r.request_status!='completed' OR e.tx_digest!=r.transaction_digest OR json_valid(e.result_json)=0))) LIMIT 1`).get(...params);
|
|
363
356
|
if (inconsistent)
|
|
364
357
|
throw new ActivityStoreReadError("internal_error", "Stored review request evidence is inconsistent.", { reason: "invalid_stored_evidence" });
|
|
365
|
-
// Aggregation must not turn a malformed current result into a valid count.
|
|
366
358
|
for (const row of this.db.prepare(`SELECT rs.id,rs.current_attempt_id,a.sui_address AS account
|
|
367
359
|
FROM review_sessions rs JOIN accounts a ON a.id=rs.account_id ${where} AND rs.current_attempt_id IS NOT NULL`)
|
|
368
360
|
.all(...params)) {
|
|
@@ -315,8 +315,6 @@ export function parseActionPlanEvidence(plan) {
|
|
|
315
315
|
return parsed.data;
|
|
316
316
|
}
|
|
317
317
|
export function extractRequestedIntent(plan) {
|
|
318
|
-
// Adapter convention: full plan_json stays canonical, while requestedIntent
|
|
319
|
-
// is materialized only to make activity queries avoid nested JSON paths.
|
|
320
318
|
return plan.adapterData.requestedIntent;
|
|
321
319
|
}
|
|
322
320
|
export function reasonForReviewState(state) {
|