@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.
- package/LICENSE +21 -0
- package/LICENSES/@mysten-bcs-2.0.5-Apache-2.0.txt +205 -0
- package/LICENSES/@mysten-sui-2.17.0-Apache-2.0.txt +201 -0
- package/LICENSES/@mysten-utils-0.3.3-Apache-2.0.txt +205 -0
- package/LICENSES/fancy-canvas-2.1.0-MIT.txt +7 -0
- package/LICENSES/lightweight-charts-5.2.0-NOTICE.txt +2 -0
- package/README.md +288 -0
- package/dist/adapters/adapterLifecycleValidators.js +5 -0
- package/dist/adapters/adapterPromptSurfaces.js +60 -0
- package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +178 -0
- package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
- package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
- package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
- package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
- package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +254 -0
- package/dist/adapters/intentPlanFactories.js +51 -0
- package/dist/adapters/reviewAdapters.js +47 -0
- package/dist/core/action/adapterLifecycleValidation.js +12 -0
- package/dist/core/action/contractNameRegistry.js +151 -0
- package/dist/core/action/forbiddenFields.js +43 -0
- package/dist/core/action/humanReadableReviewEvidence.js +193 -0
- package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
- package/dist/core/action/ptbVisualizationProducer.js +71 -0
- package/dist/core/action/receiptPtbGraph.js +29 -0
- package/dist/core/action/reviewCheckResults.js +6 -0
- package/dist/core/action/reviewStateValidation.js +11 -0
- package/dist/core/action/reviewTimeSimulationEvidence.js +470 -0
- package/dist/core/action/schemas.js +491 -0
- package/dist/core/action/signableAdapterContract.js +998 -0
- package/dist/core/action/suiChainReceiptEvidence.js +106 -0
- package/dist/core/action/suiChainReceiptMapping.js +107 -0
- package/dist/core/action/suiChainReceiptReader.js +211 -0
- package/dist/core/action/suiChainReceiptVerifier.js +178 -0
- package/dist/core/action/swapHumanReadableReviewProjection.js +130 -0
- package/dist/core/action/swapQuotePolicyEvidence.js +271 -0
- package/dist/core/action/transactionFunding.js +244 -0
- package/dist/core/action/transactionObjectOwnershipEvidence.js +256 -0
- package/dist/core/action/transactionObjectOwnershipProducer.js +323 -0
- package/dist/core/action/types.js +33 -0
- package/dist/core/action/walletReviewContractAssembler.js +286 -0
- package/dist/core/activity/accountAssetTimeline.js +157 -0
- package/dist/core/activity/accountAssetTimelineUsdcReferences.js +120 -0
- package/dist/core/activity/activityStore.js +16 -0
- package/dist/core/activity/externalActivityCoverage.js +76 -0
- package/dist/core/activity/externalActivityTransactionStream.js +82 -0
- package/dist/core/activity/localDataService.js +296 -0
- package/dist/core/activity/localDataTypes.js +11 -0
- package/dist/core/activity/localDataValidation.js +439 -0
- package/dist/core/activity/schemaVersion.js +2 -0
- package/dist/core/activity/sqliteActivityStore.js +1068 -0
- package/dist/core/activity/sqliteActivityStoreRows.js +389 -0
- package/dist/core/activity/sqliteActivityStoreSchema.js +367 -0
- package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
- package/dist/core/activity/suiFunctionTarget.js +43 -0
- package/dist/core/activity/transactionActivityAccountEffects.js +186 -0
- package/dist/core/activity/transactionActivityAnalysis.js +295 -0
- package/dist/core/activity/transactionActivityClassifier.js +306 -0
- package/dist/core/activity/transactionActivityDetails.js +229 -0
- package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
- package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
- package/dist/core/activity/transactionActivityService.js +379 -0
- package/dist/core/activity/transactionActivityTypes.js +18 -0
- package/dist/core/eventlog/sink.js +35 -0
- package/dist/core/evidence/settlementFamilies.js +85 -0
- package/dist/core/evidence/userAnswerUse.js +1 -0
- package/dist/core/numeric/rawU64.js +63 -0
- package/dist/core/preferences/preferencesStore.js +26 -0
- package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
- package/dist/core/proposal/externalProposalReview.js +347 -0
- package/dist/core/proposal/schemas.js +208 -0
- package/dist/core/proposal/types.js +35 -0
- package/dist/core/read/amounts.js +14 -0
- package/dist/core/read/coinMetadata.js +60 -0
- package/dist/core/read/deepbookOfficialIndexerSource.js +373 -0
- package/dist/core/read/deepbookRawQuoteClient.js +86 -0
- package/dist/core/read/deepbookReadHelpers.js +321 -0
- package/dist/core/read/deepbookRegistry.js +157 -0
- package/dist/core/read/deepbookSourceOwners.js +159 -0
- package/dist/core/read/deepbookUsdcChartService.js +342 -0
- package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
- package/dist/core/read/readCardInputs.js +19 -0
- package/dist/core/read/readCardResult.js +40 -0
- package/dist/core/read/readResponseGuidance.js +402 -0
- package/dist/core/read/readService.js +1639 -0
- package/dist/core/read/readServiceTypes.js +88 -0
- package/dist/core/read/settlementParityFormatting.js +82 -0
- package/dist/core/read/walletReadHelpers.js +99 -0
- package/dist/core/review/reviewChecks.js +45 -0
- package/dist/core/review/reviewComputation.js +38 -0
- package/dist/core/review/reviewComputationResult.js +87 -0
- package/dist/core/session/cardSession.js +31 -0
- package/dist/core/session/cardSessionStore.js +204 -0
- package/dist/core/session/keyedRecordStore.js +18 -0
- package/dist/core/session/liveReviewSessionContract.js +3 -0
- package/dist/core/session/localSession.js +31 -0
- package/dist/core/session/privateReviewArtifacts.js +75 -0
- package/dist/core/session/reviewValidity.js +49 -0
- package/dist/core/session/sessionErrors.js +9 -0
- package/dist/core/session/sessionRecordStore.js +40 -0
- package/dist/core/session/sessionStore.js +655 -0
- package/dist/core/session/settingsSession.js +1 -0
- package/dist/core/session/settingsSessions.js +45 -0
- package/dist/core/session/sqliteCardStore.js +88 -0
- package/dist/core/session/sqliteSessionStore.js +213 -0
- package/dist/core/session/sqliteTransactionMaterialStore.js +64 -0
- package/dist/core/session/sqliteWalletWorkflowStore.js +821 -0
- package/dist/core/session/status.js +60 -0
- package/dist/core/session/transactionMaterialStore.js +221 -0
- package/dist/core/session/transactionRequest.js +72 -0
- package/dist/core/session/wait.js +79 -0
- package/dist/core/session/walletConnection.js +92 -0
- package/dist/core/session/walletRuntime.js +38 -0
- package/dist/core/session/walletWorkflow.js +608 -0
- package/dist/core/session/workflowState.js +80 -0
- package/dist/core/session/workflowView.js +150 -0
- package/dist/core/suiAddress.js +18 -0
- package/dist/core/suiEndpoint.js +72 -0
- package/dist/mcp/activeAccountResponse.js +24 -0
- package/dist/mcp/prompts.js +146 -0
- package/dist/mcp/registerTool.js +19 -0
- package/dist/mcp/resources.js +72 -0
- package/dist/mcp/responseGuidance.js +326 -0
- package/dist/mcp/result.js +17 -0
- package/dist/mcp/schemas.js +11 -0
- package/dist/mcp/server.js +32 -0
- package/dist/mcp/serverInfo.js +131 -0
- package/dist/mcp/toolErrors.js +118 -0
- package/dist/mcp/toolNames.js +58 -0
- package/dist/mcp/tools/account/index.js +44 -0
- package/dist/mcp/tools/action/prepareSuiActionReview.js +126 -0
- package/dist/mcp/tools/read/commonSchemas.js +43 -0
- package/dist/mcp/tools/read/deepbookReadTools.js +686 -0
- package/dist/mcp/tools/read/index.js +14 -0
- package/dist/mcp/tools/read/readToolHelpers.js +97 -0
- package/dist/mcp/tools/read/reviewActivityTools.js +179 -0
- package/dist/mcp/tools/read/serverStatusTools.js +100 -0
- package/dist/mcp/tools/read/transactionActivityOutput.js +371 -0
- package/dist/mcp/tools/read/transactionActivityTools.js +858 -0
- package/dist/mcp/tools/read/walletReadTools.js +734 -0
- package/dist/mcp/tools/session/executionResultTools.js +44 -0
- package/dist/mcp/tools/session/index.js +8 -0
- package/dist/mcp/tools/session/shared.js +36 -0
- package/dist/mcp/tools/session/statusTools.js +69 -0
- package/dist/mcp/tools/session/walletConnectionTools.js +69 -0
- package/dist/mcp/tools/settings/index.js +66 -0
- package/dist/mcp-app/account.html +3133 -0
- package/dist/mcp-app/account.notices.txt +973 -0
- package/dist/mcp-app/chart.html +3354 -0
- package/dist/mcp-app/chart.notices.txt +1194 -0
- package/dist/mcp-app/connect.html +3179 -0
- package/dist/mcp-app/connect.notices.txt +1012 -0
- package/dist/mcp-app/receipt.html +8430 -0
- package/dist/mcp-app/receipt.notices.txt +3549 -0
- package/dist/mcp-app/review.html +8430 -0
- package/dist/mcp-app/review.notices.txt +3549 -0
- package/dist/mcp-ui/contracts.js +31 -0
- package/dist/mcp-ui/readCards.js +28 -0
- package/dist/mcp-ui/tools.js +134 -0
- package/dist/review-app/brand-dark.svg +1 -0
- package/dist/review-app/brand-light.svg +1 -0
- package/dist/review-app/favicon.svg +1 -0
- package/dist/review-app/settings.css +1 -0
- package/dist/review-app/settings.js +1 -0
- package/dist/review-app/ui.css +809 -0
- package/dist/review-server/assets.js +48 -0
- package/dist/review-server/html.js +45 -0
- package/dist/review-server/http.js +47 -0
- package/dist/review-server/middleware/hostOrigin.js +48 -0
- package/dist/review-server/middleware/reviewToken.js +7 -0
- package/dist/review-server/reviewServerPolicy.js +1 -0
- package/dist/review-server/server.js +72 -0
- package/dist/review-server/settingsApi.js +158 -0
- package/dist/runtime/application.js +203 -0
- package/dist/runtime/config.js +97 -0
- package/dist/runtime/localSettingsService.js +198 -0
- package/dist/runtime/logger.js +66 -0
- package/dist/runtime/reviewDependencies.js +58 -0
- package/dist/runtime/reviewServerAcquire.js +93 -0
- package/dist/runtime/runtimeTermination.js +60 -0
- package/dist/runtime/shared/authenticatedFetch.js +127 -0
- package/dist/runtime/shared/control.js +93 -0
- package/dist/runtime/shared/dataAccess.js +66 -0
- package/dist/runtime/shared/mcpHttp.js +84 -0
- package/dist/runtime/shared/ownerLease.js +50 -0
- package/dist/runtime/shared/server.js +98 -0
- package/dist/runtime/shared/stdio.js +113 -0
- package/dist/runtime/smokeMainnetRead.js +391 -0
- package/dist/runtime/smokeMainnetReadAssertions.js +306 -0
- package/dist/runtime/smokeReview.js +40 -0
- package/dist/runtime/start.js +90 -0
- package/dist/runtime/suiEndpoint.js +97 -0
- package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
- package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
- package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
- package/dist/runtime/walletConnectConfig.js +3 -0
- package/dist/runtime/walletConnectStorage.js +99 -0
- package/dist/runtime/walletConnectTransport.js +128 -0
- package/dist/runtime/walletSdkChild.js +3 -0
- package/dist/runtime/walletSdkChildRuntime.js +160 -0
- package/dist/runtime/walletSdkIpc.js +26 -0
- package/dist/runtime/walletSdkProcess.js +358 -0
- package/docs/AGENT_BEHAVIOR.md +524 -0
- package/docs/AGENT_DEVELOPMENT_POLICY.md +815 -0
- package/docs/FRONTEND_POLICY.md +534 -0
- package/docs/LOCAL_DB_ARCHITECTURE.md +381 -0
- package/docs/MCP_SETUP.md +742 -0
- package/docs/MCP_TOOLS.md +1318 -0
- package/docs/SDK_API.md +206 -0
- package/docs/SIGNABLE_ADAPTER_CONTRACT.md +396 -0
- package/docs/TRANSACTION_ACTIVITY_LOG.md +194 -0
- package/docs/UTILITY_INDEX.md +275 -0
- package/docs/WALLET_CONNECTION.md +238 -0
- package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +166 -0
- package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +161 -0
- package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +259 -0
- package/package.json +79 -0
- package/protocols/deepbook-margin.md +15 -0
- package/protocols/deepbook-v3.md +24 -0
|
@@ -0,0 +1,998 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { normalizeCoinType } from "../read/coinMetadata.js";
|
|
3
|
+
import { SUI_TYPE_ARG } from "@mysten/sui/utils";
|
|
4
|
+
import { makeCanonicalRawU64StringSchema } from "../numeric/rawU64.js";
|
|
5
|
+
import { suiAddressStringSchema, suiTransactionDigestSchema } from "../suiAddress.js";
|
|
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
|
+
export const reviewSimulationProviderSchema = z.enum(["client.core.simulateTransaction", REVIEW_TIME_SIMULATION_PROVIDER]);
|
|
9
|
+
export const TRANSACTION_REVIEW_SCHEMA_VERSION = 1;
|
|
10
|
+
export const PTB_VISUALIZATION_CONTRACT_VERSION = "ptb-visualization-contract-alpha-2026-05-25";
|
|
11
|
+
export const WALLET_REVIEW_REQUIRED_HUMAN_FIELDS = [
|
|
12
|
+
"proposedAction",
|
|
13
|
+
"assetFlow",
|
|
14
|
+
"recipients",
|
|
15
|
+
"targets",
|
|
16
|
+
"evidenceUsed",
|
|
17
|
+
"missingEvidence",
|
|
18
|
+
"requiredUserChoices",
|
|
19
|
+
"unsupportedClaims",
|
|
20
|
+
"freshness",
|
|
21
|
+
"blockingChecks"
|
|
22
|
+
];
|
|
23
|
+
export const WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS = [
|
|
24
|
+
"effects",
|
|
25
|
+
"balanceChanges",
|
|
26
|
+
"objectTypes",
|
|
27
|
+
"transaction"
|
|
28
|
+
];
|
|
29
|
+
export const WALLET_REVIEW_REQUIRED_PROHIBITED_OUTPUTS = [
|
|
30
|
+
"transaction_bytes",
|
|
31
|
+
"serialized_transaction",
|
|
32
|
+
"wallet_signature_request",
|
|
33
|
+
"private_key_material",
|
|
34
|
+
"wallet_authorization",
|
|
35
|
+
"signing_data",
|
|
36
|
+
"signing_readiness",
|
|
37
|
+
"payment_execution_readiness"
|
|
38
|
+
];
|
|
39
|
+
export const PTB_VISUALIZATION_REQUIRED_UNSUPPORTED_USES = [
|
|
40
|
+
"transaction_building_input",
|
|
41
|
+
"wallet_authorization",
|
|
42
|
+
"signing_data",
|
|
43
|
+
"signing_readiness",
|
|
44
|
+
"payment_execution_readiness",
|
|
45
|
+
"route_recommendation"
|
|
46
|
+
];
|
|
47
|
+
// The generic review contract must not load a protocol registry to name Sui gas.
|
|
48
|
+
export const SUI_GAS_COIN_TYPE = SUI_TYPE_ARG;
|
|
49
|
+
export const SUI_GAS_RAW_UNIT = "MIST";
|
|
50
|
+
const EXPIRY_TIMESTAMP_SOURCE_FIELDS = ["checkedAt", "expiresAt"];
|
|
51
|
+
const EXPIRY_UNAVAILABLE_SOURCE_FIELDS = ["checkedAt", "expiryStatus"];
|
|
52
|
+
const RAW_AMOUNT_SOURCE_FIELDS = ["rawAmount", "asset", "amountRole"];
|
|
53
|
+
const MIN_OUT_AMOUNT_SOURCE_FIELDS = ["minOutRaw", "asset", "amountRole"];
|
|
54
|
+
const GAS_BUDGET_AMOUNT_SOURCE_FIELDS = ["gasBudgetRaw", "asset", "amountRole"];
|
|
55
|
+
const GAS_USED_AMOUNT_SOURCE_FIELDS = ["gasUsedRaw", "asset", "amountRole"];
|
|
56
|
+
const GAS_UNRESOLVED_SOURCE_FIELDS = ["checkedAt", "gasResolutionStatus", "unresolvedReason"];
|
|
57
|
+
const BALANCE_DELTA_AMOUNT_SOURCE_FIELDS = ["balanceChanges", "asset", "amountRole"];
|
|
58
|
+
const UNIT_SOURCE_FIELDS = ["coinType", "decimals"];
|
|
59
|
+
const QUOTE_MIN_OUT_SOURCE_FIELDS = ["quoteEvidenceId", "minOutRaw"];
|
|
60
|
+
const USER_SLIPPAGE_POLICY_SOURCE_FIELDS = ["maxSlippageBps", "userSelection"];
|
|
61
|
+
const ADAPTER_SLIPPAGE_POLICY_SOURCE_FIELDS = ["maxSlippageBps", "minOutRaw"];
|
|
62
|
+
const OBJECT_OWNERSHIP_SOURCE_FIELDS = ["ownerAccount", "objects"];
|
|
63
|
+
const ADAPTER_SOURCE_OF_TRUTH_KINDS = [
|
|
64
|
+
"pinned_sdk_registry",
|
|
65
|
+
"verified_mainnet_onchain_metadata",
|
|
66
|
+
"wallet_account_read",
|
|
67
|
+
"quote_evidence",
|
|
68
|
+
"review_time_simulation",
|
|
69
|
+
"validated_request_fact",
|
|
70
|
+
"user_explicit_choice"
|
|
71
|
+
];
|
|
72
|
+
const RAW_QUANTITY_ROLES = [
|
|
73
|
+
"input",
|
|
74
|
+
"expected_output",
|
|
75
|
+
"minimum_output",
|
|
76
|
+
"gas_budget",
|
|
77
|
+
"gas_used",
|
|
78
|
+
"fee",
|
|
79
|
+
"balance_delta"
|
|
80
|
+
];
|
|
81
|
+
const SAFETY_CRITICAL_FACT_KINDS = [
|
|
82
|
+
"raw_quantity_amount",
|
|
83
|
+
"unit_metadata",
|
|
84
|
+
"gas_unresolved_status",
|
|
85
|
+
"expiry_status",
|
|
86
|
+
"quote_min_out",
|
|
87
|
+
"slippage_policy",
|
|
88
|
+
"object_ownership",
|
|
89
|
+
"simulation_result"
|
|
90
|
+
];
|
|
91
|
+
const RAW_QUANTITY_AMOUNT_EVIDENCE_BY_ROLE = {
|
|
92
|
+
input: {
|
|
93
|
+
kinds: ["validated_request_fact", "wallet_account_read", "user_explicit_choice"],
|
|
94
|
+
fields: RAW_AMOUNT_SOURCE_FIELDS
|
|
95
|
+
},
|
|
96
|
+
expected_output: {
|
|
97
|
+
kinds: ["quote_evidence", "review_time_simulation"],
|
|
98
|
+
fields: RAW_AMOUNT_SOURCE_FIELDS
|
|
99
|
+
},
|
|
100
|
+
minimum_output: {
|
|
101
|
+
kinds: ["quote_evidence"],
|
|
102
|
+
fields: MIN_OUT_AMOUNT_SOURCE_FIELDS
|
|
103
|
+
},
|
|
104
|
+
gas_budget: {
|
|
105
|
+
kinds: ["review_time_simulation"],
|
|
106
|
+
fields: GAS_BUDGET_AMOUNT_SOURCE_FIELDS
|
|
107
|
+
},
|
|
108
|
+
gas_used: {
|
|
109
|
+
kinds: ["review_time_simulation"],
|
|
110
|
+
fields: GAS_USED_AMOUNT_SOURCE_FIELDS
|
|
111
|
+
},
|
|
112
|
+
fee: {
|
|
113
|
+
kinds: ["review_time_simulation"],
|
|
114
|
+
fields: RAW_AMOUNT_SOURCE_FIELDS
|
|
115
|
+
},
|
|
116
|
+
balance_delta: {
|
|
117
|
+
kinds: ["review_time_simulation"],
|
|
118
|
+
fields: BALANCE_DELTA_AMOUNT_SOURCE_FIELDS
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const UNIT_EVIDENCE_BY_SOURCE = {
|
|
122
|
+
pinned_sdk_metadata: {
|
|
123
|
+
kinds: ["pinned_sdk_registry"],
|
|
124
|
+
fields: UNIT_SOURCE_FIELDS
|
|
125
|
+
},
|
|
126
|
+
verified_mainnet_onchain_metadata: {
|
|
127
|
+
kinds: ["verified_mainnet_onchain_metadata"],
|
|
128
|
+
fields: UNIT_SOURCE_FIELDS
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const SLIPPAGE_POLICY_EVIDENCE_BY_SOURCE = {
|
|
132
|
+
user_explicit: {
|
|
133
|
+
kinds: ["user_explicit_choice"],
|
|
134
|
+
fields: USER_SLIPPAGE_POLICY_SOURCE_FIELDS
|
|
135
|
+
},
|
|
136
|
+
adapter_policy_from_quote_evidence: {
|
|
137
|
+
kinds: ["quote_evidence"],
|
|
138
|
+
fields: ADAPTER_SLIPPAGE_POLICY_SOURCE_FIELDS
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const GAS_UNRESOLVED_EVIDENCE_REQUIREMENT = {
|
|
142
|
+
kinds: ["review_time_simulation"],
|
|
143
|
+
fields: GAS_UNRESOLVED_SOURCE_FIELDS
|
|
144
|
+
};
|
|
145
|
+
const QUOTE_MIN_OUT_EVIDENCE_REQUIREMENT = {
|
|
146
|
+
kinds: ["quote_evidence"],
|
|
147
|
+
fields: QUOTE_MIN_OUT_SOURCE_FIELDS
|
|
148
|
+
};
|
|
149
|
+
const EXPIRY_EVIDENCE_REQUIREMENT = {
|
|
150
|
+
kinds: ["validated_request_fact"],
|
|
151
|
+
fields: EXPIRY_TIMESTAMP_SOURCE_FIELDS
|
|
152
|
+
};
|
|
153
|
+
const EXPIRY_UNAVAILABLE_EVIDENCE_REQUIREMENT = {
|
|
154
|
+
kinds: ["validated_request_fact"],
|
|
155
|
+
fields: EXPIRY_UNAVAILABLE_SOURCE_FIELDS
|
|
156
|
+
};
|
|
157
|
+
const OBJECT_OWNERSHIP_EVIDENCE_REQUIREMENT = {
|
|
158
|
+
kinds: ["wallet_account_read"],
|
|
159
|
+
fields: OBJECT_OWNERSHIP_SOURCE_FIELDS
|
|
160
|
+
};
|
|
161
|
+
const SIMULATION_RESULT_EVIDENCE_REQUIREMENT = {
|
|
162
|
+
kinds: ["review_time_simulation"],
|
|
163
|
+
fields: WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS
|
|
164
|
+
};
|
|
165
|
+
export const SAFETY_CRITICAL_FACT_MATRIX = {
|
|
166
|
+
raw_quantity_amount: {
|
|
167
|
+
byRole: RAW_QUANTITY_AMOUNT_EVIDENCE_BY_ROLE
|
|
168
|
+
},
|
|
169
|
+
unit_metadata: {
|
|
170
|
+
bySource: UNIT_EVIDENCE_BY_SOURCE
|
|
171
|
+
},
|
|
172
|
+
gas_unresolved_status: GAS_UNRESOLVED_EVIDENCE_REQUIREMENT,
|
|
173
|
+
expiry_status: {
|
|
174
|
+
current: EXPIRY_EVIDENCE_REQUIREMENT,
|
|
175
|
+
expired: EXPIRY_EVIDENCE_REQUIREMENT,
|
|
176
|
+
not_provided: EXPIRY_UNAVAILABLE_EVIDENCE_REQUIREMENT,
|
|
177
|
+
not_applicable: EXPIRY_UNAVAILABLE_EVIDENCE_REQUIREMENT
|
|
178
|
+
},
|
|
179
|
+
quote_min_out: QUOTE_MIN_OUT_EVIDENCE_REQUIREMENT,
|
|
180
|
+
slippage_policy: {
|
|
181
|
+
bySource: SLIPPAGE_POLICY_EVIDENCE_BY_SOURCE
|
|
182
|
+
},
|
|
183
|
+
object_ownership: OBJECT_OWNERSHIP_EVIDENCE_REQUIREMENT,
|
|
184
|
+
simulation_result: SIMULATION_RESULT_EVIDENCE_REQUIREMENT
|
|
185
|
+
};
|
|
186
|
+
export const CONSUMER_INVARIANT_MATRIX = {
|
|
187
|
+
gasBudgetClaimId: {
|
|
188
|
+
factKind: "raw_quantity_amount",
|
|
189
|
+
role: "gas_budget",
|
|
190
|
+
assetCoinType: SUI_GAS_COIN_TYPE,
|
|
191
|
+
rawUnit: SUI_GAS_RAW_UNIT
|
|
192
|
+
},
|
|
193
|
+
gasUsedClaimId: {
|
|
194
|
+
factKind: "raw_quantity_amount",
|
|
195
|
+
role: "gas_used",
|
|
196
|
+
assetCoinType: SUI_GAS_COIN_TYPE,
|
|
197
|
+
rawUnit: SUI_GAS_RAW_UNIT
|
|
198
|
+
},
|
|
199
|
+
gasObjectOwnershipClaimId: {
|
|
200
|
+
factKind: "object_ownership",
|
|
201
|
+
ownership: "owned_by_account"
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const isoUtcStringSchema = z.string().refine((value) => {
|
|
205
|
+
const parsed = new Date(value);
|
|
206
|
+
return !Number.isNaN(parsed.getTime()) && parsed.toISOString() === value;
|
|
207
|
+
}, "Expected ISO 8601 UTC timestamp");
|
|
208
|
+
const unsignedIntegerStringSchema = makeCanonicalRawU64StringSchema("unsigned integer");
|
|
209
|
+
const suiObjectIdSchema = z.string().regex(/^0x[0-9a-fA-F]{1,64}$/, {
|
|
210
|
+
message: "Expected a Sui object id"
|
|
211
|
+
});
|
|
212
|
+
const coinTypeSchema = z.string().min(1).max(512).refine((value) => {
|
|
213
|
+
try {
|
|
214
|
+
return normalizeCoinType(value) === value;
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}, "Expected a normalized Sui struct tag coin type");
|
|
220
|
+
const evidenceIdSchema = z.string().min(1).max(120);
|
|
221
|
+
const resolvedAssetReferenceSchema = z.object({
|
|
222
|
+
symbol: z.string().min(1).max(64).optional(),
|
|
223
|
+
coinType: coinTypeSchema
|
|
224
|
+
}).strict();
|
|
225
|
+
const EXECUTABLE_MATERIAL_TEXT_PATTERN = /transaction\s*bytes?|tx\s*bytes?|serialized\s*transaction|serializedTransaction|signed\s*transaction|signing\s*request|signingRequest|wallet\s*authorization|walletAuthorization|private\s*key|privateKey|secret\s*key|secretKey|seed\s*phrase|mnemonic|suiprivkey|signature|bcs\s*transaction/i;
|
|
226
|
+
const LONG_HEX_OR_BASE64LIKE_PAYLOAD_PATTERN = /(?:0x[0-9a-fA-F]{160,}|[A-Za-z0-9+/_=-]{160,})/;
|
|
227
|
+
function displayTextWithoutExecutableMaterial(maxLength, fieldName) {
|
|
228
|
+
return z.string().min(1).max(maxLength).superRefine((value, ctx) => {
|
|
229
|
+
if (EXECUTABLE_MATERIAL_TEXT_PATTERN.test(value) ||
|
|
230
|
+
LONG_HEX_OR_BASE64LIKE_PAYLOAD_PATTERN.test(value)) {
|
|
231
|
+
ctx.addIssue({
|
|
232
|
+
code: "custom",
|
|
233
|
+
message: `${fieldName} must not contain executable transaction, signing, private-key, mnemonic, or long encoded material`
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function missingRequiredValues(values, required) {
|
|
239
|
+
const present = new Set(values);
|
|
240
|
+
return required.filter((value) => !present.has(value));
|
|
241
|
+
}
|
|
242
|
+
function addMissingRequiredValuesIssue(ctx, values, required, path, label) {
|
|
243
|
+
const missing = missingRequiredValues(values, required);
|
|
244
|
+
if (missing.length === 0) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
ctx.addIssue({
|
|
248
|
+
code: "custom",
|
|
249
|
+
path,
|
|
250
|
+
message: `${label} must include ${missing.join(", ")}`
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function requireAllValues(ctx, values, required, path, label) {
|
|
254
|
+
addMissingRequiredValuesIssue(ctx, values, required, path, label);
|
|
255
|
+
}
|
|
256
|
+
function addMismatchIssue(ctx, path, label) {
|
|
257
|
+
ctx.addIssue({
|
|
258
|
+
code: "custom",
|
|
259
|
+
path,
|
|
260
|
+
message: `${label} must match referenced evidence claim`
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function requireEqual(ctx, actual, expected, path, label) {
|
|
264
|
+
if (actual !== expected) {
|
|
265
|
+
addMismatchIssue(ctx, path, label);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function requireSameStringSet(ctx, actual, expected, path, label) {
|
|
269
|
+
const actualSet = new Set(actual);
|
|
270
|
+
const expectedSet = new Set(expected);
|
|
271
|
+
if (actualSet.size !== expectedSet.size) {
|
|
272
|
+
addMismatchIssue(ctx, path, label);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
for (const value of actualSet) {
|
|
276
|
+
if (!expectedSet.has(value)) {
|
|
277
|
+
addMismatchIssue(ctx, path, label);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function requireSameAsset(ctx, actual, expected, path, label) {
|
|
283
|
+
if (actual.symbol !== expected.symbol || actual.coinType !== expected.coinType) {
|
|
284
|
+
addMismatchIssue(ctx, path, label);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function requireGasAsset(ctx, asset, expectedCoinType, path, label) {
|
|
288
|
+
try {
|
|
289
|
+
if (normalizeCoinType(asset.coinType) !== expectedCoinType) {
|
|
290
|
+
addMismatchIssue(ctx, path, label);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
addMismatchIssue(ctx, path, label);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
export const adapterInputProvenanceSchema = z.object({
|
|
298
|
+
kind: z.enum(["natural_language_intent", "external_proposal", "mcp_action_request", "local_review_request"]),
|
|
299
|
+
sourceId: z.string().min(1).max(160),
|
|
300
|
+
capturedAt: isoUtcStringSchema,
|
|
301
|
+
authority: z.literal("untrusted_until_review_regenerates_and_verifies"),
|
|
302
|
+
userSelectionSource: z.enum(["user_explicit", "prior_user_explicit_context"]).optional()
|
|
303
|
+
}).strict();
|
|
304
|
+
export const adapterSourceOfTruthSchema = z.object({
|
|
305
|
+
id: evidenceIdSchema,
|
|
306
|
+
kind: z.enum(ADAPTER_SOURCE_OF_TRUTH_KINDS),
|
|
307
|
+
network: z.literal("sui:mainnet"),
|
|
308
|
+
source: z.string().min(1).max(240),
|
|
309
|
+
verifiedAt: isoUtcStringSchema,
|
|
310
|
+
fields: z.array(z.string().min(1).max(120)).min(1)
|
|
311
|
+
}).strict();
|
|
312
|
+
const rawQuantityAmountClaimSchema = z.object({
|
|
313
|
+
id: evidenceIdSchema,
|
|
314
|
+
factKind: z.literal("raw_quantity_amount"),
|
|
315
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
316
|
+
role: z.enum(RAW_QUANTITY_ROLES),
|
|
317
|
+
asset: resolvedAssetReferenceSchema,
|
|
318
|
+
rawAmount: unsignedIntegerStringSchema
|
|
319
|
+
}).strict();
|
|
320
|
+
const unitMetadataClaimSchema = z.object({
|
|
321
|
+
id: evidenceIdSchema,
|
|
322
|
+
factKind: z.literal("unit_metadata"),
|
|
323
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
324
|
+
source: z.enum(["pinned_sdk_metadata", "verified_mainnet_onchain_metadata"]),
|
|
325
|
+
coinType: coinTypeSchema,
|
|
326
|
+
decimals: z.number().int().min(0).max(255)
|
|
327
|
+
}).strict();
|
|
328
|
+
const gasUnresolvedStatusClaimSchema = z.object({
|
|
329
|
+
id: evidenceIdSchema,
|
|
330
|
+
factKind: z.literal("gas_unresolved_status"),
|
|
331
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
332
|
+
checkedAt: isoUtcStringSchema,
|
|
333
|
+
status: z.literal("unresolved"),
|
|
334
|
+
reason: z.string().min(1).max(240)
|
|
335
|
+
}).strict();
|
|
336
|
+
const expiryStatusClaimSchema = z.object({
|
|
337
|
+
id: evidenceIdSchema,
|
|
338
|
+
factKind: z.literal("expiry_status"),
|
|
339
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
340
|
+
checkedAt: isoUtcStringSchema,
|
|
341
|
+
status: z.enum(["current", "expired", "not_provided", "not_applicable"]),
|
|
342
|
+
expiresAt: isoUtcStringSchema.optional(),
|
|
343
|
+
reason: z.string().min(1).max(240).optional()
|
|
344
|
+
}).strict();
|
|
345
|
+
const quoteMinOutClaimSchema = z.object({
|
|
346
|
+
id: evidenceIdSchema,
|
|
347
|
+
factKind: z.literal("quote_min_out"),
|
|
348
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
349
|
+
quoteEvidenceId: z.string().min(1).max(120),
|
|
350
|
+
minOutRaw: unsignedIntegerStringSchema
|
|
351
|
+
}).strict();
|
|
352
|
+
const slippagePolicyClaimSchema = z.object({
|
|
353
|
+
id: evidenceIdSchema,
|
|
354
|
+
factKind: z.literal("slippage_policy"),
|
|
355
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
356
|
+
policySource: z.enum(["user_explicit", "adapter_policy_from_quote_evidence"]),
|
|
357
|
+
maxSlippageBps: z.number().int().min(0).max(10_000),
|
|
358
|
+
minOutRaw: unsignedIntegerStringSchema.optional()
|
|
359
|
+
}).strict();
|
|
360
|
+
const objectOwnershipClaimSchema = z.object({
|
|
361
|
+
id: evidenceIdSchema,
|
|
362
|
+
factKind: z.literal("object_ownership"),
|
|
363
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
364
|
+
objectId: suiObjectIdSchema,
|
|
365
|
+
ownerAccount: suiAddressStringSchema,
|
|
366
|
+
ownership: z.enum(["owned_by_account", "shared_object", "immutable_or_package", "not_owned_by_account"])
|
|
367
|
+
}).strict();
|
|
368
|
+
const simulationResultClaimSchema = z.object({
|
|
369
|
+
id: evidenceIdSchema,
|
|
370
|
+
factKind: z.literal("simulation_result"),
|
|
371
|
+
sourceEvidenceId: evidenceIdSchema,
|
|
372
|
+
provider: reviewSimulationProviderSchema,
|
|
373
|
+
checksEnabled: z.literal(true),
|
|
374
|
+
simulatedAt: isoUtcStringSchema,
|
|
375
|
+
status: z.enum(["success", "failed", "unavailable"]),
|
|
376
|
+
requiredFields: z.array(z.enum(WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS)).min(WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS.length),
|
|
377
|
+
missingFields: z.array(z.enum(WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS)).default([]),
|
|
378
|
+
failureReason: z.string().min(1).max(240).optional()
|
|
379
|
+
}).strict();
|
|
380
|
+
export const adapterEvidenceClaimSchema = z.discriminatedUnion("factKind", [
|
|
381
|
+
rawQuantityAmountClaimSchema,
|
|
382
|
+
unitMetadataClaimSchema,
|
|
383
|
+
gasUnresolvedStatusClaimSchema,
|
|
384
|
+
expiryStatusClaimSchema,
|
|
385
|
+
quoteMinOutClaimSchema,
|
|
386
|
+
slippagePolicyClaimSchema,
|
|
387
|
+
objectOwnershipClaimSchema,
|
|
388
|
+
simulationResultClaimSchema
|
|
389
|
+
]);
|
|
390
|
+
export const adapterRawQuantitySchema = z.object({
|
|
391
|
+
id: evidenceIdSchema,
|
|
392
|
+
role: z.enum(RAW_QUANTITY_ROLES),
|
|
393
|
+
asset: resolvedAssetReferenceSchema,
|
|
394
|
+
rawAmount: unsignedIntegerStringSchema,
|
|
395
|
+
unit: z.object({
|
|
396
|
+
decimals: z.number().int().min(0).max(255),
|
|
397
|
+
source: z.enum(["pinned_sdk_metadata", "verified_mainnet_onchain_metadata"]),
|
|
398
|
+
sourceField: z.string().min(1).max(160),
|
|
399
|
+
unitClaimId: evidenceIdSchema
|
|
400
|
+
}).strict(),
|
|
401
|
+
amountClaimId: evidenceIdSchema,
|
|
402
|
+
displayOnly: z.object({
|
|
403
|
+
amountDisplay: z.string().min(1).max(120),
|
|
404
|
+
reason: z.literal("presentation_only_not_signing_input")
|
|
405
|
+
}).strict().optional()
|
|
406
|
+
}).strict();
|
|
407
|
+
export const adapterGasObjectOwnershipLinkSchema = z.object({
|
|
408
|
+
objectId: suiObjectIdSchema,
|
|
409
|
+
ownerAccount: suiAddressStringSchema,
|
|
410
|
+
ownershipClaimId: evidenceIdSchema
|
|
411
|
+
}).strict();
|
|
412
|
+
export const adapterGasEvidenceSchema = z.object({
|
|
413
|
+
source: z.literal("review_time_simulation"),
|
|
414
|
+
checkedAt: isoUtcStringSchema,
|
|
415
|
+
gasBudgetRaw: unsignedIntegerStringSchema.optional(),
|
|
416
|
+
gasBudgetClaimId: evidenceIdSchema.optional(),
|
|
417
|
+
gasUsedRaw: unsignedIntegerStringSchema.optional(),
|
|
418
|
+
gasUsedClaimId: evidenceIdSchema.optional(),
|
|
419
|
+
gasObjects: z.array(adapterGasObjectOwnershipLinkSchema).optional(),
|
|
420
|
+
unresolvedReason: z.string().min(1).max(240).optional(),
|
|
421
|
+
unresolvedClaimId: evidenceIdSchema.optional()
|
|
422
|
+
}).strict().superRefine((value, ctx) => {
|
|
423
|
+
const hasRawGas = value.gasBudgetRaw !== undefined || value.gasUsedRaw !== undefined;
|
|
424
|
+
if (!hasRawGas && value.unresolvedReason === undefined) {
|
|
425
|
+
ctx.addIssue({
|
|
426
|
+
code: "custom",
|
|
427
|
+
message: "Expected gas evidence or unresolvedReason"
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
if (hasRawGas && value.unresolvedReason !== undefined) {
|
|
431
|
+
ctx.addIssue({
|
|
432
|
+
code: "custom",
|
|
433
|
+
path: ["unresolvedReason"],
|
|
434
|
+
message: "Resolved gas quantities must not include unresolvedReason"
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
if (value.gasBudgetRaw !== undefined && value.gasBudgetClaimId === undefined) {
|
|
438
|
+
ctx.addIssue({
|
|
439
|
+
code: "custom",
|
|
440
|
+
path: ["gasBudgetClaimId"],
|
|
441
|
+
message: "gasBudgetRaw requires gasBudgetClaimId"
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (value.gasBudgetRaw === undefined && value.gasBudgetClaimId !== undefined) {
|
|
445
|
+
ctx.addIssue({
|
|
446
|
+
code: "custom",
|
|
447
|
+
path: ["gasBudgetClaimId"],
|
|
448
|
+
message: "gasBudgetClaimId requires gasBudgetRaw"
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
if (value.gasUsedRaw !== undefined && value.gasUsedClaimId === undefined) {
|
|
452
|
+
ctx.addIssue({
|
|
453
|
+
code: "custom",
|
|
454
|
+
path: ["gasUsedClaimId"],
|
|
455
|
+
message: "gasUsedRaw requires gasUsedClaimId"
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
if (value.gasUsedRaw === undefined && value.gasUsedClaimId !== undefined) {
|
|
459
|
+
ctx.addIssue({
|
|
460
|
+
code: "custom",
|
|
461
|
+
path: ["gasUsedClaimId"],
|
|
462
|
+
message: "gasUsedClaimId requires gasUsedRaw"
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
if (value.unresolvedReason !== undefined && value.unresolvedClaimId === undefined) {
|
|
466
|
+
ctx.addIssue({
|
|
467
|
+
code: "custom",
|
|
468
|
+
path: ["unresolvedClaimId"],
|
|
469
|
+
message: "unresolvedReason requires unresolvedClaimId"
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (value.unresolvedReason === undefined && value.unresolvedClaimId !== undefined) {
|
|
473
|
+
ctx.addIssue({
|
|
474
|
+
code: "custom",
|
|
475
|
+
path: ["unresolvedClaimId"],
|
|
476
|
+
message: "unresolvedClaimId requires unresolvedReason"
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
export const adapterExpiryEvidenceSchema = z.discriminatedUnion("status", [
|
|
481
|
+
z.object({
|
|
482
|
+
checkedAt: isoUtcStringSchema,
|
|
483
|
+
status: z.literal("current"),
|
|
484
|
+
expiresAt: isoUtcStringSchema,
|
|
485
|
+
evidenceClaimId: evidenceIdSchema
|
|
486
|
+
}).strict(),
|
|
487
|
+
z.object({
|
|
488
|
+
checkedAt: isoUtcStringSchema,
|
|
489
|
+
status: z.literal("expired"),
|
|
490
|
+
expiresAt: isoUtcStringSchema,
|
|
491
|
+
evidenceClaimId: evidenceIdSchema
|
|
492
|
+
}).strict(),
|
|
493
|
+
z.object({
|
|
494
|
+
checkedAt: isoUtcStringSchema,
|
|
495
|
+
status: z.literal("not_provided"),
|
|
496
|
+
evidenceClaimId: evidenceIdSchema,
|
|
497
|
+
reason: z.string().min(1).max(240),
|
|
498
|
+
expiresAt: z.never().optional()
|
|
499
|
+
}).strict(),
|
|
500
|
+
z.object({
|
|
501
|
+
checkedAt: isoUtcStringSchema,
|
|
502
|
+
status: z.literal("not_applicable"),
|
|
503
|
+
evidenceClaimId: evidenceIdSchema,
|
|
504
|
+
reason: z.string().min(1).max(240),
|
|
505
|
+
expiresAt: z.never().optional()
|
|
506
|
+
}).strict()
|
|
507
|
+
]).superRefine((value, ctx) => {
|
|
508
|
+
if (value.status === "current" && Date.parse(value.expiresAt) <= Date.parse(value.checkedAt)) {
|
|
509
|
+
ctx.addIssue({
|
|
510
|
+
code: "custom",
|
|
511
|
+
path: ["expiresAt"],
|
|
512
|
+
message: "Current expiry evidence requires expiresAt after checkedAt"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
if (value.status === "expired" && Date.parse(value.expiresAt) > Date.parse(value.checkedAt)) {
|
|
516
|
+
ctx.addIssue({
|
|
517
|
+
code: "custom",
|
|
518
|
+
path: ["expiresAt"],
|
|
519
|
+
message: "Expired expiry evidence requires expiresAt at or before checkedAt"
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
export const adapterSlippageOrMinOutEvidenceSchema = z.object({
|
|
524
|
+
status: z.enum(["required_and_verified", "not_required_for_action", "missing", "stale"]),
|
|
525
|
+
quoteEvidenceId: z.string().min(1).max(120).optional(),
|
|
526
|
+
quoteEvidenceClaimId: evidenceIdSchema.optional(),
|
|
527
|
+
maxSlippageBps: z.number().int().min(0).max(10_000).optional(),
|
|
528
|
+
minOutRaw: unsignedIntegerStringSchema.optional(),
|
|
529
|
+
policySource: z.enum(["user_explicit", "adapter_policy_from_quote_evidence"]).optional(),
|
|
530
|
+
policyEvidenceClaimId: evidenceIdSchema.optional()
|
|
531
|
+
}).strict().superRefine((value, ctx) => {
|
|
532
|
+
if (value.status === "required_and_verified" &&
|
|
533
|
+
(value.quoteEvidenceId === undefined ||
|
|
534
|
+
value.quoteEvidenceClaimId === undefined ||
|
|
535
|
+
value.maxSlippageBps === undefined ||
|
|
536
|
+
value.minOutRaw === undefined ||
|
|
537
|
+
value.policySource === undefined ||
|
|
538
|
+
value.policyEvidenceClaimId === undefined)) {
|
|
539
|
+
ctx.addIssue({
|
|
540
|
+
code: "custom",
|
|
541
|
+
message: "Verified min-out evidence requires quoteEvidenceId, quoteEvidenceClaimId, maxSlippageBps, minOutRaw, policySource, and policyEvidenceClaimId"
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (value.quoteEvidenceId !== undefined && value.quoteEvidenceClaimId === undefined) {
|
|
545
|
+
ctx.addIssue({
|
|
546
|
+
code: "custom",
|
|
547
|
+
path: ["quoteEvidenceClaimId"],
|
|
548
|
+
message: "quoteEvidenceId requires quoteEvidenceClaimId"
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
if (value.quoteEvidenceId === undefined && value.quoteEvidenceClaimId !== undefined) {
|
|
552
|
+
ctx.addIssue({
|
|
553
|
+
code: "custom",
|
|
554
|
+
path: ["quoteEvidenceClaimId"],
|
|
555
|
+
message: "quoteEvidenceClaimId requires quoteEvidenceId"
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
if (value.minOutRaw !== undefined && value.quoteEvidenceClaimId === undefined) {
|
|
559
|
+
ctx.addIssue({
|
|
560
|
+
code: "custom",
|
|
561
|
+
path: ["quoteEvidenceClaimId"],
|
|
562
|
+
message: "minOutRaw requires quoteEvidenceClaimId"
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
if (value.maxSlippageBps !== undefined && value.policySource === undefined) {
|
|
566
|
+
ctx.addIssue({
|
|
567
|
+
code: "custom",
|
|
568
|
+
path: ["policySource"],
|
|
569
|
+
message: "maxSlippageBps requires policySource"
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
if (value.policySource !== undefined && value.policyEvidenceClaimId === undefined) {
|
|
573
|
+
ctx.addIssue({
|
|
574
|
+
code: "custom",
|
|
575
|
+
path: ["policyEvidenceClaimId"],
|
|
576
|
+
message: "policySource requires policyEvidenceClaimId"
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
if (value.policySource === undefined && value.policyEvidenceClaimId !== undefined) {
|
|
580
|
+
ctx.addIssue({
|
|
581
|
+
code: "custom",
|
|
582
|
+
path: ["policyEvidenceClaimId"],
|
|
583
|
+
message: "policyEvidenceClaimId requires policySource"
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
export const adapterObjectOwnershipEvidenceSchema = z.object({
|
|
588
|
+
checkedAt: isoUtcStringSchema,
|
|
589
|
+
ownerAccount: suiAddressStringSchema,
|
|
590
|
+
objects: z.array(z.object({
|
|
591
|
+
objectId: suiObjectIdSchema,
|
|
592
|
+
role: z.enum(["input_coin", "gas_coin", "protocol_object", "shared_object", "receiving_object"]),
|
|
593
|
+
ownership: z.enum(["owned_by_account", "shared_object", "immutable_or_package", "not_owned_by_account"]),
|
|
594
|
+
evidenceClaimId: evidenceIdSchema
|
|
595
|
+
}).strict()).min(1)
|
|
596
|
+
}).strict();
|
|
597
|
+
export const adapterSimulationEvidenceSchema = z.object({
|
|
598
|
+
evidenceClaimId: evidenceIdSchema,
|
|
599
|
+
transactionDigest: suiTransactionDigestSchema,
|
|
600
|
+
provider: reviewSimulationProviderSchema,
|
|
601
|
+
checksEnabled: z.literal(true),
|
|
602
|
+
simulatedAt: isoUtcStringSchema,
|
|
603
|
+
status: z.enum(["success", "failed", "unavailable"]),
|
|
604
|
+
requiredFields: z.array(z.enum(WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS)).min(WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS.length),
|
|
605
|
+
missingFields: z.array(z.enum(WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS)).default([]),
|
|
606
|
+
failureReason: z.string().min(1).max(240).optional()
|
|
607
|
+
}).strict().refine((value) => value.status !== "success" || value.missingFields.length === 0, {
|
|
608
|
+
message: "Successful simulation evidence must not have missing required fields"
|
|
609
|
+
}).refine((value) => WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS.every((field) => value.requiredFields.includes(field)), {
|
|
610
|
+
message: "Simulation evidence must include every required simulation field"
|
|
611
|
+
}).refine((value) => value.status === "success" || value.failureReason !== undefined, {
|
|
612
|
+
message: "Failed or unavailable simulation evidence requires failureReason"
|
|
613
|
+
}).refine((value) => value.status !== "success" || value.failureReason === undefined, {
|
|
614
|
+
message: "Successful simulation evidence must not include failureReason"
|
|
615
|
+
});
|
|
616
|
+
export const adapterHumanReadableReviewSchema = z.object({
|
|
617
|
+
fields: z.array(z.enum(WALLET_REVIEW_REQUIRED_HUMAN_FIELDS)).min(WALLET_REVIEW_REQUIRED_HUMAN_FIELDS.length),
|
|
618
|
+
transactionDigest: suiTransactionDigestSchema,
|
|
619
|
+
source: z.literal("review_model_or_adapter_equivalent"),
|
|
620
|
+
purpose: z.literal("human_review_before_wallet_authorization")
|
|
621
|
+
}).strict().refine((value) => WALLET_REVIEW_REQUIRED_HUMAN_FIELDS.every((field) => value.fields.includes(field)), {
|
|
622
|
+
message: "Human-readable review fields must include every required review field"
|
|
623
|
+
});
|
|
624
|
+
export const adapterOutputBoundarySchema = z.object({
|
|
625
|
+
runtimeStatus: z.literal("emitted_pre_handoff"),
|
|
626
|
+
mcpAndReviewUiMayExpose: z.array(z.enum([
|
|
627
|
+
"human_readable_review",
|
|
628
|
+
"ptb_visualization_artifact",
|
|
629
|
+
"diagnostics",
|
|
630
|
+
"status_checks"
|
|
631
|
+
])).min(1),
|
|
632
|
+
prohibited: z.array(z.enum(WALLET_REVIEW_REQUIRED_PROHIBITED_OUTPUTS)).min(1)
|
|
633
|
+
}).strict().superRefine((value, ctx) => {
|
|
634
|
+
requireAllValues(ctx, value.prohibited, WALLET_REVIEW_REQUIRED_PROHIBITED_OUTPUTS, ["prohibited"], "prohibited");
|
|
635
|
+
});
|
|
636
|
+
export const transactionReviewDataSchema = z.object({
|
|
637
|
+
schemaVersion: z.literal(TRANSACTION_REVIEW_SCHEMA_VERSION),
|
|
638
|
+
adapterId: z.string().min(1).max(120),
|
|
639
|
+
protocol: z.string().min(1).max(120),
|
|
640
|
+
actionKind: z.string().min(1).max(120),
|
|
641
|
+
network: z.literal("sui:mainnet"),
|
|
642
|
+
inputProvenance: adapterInputProvenanceSchema,
|
|
643
|
+
sourceReferences: z.array(adapterSourceOfTruthSchema).min(1),
|
|
644
|
+
evidenceClaims: z.array(adapterEvidenceClaimSchema).min(1),
|
|
645
|
+
rawQuantities: z.array(adapterRawQuantitySchema).min(1),
|
|
646
|
+
gas: adapterGasEvidenceSchema,
|
|
647
|
+
expiry: adapterExpiryEvidenceSchema,
|
|
648
|
+
slippageOrMinOut: adapterSlippageOrMinOutEvidenceSchema,
|
|
649
|
+
objectOwnership: adapterObjectOwnershipEvidenceSchema,
|
|
650
|
+
simulation: adapterSimulationEvidenceSchema,
|
|
651
|
+
humanReadableReview: adapterHumanReadableReviewSchema,
|
|
652
|
+
outputBoundary: adapterOutputBoundarySchema,
|
|
653
|
+
reviewedTransactionDigest: suiTransactionDigestSchema
|
|
654
|
+
}).strict().superRefine((value, ctx) => {
|
|
655
|
+
const sourceIds = new Set();
|
|
656
|
+
const sourceById = new Map();
|
|
657
|
+
const sourceIndexById = new Map();
|
|
658
|
+
value.sourceReferences.forEach((source, index) => {
|
|
659
|
+
if (sourceIds.has(source.id)) {
|
|
660
|
+
ctx.addIssue({
|
|
661
|
+
code: "custom",
|
|
662
|
+
path: ["sourceReferences", index, "id"],
|
|
663
|
+
message: `sourceReferences id must be unique: ${source.id}`
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
sourceIds.add(source.id);
|
|
667
|
+
if (!sourceById.has(source.id)) {
|
|
668
|
+
sourceById.set(source.id, source);
|
|
669
|
+
sourceIndexById.set(source.id, index);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
const claimIds = new Set();
|
|
673
|
+
const claimById = new Map();
|
|
674
|
+
const claimIndexById = new Map();
|
|
675
|
+
value.evidenceClaims.forEach((claim, index) => {
|
|
676
|
+
if (claimIds.has(claim.id)) {
|
|
677
|
+
ctx.addIssue({
|
|
678
|
+
code: "custom",
|
|
679
|
+
path: ["evidenceClaims", index, "id"],
|
|
680
|
+
message: `evidenceClaims id must be unique: ${claim.id}`
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
claimIds.add(claim.id);
|
|
684
|
+
if (!claimById.has(claim.id)) {
|
|
685
|
+
claimById.set(claim.id, claim);
|
|
686
|
+
claimIndexById.set(claim.id, index);
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
const requireSourceId = (sourceEvidenceId, path) => {
|
|
690
|
+
if (sourceEvidenceId === undefined) {
|
|
691
|
+
return undefined;
|
|
692
|
+
}
|
|
693
|
+
const source = sourceById.get(sourceEvidenceId);
|
|
694
|
+
if (source !== undefined) {
|
|
695
|
+
return source;
|
|
696
|
+
}
|
|
697
|
+
ctx.addIssue({
|
|
698
|
+
code: "custom",
|
|
699
|
+
path,
|
|
700
|
+
message: `source evidence id must reference sourceReferences[].id: ${sourceEvidenceId}`
|
|
701
|
+
});
|
|
702
|
+
return undefined;
|
|
703
|
+
};
|
|
704
|
+
const requireSourceEvidence = (sourceEvidenceId, requirement, path, label) => {
|
|
705
|
+
if (sourceEvidenceId === undefined) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const source = requireSourceId(sourceEvidenceId, path);
|
|
709
|
+
if (source === undefined) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const sourceIndex = sourceIndexById.get(sourceEvidenceId);
|
|
713
|
+
const sourcePath = sourceIndex === undefined ? path : ["sourceReferences", sourceIndex];
|
|
714
|
+
if (!requirement.kinds.includes(source.kind)) {
|
|
715
|
+
ctx.addIssue({
|
|
716
|
+
code: "custom",
|
|
717
|
+
path: [...sourcePath, "kind"],
|
|
718
|
+
message: `${label} source kind must be one of ${requirement.kinds.join(", ")}`
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
addMissingRequiredValuesIssue(ctx, source.fields, requirement.fields, [...sourcePath, "fields"], `${label} sourceReferences fields`);
|
|
722
|
+
};
|
|
723
|
+
const requireClaim = (claimId, factKind, path, label) => {
|
|
724
|
+
if (claimId === undefined) {
|
|
725
|
+
ctx.addIssue({
|
|
726
|
+
code: "custom",
|
|
727
|
+
path,
|
|
728
|
+
message: `${label} must reference evidenceClaims[].id`
|
|
729
|
+
});
|
|
730
|
+
return undefined;
|
|
731
|
+
}
|
|
732
|
+
const claim = claimById.get(claimId);
|
|
733
|
+
if (claim === undefined) {
|
|
734
|
+
ctx.addIssue({
|
|
735
|
+
code: "custom",
|
|
736
|
+
path,
|
|
737
|
+
message: `${label} must reference evidenceClaims[].id: ${claimId}`
|
|
738
|
+
});
|
|
739
|
+
return undefined;
|
|
740
|
+
}
|
|
741
|
+
if (claim.factKind !== factKind) {
|
|
742
|
+
const claimIndex = claimIndexById.get(claimId);
|
|
743
|
+
const claimPath = claimIndex === undefined ? path : ["evidenceClaims", claimIndex, "factKind"];
|
|
744
|
+
ctx.addIssue({
|
|
745
|
+
code: "custom",
|
|
746
|
+
path: claimPath,
|
|
747
|
+
message: `${label} evidence claim factKind must be ${factKind}`
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
return claim;
|
|
751
|
+
};
|
|
752
|
+
value.evidenceClaims.forEach((claim, index) => {
|
|
753
|
+
if (claim.factKind === "raw_quantity_amount") {
|
|
754
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.raw_quantity_amount.byRole[claim.role], ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].raw_quantity_amount`);
|
|
755
|
+
}
|
|
756
|
+
if (claim.factKind === "unit_metadata") {
|
|
757
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.unit_metadata.bySource[claim.source], ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].unit_metadata`);
|
|
758
|
+
}
|
|
759
|
+
if (claim.factKind === "gas_unresolved_status") {
|
|
760
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.gas_unresolved_status, ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].gas_unresolved_status`);
|
|
761
|
+
}
|
|
762
|
+
if (claim.factKind === "expiry_status") {
|
|
763
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.expiry_status[claim.status], ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].expiry_status`);
|
|
764
|
+
if ((claim.status === "current" || claim.status === "expired") && claim.expiresAt === undefined) {
|
|
765
|
+
ctx.addIssue({
|
|
766
|
+
code: "custom",
|
|
767
|
+
path: ["evidenceClaims", index, "expiresAt"],
|
|
768
|
+
message: "Current or expired expiry claim requires expiresAt"
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
if ((claim.status === "not_provided" || claim.status === "not_applicable") && claim.reason === undefined) {
|
|
772
|
+
ctx.addIssue({
|
|
773
|
+
code: "custom",
|
|
774
|
+
path: ["evidenceClaims", index, "reason"],
|
|
775
|
+
message: "Unavailable expiry claim requires reason"
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
if ((claim.status === "not_provided" || claim.status === "not_applicable") && claim.expiresAt !== undefined) {
|
|
779
|
+
ctx.addIssue({
|
|
780
|
+
code: "custom",
|
|
781
|
+
path: ["evidenceClaims", index, "expiresAt"],
|
|
782
|
+
message: "Unavailable expiry claim must not include expiresAt"
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
if (claim.status === "current" &&
|
|
786
|
+
claim.expiresAt !== undefined &&
|
|
787
|
+
Date.parse(claim.expiresAt) <= Date.parse(claim.checkedAt)) {
|
|
788
|
+
ctx.addIssue({
|
|
789
|
+
code: "custom",
|
|
790
|
+
path: ["evidenceClaims", index, "expiresAt"],
|
|
791
|
+
message: "Current expiry claim requires expiresAt after checkedAt"
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
if (claim.status === "expired" &&
|
|
795
|
+
claim.expiresAt !== undefined &&
|
|
796
|
+
Date.parse(claim.expiresAt) > Date.parse(claim.checkedAt)) {
|
|
797
|
+
ctx.addIssue({
|
|
798
|
+
code: "custom",
|
|
799
|
+
path: ["evidenceClaims", index, "expiresAt"],
|
|
800
|
+
message: "Expired expiry claim requires expiresAt at or before checkedAt"
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
if (claim.factKind === "quote_min_out") {
|
|
805
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.quote_min_out, ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].quote_min_out`);
|
|
806
|
+
}
|
|
807
|
+
if (claim.factKind === "slippage_policy") {
|
|
808
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.slippage_policy.bySource[claim.policySource], ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].slippage_policy`);
|
|
809
|
+
if (claim.policySource === "adapter_policy_from_quote_evidence" && claim.minOutRaw === undefined) {
|
|
810
|
+
ctx.addIssue({
|
|
811
|
+
code: "custom",
|
|
812
|
+
path: ["evidenceClaims", index, "minOutRaw"],
|
|
813
|
+
message: "Adapter-derived slippage policy claim requires minOutRaw"
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (claim.factKind === "object_ownership") {
|
|
818
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.object_ownership, ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].object_ownership`);
|
|
819
|
+
}
|
|
820
|
+
if (claim.factKind === "simulation_result") {
|
|
821
|
+
requireSourceEvidence(claim.sourceEvidenceId, SAFETY_CRITICAL_FACT_MATRIX.simulation_result, ["evidenceClaims", index, "sourceEvidenceId"], `evidenceClaims[${index}].simulation_result`);
|
|
822
|
+
if (claim.status === "success" && claim.missingFields.length !== 0) {
|
|
823
|
+
ctx.addIssue({
|
|
824
|
+
code: "custom",
|
|
825
|
+
path: ["evidenceClaims", index, "missingFields"],
|
|
826
|
+
message: "Successful simulation claim must not have missing required fields"
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
if (!WALLET_REVIEW_REQUIRED_SIMULATION_FIELDS.every((field) => claim.requiredFields.includes(field))) {
|
|
830
|
+
ctx.addIssue({
|
|
831
|
+
code: "custom",
|
|
832
|
+
path: ["evidenceClaims", index, "requiredFields"],
|
|
833
|
+
message: "Simulation claim must include every required simulation field"
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
if (claim.status !== "success" && claim.failureReason === undefined) {
|
|
837
|
+
ctx.addIssue({
|
|
838
|
+
code: "custom",
|
|
839
|
+
path: ["evidenceClaims", index, "failureReason"],
|
|
840
|
+
message: "Failed or unavailable simulation claim requires failureReason"
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
if (claim.status === "success" && claim.failureReason !== undefined) {
|
|
844
|
+
ctx.addIssue({
|
|
845
|
+
code: "custom",
|
|
846
|
+
path: ["evidenceClaims", index, "failureReason"],
|
|
847
|
+
message: "Successful simulation claim must not include failureReason"
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
value.rawQuantities.forEach((quantity, index) => {
|
|
853
|
+
const amountClaim = requireClaim(quantity.amountClaimId, "raw_quantity_amount", ["rawQuantities", index, "amountClaimId"], `rawQuantities[${index}].amountClaimId`);
|
|
854
|
+
if (amountClaim?.factKind === "raw_quantity_amount") {
|
|
855
|
+
requireEqual(ctx, quantity.role, amountClaim.role, ["rawQuantities", index, "role"], `rawQuantities[${index}].role`);
|
|
856
|
+
requireEqual(ctx, quantity.rawAmount, amountClaim.rawAmount, ["rawQuantities", index, "rawAmount"], `rawQuantities[${index}].rawAmount`);
|
|
857
|
+
requireSameAsset(ctx, quantity.asset, amountClaim.asset, ["rawQuantities", index, "asset"], `rawQuantities[${index}].asset`);
|
|
858
|
+
}
|
|
859
|
+
const unitClaim = requireClaim(quantity.unit.unitClaimId, "unit_metadata", ["rawQuantities", index, "unit", "unitClaimId"], `rawQuantities[${index}].unit.unitClaimId`);
|
|
860
|
+
if (unitClaim?.factKind === "unit_metadata") {
|
|
861
|
+
requireEqual(ctx, quantity.unit.source, unitClaim.source, ["rawQuantities", index, "unit", "source"], `rawQuantities[${index}].unit.source`);
|
|
862
|
+
requireEqual(ctx, quantity.unit.decimals, unitClaim.decimals, ["rawQuantities", index, "unit", "decimals"], `rawQuantities[${index}].unit.decimals`);
|
|
863
|
+
if (quantity.asset.coinType !== undefined) {
|
|
864
|
+
requireEqual(ctx, quantity.asset.coinType, unitClaim.coinType, ["rawQuantities", index, "asset", "coinType"], `rawQuantities[${index}].asset.coinType`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
if (value.gas.gasBudgetRaw !== undefined) {
|
|
869
|
+
const gasBudgetClaim = requireClaim(value.gas.gasBudgetClaimId, "raw_quantity_amount", ["gas", "gasBudgetClaimId"], "gas.gasBudgetClaimId");
|
|
870
|
+
if (gasBudgetClaim?.factKind === "raw_quantity_amount") {
|
|
871
|
+
requireEqual(ctx, gasBudgetClaim.role, CONSUMER_INVARIANT_MATRIX.gasBudgetClaimId.role, ["gas", "gasBudgetClaimId"], "gas.gasBudgetClaimId role");
|
|
872
|
+
requireGasAsset(ctx, gasBudgetClaim.asset, CONSUMER_INVARIANT_MATRIX.gasBudgetClaimId.assetCoinType, ["gas", "gasBudgetClaimId"], "gas.gasBudgetClaimId asset");
|
|
873
|
+
requireEqual(ctx, value.gas.gasBudgetRaw, gasBudgetClaim.rawAmount, ["gas", "gasBudgetRaw"], "gas.gasBudgetRaw");
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
if (value.gas.gasUsedRaw !== undefined) {
|
|
877
|
+
const gasUsedClaim = requireClaim(value.gas.gasUsedClaimId, "raw_quantity_amount", ["gas", "gasUsedClaimId"], "gas.gasUsedClaimId");
|
|
878
|
+
if (gasUsedClaim?.factKind === "raw_quantity_amount") {
|
|
879
|
+
requireEqual(ctx, gasUsedClaim.role, CONSUMER_INVARIANT_MATRIX.gasUsedClaimId.role, ["gas", "gasUsedClaimId"], "gas.gasUsedClaimId role");
|
|
880
|
+
requireGasAsset(ctx, gasUsedClaim.asset, CONSUMER_INVARIANT_MATRIX.gasUsedClaimId.assetCoinType, ["gas", "gasUsedClaimId"], "gas.gasUsedClaimId asset");
|
|
881
|
+
requireEqual(ctx, value.gas.gasUsedRaw, gasUsedClaim.rawAmount, ["gas", "gasUsedRaw"], "gas.gasUsedRaw");
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (value.gas.unresolvedReason !== undefined) {
|
|
885
|
+
const unresolvedClaim = requireClaim(value.gas.unresolvedClaimId, "gas_unresolved_status", ["gas", "unresolvedClaimId"], "gas.unresolvedClaimId");
|
|
886
|
+
if (unresolvedClaim?.factKind === "gas_unresolved_status") {
|
|
887
|
+
requireEqual(ctx, unresolvedClaim.status, "unresolved", ["gas", "unresolvedClaimId"], "gas.unresolvedClaimId status");
|
|
888
|
+
requireEqual(ctx, value.gas.checkedAt, unresolvedClaim.checkedAt, ["gas", "checkedAt"], "gas.checkedAt");
|
|
889
|
+
requireEqual(ctx, value.gas.unresolvedReason, unresolvedClaim.reason, ["gas", "unresolvedReason"], "gas.unresolvedReason");
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
value.gas.gasObjects?.forEach((gasObject, index) => {
|
|
893
|
+
const ownershipClaim = requireClaim(gasObject.ownershipClaimId, "object_ownership", ["gas", "gasObjects", index, "ownershipClaimId"], `gas.gasObjects[${index}].ownershipClaimId`);
|
|
894
|
+
if (ownershipClaim?.factKind === "object_ownership") {
|
|
895
|
+
requireEqual(ctx, gasObject.objectId, ownershipClaim.objectId, ["gas", "gasObjects", index, "objectId"], `gas.gasObjects[${index}].objectId`);
|
|
896
|
+
requireEqual(ctx, gasObject.ownerAccount, ownershipClaim.ownerAccount, ["gas", "gasObjects", index, "ownerAccount"], `gas.gasObjects[${index}].ownerAccount`);
|
|
897
|
+
requireEqual(ctx, ownershipClaim.ownership, CONSUMER_INVARIANT_MATRIX.gasObjectOwnershipClaimId.ownership, ["gas", "gasObjects", index, "ownershipClaimId"], `gas.gasObjects[${index}].ownershipClaimId ownership`);
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
const expiryClaim = requireClaim(value.expiry.evidenceClaimId, "expiry_status", ["expiry", "evidenceClaimId"], "expiry.evidenceClaimId");
|
|
901
|
+
if (expiryClaim?.factKind === "expiry_status") {
|
|
902
|
+
requireEqual(ctx, value.expiry.status, expiryClaim.status, ["expiry", "status"], "expiry.status");
|
|
903
|
+
requireEqual(ctx, value.expiry.checkedAt, expiryClaim.checkedAt, ["expiry", "checkedAt"], "expiry.checkedAt");
|
|
904
|
+
if (value.expiry.status === "current" || value.expiry.status === "expired") {
|
|
905
|
+
requireEqual(ctx, value.expiry.expiresAt, expiryClaim.expiresAt, ["expiry", "expiresAt"], "expiry.expiresAt");
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
requireEqual(ctx, value.expiry.reason, expiryClaim.reason, ["expiry", "reason"], "expiry.reason");
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (value.slippageOrMinOut.quoteEvidenceClaimId !== undefined) {
|
|
912
|
+
const quoteClaim = requireClaim(value.slippageOrMinOut.quoteEvidenceClaimId, "quote_min_out", ["slippageOrMinOut", "quoteEvidenceClaimId"], "slippageOrMinOut.quoteEvidenceClaimId");
|
|
913
|
+
if (quoteClaim?.factKind === "quote_min_out") {
|
|
914
|
+
requireEqual(ctx, value.slippageOrMinOut.quoteEvidenceId, quoteClaim.quoteEvidenceId, ["slippageOrMinOut", "quoteEvidenceId"], "slippageOrMinOut.quoteEvidenceId");
|
|
915
|
+
requireEqual(ctx, value.slippageOrMinOut.minOutRaw, quoteClaim.minOutRaw, ["slippageOrMinOut", "minOutRaw"], "slippageOrMinOut.minOutRaw");
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (value.slippageOrMinOut.minOutRaw !== undefined) {
|
|
919
|
+
const matchingMinOutQuantity = value.rawQuantities.some((quantity) => quantity.role === "minimum_output" &&
|
|
920
|
+
quantity.rawAmount === value.slippageOrMinOut.minOutRaw);
|
|
921
|
+
if (!matchingMinOutQuantity) {
|
|
922
|
+
ctx.addIssue({
|
|
923
|
+
code: "custom",
|
|
924
|
+
path: ["slippageOrMinOut", "minOutRaw"],
|
|
925
|
+
message: "slippageOrMinOut.minOutRaw must match a minimum_output rawQuantities entry"
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (value.slippageOrMinOut.policyEvidenceClaimId !== undefined) {
|
|
930
|
+
const policyClaim = requireClaim(value.slippageOrMinOut.policyEvidenceClaimId, "slippage_policy", ["slippageOrMinOut", "policyEvidenceClaimId"], "slippageOrMinOut.policyEvidenceClaimId");
|
|
931
|
+
if (policyClaim?.factKind === "slippage_policy") {
|
|
932
|
+
requireEqual(ctx, value.slippageOrMinOut.policySource, policyClaim.policySource, ["slippageOrMinOut", "policySource"], "slippageOrMinOut.policySource");
|
|
933
|
+
requireEqual(ctx, value.slippageOrMinOut.maxSlippageBps, policyClaim.maxSlippageBps, ["slippageOrMinOut", "maxSlippageBps"], "slippageOrMinOut.maxSlippageBps");
|
|
934
|
+
if (policyClaim.policySource === "adapter_policy_from_quote_evidence") {
|
|
935
|
+
requireEqual(ctx, value.slippageOrMinOut.minOutRaw, policyClaim.minOutRaw, ["slippageOrMinOut", "minOutRaw"], "slippageOrMinOut.minOutRaw");
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
value.objectOwnership.objects.forEach((object, index) => {
|
|
940
|
+
const ownershipClaim = requireClaim(object.evidenceClaimId, "object_ownership", ["objectOwnership", "objects", index, "evidenceClaimId"], `objectOwnership.objects[${index}].evidenceClaimId`);
|
|
941
|
+
if (ownershipClaim?.factKind === "object_ownership") {
|
|
942
|
+
requireEqual(ctx, object.objectId, ownershipClaim.objectId, ["objectOwnership", "objects", index, "objectId"], `objectOwnership.objects[${index}].objectId`);
|
|
943
|
+
requireEqual(ctx, value.objectOwnership.ownerAccount, ownershipClaim.ownerAccount, ["objectOwnership", "ownerAccount"], "objectOwnership.ownerAccount");
|
|
944
|
+
requireEqual(ctx, object.ownership, ownershipClaim.ownership, ["objectOwnership", "objects", index, "ownership"], `objectOwnership.objects[${index}].ownership`);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
const simulationClaim = requireClaim(value.simulation.evidenceClaimId, "simulation_result", ["simulation", "evidenceClaimId"], "simulation.evidenceClaimId");
|
|
948
|
+
if (simulationClaim?.factKind === "simulation_result") {
|
|
949
|
+
requireEqual(ctx, value.simulation.provider, simulationClaim.provider, ["simulation", "provider"], "simulation.provider");
|
|
950
|
+
requireEqual(ctx, value.simulation.checksEnabled, simulationClaim.checksEnabled, ["simulation", "checksEnabled"], "simulation.checksEnabled");
|
|
951
|
+
requireEqual(ctx, value.simulation.simulatedAt, simulationClaim.simulatedAt, ["simulation", "simulatedAt"], "simulation.simulatedAt");
|
|
952
|
+
requireEqual(ctx, value.simulation.status, simulationClaim.status, ["simulation", "status"], "simulation.status");
|
|
953
|
+
requireSameStringSet(ctx, value.simulation.requiredFields, simulationClaim.requiredFields, ["simulation", "requiredFields"], "simulation.requiredFields");
|
|
954
|
+
requireSameStringSet(ctx, value.simulation.missingFields, simulationClaim.missingFields, ["simulation", "missingFields"], "simulation.missingFields");
|
|
955
|
+
requireEqual(ctx, value.simulation.failureReason, simulationClaim.failureReason, ["simulation", "failureReason"], "simulation.failureReason");
|
|
956
|
+
}
|
|
957
|
+
}).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
|
+
const handoffDigest = value.reviewedTransactionDigest;
|
|
962
|
+
requireEqual(ctx, value.humanReadableReview.transactionDigest, handoffDigest, ["humanReadableReview", "transactionDigest"], "humanReadableReview.transactionDigest");
|
|
963
|
+
requireEqual(ctx, value.simulation.transactionDigest, handoffDigest, ["simulation", "transactionDigest"], "simulation.transactionDigest");
|
|
964
|
+
});
|
|
965
|
+
export const ptbVisualizationArtifactSchema = z.object({
|
|
966
|
+
contractVersion: z.literal(PTB_VISUALIZATION_CONTRACT_VERSION),
|
|
967
|
+
artifactKind: z.literal("ptb_visualization"),
|
|
968
|
+
generatedAt: isoUtcStringSchema,
|
|
969
|
+
source: z.object({
|
|
970
|
+
adapterId: z.string().min(1).max(120),
|
|
971
|
+
planId: z.string().min(1).max(120).optional(),
|
|
972
|
+
sourceKind: z.enum(["review_time_generated_transaction_kind", "review_time_ir", "renderer_model"]),
|
|
973
|
+
authority: z.literal("visualization_only_not_wallet_authorization"),
|
|
974
|
+
renderer: z.object({
|
|
975
|
+
name: z.string().min(1).max(120),
|
|
976
|
+
packageName: z.string().min(1).max(120).optional(),
|
|
977
|
+
version: z.string().min(1).max(80).optional()
|
|
978
|
+
}).strict().optional()
|
|
979
|
+
}).strict(),
|
|
980
|
+
mermaid: z.object({
|
|
981
|
+
diagramType: z.literal("flowchart"),
|
|
982
|
+
text: displayTextWithoutExecutableMaterial(20_000, "mermaid.text"),
|
|
983
|
+
namedText: displayTextWithoutExecutableMaterial(20_000, "mermaid.namedText")
|
|
984
|
+
}).strict(),
|
|
985
|
+
diagnostics: z.array(z.object({
|
|
986
|
+
severity: z.enum(["info", "warning", "error"]),
|
|
987
|
+
code: z.string().min(1).max(120),
|
|
988
|
+
message: displayTextWithoutExecutableMaterial(512, "diagnostics.message"),
|
|
989
|
+
source: z.enum(["adapter", "renderer", "schema", "simulation"])
|
|
990
|
+
}).strict()).default([]),
|
|
991
|
+
unsupportedUse: z.array(z.enum(PTB_VISUALIZATION_REQUIRED_UNSUPPORTED_USES)).min(1),
|
|
992
|
+
executableMaterial: z.object({
|
|
993
|
+
included: z.literal(false),
|
|
994
|
+
policy: z.literal("mcp_and_review_ui_outputs_must_not_include_executable_transaction_material")
|
|
995
|
+
}).strict()
|
|
996
|
+
}).strict().superRefine((value, ctx) => {
|
|
997
|
+
requireAllValues(ctx, value.unsupportedUse, PTB_VISUALIZATION_REQUIRED_UNSUPPORTED_USES, ["unsupportedUse"], "unsupportedUse");
|
|
998
|
+
});
|