@stelis/say-ur-intent 0.0.0 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -39
- package/dist/adapters/adapterLifecycleValidators.js +7 -0
- package/dist/adapters/adapterPromptSurfaces.js +71 -0
- package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +175 -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 +269 -0
- package/dist/adapters/flowx/flowxSwapHumanReviewProducer.js +176 -0
- package/dist/adapters/flowx/flowxSwapIntent.js +79 -0
- package/dist/adapters/flowx/flowxSwapQuotePolicy.js +104 -0
- package/dist/adapters/flowx/flowxSwapReviewEvidence.js +468 -0
- package/dist/adapters/flowx/flowxSwapReviewLifecycle.js +85 -0
- package/dist/adapters/flowx/flowxSwapTransactionMaterialProducer.js +362 -0
- package/dist/adapters/intentPlanFactories.js +59 -0
- package/dist/adapters/reviewAdapters.js +81 -0
- package/dist/core/action/adapterLifecycleValidation.js +12 -0
- package/dist/core/action/forbiddenFields.js +43 -0
- package/dist/core/action/humanReadableReviewEvidence.js +203 -0
- package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
- package/dist/core/action/ptbVisualizationProducer.js +66 -0
- package/dist/core/action/reviewCheckResults.js +6 -0
- package/dist/core/action/reviewStateValidation.js +11 -0
- package/dist/core/action/reviewTimeSimulationEvidence.js +471 -0
- package/dist/core/action/schemas.js +529 -0
- package/dist/core/action/signableAdapterContract.js +993 -0
- package/dist/core/action/swapHumanReadableReviewProjection.js +124 -0
- package/dist/core/action/swapQuotePolicyEvidence.js +278 -0
- package/dist/core/action/transactionObjectOwnershipEvidence.js +247 -0
- package/dist/core/action/transactionObjectOwnershipProducer.js +329 -0
- package/dist/core/action/types.js +35 -0
- package/dist/core/action/walletReviewContractAssembler.js +282 -0
- package/dist/core/activity/activityStore.js +15 -0
- package/dist/core/activity/localDataService.js +258 -0
- package/dist/core/activity/localDataTypes.js +11 -0
- package/dist/core/activity/localDataValidation.js +396 -0
- package/dist/core/activity/schemaVersion.js +1 -0
- package/dist/core/activity/sqliteActivityStore.js +820 -0
- package/dist/core/activity/sqliteActivityStoreRows.js +430 -0
- package/dist/core/activity/sqliteActivityStoreSchema.js +258 -0
- package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
- package/dist/core/activity/suiFunctionTarget.js +43 -0
- package/dist/core/activity/transactionActivityAccountEffects.js +189 -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 +87 -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/deepbookRawQuoteClient.js +86 -0
- package/dist/core/read/deepbookReadHelpers.js +265 -0
- package/dist/core/read/deepbookRegistry.js +133 -0
- package/dist/core/read/flowxQuoteClient.js +117 -0
- package/dist/core/read/flowxReadHelpers.js +145 -0
- package/dist/core/read/flowxRegistry.js +174 -0
- package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
- package/dist/core/read/readResponseGuidance.js +451 -0
- package/dist/core/read/readService.js +1164 -0
- package/dist/core/read/readServiceTypes.js +59 -0
- package/dist/core/read/settlementParityFormatting.js +82 -0
- package/dist/core/read/walletReadHelpers.js +99 -0
- package/dist/core/review/reviewChecks.js +54 -0
- package/dist/core/review/reviewComputation.js +38 -0
- package/dist/core/review/reviewComputationResult.js +87 -0
- package/dist/core/session/localSession.js +31 -0
- package/dist/core/session/privateReviewArtifacts.js +73 -0
- package/dist/core/session/sessionErrors.js +9 -0
- package/dist/core/session/sessionStore.js +821 -0
- package/dist/core/session/settingsSession.js +1 -0
- package/dist/core/session/settingsSessions.js +43 -0
- package/dist/core/session/status.js +86 -0
- package/dist/core/session/transactionMaterialStore.js +205 -0
- package/dist/core/session/wait.js +102 -0
- package/dist/core/session/walletIdentity.js +103 -0
- package/dist/core/session/walletIdentitySessions.js +189 -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 +381 -0
- package/dist/mcp/result.js +17 -0
- package/dist/mcp/schemas.js +8 -0
- package/dist/mcp/server.js +30 -0
- package/dist/mcp/serverInfo.js +123 -0
- package/dist/mcp/toolErrors.js +105 -0
- package/dist/mcp/toolNames.js +50 -0
- package/dist/mcp/tools/account/index.js +44 -0
- package/dist/mcp/tools/action/prepareSuiActionReview.js +120 -0
- package/dist/mcp/tools/read/commonSchemas.js +43 -0
- package/dist/mcp/tools/read/deepbookReadTools.js +453 -0
- package/dist/mcp/tools/read/flowxReadTools.js +135 -0
- package/dist/mcp/tools/read/index.js +16 -0
- package/dist/mcp/tools/read/readToolHelpers.js +68 -0
- package/dist/mcp/tools/read/reviewActivityTools.js +176 -0
- package/dist/mcp/tools/read/serverStatusTools.js +103 -0
- package/dist/mcp/tools/read/transactionActivityOutput.js +300 -0
- package/dist/mcp/tools/read/transactionActivityTools.js +544 -0
- package/dist/mcp/tools/read/walletReadTools.js +733 -0
- package/dist/mcp/tools/session/executionResultTools.js +92 -0
- package/dist/mcp/tools/session/index.js +8 -0
- package/dist/mcp/tools/session/shared.js +79 -0
- package/dist/mcp/tools/session/statusTools.js +134 -0
- package/dist/mcp/tools/session/walletIdentityTools.js +119 -0
- package/dist/mcp/tools/settings/index.js +64 -0
- package/dist/review-app/analysis.css +1 -0
- package/dist/review-app/analysis.js +1 -0
- package/dist/review-app/arc-BjIacwQm.js +1 -0
- package/dist/review-app/architecture-U656AL7Q-aSB9x1OK.js +1 -0
- package/dist/review-app/architectureDiagram-VXUJARFQ-C5W6re2I.js +36 -0
- package/dist/review-app/array-BmXUUrU6.js +1 -0
- package/dist/review-app/blockDiagram-VD42YOAC-20MLNcUm.js +122 -0
- package/dist/review-app/c4Diagram-YG6GDRKO-BZXRrcck.js +10 -0
- package/dist/review-app/channel-lk2p_CUu.js +1 -0
- package/dist/review-app/chunk-4BX2VUAB-BPITOdjX.js +1 -0
- package/dist/review-app/chunk-55IACEB6-Dz-pyw5k.js +1 -0
- package/dist/review-app/chunk-76Q3JFCE-cK_X1P_l.js +1 -0
- package/dist/review-app/chunk-ABZYJK2D-Dt4W53JI.js +81 -0
- package/dist/review-app/chunk-ATLVNIR6-fZHLXURb.js +1 -0
- package/dist/review-app/chunk-B4BG7PRW-BbgcjusC.js +165 -0
- package/dist/review-app/chunk-BJD4TVEz.js +1 -0
- package/dist/review-app/chunk-CVBHYZKI-CViawAKX.js +1 -0
- package/dist/review-app/chunk-DI55MBZ5-C5aoul-d.js +220 -0
- package/dist/review-app/chunk-FMBD7UC4-Chxmw62A.js +15 -0
- package/dist/review-app/chunk-FPAJGGOC-DDHjQ09H.js +80 -0
- package/dist/review-app/chunk-FWNWRKHM-CVVQUptk.js +1 -0
- package/dist/review-app/chunk-HN2XXSSU-yzNpjaSZ.js +1 -0
- package/dist/review-app/chunk-JA3XYJ7Z-C5ZJdU01.js +70 -0
- package/dist/review-app/chunk-JZLCHNYA-BBST4Cnk.js +54 -0
- package/dist/review-app/chunk-LBM3YZW2-CdwAPuHr.js +1 -0
- package/dist/review-app/chunk-LHMN2FUI-BtB5uDcp.js +1 -0
- package/dist/review-app/chunk-O7ZBX7Z2-pxdK4Sa3.js +1 -0
- package/dist/review-app/chunk-QN33PNHL-CbVv3uGK.js +1 -0
- package/dist/review-app/chunk-QXUST7PY-DKM2-t2c.js +7 -0
- package/dist/review-app/chunk-QZHKN3VN-C5ni2pN_.js +1 -0
- package/dist/review-app/chunk-S3R3BYOJ-BWvOhDs0.js +2 -0
- package/dist/review-app/chunk-S6J4BHB3-D9Fk0YeD.js +1 -0
- package/dist/review-app/chunk-T53DSG4Q-C1qEyzyV.js +1 -0
- package/dist/review-app/chunk-TZMSLE5B-B--7eU69.js +1 -0
- package/dist/review-app/classDiagram-2ON5EDUG-DlL1m2bp.js +1 -0
- package/dist/review-app/classDiagram-v2-WZHVMYZB-FXRskT1j.js +1 -0
- package/dist/review-app/clone-BZZb7gpZ.js +1 -0
- package/dist/review-app/cose-bilkent-S5V4N54A-CRIb8XEO.js +1 -0
- package/dist/review-app/cytoscape.esm-C7jYqDP5.js +321 -0
- package/dist/review-app/dagre-6UL2VRFP-FNCAXbdE.js +4 -0
- package/dist/review-app/dagre-Be46QtUd.js +1 -0
- package/dist/review-app/defaultLocale-BaWNtAUL.js +1 -0
- package/dist/review-app/diagram-PSM6KHXK-ylLWjiNM.js +24 -0
- package/dist/review-app/diagram-QEK2KX5R-BCDcESxs.js +43 -0
- package/dist/review-app/diagram-S2PKOQOG-Vdrc-vrO.js +24 -0
- package/dist/review-app/dist-WPc74x_f.js +1 -0
- package/dist/review-app/erDiagram-Q2GNP2WA-E5ZsUbDF.js +60 -0
- package/dist/review-app/flatten-DHf9IeNI.js +1 -0
- package/dist/review-app/flowDiagram-NV44I4VS-DBSQuj6x.js +162 -0
- package/dist/review-app/ganttDiagram-LVOFAZNH-CKUOsqwl.js +267 -0
- package/dist/review-app/gitGraph-F6HP7TQM-DsAD6qK1.js +1 -0
- package/dist/review-app/gitGraphDiagram-NY62KEGX-BCeIMWdl.js +65 -0
- package/dist/review-app/graphlib-CiX5CXxR.js +1 -0
- package/dist/review-app/http-DMvwuuFk.js +1 -0
- package/dist/review-app/identity-DY8PXc6t.js +1 -0
- package/dist/review-app/info-NVLQJR56-Dlx1nZic.js +1 -0
- package/dist/review-app/infoDiagram-F6ZHWCRC-CAuANIrz.js +2 -0
- package/dist/review-app/init-BvqephKz.js +1 -0
- package/dist/review-app/journeyDiagram-XKPGCS4Q-C-Z9phnx.js +139 -0
- package/dist/review-app/kanban-definition-3W4ZIXB7-DufgZABq.js +89 -0
- package/dist/review-app/katex-B-Z-NXXN.js +257 -0
- package/dist/review-app/line-DiIv3Jgw.js +1 -0
- package/dist/review-app/linear-Cv-UPvo1.js +1 -0
- package/dist/review-app/math-kmyYrkHL.js +1 -0
- package/dist/review-app/mermaid-parser.core-DkwUYTPl.js +4 -0
- package/dist/review-app/mindmap-definition-VGOIOE7T-TM_CqdmV.js +68 -0
- package/dist/review-app/ordinal-BliTlkoG.js +1 -0
- package/dist/review-app/packet-BFZMPI3H-DqbnU92v.js +1 -0
- package/dist/review-app/path-AEo9W6mQ.js +1 -0
- package/dist/review-app/pie-7BOR55EZ-LJzaLkgr.js +1 -0
- package/dist/review-app/pieDiagram-ADFJNKIX-BAs8OfRS.js +30 -0
- package/dist/review-app/quadrantDiagram-AYHSOK5B-CyUDZP5S.js +7 -0
- package/dist/review-app/radar-NHE76QYJ-DBpHc8_Y.js +1 -0
- package/dist/review-app/reduce-B-HuPpdd.js +1 -0
- package/dist/review-app/requirementDiagram-UZGBJVZJ-BEHix78P.js +64 -0
- package/dist/review-app/review.css +1 -0
- package/dist/review-app/review.js +43 -0
- package/dist/review-app/sankeyDiagram-TZEHDZUN-B2bKbmsm.js +10 -0
- package/dist/review-app/sequenceDiagram-WL72ISMW-DVLOORFJ.js +145 -0
- package/dist/review-app/settings.css +1 -0
- package/dist/review-app/settings.js +1 -0
- package/dist/review-app/src-Buml7cM5.js +1 -0
- package/dist/review-app/stateDiagram-FKZM4ZOC-sFGGp2kV.js +1 -0
- package/dist/review-app/stateDiagram-v2-4FDKWEC3-BHfCF4dX.js +1 -0
- package/dist/review-app/timeline-definition-IT6M3QCI-BESnBijC.js +61 -0
- package/dist/review-app/treemap-KMMF4GRG-wnVLBDeQ.js +1 -0
- package/dist/review-app/walletStatus-CcojOdGy.js +7 -0
- package/dist/review-app/xychartDiagram-PRI3JC2R-BGWVfCx4.js +7 -0
- package/dist/review-server/assets.js +48 -0
- package/dist/review-server/html.js +66 -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 +10 -0
- package/dist/review-server/server.js +568 -0
- package/dist/review-server/settingsApi.js +182 -0
- package/dist/review-server/walletIdentityResponse.js +13 -0
- package/dist/runtime/config.js +103 -0
- package/dist/runtime/localSettingsService.js +198 -0
- package/dist/runtime/logger.js +50 -0
- package/dist/runtime/reviewServerAcquire.js +128 -0
- package/dist/runtime/smokeMainnetRead.js +529 -0
- package/dist/runtime/smokeMainnetReadAssertions.js +308 -0
- package/dist/runtime/start.js +295 -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/docs/AGENT_BEHAVIOR.md +1 -1
- package/docs/AGENT_DEVELOPMENT_POLICY.md +20 -0
- package/docs/FRONTEND_POLICY.md +4 -3
- package/docs/MCP_SETUP.md +59 -7
- package/docs/MCP_TOOLS.md +1 -1
- package/docs/SDK_API.md +5 -1
- package/package.json +3 -2
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { deepbookSwapActionPlanDataSchema } from "./deepbookSwapIntent.js";
|
|
2
|
+
import { deriveDeepbookSwapQuotePolicy } from "./deepbookQuotePolicy.js";
|
|
3
|
+
import { deepbookSwapReviewLifecycleStageLabel, newDeepbookSwapReviewLifecycle } from "./deepbookReviewLifecycle.js";
|
|
4
|
+
import { createSwapQuotePolicyEvidence } from "../../core/action/swapQuotePolicyEvidence.js";
|
|
5
|
+
import { publicHumanReadableReviewFromEvidence } from "../../core/action/humanReadableReviewEvidence.js";
|
|
6
|
+
import { publicTransactionSimulationSummaryFromEvidence } from "../../core/action/reviewTimeSimulationEvidence.js";
|
|
7
|
+
import { ReadServiceInputError } from "../../core/read/readServiceTypes.js";
|
|
8
|
+
import { resolveDeepbookPoolForSymbols } from "../../core/read/deepbookRegistry.js";
|
|
9
|
+
import { blockedAdapterLifecycleReviewResult, failReviewCheck, passReviewCheck, producerStageMissingReviewResult, refreshRequiredAdapterLifecycleReviewResult, walletReviewContractEmitMissingResult, walletReviewContractEmittedResult } from "../../core/review/reviewComputationResult.js";
|
|
10
|
+
import { assembleWalletReviewAdapterContract } from "../../core/action/walletReviewContractAssembler.js";
|
|
11
|
+
export async function computeDeepbookSwapReviewEvidence(input) {
|
|
12
|
+
const lifecycle = newDeepbookSwapReviewLifecycle(input.plan);
|
|
13
|
+
const checks = [];
|
|
14
|
+
const now = input.now ?? new Date();
|
|
15
|
+
const intentStage = await runDeepbookReviewStage({
|
|
16
|
+
lifecycle,
|
|
17
|
+
checks,
|
|
18
|
+
stage: "intent_normalized",
|
|
19
|
+
run: () => normalizeIntentStage(input.plan)
|
|
20
|
+
});
|
|
21
|
+
if (!intentStage.ok) {
|
|
22
|
+
return { result: intentStage.result };
|
|
23
|
+
}
|
|
24
|
+
const requestedIntent = intentStage.evidence;
|
|
25
|
+
const poolStage = await runDeepbookReviewStage({
|
|
26
|
+
lifecycle,
|
|
27
|
+
checks,
|
|
28
|
+
stage: "pool_resolved",
|
|
29
|
+
run: () => resolvePoolStage(requestedIntent)
|
|
30
|
+
});
|
|
31
|
+
if (!poolStage.ok) {
|
|
32
|
+
return { result: poolStage.result };
|
|
33
|
+
}
|
|
34
|
+
const poolResolution = poolStage.evidence;
|
|
35
|
+
const quoteStage = await runDeepbookReviewStage({
|
|
36
|
+
lifecycle,
|
|
37
|
+
checks,
|
|
38
|
+
stage: "quote_evidence_fetched",
|
|
39
|
+
run: () => quoteEvidenceStage(input, requestedIntent, poolResolution)
|
|
40
|
+
});
|
|
41
|
+
if (!quoteStage.ok) {
|
|
42
|
+
return { result: quoteStage.result };
|
|
43
|
+
}
|
|
44
|
+
const { quote, feeMode } = quoteStage.evidence;
|
|
45
|
+
const policyStage = await runDeepbookReviewStage({
|
|
46
|
+
lifecycle,
|
|
47
|
+
checks,
|
|
48
|
+
stage: "quote_policy_derived",
|
|
49
|
+
run: () => quotePolicyStage(input, requestedIntent, quote, feeMode, now)
|
|
50
|
+
});
|
|
51
|
+
if (!policyStage.ok) {
|
|
52
|
+
return { result: policyStage.result };
|
|
53
|
+
}
|
|
54
|
+
const quotePolicy = policyStage.evidence;
|
|
55
|
+
if (!input.transactionMaterialProducer) {
|
|
56
|
+
return {
|
|
57
|
+
result: missingProducerStageBlockedResult(lifecycle.snapshot(), checks)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const materialStage = await runDeepbookReviewStage({
|
|
61
|
+
lifecycle,
|
|
62
|
+
checks,
|
|
63
|
+
stage: "transaction_material_build_or_verify",
|
|
64
|
+
run: () => input.transactionMaterialProducer({
|
|
65
|
+
reviewSessionId: input.reviewSessionId,
|
|
66
|
+
plan: input.plan,
|
|
67
|
+
account: input.account,
|
|
68
|
+
requestedIntent,
|
|
69
|
+
poolResolution,
|
|
70
|
+
quote,
|
|
71
|
+
quotePolicy,
|
|
72
|
+
now
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
if (!materialStage.ok) {
|
|
76
|
+
return { result: materialStage.result };
|
|
77
|
+
}
|
|
78
|
+
if (!input.transactionMaterialDigestProducer) {
|
|
79
|
+
return {
|
|
80
|
+
result: missingProducerStageBlockedResult(lifecycle.snapshot(), checks)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const digestStage = await runDeepbookReviewStage({
|
|
84
|
+
lifecycle,
|
|
85
|
+
checks,
|
|
86
|
+
stage: "digest_commitment",
|
|
87
|
+
run: () => input.transactionMaterialDigestProducer({
|
|
88
|
+
materialHandle: materialStage.evidence,
|
|
89
|
+
now
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
if (!digestStage.ok) {
|
|
93
|
+
return { result: digestStage.result };
|
|
94
|
+
}
|
|
95
|
+
let swapQuotePolicy;
|
|
96
|
+
try {
|
|
97
|
+
swapQuotePolicy = createSwapQuotePolicyEvidence({
|
|
98
|
+
materialHandle: materialStage.evidence,
|
|
99
|
+
adapterId: input.plan.adapterId,
|
|
100
|
+
protocol: input.plan.protocol,
|
|
101
|
+
actionKind: input.plan.actionKind,
|
|
102
|
+
quoteEvidenceId: `deepbook_raw_quote:${input.plan.id}`,
|
|
103
|
+
quoteSource: {
|
|
104
|
+
provider: input.plan.protocol,
|
|
105
|
+
poolKey: poolResolution.poolKey,
|
|
106
|
+
direction: quotePolicy.direction,
|
|
107
|
+
fetchedAt: quotePolicy.fetchedAt,
|
|
108
|
+
sourceMoveFunction: quote.rawQuote.sourceMoveFunction
|
|
109
|
+
},
|
|
110
|
+
maxSlippageBps: quotePolicy.maxSlippageBps,
|
|
111
|
+
staleAfterMs: quotePolicy.staleAfterMs,
|
|
112
|
+
sourceAmount: quotePolicyAmount(quote.rawQuote.inputAmount),
|
|
113
|
+
expectedOutput: quotePolicyAmount(quote.rawQuote.directionalOutput),
|
|
114
|
+
minimumOutput: {
|
|
115
|
+
raw: quotePolicy.minOutRaw,
|
|
116
|
+
asset: quotePolicyAmount(quote.rawQuote.directionalOutput).asset
|
|
117
|
+
},
|
|
118
|
+
protocolFee: quotePolicyAmount(quote.rawQuote.deepRequired),
|
|
119
|
+
// The pipeline anchor `now` is captured before the network quote fetch,
|
|
120
|
+
// so anchor derivedAt at the quote timestamp when the fetch lands later.
|
|
121
|
+
derivedAt: new Date(Math.max(now.getTime(), Date.parse(quotePolicy.fetchedAt)))
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
checks.push(failReviewCheck("deepbook_quote_policy_material_binding_failed", "Quote policy material binding", error instanceof Error ? error.message : "DeepBook quote policy evidence did not match the stored transaction material.", "quote"));
|
|
126
|
+
return {
|
|
127
|
+
result: blockedAdapterLifecycleReviewResult("amount_mismatch", checks, lifecycle.snapshot())
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const privateArtifacts = {
|
|
131
|
+
transactionMaterial: materialStage.evidence,
|
|
132
|
+
transactionMaterialDigest: digestStage.evidence,
|
|
133
|
+
swapQuotePolicy
|
|
134
|
+
};
|
|
135
|
+
// Render the PTB visualization as soon as material and digest exist, so the
|
|
136
|
+
// review page can draw the transaction even when a later stage stops the
|
|
137
|
+
// review. The artifact is commitment-bound at production and carries no
|
|
138
|
+
// digest or bytes.
|
|
139
|
+
let ptbVisualization;
|
|
140
|
+
if (input.ptbVisualizationProducer) {
|
|
141
|
+
const rendered = await input.ptbVisualizationProducer({
|
|
142
|
+
transactionMaterial: materialStage.evidence,
|
|
143
|
+
transactionMaterialDigest: digestStage.evidence,
|
|
144
|
+
adapterId: input.plan.adapterId,
|
|
145
|
+
planId: input.plan.id,
|
|
146
|
+
now
|
|
147
|
+
});
|
|
148
|
+
if (rendered.status === "rendered") {
|
|
149
|
+
ptbVisualization = rendered.artifact;
|
|
150
|
+
checks.push(ptbVisualizationRenderedCheck());
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
checks.push(ptbVisualizationUnavailableCheck(rendered.reason));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const withPtb = (result) => ptbVisualization ? { ...result, ptbVisualization } : result;
|
|
157
|
+
if (!input.transactionObjectOwnershipProducer) {
|
|
158
|
+
return {
|
|
159
|
+
result: withPtb(missingProducerStageBlockedResult(lifecycle.snapshot(), checks)),
|
|
160
|
+
privateArtifacts
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const ownershipStage = await runDeepbookReviewStage({
|
|
164
|
+
lifecycle,
|
|
165
|
+
checks,
|
|
166
|
+
stage: "object_ownership",
|
|
167
|
+
run: () => input.transactionObjectOwnershipProducer({
|
|
168
|
+
materialHandle: materialStage.evidence,
|
|
169
|
+
materialDigest: digestStage.evidence,
|
|
170
|
+
now
|
|
171
|
+
})
|
|
172
|
+
});
|
|
173
|
+
if (!ownershipStage.ok) {
|
|
174
|
+
return {
|
|
175
|
+
result: withPtb(ownershipStage.result),
|
|
176
|
+
...(ownershipStage.result.status === "refresh_required" ? {} : { privateArtifacts })
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
privateArtifacts.transactionObjectOwnership = ownershipStage.evidence;
|
|
180
|
+
if (!input.humanReadableReviewProducer) {
|
|
181
|
+
return {
|
|
182
|
+
result: withPtb(missingProducerStageBlockedResult(lifecycle.snapshot(), checks)),
|
|
183
|
+
privateArtifacts
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const humanReviewStage = await runDeepbookReviewStage({
|
|
187
|
+
lifecycle,
|
|
188
|
+
checks,
|
|
189
|
+
stage: "human_readable_review",
|
|
190
|
+
run: () => input.humanReadableReviewProducer({
|
|
191
|
+
plan: input.plan,
|
|
192
|
+
account: input.account,
|
|
193
|
+
requestedIntent,
|
|
194
|
+
poolResolution,
|
|
195
|
+
quotePolicy,
|
|
196
|
+
transactionMaterial: materialStage.evidence,
|
|
197
|
+
transactionMaterialDigest: digestStage.evidence,
|
|
198
|
+
swapQuotePolicy,
|
|
199
|
+
transactionObjectOwnership: ownershipStage.evidence,
|
|
200
|
+
now
|
|
201
|
+
})
|
|
202
|
+
});
|
|
203
|
+
if (!humanReviewStage.ok) {
|
|
204
|
+
return {
|
|
205
|
+
result: withPtb(humanReviewStage.result),
|
|
206
|
+
privateArtifacts
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
privateArtifacts.humanReadableReview = humanReviewStage.evidence;
|
|
210
|
+
if (!input.reviewTimeSimulationProducer) {
|
|
211
|
+
return {
|
|
212
|
+
result: withPtb(missingProducerStageBlockedResult(lifecycle.snapshot(), checks, {
|
|
213
|
+
humanReadableReview: publicHumanReadableReviewFromEvidence(humanReviewStage.evidence)
|
|
214
|
+
})),
|
|
215
|
+
privateArtifacts
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const simulationStage = await runDeepbookReviewStage({
|
|
219
|
+
lifecycle,
|
|
220
|
+
checks,
|
|
221
|
+
stage: "review_time_simulation",
|
|
222
|
+
run: () => input.reviewTimeSimulationProducer({
|
|
223
|
+
transactionMaterial: materialStage.evidence,
|
|
224
|
+
transactionMaterialDigest: digestStage.evidence,
|
|
225
|
+
now
|
|
226
|
+
})
|
|
227
|
+
});
|
|
228
|
+
if (!simulationStage.ok) {
|
|
229
|
+
return {
|
|
230
|
+
// Keep the public projection paired with the stored private evidence:
|
|
231
|
+
// the human-readable review stays visible while simulation asks for a
|
|
232
|
+
// refresh, and the store's projection binding stays satisfied.
|
|
233
|
+
result: withPtb({
|
|
234
|
+
...simulationStage.result,
|
|
235
|
+
humanReadableReview: publicHumanReadableReviewFromEvidence(humanReviewStage.evidence)
|
|
236
|
+
}),
|
|
237
|
+
privateArtifacts
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
privateArtifacts.reviewTimeSimulation = simulationStage.evidence;
|
|
241
|
+
const assembly = assembleWalletReviewAdapterContract({
|
|
242
|
+
adapterId: input.plan.adapterId,
|
|
243
|
+
protocol: input.plan.protocol,
|
|
244
|
+
actionKind: input.plan.actionKind,
|
|
245
|
+
provenance: {
|
|
246
|
+
kind: "mcp_action_request",
|
|
247
|
+
sourceId: input.plan.id,
|
|
248
|
+
capturedAt: input.plan.createdAt
|
|
249
|
+
},
|
|
250
|
+
quotePolicy: swapQuotePolicy,
|
|
251
|
+
objectOwnership: ownershipStage.evidence,
|
|
252
|
+
humanReadableReview: humanReviewStage.evidence,
|
|
253
|
+
reviewTimeSimulation: simulationStage.evidence,
|
|
254
|
+
transactionMaterialCommitment: digestStage.evidence.transactionDigest,
|
|
255
|
+
now
|
|
256
|
+
});
|
|
257
|
+
if (assembly.status === "emitted") {
|
|
258
|
+
checks.push(contractEmittedCheck());
|
|
259
|
+
return {
|
|
260
|
+
result: walletReviewContractEmittedResult(checks, lifecycle.snapshot(), publicHumanReadableReviewFromEvidence(humanReviewStage.evidence), publicTransactionSimulationSummaryFromEvidence(simulationStage.evidence), assembly.contract, ptbVisualization),
|
|
261
|
+
privateArtifacts
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
checks.push(contractEmitDeclinedCheck(assembly.reason));
|
|
265
|
+
return {
|
|
266
|
+
result: walletReviewContractEmitMissingResult(checks, lifecycle.snapshot(), publicHumanReadableReviewFromEvidence(humanReviewStage.evidence), publicTransactionSimulationSummaryFromEvidence(simulationStage.evidence)),
|
|
267
|
+
privateArtifacts
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
async function runDeepbookReviewStage(input) {
|
|
271
|
+
const outcome = await input.run();
|
|
272
|
+
input.checks.push(...outcome.checks);
|
|
273
|
+
if (outcome.status === "completed") {
|
|
274
|
+
input.lifecycle.complete(input.stage);
|
|
275
|
+
return { ok: true, evidence: outcome.evidence };
|
|
276
|
+
}
|
|
277
|
+
const adapterLifecycle = input.lifecycle.snapshot();
|
|
278
|
+
if (outcome.status === "blocked") {
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
result: blockedAdapterLifecycleReviewResult(outcome.blockedReason, input.checks, adapterLifecycle)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
ok: false,
|
|
286
|
+
result: refreshRequiredAdapterLifecycleReviewResult(outcome.refreshReason, input.checks, adapterLifecycle)
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function normalizeIntentStage(plan) {
|
|
290
|
+
const parsedData = deepbookSwapActionPlanDataSchema.safeParse(plan.adapterData);
|
|
291
|
+
if (!parsedData.success) {
|
|
292
|
+
return {
|
|
293
|
+
status: "blocked",
|
|
294
|
+
blockedReason: "unsupported_action",
|
|
295
|
+
checks: [
|
|
296
|
+
failReviewCheck("deepbook_requested_intent_invalid", "Requested DeepBook intent", "The action plan does not contain a valid DeepBook swap display intent.", "adapter")
|
|
297
|
+
]
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const requestedIntent = parsedData.data.requestedIntent;
|
|
301
|
+
return {
|
|
302
|
+
status: "completed",
|
|
303
|
+
evidence: requestedIntent,
|
|
304
|
+
checks: [
|
|
305
|
+
passReviewCheck("deepbook_display_intent", "Display intent", `Requested display intent is ${requestedIntent.from.amountDisplay} ${requestedIntent.from.symbol} to ${requestedIntent.to.symbol} with max slippage ${requestedIntent.maxSlippageBps} bps. This display amount is not signing input.`, "adapter")
|
|
306
|
+
]
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function resolvePoolStage(requestedIntent) {
|
|
310
|
+
try {
|
|
311
|
+
const poolResolution = resolveDeepbookPoolForSymbols({
|
|
312
|
+
sourceSymbol: requestedIntent.from.symbol,
|
|
313
|
+
targetSymbol: requestedIntent.to.symbol
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
status: "completed",
|
|
317
|
+
evidence: poolResolution,
|
|
318
|
+
checks: [
|
|
319
|
+
passReviewCheck("deepbook_pool_resolution", "DeepBook pool", `Resolved direct DeepBook pool ${poolResolution.poolKey} for ${poolResolution.sourceSymbol} to ${poolResolution.targetSymbol}; direction is ${poolResolution.direction}.`, "registry")
|
|
320
|
+
]
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
return {
|
|
325
|
+
status: "blocked",
|
|
326
|
+
blockedReason: "asset_mismatch",
|
|
327
|
+
checks: [
|
|
328
|
+
failReviewCheck("deepbook_pool_resolution_failed", "DeepBook pool", error instanceof Error ? error.message : "DeepBook pool resolution failed.", "registry")
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async function quoteEvidenceStage(input, requestedIntent, poolResolution) {
|
|
334
|
+
try {
|
|
335
|
+
const fetchQuote = (feeMode) => input.quoteSource.quoteDeepbookDisplayAmount({
|
|
336
|
+
poolKey: poolResolution.poolKey,
|
|
337
|
+
direction: poolResolution.direction,
|
|
338
|
+
amountDisplay: requestedIntent.from.amountDisplay,
|
|
339
|
+
simulationSender: input.account,
|
|
340
|
+
feeMode
|
|
341
|
+
});
|
|
342
|
+
let feeMode = "deep";
|
|
343
|
+
let feeModeMessage = "Fee mode is deep: no DEEP balance source is wired, so the review quotes DeepBook fees in DEEP.";
|
|
344
|
+
let quote = await fetchQuote("deep");
|
|
345
|
+
if (input.deepBalanceSource) {
|
|
346
|
+
const deepBalanceRaw = BigInt(await input.deepBalanceSource(input.account));
|
|
347
|
+
const deepRequiredRaw = BigInt(quote.rawQuote.deepRequired.raw);
|
|
348
|
+
if (deepBalanceRaw >= deepRequiredRaw) {
|
|
349
|
+
feeModeMessage = `Fee mode is deep: account DEEP balance ${deepBalanceRaw} raw covers the required ${deepRequiredRaw} raw fee.`;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
feeMode = "input_coin";
|
|
353
|
+
quote = await fetchQuote("input_coin");
|
|
354
|
+
feeModeMessage = `Fee mode is input_coin: account DEEP balance ${deepBalanceRaw} raw is below the required ${deepRequiredRaw} raw fee, so the swap pays the taker fee in the source coin at the protocol fee penalty. The quoted output already reflects that fee.`;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
status: "completed",
|
|
359
|
+
evidence: { quote, feeMode },
|
|
360
|
+
checks: [
|
|
361
|
+
passReviewCheck("deepbook_raw_quote_evidence", "Raw quote evidence", `Fetched raw DeepBook quote evidence at ${quote.fetchedAt} from ${quote.rawQuote.sourceMoveFunction}; expected output before slippage is ${quote.rawQuote.directionalOutput.raw} ${quote.rawQuote.directionalOutput.symbol} raw units and DEEP fee evidence is ${quote.rawQuote.deepRequired.raw} raw units.`, "quote"),
|
|
362
|
+
passReviewCheck("deepbook_fee_mode", "Fee mode", feeModeMessage, "quote")
|
|
363
|
+
]
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
return quoteSourceFailureOutcome(error);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function quotePolicyStage(input, requestedIntent, quote, feeMode, now) {
|
|
371
|
+
let policy;
|
|
372
|
+
try {
|
|
373
|
+
policy = deriveDeepbookSwapQuotePolicy({
|
|
374
|
+
rawQuote: quote.rawQuote,
|
|
375
|
+
fetchedAt: quote.fetchedAt,
|
|
376
|
+
maxSlippageBps: requestedIntent.maxSlippageBps,
|
|
377
|
+
feeMode,
|
|
378
|
+
now
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
return {
|
|
383
|
+
status: "blocked",
|
|
384
|
+
blockedReason: "amount_mismatch",
|
|
385
|
+
checks: [
|
|
386
|
+
failReviewCheck("deepbook_quote_policy_invalid", "Quote policy", error instanceof Error ? error.message : "DeepBook quote policy could not be derived.", "quote")
|
|
387
|
+
]
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
if (policy.status === "refresh_required") {
|
|
391
|
+
return {
|
|
392
|
+
status: "refresh_required",
|
|
393
|
+
refreshReason: policy.refreshReason,
|
|
394
|
+
checks: [
|
|
395
|
+
failReviewCheck("deepbook_quote_policy_refresh_required", "Quote policy", `Quote policy requires refresh: ${policy.reason}. Quote age is ${policy.quoteAgeMs}ms with stale threshold ${policy.staleAfterMs}ms.`, "quote")
|
|
396
|
+
]
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
status: "completed",
|
|
401
|
+
evidence: policy,
|
|
402
|
+
checks: [
|
|
403
|
+
passReviewCheck("deepbook_quote_policy", "Quote policy", `Derived review policy from raw quote evidence: feeMode ${policy.feeMode}, sourceAmountRaw ${policy.sourceAmountRaw}, expectedOutRaw ${policy.expectedOutRaw}, minOutRaw ${policy.minOutRaw}, deepAmountRaw ${policy.deepAmountRaw}, quoteAgeMs ${policy.quoteAgeMs}. These values are review evidence only and are not transaction bytes, signing data, or signing readiness.`, "quote")
|
|
404
|
+
]
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function quotePolicyAmount(amount) {
|
|
408
|
+
return {
|
|
409
|
+
raw: amount.raw,
|
|
410
|
+
asset: {
|
|
411
|
+
symbol: amount.symbol,
|
|
412
|
+
coinType: amount.coinType,
|
|
413
|
+
decimals: amount.decimals,
|
|
414
|
+
unitSource: amount.unitSource
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function quoteSourceFailureCheck(error) {
|
|
419
|
+
if (error instanceof ReadServiceInputError) {
|
|
420
|
+
return failReviewCheck("deepbook_quote_source_failed", "Quote source", error.message, "quote");
|
|
421
|
+
}
|
|
422
|
+
return failReviewCheck("deepbook_quote_source_failed", "Quote source", error instanceof Error ? error.message : "DeepBook quote source failed.", "quote");
|
|
423
|
+
}
|
|
424
|
+
function quoteSourceFailureOutcome(error) {
|
|
425
|
+
const checks = [quoteSourceFailureCheck(error)];
|
|
426
|
+
if (error instanceof ReadServiceInputError) {
|
|
427
|
+
if (error.kind === "input_invalid") {
|
|
428
|
+
return { status: "blocked", blockedReason: "amount_mismatch", checks };
|
|
429
|
+
}
|
|
430
|
+
if (error.kind === "registry_miss") {
|
|
431
|
+
return { status: "blocked", blockedReason: "asset_mismatch", checks };
|
|
432
|
+
}
|
|
433
|
+
if (error.kind === "quote_unavailable") {
|
|
434
|
+
return { status: "refresh_required", refreshReason: "quote_unavailable", checks };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return { status: "blocked", blockedReason: "object_resolution_failed", checks };
|
|
438
|
+
}
|
|
439
|
+
function missingProducerStageCheck(adapterLifecycle) {
|
|
440
|
+
const nextMissing = adapterLifecycle.missingStages[0];
|
|
441
|
+
if (nextMissing === undefined) {
|
|
442
|
+
return contractEmitMissingCheck();
|
|
443
|
+
}
|
|
444
|
+
const nextMissingLabel = nextMissing ? deepbookSwapReviewLifecycleStageLabel(nextMissing) : undefined;
|
|
445
|
+
return {
|
|
446
|
+
id: `deepbook_${nextMissing}_missing`,
|
|
447
|
+
label: nextMissingLabel ?? "Producer stage",
|
|
448
|
+
status: "fail",
|
|
449
|
+
message: `DeepBook account-bound review has not completed ${nextMissingLabel}. This is required before wallet handoff, signing, or execution, and no transaction bytes or signing readiness are available.`,
|
|
450
|
+
source: "adapter"
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function missingProducerStageBlockedResult(adapterLifecycle, checks, fields = {}) {
|
|
454
|
+
const nextMissing = adapterLifecycle.missingStages[0];
|
|
455
|
+
if (nextMissing === undefined) {
|
|
456
|
+
if (!fields.humanReadableReview || !fields.simulation) {
|
|
457
|
+
throw new Error("wallet_review_contract_emit_missing requires human-readable review and simulation public evidence");
|
|
458
|
+
}
|
|
459
|
+
return walletReviewContractEmitMissingResult([...checks, contractEmitMissingCheck()], adapterLifecycle, fields.humanReadableReview, fields.simulation);
|
|
460
|
+
}
|
|
461
|
+
return producerStageMissingReviewResult([...checks, missingProducerStageCheck(adapterLifecycle)], adapterLifecycle, fields.humanReadableReview ? { humanReadableReview: fields.humanReadableReview } : {});
|
|
462
|
+
}
|
|
463
|
+
function ptbVisualizationRenderedCheck() {
|
|
464
|
+
return {
|
|
465
|
+
id: "deepbook_ptb_visualization",
|
|
466
|
+
label: "PTB visualization",
|
|
467
|
+
status: "pass",
|
|
468
|
+
message: "Rendered a Mermaid PTB visualization artifact from the stored local transaction material. Visualization only; it is not wallet authorization, not signing data, not signing readiness, and not execution readiness.",
|
|
469
|
+
source: "adapter"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function ptbVisualizationUnavailableCheck(reason) {
|
|
473
|
+
return {
|
|
474
|
+
id: "deepbook_ptb_visualization_unavailable",
|
|
475
|
+
label: "PTB visualization",
|
|
476
|
+
status: "warning",
|
|
477
|
+
message: `PTB visualization is unavailable for this review: ${reason}. The emitted wallet review contract remains valid; visualization is optional review evidence.`,
|
|
478
|
+
source: "adapter"
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function contractEmittedCheck() {
|
|
482
|
+
return {
|
|
483
|
+
id: "deepbook_wallet_review_contract_emitted",
|
|
484
|
+
label: "Wallet review contract emit",
|
|
485
|
+
status: "pass",
|
|
486
|
+
message: "DeepBook account-bound review assembled and schema-validated a wallet review contract from verified review evidence. The local review page can now request the digest-gated handoff for user-controlled wallet signing; MCP output stays free of signing data.",
|
|
487
|
+
source: "adapter"
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function contractEmitDeclinedCheck(reason) {
|
|
491
|
+
return {
|
|
492
|
+
id: "deepbook_wallet_review_contract_emit_missing",
|
|
493
|
+
label: "Wallet review contract emit",
|
|
494
|
+
status: "fail",
|
|
495
|
+
message: `DeepBook account-bound review could not assemble the wallet review contract from the current review evidence: ${reason}. Signing stays blocked.`,
|
|
496
|
+
source: "adapter"
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
function contractEmitMissingCheck() {
|
|
500
|
+
return {
|
|
501
|
+
id: "deepbook_wallet_review_contract_emit_missing",
|
|
502
|
+
label: "Wallet review contract emit",
|
|
503
|
+
status: "fail",
|
|
504
|
+
message: "DeepBook account-bound review completed review-time simulation, but this review did not assemble a wallet review contract, so signing stays blocked for this session.",
|
|
505
|
+
source: "adapter"
|
|
506
|
+
};
|
|
507
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DEEPBOOK_SWAP_ACTION_KIND, DEEPBOOK_SWAP_ADAPTER_ID, DEEPBOOK_SWAP_PROTOCOL } from "./deepbookSwapIntent.js";
|
|
3
|
+
export const DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGE_CATALOG_ID = "deepbook_swap_review_v1";
|
|
4
|
+
export const DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGES = [
|
|
5
|
+
"intent_normalized",
|
|
6
|
+
"pool_resolved",
|
|
7
|
+
"quote_evidence_fetched",
|
|
8
|
+
"quote_policy_derived",
|
|
9
|
+
"transaction_material_build_or_verify",
|
|
10
|
+
"digest_commitment",
|
|
11
|
+
"object_ownership",
|
|
12
|
+
"human_readable_review",
|
|
13
|
+
"review_time_simulation"
|
|
14
|
+
];
|
|
15
|
+
export const deepbookSwapReviewLifecycleStageSchema = z.enum(DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGES);
|
|
16
|
+
export const deepbookSwapReviewLifecycleSchema = z.object({
|
|
17
|
+
stageCatalogId: z.literal(DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGE_CATALOG_ID),
|
|
18
|
+
adapterId: z.literal(DEEPBOOK_SWAP_ADAPTER_ID),
|
|
19
|
+
protocol: z.literal(DEEPBOOK_SWAP_PROTOCOL),
|
|
20
|
+
actionKind: z.literal(DEEPBOOK_SWAP_ACTION_KIND),
|
|
21
|
+
completedStages: z.array(deepbookSwapReviewLifecycleStageSchema),
|
|
22
|
+
missingStages: z.array(deepbookSwapReviewLifecycleStageSchema)
|
|
23
|
+
}).strict().superRefine((lifecycle, ctx) => {
|
|
24
|
+
const expectedCompleted = DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGES.slice(0, lifecycle.completedStages.length);
|
|
25
|
+
const expectedMissing = DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGES.slice(lifecycle.completedStages.length);
|
|
26
|
+
if (!sameStringArray(lifecycle.completedStages, expectedCompleted)) {
|
|
27
|
+
ctx.addIssue({
|
|
28
|
+
code: "custom",
|
|
29
|
+
path: ["completedStages"],
|
|
30
|
+
message: "completedStages must be the canonical DeepBook swap review lifecycle prefix"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (!sameStringArray(lifecycle.missingStages, expectedMissing)) {
|
|
34
|
+
ctx.addIssue({
|
|
35
|
+
code: "custom",
|
|
36
|
+
path: ["missingStages"],
|
|
37
|
+
message: "missingStages must be the canonical DeepBook swap review lifecycle remainder"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
const DEEPBOOK_SWAP_REVIEW_STAGE_LABELS = {
|
|
42
|
+
intent_normalized: "Intent normalized",
|
|
43
|
+
pool_resolved: "DeepBook pool resolved",
|
|
44
|
+
quote_evidence_fetched: "DeepBook quote evidence fetched",
|
|
45
|
+
quote_policy_derived: "Quote policy derived",
|
|
46
|
+
transaction_material_build_or_verify: "Transaction material build or verify",
|
|
47
|
+
digest_commitment: "Digest commitment",
|
|
48
|
+
object_ownership: "Object ownership",
|
|
49
|
+
human_readable_review: "Human-readable review",
|
|
50
|
+
review_time_simulation: "Review-time simulation"
|
|
51
|
+
};
|
|
52
|
+
export function newDeepbookSwapReviewLifecycle(plan) {
|
|
53
|
+
const completedStages = [];
|
|
54
|
+
return {
|
|
55
|
+
complete(stage) {
|
|
56
|
+
const expectedStage = DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGES[completedStages.length];
|
|
57
|
+
if (stage !== expectedStage) {
|
|
58
|
+
throw new Error(`DeepBook swap review lifecycle stage '${stage}' cannot complete before '${expectedStage ?? "none"}'.`);
|
|
59
|
+
}
|
|
60
|
+
completedStages.push(stage);
|
|
61
|
+
},
|
|
62
|
+
snapshot() {
|
|
63
|
+
return deepbookSwapReviewLifecycleSchema.parse({
|
|
64
|
+
stageCatalogId: DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGE_CATALOG_ID,
|
|
65
|
+
adapterId: plan.adapterId,
|
|
66
|
+
protocol: plan.protocol,
|
|
67
|
+
actionKind: plan.actionKind,
|
|
68
|
+
completedStages: [...completedStages],
|
|
69
|
+
missingStages: DEEPBOOK_SWAP_REVIEW_LIFECYCLE_STAGES.filter((stage) => !completedStages.includes(stage))
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function deepbookSwapReviewLifecycleStageLabel(stage) {
|
|
75
|
+
return DEEPBOOK_SWAP_REVIEW_STAGE_LABELS[stage];
|
|
76
|
+
}
|
|
77
|
+
export function validateDeepbookSwapReviewLifecycle(lifecycle) {
|
|
78
|
+
deepbookSwapReviewLifecycleSchema.parse(lifecycle);
|
|
79
|
+
}
|
|
80
|
+
function sameStringArray(left, right) {
|
|
81
|
+
if (left.length !== right.length) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return left.every((value, index) => value === right[index]);
|
|
85
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { accountBoundReviewRequiredCheck, signingViaLocalReviewOnlyCheck } from "../../core/review/reviewChecks.js";
|
|
4
|
+
import { canonicalDeepbookSymbol } from "../../core/read/deepbookRegistry.js";
|
|
5
|
+
export const DEEPBOOK_SWAP_ACTION_KIND = "swap";
|
|
6
|
+
export const DEEPBOOK_SWAP_ADAPTER_ID = "deepbook-swap";
|
|
7
|
+
export const DEEPBOOK_SWAP_PROTOCOL = "DeepBookV3";
|
|
8
|
+
export const deepbookSwapIntentInputSchema = z.object({
|
|
9
|
+
type: z.literal(DEEPBOOK_SWAP_ACTION_KIND),
|
|
10
|
+
from: z.object({
|
|
11
|
+
symbol: z.string().min(1),
|
|
12
|
+
amount: z.string().min(1)
|
|
13
|
+
}),
|
|
14
|
+
to: z.object({
|
|
15
|
+
symbol: z.string().min(1)
|
|
16
|
+
}),
|
|
17
|
+
maxSlippageBps: z.number().int().min(1).max(1000)
|
|
18
|
+
});
|
|
19
|
+
export const deepbookSwapRequestedIntentSchema = z.object({
|
|
20
|
+
type: z.literal(DEEPBOOK_SWAP_ACTION_KIND),
|
|
21
|
+
from: z.object({
|
|
22
|
+
symbol: z.string().min(1),
|
|
23
|
+
amountDisplay: z.string().min(1)
|
|
24
|
+
}),
|
|
25
|
+
to: z.object({
|
|
26
|
+
symbol: z.string().min(1)
|
|
27
|
+
}),
|
|
28
|
+
maxSlippageBps: z.number().int().min(1).max(1000)
|
|
29
|
+
});
|
|
30
|
+
export function createDeepbookSwapActionPlan(intent, now) {
|
|
31
|
+
const requestedIntent = normalizeDeepbookSwapRequestedIntent(intent);
|
|
32
|
+
return {
|
|
33
|
+
id: `plan_${randomUUID()}`,
|
|
34
|
+
actionKind: DEEPBOOK_SWAP_ACTION_KIND,
|
|
35
|
+
adapterId: DEEPBOOK_SWAP_ADAPTER_ID,
|
|
36
|
+
protocol: DEEPBOOK_SWAP_PROTOCOL,
|
|
37
|
+
title: `Review ${requestedIntent.from.amountDisplay} ${requestedIntent.from.symbol} to ${requestedIntent.to.symbol}`,
|
|
38
|
+
summary: "Account-bound DeepBook review evidence is computed after a wallet account is connected. The review URL displays the proposal and local review evidence; when every evidence stage completes, the review page offers user-controlled wallet signing through a digest-gated handoff. This MCP response contains no sign action, signing data, transaction bytes, or signing readiness.",
|
|
39
|
+
assetFlowPreview: {
|
|
40
|
+
outgoing: [
|
|
41
|
+
{ symbol: requestedIntent.from.symbol, amount: requestedIntent.from.amountDisplay, amountKind: "display_intent" }
|
|
42
|
+
],
|
|
43
|
+
expectedIncoming: [
|
|
44
|
+
{ symbol: requestedIntent.to.symbol, amount: "unknown", amountKind: "display_intent", approx: true }
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
adapterData: {
|
|
48
|
+
requestedIntent
|
|
49
|
+
},
|
|
50
|
+
createdAt: now.toISOString(),
|
|
51
|
+
preliminaryChecks: [accountBoundReviewRequiredCheck(), signingViaLocalReviewOnlyCheck()]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export const deepbookSwapActionPlanDataSchema = z.object({
|
|
55
|
+
requestedIntent: deepbookSwapRequestedIntentSchema
|
|
56
|
+
});
|
|
57
|
+
export const deepbookSwapActionPlanIdentitySchema = z.object({
|
|
58
|
+
actionKind: z.literal(DEEPBOOK_SWAP_ACTION_KIND),
|
|
59
|
+
adapterId: z.literal(DEEPBOOK_SWAP_ADAPTER_ID),
|
|
60
|
+
protocol: z.literal(DEEPBOOK_SWAP_PROTOCOL)
|
|
61
|
+
}).passthrough();
|
|
62
|
+
export function isDeepbookSwapActionPlanIdentity(plan) {
|
|
63
|
+
return deepbookSwapActionPlanIdentitySchema.safeParse(plan).success;
|
|
64
|
+
}
|
|
65
|
+
export function normalizeDeepbookSwapRequestedIntent(input) {
|
|
66
|
+
const fromSymbol = canonicalDeepbookSymbol(input.from.symbol) ?? input.from.symbol.trim();
|
|
67
|
+
const toSymbol = canonicalDeepbookSymbol(input.to.symbol) ?? input.to.symbol.trim();
|
|
68
|
+
return {
|
|
69
|
+
type: DEEPBOOK_SWAP_ACTION_KIND,
|
|
70
|
+
from: {
|
|
71
|
+
symbol: fromSymbol,
|
|
72
|
+
amountDisplay: input.from.amount
|
|
73
|
+
},
|
|
74
|
+
to: {
|
|
75
|
+
symbol: toSymbol
|
|
76
|
+
},
|
|
77
|
+
maxSlippageBps: input.maxSlippageBps
|
|
78
|
+
};
|
|
79
|
+
}
|