@zktx.io/sui-mcp 0.5.0

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 (218) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES/@mysten-bcs-2.0.5-Apache-2.0.txt +205 -0
  3. package/LICENSES/@mysten-sui-2.17.0-Apache-2.0.txt +201 -0
  4. package/LICENSES/@mysten-utils-0.3.3-Apache-2.0.txt +205 -0
  5. package/LICENSES/fancy-canvas-2.1.0-MIT.txt +7 -0
  6. package/LICENSES/lightweight-charts-5.2.0-NOTICE.txt +2 -0
  7. package/README.md +288 -0
  8. package/dist/adapters/adapterLifecycleValidators.js +5 -0
  9. package/dist/adapters/adapterPromptSurfaces.js +60 -0
  10. package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +178 -0
  11. package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
  12. package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
  13. package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
  14. package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
  15. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +254 -0
  16. package/dist/adapters/intentPlanFactories.js +51 -0
  17. package/dist/adapters/reviewAdapters.js +47 -0
  18. package/dist/core/action/adapterLifecycleValidation.js +12 -0
  19. package/dist/core/action/contractNameRegistry.js +151 -0
  20. package/dist/core/action/forbiddenFields.js +43 -0
  21. package/dist/core/action/humanReadableReviewEvidence.js +193 -0
  22. package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
  23. package/dist/core/action/ptbVisualizationProducer.js +71 -0
  24. package/dist/core/action/receiptPtbGraph.js +29 -0
  25. package/dist/core/action/reviewCheckResults.js +6 -0
  26. package/dist/core/action/reviewStateValidation.js +11 -0
  27. package/dist/core/action/reviewTimeSimulationEvidence.js +470 -0
  28. package/dist/core/action/schemas.js +491 -0
  29. package/dist/core/action/signableAdapterContract.js +998 -0
  30. package/dist/core/action/suiChainReceiptEvidence.js +106 -0
  31. package/dist/core/action/suiChainReceiptMapping.js +107 -0
  32. package/dist/core/action/suiChainReceiptReader.js +211 -0
  33. package/dist/core/action/suiChainReceiptVerifier.js +178 -0
  34. package/dist/core/action/swapHumanReadableReviewProjection.js +130 -0
  35. package/dist/core/action/swapQuotePolicyEvidence.js +271 -0
  36. package/dist/core/action/transactionFunding.js +244 -0
  37. package/dist/core/action/transactionObjectOwnershipEvidence.js +256 -0
  38. package/dist/core/action/transactionObjectOwnershipProducer.js +323 -0
  39. package/dist/core/action/types.js +33 -0
  40. package/dist/core/action/walletReviewContractAssembler.js +286 -0
  41. package/dist/core/activity/accountAssetTimeline.js +157 -0
  42. package/dist/core/activity/accountAssetTimelineUsdcReferences.js +120 -0
  43. package/dist/core/activity/activityStore.js +16 -0
  44. package/dist/core/activity/externalActivityCoverage.js +76 -0
  45. package/dist/core/activity/externalActivityTransactionStream.js +82 -0
  46. package/dist/core/activity/localDataService.js +296 -0
  47. package/dist/core/activity/localDataTypes.js +11 -0
  48. package/dist/core/activity/localDataValidation.js +439 -0
  49. package/dist/core/activity/schemaVersion.js +2 -0
  50. package/dist/core/activity/sqliteActivityStore.js +1068 -0
  51. package/dist/core/activity/sqliteActivityStoreRows.js +389 -0
  52. package/dist/core/activity/sqliteActivityStoreSchema.js +367 -0
  53. package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
  54. package/dist/core/activity/suiFunctionTarget.js +43 -0
  55. package/dist/core/activity/transactionActivityAccountEffects.js +186 -0
  56. package/dist/core/activity/transactionActivityAnalysis.js +295 -0
  57. package/dist/core/activity/transactionActivityClassifier.js +306 -0
  58. package/dist/core/activity/transactionActivityDetails.js +229 -0
  59. package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
  60. package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
  61. package/dist/core/activity/transactionActivityService.js +379 -0
  62. package/dist/core/activity/transactionActivityTypes.js +18 -0
  63. package/dist/core/eventlog/sink.js +35 -0
  64. package/dist/core/evidence/settlementFamilies.js +85 -0
  65. package/dist/core/evidence/userAnswerUse.js +1 -0
  66. package/dist/core/numeric/rawU64.js +63 -0
  67. package/dist/core/preferences/preferencesStore.js +26 -0
  68. package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
  69. package/dist/core/proposal/externalProposalReview.js +347 -0
  70. package/dist/core/proposal/schemas.js +208 -0
  71. package/dist/core/proposal/types.js +35 -0
  72. package/dist/core/read/amounts.js +14 -0
  73. package/dist/core/read/coinMetadata.js +60 -0
  74. package/dist/core/read/deepbookOfficialIndexerSource.js +373 -0
  75. package/dist/core/read/deepbookRawQuoteClient.js +86 -0
  76. package/dist/core/read/deepbookReadHelpers.js +321 -0
  77. package/dist/core/read/deepbookRegistry.js +157 -0
  78. package/dist/core/read/deepbookSourceOwners.js +159 -0
  79. package/dist/core/read/deepbookUsdcChartService.js +342 -0
  80. package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
  81. package/dist/core/read/readCardInputs.js +19 -0
  82. package/dist/core/read/readCardResult.js +40 -0
  83. package/dist/core/read/readResponseGuidance.js +402 -0
  84. package/dist/core/read/readService.js +1639 -0
  85. package/dist/core/read/readServiceTypes.js +88 -0
  86. package/dist/core/read/settlementParityFormatting.js +82 -0
  87. package/dist/core/read/walletReadHelpers.js +99 -0
  88. package/dist/core/review/reviewChecks.js +45 -0
  89. package/dist/core/review/reviewComputation.js +38 -0
  90. package/dist/core/review/reviewComputationResult.js +87 -0
  91. package/dist/core/session/cardSession.js +31 -0
  92. package/dist/core/session/cardSessionStore.js +204 -0
  93. package/dist/core/session/keyedRecordStore.js +18 -0
  94. package/dist/core/session/liveReviewSessionContract.js +3 -0
  95. package/dist/core/session/localSession.js +31 -0
  96. package/dist/core/session/privateReviewArtifacts.js +75 -0
  97. package/dist/core/session/reviewValidity.js +49 -0
  98. package/dist/core/session/sessionErrors.js +9 -0
  99. package/dist/core/session/sessionRecordStore.js +40 -0
  100. package/dist/core/session/sessionStore.js +655 -0
  101. package/dist/core/session/settingsSession.js +1 -0
  102. package/dist/core/session/settingsSessions.js +45 -0
  103. package/dist/core/session/sqliteCardStore.js +88 -0
  104. package/dist/core/session/sqliteSessionStore.js +213 -0
  105. package/dist/core/session/sqliteTransactionMaterialStore.js +64 -0
  106. package/dist/core/session/sqliteWalletWorkflowStore.js +821 -0
  107. package/dist/core/session/status.js +60 -0
  108. package/dist/core/session/transactionMaterialStore.js +221 -0
  109. package/dist/core/session/transactionRequest.js +72 -0
  110. package/dist/core/session/wait.js +79 -0
  111. package/dist/core/session/walletConnection.js +92 -0
  112. package/dist/core/session/walletRuntime.js +38 -0
  113. package/dist/core/session/walletWorkflow.js +608 -0
  114. package/dist/core/session/workflowState.js +80 -0
  115. package/dist/core/session/workflowView.js +150 -0
  116. package/dist/core/suiAddress.js +18 -0
  117. package/dist/core/suiEndpoint.js +72 -0
  118. package/dist/mcp/activeAccountResponse.js +24 -0
  119. package/dist/mcp/prompts.js +146 -0
  120. package/dist/mcp/registerTool.js +19 -0
  121. package/dist/mcp/resources.js +72 -0
  122. package/dist/mcp/responseGuidance.js +326 -0
  123. package/dist/mcp/result.js +17 -0
  124. package/dist/mcp/schemas.js +11 -0
  125. package/dist/mcp/server.js +32 -0
  126. package/dist/mcp/serverInfo.js +131 -0
  127. package/dist/mcp/toolErrors.js +118 -0
  128. package/dist/mcp/toolNames.js +58 -0
  129. package/dist/mcp/tools/account/index.js +44 -0
  130. package/dist/mcp/tools/action/prepareSuiActionReview.js +126 -0
  131. package/dist/mcp/tools/read/commonSchemas.js +43 -0
  132. package/dist/mcp/tools/read/deepbookReadTools.js +686 -0
  133. package/dist/mcp/tools/read/index.js +14 -0
  134. package/dist/mcp/tools/read/readToolHelpers.js +97 -0
  135. package/dist/mcp/tools/read/reviewActivityTools.js +179 -0
  136. package/dist/mcp/tools/read/serverStatusTools.js +100 -0
  137. package/dist/mcp/tools/read/transactionActivityOutput.js +371 -0
  138. package/dist/mcp/tools/read/transactionActivityTools.js +858 -0
  139. package/dist/mcp/tools/read/walletReadTools.js +734 -0
  140. package/dist/mcp/tools/session/executionResultTools.js +44 -0
  141. package/dist/mcp/tools/session/index.js +8 -0
  142. package/dist/mcp/tools/session/shared.js +36 -0
  143. package/dist/mcp/tools/session/statusTools.js +69 -0
  144. package/dist/mcp/tools/session/walletConnectionTools.js +69 -0
  145. package/dist/mcp/tools/settings/index.js +66 -0
  146. package/dist/mcp-app/account.html +3133 -0
  147. package/dist/mcp-app/account.notices.txt +973 -0
  148. package/dist/mcp-app/chart.html +3354 -0
  149. package/dist/mcp-app/chart.notices.txt +1194 -0
  150. package/dist/mcp-app/connect.html +3179 -0
  151. package/dist/mcp-app/connect.notices.txt +1012 -0
  152. package/dist/mcp-app/receipt.html +8430 -0
  153. package/dist/mcp-app/receipt.notices.txt +3549 -0
  154. package/dist/mcp-app/review.html +8430 -0
  155. package/dist/mcp-app/review.notices.txt +3549 -0
  156. package/dist/mcp-ui/contracts.js +31 -0
  157. package/dist/mcp-ui/readCards.js +28 -0
  158. package/dist/mcp-ui/tools.js +134 -0
  159. package/dist/review-app/brand-dark.svg +1 -0
  160. package/dist/review-app/brand-light.svg +1 -0
  161. package/dist/review-app/favicon.svg +1 -0
  162. package/dist/review-app/settings.css +1 -0
  163. package/dist/review-app/settings.js +1 -0
  164. package/dist/review-app/ui.css +809 -0
  165. package/dist/review-server/assets.js +48 -0
  166. package/dist/review-server/html.js +45 -0
  167. package/dist/review-server/http.js +47 -0
  168. package/dist/review-server/middleware/hostOrigin.js +48 -0
  169. package/dist/review-server/middleware/reviewToken.js +7 -0
  170. package/dist/review-server/reviewServerPolicy.js +1 -0
  171. package/dist/review-server/server.js +72 -0
  172. package/dist/review-server/settingsApi.js +158 -0
  173. package/dist/runtime/application.js +203 -0
  174. package/dist/runtime/config.js +97 -0
  175. package/dist/runtime/localSettingsService.js +198 -0
  176. package/dist/runtime/logger.js +66 -0
  177. package/dist/runtime/reviewDependencies.js +58 -0
  178. package/dist/runtime/reviewServerAcquire.js +93 -0
  179. package/dist/runtime/runtimeTermination.js +60 -0
  180. package/dist/runtime/shared/authenticatedFetch.js +127 -0
  181. package/dist/runtime/shared/control.js +93 -0
  182. package/dist/runtime/shared/dataAccess.js +66 -0
  183. package/dist/runtime/shared/mcpHttp.js +84 -0
  184. package/dist/runtime/shared/ownerLease.js +50 -0
  185. package/dist/runtime/shared/server.js +98 -0
  186. package/dist/runtime/shared/stdio.js +113 -0
  187. package/dist/runtime/smokeMainnetRead.js +391 -0
  188. package/dist/runtime/smokeMainnetReadAssertions.js +306 -0
  189. package/dist/runtime/smokeReview.js +40 -0
  190. package/dist/runtime/start.js +90 -0
  191. package/dist/runtime/suiEndpoint.js +97 -0
  192. package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
  193. package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
  194. package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
  195. package/dist/runtime/walletConnectConfig.js +3 -0
  196. package/dist/runtime/walletConnectStorage.js +99 -0
  197. package/dist/runtime/walletConnectTransport.js +128 -0
  198. package/dist/runtime/walletSdkChild.js +3 -0
  199. package/dist/runtime/walletSdkChildRuntime.js +160 -0
  200. package/dist/runtime/walletSdkIpc.js +26 -0
  201. package/dist/runtime/walletSdkProcess.js +358 -0
  202. package/docs/AGENT_BEHAVIOR.md +524 -0
  203. package/docs/AGENT_DEVELOPMENT_POLICY.md +815 -0
  204. package/docs/FRONTEND_POLICY.md +534 -0
  205. package/docs/LOCAL_DB_ARCHITECTURE.md +381 -0
  206. package/docs/MCP_SETUP.md +742 -0
  207. package/docs/MCP_TOOLS.md +1318 -0
  208. package/docs/SDK_API.md +206 -0
  209. package/docs/SIGNABLE_ADAPTER_CONTRACT.md +396 -0
  210. package/docs/TRANSACTION_ACTIVITY_LOG.md +194 -0
  211. package/docs/UTILITY_INDEX.md +275 -0
  212. package/docs/WALLET_CONNECTION.md +238 -0
  213. package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +166 -0
  214. package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +161 -0
  215. package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +259 -0
  216. package/package.json +79 -0
  217. package/protocols/deepbook-margin.md +15 -0
  218. package/protocols/deepbook-v3.md +24 -0
@@ -0,0 +1,128 @@
1
+ import { SignClient } from "@walletconnect/sign-client";
2
+ import { getSdkError } from "@walletconnect/utils";
3
+ import { z } from "zod";
4
+ import { parseSuiAddress } from "../core/suiAddress.js";
5
+ import { SUI_MAINNET_WALLET_CHAIN, SUI_SIGN_TRANSACTION_METHOD, WalletUserRejectedError } from "../core/session/walletConnection.js";
6
+ import { openWalletConnectStorage } from "./walletConnectStorage.js";
7
+ const signedResponse = z.object({ transactionBytes: z.string().min(1), signature: z.string().min(1) }).strict();
8
+ function transportError(error) {
9
+ const code = typeof error === "object" && error !== null && "code" in error ? error.code : undefined;
10
+ return ["USER_REJECTED", "USER_REJECTED_CHAINS", "USER_REJECTED_METHODS", "USER_REJECTED_EVENTS"]
11
+ .some((name) => getSdkError(name).code === code)
12
+ ? new WalletUserRejectedError() : new Error("The wallet request could not be completed.");
13
+ }
14
+ function sessionValue(session) {
15
+ const namespace = session.namespaces.sui ?? session.namespaces[SUI_MAINNET_WALLET_CHAIN];
16
+ if (!namespace || !Number.isSafeInteger(session.expiry) || session.expiry * 1000 <= Date.now()) {
17
+ throw new Error("A live Sui mainnet wallet session is required.");
18
+ }
19
+ const accounts = namespace.accounts.filter((account) => account.startsWith(`${SUI_MAINNET_WALLET_CHAIN}:`))
20
+ .map((account) => parseSuiAddress(account.slice(SUI_MAINNET_WALLET_CHAIN.length + 1)));
21
+ if (accounts.length === 0 || accounts.some((account) => !account))
22
+ throw new Error("The wallet did not approve a Sui mainnet account.");
23
+ return { topic: session.topic, accounts: [...new Set(accounts)], methods: [...namespace.methods],
24
+ chain: SUI_MAINNET_WALLET_CHAIN, expiresAt: new Date(session.expiry * 1000).toISOString(), walletName: session.peer.metadata.name };
25
+ }
26
+ export async function createWalletConnectTransport(options) {
27
+ if (!/^[0-9a-f]{32}$/i.test(options.projectId))
28
+ throw new Error("WalletConnect project ID format is invalid.");
29
+ const owner = openWalletConnectStorage(options.dataDirectory);
30
+ // SDK log bodies can contain pairing credentials and serialized requests.
31
+ // Product failure stages are recorded by the caller without these values.
32
+ options.onSdkStart?.();
33
+ const client = await (options.initialize ?? SignClient.init.bind(SignClient))({ projectId: options.projectId, metadata: { ...options.metadata, icons: [] },
34
+ storage: owner.storage, logger: "silent", telemetryEnabled: false });
35
+ let stopped = false;
36
+ const listeners = new Set();
37
+ const changed = (event) => { if (!stopped)
38
+ for (const listener of listeners)
39
+ listener(event.topic); };
40
+ // Pinned SignClient emits session_event without changing session.namespaces.
41
+ // Treat active account/chain changes as invalidation, not new authorization.
42
+ const selectionChanged = (event) => {
43
+ if (!stopped && ["accountsChanged", "chainChanged"].includes(event.params.event.name)) {
44
+ for (const listener of listeners)
45
+ listener(event.topic, true);
46
+ }
47
+ };
48
+ client.on("session_update", changed);
49
+ client.on("session_delete", changed);
50
+ client.on("session_expire", changed);
51
+ client.on("session_event", selectionChanged);
52
+ const inspectValue = (session) => {
53
+ try {
54
+ return { topic: session.topic, status: "present", session: sessionValue(session) };
55
+ }
56
+ catch {
57
+ return { topic: session.topic, status: "unusable" };
58
+ }
59
+ };
60
+ const inspect = (topic) => {
61
+ if (stopped)
62
+ throw new Error("WalletConnect owner stopped.");
63
+ // A store failure is not evidence that the session disappeared.
64
+ const session = client.session.getAll().find((item) => item.topic === topic);
65
+ return session ? inspectValue(session) : { topic, status: "absent" };
66
+ };
67
+ const current = (topic) => {
68
+ if (stopped)
69
+ return undefined;
70
+ const observed = inspect(topic);
71
+ return observed.status === "present" ? observed.session : undefined;
72
+ };
73
+ const requireActive = () => { if (stopped)
74
+ throw new Error("WalletConnect owner stopped."); };
75
+ return {
76
+ async restore() {
77
+ requireActive();
78
+ // A pending proposal is not an approved session and must not reappear as
79
+ // a new approval ceremony after owner replacement.
80
+ for (const pairing of client.core.pairing.getPairings()) {
81
+ if (!pairing.active)
82
+ await client.core.pairing.disconnect({ topic: pairing.topic });
83
+ }
84
+ return client.session.getAll().flatMap((session) => { const value = current(session.topic); return value ? [value] : []; });
85
+ },
86
+ async connect() {
87
+ requireActive();
88
+ const connecting = await client.connect({ requiredNamespaces: { sui: {
89
+ chains: [SUI_MAINNET_WALLET_CHAIN], methods: [SUI_SIGN_TRANSACTION_METHOD], events: ["accountsChanged", "chainChanged"]
90
+ } } });
91
+ const approval = connecting.approval().then(sessionValue).catch((error) => { throw transportError(error); });
92
+ // Attach a rejection handler immediately; the backend records the same
93
+ // promise's outcome once the private QR has been associated with its row.
94
+ void approval.catch(() => undefined);
95
+ if (!connecting.uri)
96
+ throw new Error("The wallet pairing could not be prepared.");
97
+ const topic = /^wc:([^@]+)@2\?/.exec(connecting.uri)?.[1];
98
+ const pairing = topic ? client.core.pairing.getPairings().find((item) => item.topic === topic) : undefined;
99
+ if (!pairing || !Number.isSafeInteger(pairing.expiry))
100
+ throw new Error("Wallet pairing expiry is unavailable.");
101
+ return { uri: connecting.uri, expiresAt: new Date(pairing.expiry * 1000).toISOString(), approval };
102
+ },
103
+ async disconnect(topic) { requireActive(); await client.disconnect({ topic, reason: getSdkError("USER_DISCONNECTED") }); },
104
+ async sign(input) {
105
+ requireActive();
106
+ const session = current(input.topic);
107
+ if (!session?.accounts.includes(input.account) || !session.methods.includes(SUI_SIGN_TRANSACTION_METHOD)) {
108
+ throw new Error("This wallet session cannot sign for the selected Sui account.");
109
+ }
110
+ return signedResponse.parse(await client.request({ topic: input.topic, chainId: SUI_MAINNET_WALLET_CHAIN,
111
+ request: { method: SUI_SIGN_TRANSACTION_METHOD, params: { transaction: input.transactionBytesBase64, address: input.account } } }));
112
+ },
113
+ session: current,
114
+ inspect,
115
+ inspectAll: () => { requireActive(); return client.session.getAll().map(inspectValue); },
116
+ onSessionChanged(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; },
117
+ stop() {
118
+ stopped = true;
119
+ listeners.clear();
120
+ client.off("session_update", changed);
121
+ client.off("session_delete", changed);
122
+ client.off("session_expire", changed);
123
+ client.off("session_event", selectionChanged);
124
+ // SignClient has no supported complete in-process disposal. The runtime
125
+ // ends the owner process; it must not close injected storage under timers.
126
+ }
127
+ };
128
+ }
@@ -0,0 +1,3 @@
1
+ import { createWalletConnectTransport } from "./walletConnectTransport.js";
2
+ import { runWalletSdkChild } from "./walletSdkChildRuntime.js";
3
+ runWalletSdkChild(({ projectId, dataDirectory, metadata }) => createWalletConnectTransport({ projectId, dataDirectory, metadata }));
@@ -0,0 +1,160 @@
1
+ import { join } from "node:path";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ import { WalletUserRejectedError } from "../core/session/walletConnection.js";
4
+ import { acquireDataDirectoryOwner } from "./shared/ownerLease.js";
5
+ import { walletCommandSchema, walletEventSchema } from "./walletSdkIpc.js";
6
+ // Production and the real-SDK wire fixture run this same process lifecycle.
7
+ // The factory is a code dependency, never a serialized command or setting.
8
+ export function runWalletSdkChild(createTransport) {
9
+ let init, transport;
10
+ let sequence = 0, lastRequest = 0, ready = false, exiting = false;
11
+ let stage = "initialization_failed";
12
+ let ownership;
13
+ const sessions = new Map();
14
+ const send = (message) => {
15
+ if (!process.connected || !process.send) {
16
+ exit();
17
+ return;
18
+ }
19
+ process.send(walletEventSchema.parse(message), (error) => { if (error)
20
+ exit(); });
21
+ };
22
+ const envelope = () => ({ protocolVersion: 1, runId: init.runId });
23
+ const exit = () => { if (!exiting) {
24
+ exiting = true;
25
+ void ownership;
26
+ process.exit(0);
27
+ } };
28
+ const fail = (reason) => {
29
+ ready = false;
30
+ if (init && !exiting)
31
+ send({ ...envelope(), type: "failure", reason });
32
+ // Neither SDK cleanup nor a callback is a prerequisite for process exit.
33
+ if (!exiting) {
34
+ exiting = true;
35
+ process.exit(1);
36
+ }
37
+ };
38
+ process.once("disconnect", exit);
39
+ process.once("SIGTERM", exit);
40
+ process.once("SIGINT", exit);
41
+ process.on("uncaughtException", () => fail(stage));
42
+ process.on("unhandledRejection", () => fail(stage));
43
+ if (!process.connected) {
44
+ exit();
45
+ return;
46
+ }
47
+ const inspect = (value, changedTopic) => {
48
+ const prior = sessions.get(value.topic);
49
+ const priorValue = prior && (prior.status === "present" ? { topic: prior.topic, status: prior.status, session: prior.session } : { topic: prior.topic, status: prior.status });
50
+ const version = (prior?.version ?? 0) + Number(!prior || !isDeepStrictEqual(priorValue, value) || changedTopic === value.topic);
51
+ const next = { ...value, version };
52
+ sessions.set(value.topic, next);
53
+ return next;
54
+ };
55
+ const snapshot = (isReady, changedTopic, checkTopic) => {
56
+ try {
57
+ if (!ownership || !transport)
58
+ throw new Error("Wallet storage ownership is unavailable.");
59
+ const values = transport.inspectAll(), topics = new Set(values.map((item) => item.topic));
60
+ for (const topic of sessions.keys())
61
+ if (!topics.has(topic))
62
+ values.push({ topic, status: "absent" });
63
+ if (checkTopic && !values.some((item) => item.topic === checkTopic))
64
+ values.push(transport.inspect(checkTopic));
65
+ const value = { runId: init.runId, sequence: ++sequence, observedAt: new Date().toISOString(),
66
+ sessions: values.map((item) => inspect(item, changedTopic)) };
67
+ send({ ...envelope(), type: "snapshot", snapshot: value, ready: isReady });
68
+ return value;
69
+ }
70
+ catch {
71
+ fail("wallet_state_unavailable");
72
+ throw new Error("Wallet state inspection failed.");
73
+ }
74
+ };
75
+ const start = async (input) => {
76
+ init = input;
77
+ // The child alone opens the SDK store; OS exit releases this separate lock.
78
+ ownership = acquireDataDirectoryOwner(join(input.dataDirectory, "walletconnect", "sessions.sqlite"));
79
+ send({ ...envelope(), type: "stage", stage: "sdk_start" });
80
+ transport = await createTransport(input);
81
+ stage = "restoration_failed";
82
+ send({ ...envelope(), type: "stage", stage: "session_restore" });
83
+ await transport.restore();
84
+ transport.onSessionChanged((topic, selectionChanged) => {
85
+ try {
86
+ snapshot(false, selectionChanged ? topic : undefined);
87
+ }
88
+ catch {
89
+ fail("wallet_state_unavailable");
90
+ }
91
+ });
92
+ stage = "wallet_state_unavailable";
93
+ snapshot(true);
94
+ ready = true;
95
+ };
96
+ const command = async (input) => {
97
+ const reply = { ...envelope(), requestId: input.requestId, operationId: input.operationId };
98
+ try {
99
+ if (!ready || !transport)
100
+ throw new Error("Wallet service is not ready.");
101
+ switch (input.type) {
102
+ case "connect": {
103
+ const pairing = await transport.connect();
104
+ send({ ...reply, type: "pairing", uri: pairing.uri, expiresAt: pairing.expiresAt });
105
+ const session = await pairing.approval;
106
+ snapshot(false);
107
+ send({ ...reply, type: "connected", session });
108
+ return;
109
+ }
110
+ case "disconnect":
111
+ await transport.disconnect(input.topic);
112
+ snapshot(false);
113
+ send({ ...reply, type: "disconnected" });
114
+ return;
115
+ case "check_session": {
116
+ const value = snapshot(false, undefined, input.topic);
117
+ send({ ...reply, type: "checked", sequence: value.sequence, observation: sessions.get(input.topic) });
118
+ return;
119
+ }
120
+ case "sign": {
121
+ snapshot(false, undefined, input.topic);
122
+ const current = sessions.get(input.topic);
123
+ if (current.status !== "present" || current.version !== input.sessionVersion ||
124
+ !current.session.accounts.includes(input.account) || !current.session.methods.includes("sui_signTransaction")) {
125
+ send({ ...reply, type: "error", code: "session_changed" });
126
+ return;
127
+ }
128
+ const result = await transport.sign(input);
129
+ snapshot(false, undefined, input.topic);
130
+ send({ ...reply, type: "signed", ...result, sessionVersion: sessions.get(input.topic).version });
131
+ return;
132
+ }
133
+ }
134
+ }
135
+ catch (error) {
136
+ send({ ...reply, type: "error", code: error instanceof WalletUserRejectedError ? "rejected" : "operation_failed" });
137
+ }
138
+ };
139
+ process.on("message", (raw) => {
140
+ const parsed = walletCommandSchema.safeParse(raw);
141
+ if (!parsed.success) {
142
+ fail("wallet_state_unavailable");
143
+ return;
144
+ }
145
+ const input = parsed.data;
146
+ if (input.type === "init") {
147
+ if (init) {
148
+ fail("wallet_state_unavailable");
149
+ return;
150
+ }
151
+ void start(input).catch(() => fail(stage));
152
+ return;
153
+ }
154
+ if (!init || input.runId !== init.runId || input.requestId <= lastRequest)
155
+ return;
156
+ // Reserve before awaiting anything, including a connect approval.
157
+ lastRequest = input.requestId;
158
+ void command(input).catch(() => fail("wallet_state_unavailable"));
159
+ });
160
+ }
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ import { suiAddressStringSchema } from "../core/suiAddress.js";
3
+ import { walletRunIdSchema, walletStartStageSchema, walletSnapshotSchema, walletSessionObservationSchema, walletSessionSchema } from "../core/session/walletRuntime.js";
4
+ import { walletUnavailableReasonSchema } from "../core/session/walletConnection.js";
5
+ const envelope = z.object({ protocolVersion: z.literal(1), runId: walletRunIdSchema });
6
+ const request = envelope.extend({ requestId: z.number().int().positive(), operationId: z.string().min(1) });
7
+ export const walletCommandSchema = z.union([
8
+ envelope.extend({ type: z.literal("init"), dataDirectory: z.string().min(1), projectId: z.string().regex(/^[0-9a-f]{32}$/i),
9
+ metadata: z.object({ name: z.string(), description: z.string(), url: z.string() }).strict() }).strict(),
10
+ request.extend({ type: z.literal("connect") }).strict(),
11
+ request.extend({ type: z.enum(["disconnect", "check_session"]), topic: z.string().min(1) }).strict(),
12
+ request.extend({ type: z.literal("sign"), topic: z.string().min(1), account: suiAddressStringSchema,
13
+ transactionBytesBase64: z.string().min(1), sessionVersion: z.number().int().positive() }).strict()
14
+ ]);
15
+ const reply = envelope.extend({ requestId: z.number().int().positive(), operationId: z.string().min(1) });
16
+ export const walletEventSchema = z.union([
17
+ envelope.extend({ type: z.literal("stage"), stage: walletStartStageSchema }).strict(),
18
+ envelope.extend({ type: z.literal("snapshot"), snapshot: walletSnapshotSchema, ready: z.boolean() }).strict(),
19
+ envelope.extend({ type: z.literal("failure"), reason: walletUnavailableReasonSchema }).strict(),
20
+ reply.extend({ type: z.literal("pairing"), uri: z.string().startsWith("wc:"), expiresAt: z.string().datetime() }).strict(),
21
+ reply.extend({ type: z.literal("connected"), session: walletSessionSchema }).strict(),
22
+ reply.extend({ type: z.literal("disconnected") }).strict(),
23
+ reply.extend({ type: z.literal("checked"), observation: walletSessionObservationSchema, sequence: z.number().int().positive() }).strict(),
24
+ reply.extend({ type: z.literal("signed"), transactionBytes: z.string().min(1), signature: z.string().min(1), sessionVersion: z.number().int().positive() }).strict(),
25
+ reply.extend({ type: z.literal("error"), code: z.enum(["rejected", "session_changed", "operation_failed"]) }).strict()
26
+ ]);