@stelis/say-ur-intent 0.0.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.
@@ -0,0 +1,116 @@
1
+ # SDK API Verification
2
+
3
+ This document records the pinned SDK APIs used by the current runtime. The source of truth is the installed package source in `node_modules`, not memory or external summaries.
4
+
5
+ ## Package Versions
6
+
7
+ - `@mysten/sui`: `2.16.2`
8
+ - `@mysten/deepbook-v3`: `1.3.6`
9
+ - `@mysten/dapp-kit-core`: `1.3.2`
10
+
11
+ ## Sui gRPC Client
12
+
13
+ Verified from `node_modules/@mysten/sui/src/grpc/index.ts`, `client.ts`, `core.ts`, and `node_modules/@mysten/sui/src/client/types.ts`.
14
+
15
+ - Import path: `@mysten/sui/grpc`
16
+ - Client: `SuiGrpcClient`
17
+ - Constructor input includes:
18
+ - `network`
19
+ - `baseUrl`
20
+ - optional `fetchInit`
21
+ - or custom `transport`
22
+ - Runtime construction:
23
+
24
+ ```ts
25
+ new SuiGrpcClient({
26
+ baseUrl: "https://fullnode.mainnet.sui.io:443",
27
+ network: "mainnet"
28
+ });
29
+ ```
30
+
31
+ Confirmed methods:
32
+
33
+ | Method | Signature Source | Notes |
34
+ | --- | --- | --- |
35
+ | `client.core.getChainIdentifier()` | `GrpcCoreClient.getChainIdentifier(_options?)` | Returns `{ chainIdentifier: string }`. |
36
+ | `client.core.listBalances(options)` | `GrpcCoreClient.listBalances(options)` | `options.owner` is required; `options.cursor` and `options.limit` are available. Returns `{ balances, hasNextPage, cursor }`. |
37
+ | `client.core.getCoinMetadata(options)` | `GrpcCoreClient.getCoinMetadata(options)` | `options.coinType` is required. Returns `{ coinMetadata }`; metadata can be `null`. The gRPC implementation resolves type through MVR before calling `stateService.getCoinInfo`; the pinned gRPC implementation catches `getCoinInfo` failures and returns `coinMetadata: null`, so callers cannot distinguish those failures from missing metadata through this method alone. |
38
+ | `client.core.simulateTransaction(options)` | `GrpcCoreClient.simulateTransaction(options)` | Accepts `transaction`, optional `include`, optional `checksEnabled`. |
39
+
40
+ `SimulateTransactionOptions.checksEnabled` defaults to enabled and can be set to `false` for debug/read inspection. Read-only DeepBook SDK methods use simulation internally and must not be presented as signing readiness.
41
+
42
+ For review-time transaction simulation, use the runtime gRPC `client.core.simulateTransaction(...)` path with validation checks enabled. The required first-route review include set is `effects`, `balanceChanges`, `objectTypes`, and `transaction`: those fields provide status, gas used, changed-object context, raw balance deltas, sender, gas config, inputs, and command shape. Missing required fields must fail closed; do not infer readiness from partial simulation data. Do not request `bcs` for product-facing or stored review evidence, because raw transaction bytes are not an MCP or review-app output. `commandResults` remains scoped to read-only DeepBook raw quote extraction and is not swap review simulation evidence. Failed simulations are blocked pre-signing review facts, not wallet rejection, transaction submission failure, or automatic transient retry evidence. A thrown simulation call is refreshable only when it is classified as a transport, RPC, timeout, or endpoint availability failure; malformed transaction material, request-shape bugs, incomplete SDK results, and adapter defects must remain blocked.
43
+
44
+ ## DeepBook Read Methods
45
+
46
+ Verified from `node_modules/@mysten/deepbook-v3/src/client.ts` and `node_modules/@mysten/deepbook-v3/src/types/index.ts`.
47
+
48
+ - Import path: `@mysten/deepbook-v3`
49
+ - Client: `DeepBookClient`
50
+ - Constructor requires:
51
+ - `client`
52
+ - `address`
53
+ - `network`
54
+ - Constructor also accepts optional configured `balanceManagers`; Say Ur Intent uses this only to register a user-supplied `managerAddress` as an ephemeral SDK BalanceManager key for account-bound inventory detail reads.
55
+
56
+ The `address` is used by the SDK as the transaction simulation sender for read queries. For sender-independent DeepBook orderbook, raw-quantity quote, and display-amount quote reads, Say Ur Intent supplies an internal mainnet placeholder address. This placeholder is not user identity and must not be represented as wallet authorization.
57
+
58
+ Account-bound DeepBook reads, such as `read.summarize_deepbook_account_inventory`, use the active account address as the simulation sender. The product filters manager detail reads through on-chain `getBalanceManagerIds(owner)` discovery before calling account-bound detail methods; this is active read context, not signing authorization or custody.
59
+
60
+ Confirmed methods:
61
+
62
+ | Method | Return Type | Used For |
63
+ | --- | --- | --- |
64
+ | `midPrice(poolKey)` | `Promise<number>` | Orderbook context. |
65
+ | `poolBookParams(poolKey)` | `Promise<PoolBookParams>` | Tick, lot, and min size context. |
66
+ | `getLevel2TicksFromMid(poolKey, ticks)` | `Promise<Level2TicksFromMid>` | Bid/ask levels around mid price. |
67
+ | `deepBook.getQuoteQuantityOut(poolKey, baseQuantity)` | Transaction builder thunk | Base-to-quote quantity quote. Say Ur Intent passes positive raw input quantities that fit the SDK `u64` argument and parses the simulated raw `u64` return values directly. |
68
+ | `deepBook.getBaseQuantityOut(poolKey, quoteQuantity)` | Transaction builder thunk | Quote-to-base quantity quote. Say Ur Intent passes positive raw input quantities that fit the SDK `u64` argument and parses the simulated raw `u64` return values directly. |
69
+ | `getBalanceManagerIds(owner)` | `Promise<string[]>` | Active-account BalanceManager discovery. |
70
+ | `accountExists(poolKey, managerKey)` | `Promise<boolean>` | Gate before account-bound detail reads. |
71
+ | `account(poolKey, managerKey)` | `Promise<AccountInfo>` | Display-like account ledger and rebate inventory. Not raw/signable quantity. |
72
+ | `lockedBalance(poolKey, balanceManagerKey)` | `Promise<LockedBalances>` | Display-like balances tied to open orders. Not withdrawable/spendable readiness. |
73
+ | `accountOpenOrders(poolKey, managerKey)` | `Promise<string[]>` | Open order ID inventory; Say Ur Intent caps returned IDs in its public response. |
74
+
75
+ Relevant return shapes:
76
+
77
+ ```ts
78
+ type PoolBookParams = {
79
+ tickSize: number;
80
+ lotSize: number;
81
+ minSize: number;
82
+ };
83
+
84
+ type Level2TicksFromMid = {
85
+ bid_prices: number[];
86
+ bid_quantities: number[];
87
+ ask_prices: number[];
88
+ ask_quantities: number[];
89
+ };
90
+
91
+ type QuoteQuantityOut = {
92
+ baseQuantity: number;
93
+ baseOut: number;
94
+ quoteOut: number;
95
+ deepRequired: number;
96
+ };
97
+
98
+ type BaseQuantityOut = {
99
+ quoteQuantity: number;
100
+ baseOut: number;
101
+ quoteOut: number;
102
+ deepRequired: number;
103
+ };
104
+ ```
105
+
106
+ The pinned SDK accepts `number | bigint` quote inputs. Its high-level quote query objects include an input echo field (`baseQuantity` or `quoteQuantity`) produced with `Number(inputRaw)`, plus display quote fields (`baseOut`, `quoteOut`, and `deepRequired`) produced after scalar division and `Number(...)` conversion. Say Ur Intent does not use those high-level query objects as the canonical quote source for adapter preparation. It uses the pinned SDK transaction builder quote functions, requests `client.core.simulateTransaction` command results, and parses raw `u64` return values. The simulated public Move entrypoint is `pool::get_quote_quantity_out` for base-to-quote reads and `pool::get_base_quantity_out` for quote-to-base reads; both delegate to `pool::get_quantity_out`, whose official Move source defines the return order as `base_quantity_out`, `quote_quantity_out`, and `deep_quantity_required`. Public `quote` fields are exact decimal display strings derived from those raw values through pinned DeepBook scalars; `rawQuote` carries the raw evidence. `read.quote_deepbook_action` marks the input as raw `u64`, while `read.quote_deepbook_display_amount` marks the input as a source display amount converted to raw `u64`. Raw quote evidence is not an effective price, price-impact calculation, quote-vs-mid slippage calculation, venue comparison, best-route claim, fiat cash-out estimate, external market lookup, USDC/USD peg assumption, P&L, cost basis, final min-out, signing data, or signing readiness. The account-bound DeepBook review may derive a fresh raw quote policy from this evidence and use that derived policy for local unsigned transaction material build, while keeping bytes internal and wallet signing blocked. When the digest commitment stage completes, it is derived from the locally stored transaction bytes through pinned SDK `Transaction.from(...).getDigest()` and remains an internal binding, not a public signing artifact.
107
+
108
+ ## Runtime Boundary
109
+
110
+ - JSON-RPC client imports are not used.
111
+ - Sui gRPC and GraphQL endpoints are resolved from local SQLite settings by default. `SUI_GRPC_URL` and `SUI_GRAPHQL_URL` are advanced temporary overrides and do not mutate stored settings.
112
+ - Sui gRPC URLs must include an explicit port and no credentials, path, query string, or fragment.
113
+ - Sui GraphQL URLs must use `https` and must not include credentials, query string, or fragment.
114
+ - The gRPC endpoint is verified during runtime startup. The GraphQL endpoint is verified when saved, imported, or first used by Sui activity tools.
115
+ - `fetchedAt` fields are ISO 8601 UTC strings produced by `new Date().toISOString()`.
116
+ - Read-only tools may inspect mainnet state but must not create signable transaction material.
@@ -0,0 +1,358 @@
1
+ # Signable Adapter and PTB Visualization Contract
2
+
3
+ This document defines the contract boundary for wallet-review adapters.
4
+ It is a product and implementation contract, not a current feature announcement.
5
+
6
+ The current release can build local unsigned transaction material for the
7
+ account-bound DeepBook swap review stage and internally bind a Sui transaction
8
+ digest to that stored material. The byte handoff to the same-machine browser
9
+ is gated on recomputed-digest equality, and the review page offers
10
+ user-controlled wallet signing with execution receipts recorded on the
11
+ session. No MCP tool returns
12
+ transaction bytes, signing data, signing readiness, executable transaction
13
+ material, or payment execution readiness.
14
+
15
+ The source-level schema for this contract lives in
16
+ `src/core/action/signableAdapterContract.ts`.
17
+
18
+ ## Current Status
19
+
20
+ The contract schema is implemented in TypeScript and Zod, and the runtime
21
+ DeepBook account-bound swap review emits it. When the account-bound review
22
+ completes every evidence stage (local unsigned transaction material, internal
23
+ digest commitment, object ownership, quote/policy provenance, human-readable
24
+ review facts, and review-time simulation), the review layer assembles those
25
+ private artifacts into a schema-validated `WalletReviewAdapterContract` and
26
+ records it on the public review state as `walletReviewAdapterContract` on a
27
+ `ready_for_wallet_review` status. If any required evidence is
28
+ missing or fails contract validation, no contract is emitted and the review
29
+ stays blocked with `blockedReason: "wallet_review_contract_emit_missing"`.
30
+
31
+ No MCP response or review UI response exposes executable transaction material.
32
+ The emitted contract carries the Sui transaction digest as
33
+ `transactionMaterialCommitment` (a hash only); raw transaction bytes stay
34
+ inside the review-server session and leave it only through the digest-gated
35
+ handoff endpoint for the same-machine browser. After the handoff gate, the
36
+ same-machine browser page requests the user's wallet signature and submits the
37
+ signed transaction; the contract layer itself never signs or executes.
38
+
39
+ ## Final Acceptance Gate
40
+
41
+ This document defines a contract gate, not a current signing feature. Do not add
42
+ new capability here ahead of a reviewed implementation.
43
+
44
+ The gate binds three views of the action to one transaction commitment, so a
45
+ user cannot sign something different from what they reviewed:
46
+
47
+ - The commitment is a digest (hash) of the exact serialized transaction bytes
48
+ that will be signed, represented as the Sui transaction digest. The commitment
49
+ is a hash only; raw transaction bytes remain a prohibited output and must never
50
+ appear in MCP or review responses.
51
+ - `humanReadableReview` must be derived from the transaction identified by that
52
+ commitment.
53
+ - The review-time `simulation` must have been run against the transaction with
54
+ that same commitment.
55
+ - Any bytes handed to the wallet must hash to that same commitment.
56
+
57
+ The acceptance criterion is strict equality: review-commitment equals
58
+ simulation-commitment equals handoff-commitment. If any is missing or differs,
59
+ the contract is invalid and signing stays blocked.
60
+
61
+ ### Transaction byte lifecycle (closes the handoff loophole)
62
+
63
+ The signable bytes must have exactly one origin and one handoff path, so an
64
+ adapter cannot display a commitment while signing different bytes through a side
65
+ channel:
66
+
67
+ - Origin: the bytes are produced only inside Say Ur Intent's review layer (the
68
+ signable adapter), from material Say Ur Intent independently built or verified.
69
+ They are never accepted from an external MCP or AI-client proposal.
70
+ - Storage: the bytes live only in the local review-server session keyed by
71
+ `reviewSessionId`. They never appear in an MCP tool response or in the
72
+ review-API JSON an AI client reads; those surfaces carry the commitment hash
73
+ only.
74
+ - Handoff channel: the only path that may carry the bytes is the local
75
+ review-server to same-machine browser to wallet-adapter handoff for that
76
+ session. There is no other handoff path, and the MCP layer and AI client never
77
+ receive the bytes.
78
+ - Bind at handoff: before the wallet is asked to sign, the handoff path must
79
+ recompute the digest of the exact bytes it is about to hand over and require it
80
+ to equal the commitment that `humanReadableReview` and `simulation` were bound
81
+ to. If they differ, or if any alternate or side handoff path is used, signing
82
+ is refused.
83
+
84
+ Any design that exposes a commitment but routes the signed bytes through a
85
+ different origin or channel violates this gate and is prohibited.
86
+
87
+ One review session covers exactly one transaction. While a handoff is
88
+ outstanding, the session is locked: state recomputes are refused until the
89
+ wallet result is recorded, the user cancels the handoff, or the handed-off
90
+ material expires (the lock then self-releases). A recorded `success` or
91
+ `failure` execution result is final and deletes the stored material.
92
+
93
+ DeepBook swap material is built with an explicit fee mode. When the account's
94
+ DEEP balance covers the protocol fee, the swap quotes and builds in DEEP fee
95
+ mode; otherwise it re-quotes through the protocol's input-fee dry run and
96
+ builds with an explicit zero-DEEP coin so the taker fee is paid from the
97
+ source coin at the protocol penalty. The selected fee mode is recorded as
98
+ review evidence and shown as a check. Before building, the producer verifies
99
+ the account holds the source amount plus the explicit gas budget and fails
100
+ closed with the exact required and held amounts.
101
+
102
+ This gate is enforced at the contract-schema layer. `walletReviewAdapterContractSchema`
103
+ requires a `transactionMaterialCommitment` (the Sui transaction digest of the
104
+ transaction that will be signed, validated with the pinned SDK source of truth
105
+ `isValidTransactionDigest`; a digest only, never raw bytes) and binds
106
+ `humanReadableReview.boundToCommitment` and `simulation.boundToCommitment` to it
107
+ through a `superRefine` invariant (reusing the `requireEqual` cross-field helper)
108
+ that rejects any contract whose three commitments are missing, malformed, or
109
+ unequal (`src/core/action/signableAdapterContract.ts`,
110
+ `test/signableAdapterContract.test.ts`).
111
+
112
+ The runtime bind-at-handoff step is implemented at the session-store boundary:
113
+ `prepareWalletHandoff` recomputes the digest of the exact stored bytes and
114
+ refuses the handoff unless it equals the reviewed
115
+ `transactionMaterialCommitment`. The handoff endpoint
116
+ (`POST /api/review/:id/handoff`) is the only channel that carries the bytes,
117
+ and it serves the same-machine browser only. After the gate passes, the
118
+ review page requests the wallet signature; the signature, execution, and
119
+ receipt stay user-controlled and never flow through the MCP layer.
120
+
121
+ The current release builds local unsigned DeepBook swap transaction material
122
+ inside account-bound review, binds a Sui transaction digest to the stored
123
+ material, hands the digest-verified bytes to the same-machine browser, and
124
+ lets the user sign and execute in their wallet with the receipt recorded on
125
+ the review session. The MCP layer never requests signatures or executes.
126
+
127
+ ## Adapter Contract
128
+
129
+ A wallet-review adapter that returns this contract must regenerate or
130
+ independently verify action material inside Say Ur Intent before any wallet
131
+ handoff exists. External MCP or AI-client proposals remain untrusted structured
132
+ input and cannot become transaction-building authority.
133
+
134
+ The adapter contract requires these fields:
135
+
136
+ - `inputProvenance`: where the request came from, when it was captured, and why
137
+ it remains untrusted until the local review layer regenerates and verifies it.
138
+ - `sourceOfTruth`: source metadata for pinned SDK registries, verified mainnet
139
+ onchain metadata, wallet reads, quote evidence, simulation, validated request
140
+ facts, or explicit user choices. A `sourceOfTruth` record is not itself a
141
+ value-bearing fact. It identifies where a typed evidence claim came from.
142
+ - `evidenceClaims`: typed value-bearing claims for every safety-critical fact
143
+ used by the payload. Each claim has a `factKind`, a `sourceEvidenceId` pointing
144
+ to `sourceOfTruth[].id`, and the value fields required for that fact. The
145
+ schema validates the claim against the safety-critical fact matrix and then
146
+ validates that payload fields match their referenced claim values.
147
+ - `rawQuantities`: raw integer strings plus normalized Sui coin type, verified
148
+ decimals, and unit source. `amountClaimId` must point to a
149
+ `raw_quantity_amount` claim whose role, raw amount, and normalized coin type
150
+ match the payload. Unit `unitClaimId` must point to a `unit_metadata` claim
151
+ whose decimals, normalized coin type, and metadata source match the payload.
152
+ Display amounts remain presentation-only.
153
+ - `gas`: review-time simulation evidence for gas quantities or a concrete
154
+ unresolved reason. `gasBudgetRaw` and `gasUsedRaw` must point to
155
+ `raw_quantity_amount` claims with `gas_budget` and `gas_used` roles, the Sui
156
+ gas coin type, and raw MIST units. An unresolved gas status must point to a
157
+ `gas_unresolved_status` claim. Gas object ownership must point to
158
+ `object_ownership` claims whose ownership status is `owned_by_account`.
159
+ - `expiry`: review-time expiry status. `current` and `expired` require
160
+ `checkedAt`, `expiresAt`, and `evidenceClaimId`; `current` requires
161
+ `expiresAt` after `checkedAt`, and `expired` requires `expiresAt` at or before
162
+ `checkedAt`. The referenced `expiry_status` claim must match the payload and
163
+ must reference a `sourceOfTruth` record with `checkedAt` and `expiresAt`.
164
+ `not_provided` and `not_applicable` must omit `expiresAt`, include
165
+ `evidenceClaimId` and `reason`, and reference an `expiry_status` claim backed
166
+ by `checkedAt` and `expiryStatus`.
167
+ - `slippageOrMinOut`: quote-linked max slippage and minimum-output policy when
168
+ quote evidence is used. `quoteEvidenceClaimId` must point to a `quote_min_out`
169
+ claim whose `quoteEvidenceId` and `minOutRaw` match the payload.
170
+ `policySource: "user_explicit"` requires a separate `policyEvidenceClaimId`
171
+ pointing to a `slippage_policy` claim backed by `user_explicit_choice` fields
172
+ including `maxSlippageBps` and `userSelection`.
173
+ `policySource: "adapter_policy_from_quote_evidence"` requires
174
+ `policyEvidenceClaimId` pointing to a `slippage_policy` claim backed by quote
175
+ evidence fields including `maxSlippageBps` and `minOutRaw`. If `minOutRaw` or
176
+ `maxSlippageBps` appears in the payload, it must have the matching typed claim;
177
+ stale or missing status does not make an unclaimed scalar safe to use.
178
+ `minOutRaw` must also match a `rawQuantities[]` entry with role
179
+ `minimum_output` so the raw integer has normalized coin type and verified
180
+ decimals.
181
+ - `objectOwnership`: account-bound object ownership or shared-object facts. Each
182
+ object must point to an `object_ownership` claim whose object id, owner account,
183
+ and ownership status match the payload.
184
+ - `simulation`: `client.core.simulateTransaction` with validation checks enabled
185
+ and the required fields `effects`, `balanceChanges`, `objectTypes`, and
186
+ `transaction`. The simulation payload must point to a `simulation_result`
187
+ claim whose provider, status, required fields, missing fields, and failure
188
+ reason match the payload.
189
+ - `humanReadableReview`: the fields a human must see before wallet
190
+ authorization, matching the review-model concepts already exposed for
191
+ non-signable proposal review.
192
+ - `outputBoundary`: the MCP and review UI exposure limit. Human-readable review,
193
+ PTB visualization artifacts, diagnostics, and status checks are allowed.
194
+ The required prohibited set is executable transaction material, serialized
195
+ transaction material, wallet signature requests, private-key material, wallet
196
+ authorization, signing data, signing readiness, and payment execution
197
+ readiness. Each contract must include the whole prohibited set.
198
+
199
+ ## Safety-Critical Fact Matrix
200
+
201
+ `SAFETY_CRITICAL_FACT_MATRIX` in `src/core/action/signableAdapterContract.ts`
202
+ is the contract's validation source of truth.
203
+
204
+ It defines the required source kind and source fields for these fact kinds:
205
+
206
+ - `raw_quantity_amount`, keyed by role: input, expected output, minimum output,
207
+ gas budget, gas used, fee, and balance delta;
208
+ - `unit_metadata`, keyed by metadata source;
209
+ - `gas_unresolved_status`;
210
+ - `expiry_status`, keyed by status;
211
+ - `quote_min_out`;
212
+ - `slippage_policy`, keyed by policy source;
213
+ - `object_ownership`;
214
+ - `simulation_result`.
215
+
216
+ Payload fields do not cite `sourceOfTruth` records directly as proof. They cite
217
+ typed evidence claims, and those claims cite `sourceOfTruth` records. The schema
218
+ rejects payload values that do not match their typed claims.
219
+
220
+ ## Consumer Invariant Matrix
221
+
222
+ `CONSUMER_INVARIANT_MATRIX` in
223
+ `src/core/action/signableAdapterContract.ts` defines extra requirements for
224
+ payload fields that consume typed claims. It is separate from
225
+ `SAFETY_CRITICAL_FACT_MATRIX`.
226
+
227
+ `SAFETY_CRITICAL_FACT_MATRIX` answers whether a typed claim has the required
228
+ source kind and source fields. `CONSUMER_INVARIANT_MATRIX` answers whether that
229
+ claim is valid for a specific payload use.
230
+
231
+ Current consumer invariants are:
232
+
233
+ - `gas.gasBudgetClaimId` must reference a `raw_quantity_amount` claim with role
234
+ `gas_budget`, matching raw amount, and Sui gas coin type. The raw unit is MIST.
235
+ - `gas.gasUsedClaimId` must reference a `raw_quantity_amount` claim with role
236
+ `gas_used`, matching raw amount, and Sui gas coin type. The raw unit is MIST.
237
+ - `gas.gasObjects[].ownershipClaimId` must reference an `object_ownership` claim
238
+ with matching object id, matching owner account, and
239
+ `ownership: "owned_by_account"`.
240
+
241
+ The current contract does not prove that a gas object is a `Coin<SUI>` object
242
+ type because the object-ownership claim does not carry object type evidence.
243
+ An adapter must not use object ownership alone as gas coin type evidence.
244
+
245
+ ## PTB Visualization Artifact
246
+
247
+ The PTB visualization contract is for explaining a locally generated
248
+ transaction shape before wallet authorization. It is not wallet handoff.
249
+
250
+ The artifact may contain:
251
+
252
+ - `generatedAt`;
253
+ - `source`, including adapter id, plan id when available, source kind, renderer
254
+ name, package name, and version;
255
+ - Mermaid `flowchart` text;
256
+ - diagnostics from the adapter, renderer, schema, or simulation;
257
+ - `unsupportedUse`, including transaction-building input, wallet authorization,
258
+ signing data, signing readiness, payment execution readiness, and route
259
+ recommendation;
260
+ - `executableMaterial.included: false`.
261
+
262
+ The artifact must not contain executable transaction material. Mermaid text and
263
+ diagnostic messages are screened for executable-material terms and long encoded
264
+ payloads.
265
+
266
+ ## PTB Renderer Status
267
+
268
+ The pinned renderer dependency is `@zktx.io/ptb-model@0.5.0`. The review layer
269
+ converts the stored local transaction material into a Mermaid `flowchart`
270
+ artifact with `rawTransactionToIR` and `transactionIRToMermaid`, only after the
271
+ stored bytes are recomputed to match the bound transaction material commitment;
272
+ a mismatch declines the artifact. The artifact passes
273
+ `ptbVisualizationArtifactSchema` screening before exposure and accompanies the
274
+ emitted wallet review contract as `reviewState.ptbVisualization`. PTB
275
+ visualization stays visualization-only evidence: it is not a runtime
276
+ transaction builder, not wallet handoff, and not a signing-readiness surface.
277
+
278
+ ## Adapter Registration And Stage Vocabulary
279
+
280
+ Review adapters register through `ReviewAdapterDescriptor` entries in
281
+ `src/adapters/reviewAdapters.ts` (`buildSupportedReviewAdapterDescriptors`).
282
+ A descriptor supplies the adapter id, protocol, action kind, stage catalog id,
283
+ and the evidence computer; action-plan factories live in the adapter module
284
+ and are wired by the MCP layer. The platform keeps the
285
+ safety gates non-delegable: the typed evidence claim matrices, the commitment
286
+ equality gate, the adapter lifecycle validator registry
287
+ (`src/adapters/adapterLifecycleValidators.ts`), and the digest-gated byte
288
+ handoff all run outside adapter code. `src/core` contains no adapter imports;
289
+ a source-policy test enforces that boundary.
290
+
291
+ Stage vocabulary is a cross-adapter contract. Every stage catalog that reports
292
+ public review evidence must use these stage ids for these concepts, because the
293
+ core review-state schema binds public fields to them:
294
+
295
+ - `transaction_material_build_or_verify`: local unsigned material exists.
296
+ - `digest_commitment`: the Sui transaction digest is bound to the material.
297
+ - `object_ownership`: object ownership evidence is verified.
298
+ - `human_readable_review`: required before `reviewState.humanReadableReview`.
299
+ - `review_time_simulation`: required before `reviewState.simulation`.
300
+
301
+ Adapter acceptance gate: a new protocol enters as read-only tools or
302
+ non-signable proposal review first. A signable adapter is accepted only when it
303
+ produces every required evidence stage from independently built or verified
304
+ material, passes `walletReviewAdapterContractSchema` without placeholder
305
+ values, and fails closed when any evidence is missing. Protocol names stay out
306
+ of public docs, runtime guidance, and MCP resources until a concrete
307
+ implementation or support decision exists.
308
+
309
+ ## Verification
310
+
311
+ Tests must keep this contract in place before any adapter can use it:
312
+
313
+ - schema tests for required provenance, source-of-truth, raw quantity, gas,
314
+ expiry, slippage or minimum output, object ownership, simulation, and
315
+ human-readable review fields;
316
+ - tests that reject duplicate `sourceOfTruth[].id` and `evidenceClaims[].id`
317
+ values;
318
+ - tests that reject every payload claim reference that does not resolve to a
319
+ typed `evidenceClaims[].id`;
320
+ - tests that reject every `evidenceClaims[].sourceEvidenceId` that does not
321
+ resolve to a `sourceOfTruth[].id`;
322
+ - tests that reject safety-critical claims whose source references resolve to the
323
+ wrong source kind or to records missing the required fields for that claim;
324
+ - tests that reject payload values whose raw amounts, decimals, gas quantities,
325
+ expiry values, minimum output, slippage policy, object ownership, or simulation
326
+ status do not match their typed evidence claims;
327
+ - tests that reject metadata-only records as raw amount evidence and reject
328
+ user-explicit slippage policy claims without explicit user-choice evidence;
329
+ - tests that reject raw signable quantity assets without normalized Sui coin
330
+ types;
331
+ - tests that reject min-out or slippage scalar fields when they do not have typed
332
+ evidence claims;
333
+ - tests that reject `minOutRaw` when it does not match a `rawQuantities[]` entry
334
+ with role `minimum_output`;
335
+ - tests that require `gasBudgetRaw` and `gasUsedRaw` to use
336
+ `raw_quantity_amount` claims with gas roles, the Sui gas coin type, and raw
337
+ MIST units, or require unresolved gas status to use a `gas_unresolved_status`
338
+ claim;
339
+ - tests that reject gas object references backed by object-ownership claims whose
340
+ ownership status is not `owned_by_account`;
341
+ - tests that require every mandatory prohibited output boundary and every
342
+ mandatory PTB unsupported-use boundary;
343
+ - tests that reject `current` or `expired` expiry status without timestamp
344
+ evidence, source evidence, matching source fields, and the required
345
+ `checkedAt`/`expiresAt` time relationship;
346
+ - tests that reject `not_provided` or `not_applicable` expiry status when it
347
+ includes an `expiresAt` timestamp or omits the reason and source evidence for
348
+ why timestamp evidence is unavailable;
349
+ - tests that require failed or unavailable simulation evidence to include a
350
+ concrete `failureReason`;
351
+ - tests that reject display amounts, exponent notation, or floating-point values
352
+ as raw quantities;
353
+ - tests that reject PTB visualization artifacts containing executable-material
354
+ terms or long encoded payloads;
355
+ - MCP/review output tests using the shared forbidden-field-name policy;
356
+ - documentation tests that keep PTB visualization described as visualization
357
+ only, not transaction material, signing readiness, or payment execution
358
+ readiness.
@@ -0,0 +1,182 @@
1
+ # Transaction Activity Log
2
+
3
+ This document owns local transaction activity storage boundaries: what is stored, what is not stored, and which MCP tools can return live or stored activity facts.
4
+
5
+ It does not define setup steps, tool schemas, complete-history support, P&L, route recommendations, transaction-building input, signing data, or signing readiness.
6
+
7
+ Say Ur Intent stores two kinds of local activity evidence:
8
+
9
+ - Say Ur Intent review evidence from local review sessions.
10
+ - User-requested bounded Sui activity facts that were looked up through GraphQL and matched a known local wallet.
11
+
12
+ It does not run a background indexer and does not claim complete wallet history.
13
+
14
+ ## Stored By Default
15
+
16
+ When Say Ur Intent records an execution result for an account-bound review, the runtime stores a `review_executions` row. That row can include:
17
+
18
+ - review session id
19
+ - plan id
20
+ - normalized account
21
+ - execution status
22
+ - transaction digest when available
23
+ - explorer URL when available
24
+ - failure reason when the result failed
25
+ - full execution result JSON after forbidden-field validation
26
+ - recorded and updated timestamps
27
+
28
+ This is review evidence for a Say Ur Intent flow. It is not a complete wallet transaction history.
29
+
30
+ The runtime also stores local review evidence that cannot be reconstructed from chain data:
31
+
32
+ - review session header and full action plan JSON
33
+ - materialized requested intent JSON when the adapter provides `ActionPlan.adapterData.requestedIntent`
34
+ - append-only review state snapshots
35
+ - append-only review lifecycle transitions
36
+
37
+ Lifecycle transition reads mark repeated observations with no stored status change as `isNoOp`. These rows preserve audit detail without changing funnel progress counts.
38
+
39
+ ## Stored When Requested
40
+
41
+ When the user requests a Sui transaction digest lookup or account activity scan, the runtime can store normalized external activity facts only if the transaction is tied to an address already present in the local `accounts` table.
42
+
43
+ A returned scan row is tied to a known local account only when the sender or a returned balance-change owner matches that account.
44
+
45
+ Other provider-returned rows can be used for the current response but are counted as skipped for storage.
46
+
47
+ Stored scan rows include:
48
+
49
+ - scan id
50
+ - scan kind: `digest_lookup`, `account_scan`, or `function_scan`
51
+ - known local account
52
+ - relationship: `affected` or `sent`
53
+ - input digest or bounded scan window
54
+ - request and response cursor metadata
55
+ - endpoint host and verified chain identifier
56
+ - fetched timestamp
57
+ - stored/skipped counts
58
+ - `hasMore`, `windowComplete`, and incomplete reason such as `ordering_unverified`
59
+
60
+ Stored transaction rows include:
61
+
62
+ - known local account
63
+ - digest
64
+ - relationship
65
+ - checkpoint when returned by the provider
66
+ - timestamp when returned by the provider
67
+ - status: `success`, `failure`, or `unknown`
68
+ - known sender account id only when the sender is also a known local account
69
+ - first and last scan ids
70
+ - first and last fetched timestamps
71
+ - normalized detail facts when returned by GraphQL:
72
+ - transaction kind
73
+ - capped Move call targets with package, module, and function
74
+ - raw coin balance changes in `amountRaw` signed integer strings, with the balance owner stored only when it is the known local account
75
+ - object changes with object id, change kind, and before/after object type when available
76
+ - event sequence, package, module, and event type, with the event sender stored only when it is the known local account
77
+ - gas cost summary raw integer string fields, including derived `netGasCostRaw`
78
+ - execution error message and structured abort location when available
79
+ - truncation flags for capped detail lists
80
+
81
+ `function_scan` is internal provenance for sent-function scans.
82
+
83
+ It is not a stored function-history query, a function index, or authority to filter stored summaries by function.
84
+
85
+ Legacy rows stored before this provenance existed can remain `account_scan`. Say Ur Intent does not backfill them because the stored scan row does not contain the function target needed to identify them safely.
86
+
87
+ Live scan and live-summary rows expose these fields for requested-account raw balance-change facts:
88
+
89
+ - `requestedAccountTransactionFacts`;
90
+ - `requestedAccount.coinFlows`;
91
+ - `transactions[].requestedAccountEffect`.
92
+
93
+ Use those fields for wallet-specific asset-flow answers.
94
+
95
+ `requestedAccountTransactionFacts[]` is a flattened account-scoped row surface for AI client summaries.
96
+
97
+ It repeats the row digest, status, and sender metadata.
98
+
99
+ It carries account-scoped fields such as:
100
+
101
+ - `accountBalanceChangeEvidence`;
102
+ - `accountBalanceChangeAbsenceProven`;
103
+ - `accountBalanceChangeInferencePolicy`;
104
+ - `accountBalanceChanges`;
105
+ - `accountCoinFlows`;
106
+ - `accountEffectLimitations`.
107
+
108
+ It also includes transaction-level `transactionContext` for calls, events, objects, gas, truncation, and protocol labels.
109
+
110
+ Live scan and live-summary `transactionContext` intentionally omits transaction-wide balance-change aggregates. This prevents repeated ownerless raw balance-change facts from being mistaken for a requested wallet's balance movement.
111
+
112
+ A row-level `requestedAccountEffect` has `scope: "requested_account"` plus `role`, `balanceChangeEvidence`, `accountBalanceChangeAbsenceProven`, `accountBalanceChangeInferencePolicy`, `coinFlows`, and `limitations` fields for that transaction.
113
+
114
+ `incomplete_account_balance_changes` and `account_balance_changes_unavailable` are not zero-balance evidence.
115
+
116
+ Only `accountBalanceChangeAbsenceProven: true` or `no_account_balance_changes_returned` with complete evidence means the returned details prove no requested-account balance changes in that row.
117
+
118
+ `accountBalanceChangeInferencePolicy: "do_not_infer_from_transaction_context"` means transaction-level context, visible recipient patterns, current wallet balances, compact counts, or aggregate analysis must not be used to infer the requested account's amount.
119
+
120
+ Activity `quantitySemantics` marks `amountRaw`, `increaseRaw`, `decreaseRaw`, and `netRaw` fields as raw integer facts that require verified decimals or a returned display amount before display conversion.
121
+
122
+ If `requestedAccount.balanceChangeCompleteness` or a row-level `requestedAccountEffect.balanceChangeCompleteness` is `truncated` or `unavailable`, say the account-specific balance-change evidence is incomplete instead of presenting the returned rows as complete.
123
+
124
+ Inspect responses and stored-summary rows can expose a derived `compact` view when full or stored details are available.
125
+
126
+ Activity summary responses expose `transactionDetailAvailability` for the returned `transactions` rows. `transactions[].transactionContext`, `transactions[].compact`, and `transactions[].details` are listed in `userAnswerUse.answerFields` only when every returned transaction row has details. Mixed rows must be described from `transactionDetailAvailability` and row-specific fields, not as an all-row detail guarantee.
127
+
128
+ `compact.factScope: "transaction"` and `compact.requestedAccountScoped: false` mean compact balance, object, event, and gas fields summarize the transaction, not the requested wallet.
129
+
130
+ Repeated identical compact balance-change facts can be aggregated with `count`.
131
+
132
+ `analysis.coinFlows` is a transaction/page aggregate over returned normalized facts, not wallet-specific evidence.
133
+
134
+ Gas raw values are MIST.
135
+
136
+ `compact.gasCost.display` and `analysis.gas.netGasCost.display`, when present, are SUI display facts derived with `@mysten/sui MIST_PER_SUI`.
137
+
138
+ Protocol matches are computed from normalized detail facts at response time and can include `mvrName`/`packageSource` for package-derived evidence.
139
+
140
+ They are transaction activity labels only, not stored protocol decoder output, wallet position inventory, P&L, route recommendation, transaction-building input, signing data, or signing readiness.
141
+
142
+ Live and stored summary tools can also expose deterministic `analysis` over returned normalized facts.
143
+
144
+ That analysis aggregates raw integer coin flows, gas totals, Move call targets, object/event counts, failure details, and protocol counts keyed by `protocolMatches[].protocolId`.
145
+
146
+ It is not display conversion, P&L, position inventory, route quality, protocol support, transaction-building input, signing data, or signing readiness.
147
+
148
+ Use `read.inspect_sui_transaction` when a user needs the full normalized facts for a digest.
149
+
150
+ The runtime does not store non-known party account addresses inside persisted transaction rows or normalized detail facts. If a digest lookup or explicit-account scan is not related to a known local wallet, the result can be returned for the current request but is not persisted.
151
+
152
+ ## Not Stored
153
+
154
+ This toolkit does not run automatic scans for any account.
155
+
156
+ It does not store raw GraphQL payloads, transaction bytes, signatures, BCS payloads, private or session material, balance snapshots, price observations, complete gas history, abandoned-reason logs, quote logs, or protocol decoder outputs.
157
+
158
+ Complete account transaction history is not a product surface of this data.
159
+
160
+ AI clients may answer questions only from data the listed read tools return.
161
+
162
+ `read.summarize_sui_activity_scan` summarizes a live bounded scan without full details or transaction-wide balance-change aggregates in row context.
163
+
164
+ `read.scan_sui_function_activity` and `read.summarize_sui_function_activity_scan` reuse the same requested-account fact, transaction-context, analysis, and protocol label pipeline for transactions the selected account sent that called one full `package::module::function`.
165
+
166
+ They do not create a function-specific stored history, global function index, affected-object history, or complete dApp history.
167
+
168
+ `read.summarize_sui_account_activity` summarizes account-level stored normalized facts only.
169
+
170
+ It can include rows originally observed by account scans, digest lookups, or sent-function scans, and the scan kind remains provenance rather than a user query filter.
171
+
172
+ Provider retention and rate-limit behavior are endpoint/operator properties, not guarantees made by Say Ur Intent.
173
+
174
+ Empty pages, bounded pages, and stored local summaries must not be treated as complete history.
175
+
176
+ When normalized details are missing or capped, use `transactionDetailAvailability` and the returned digest metadata with `read.inspect_sui_transaction` instead of inventing missing fields.
177
+
178
+ Inspect responses can include live GraphQL detail fields for the current response.
179
+
180
+ Live scan and live-summary rows point to that digest path through `detailLookup` instead of returning full `details`.
181
+
182
+ Stored summaries return sanitized normalized details only, omit non-known party account addresses, and may include `lastScanIncompleteReason` when the last scan that touched a row had incomplete or unverified coverage.