@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,97 @@
|
|
|
1
|
+
import { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
2
|
+
import { SuiGraphQLClient } from "@mysten/sui/graphql";
|
|
3
|
+
import { MAX_SUI_GRAPHQL_URL_LENGTH, MAX_SUI_GRPC_URL_LENGTH, SuiEndpointError, parseGraphqlUrl, parseGrpcUrl } from "../core/suiEndpoint.js";
|
|
4
|
+
export { MAX_SUI_GRAPHQL_URL_LENGTH, MAX_SUI_GRPC_URL_LENGTH, SuiEndpointError, parseGraphqlUrl, parseGrpcUrl };
|
|
5
|
+
export const SUI_MAINNET_CHAIN_IDENTIFIER = "4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S";
|
|
6
|
+
export const DEFAULT_SUI_GRPC_URL = "https://fullnode.mainnet.sui.io:443";
|
|
7
|
+
export const DEFAULT_SUI_GRAPHQL_URL = "https://graphql.mainnet.sui.io/graphql";
|
|
8
|
+
export const DEFAULT_SUI_GRPC_ENDPOINT_VERIFY_TIMEOUT_MS = 10_000;
|
|
9
|
+
export const DEFAULT_SUI_GRAPHQL_ENDPOINT_VERIFY_TIMEOUT_MS = 10_000;
|
|
10
|
+
export async function verifyMainnetGrpcEndpoint(input) {
|
|
11
|
+
const url = parseGrpcUrl(input.url);
|
|
12
|
+
const expectedChainIdentifier = input.expectedChainIdentifier ?? SUI_MAINNET_CHAIN_IDENTIFIER;
|
|
13
|
+
const timeoutMs = input.timeoutMs ?? DEFAULT_SUI_GRPC_ENDPOINT_VERIFY_TIMEOUT_MS;
|
|
14
|
+
const client = new SuiGrpcClient({
|
|
15
|
+
baseUrl: url,
|
|
16
|
+
network: "mainnet"
|
|
17
|
+
});
|
|
18
|
+
let timeout;
|
|
19
|
+
try {
|
|
20
|
+
const chainIdentifier = await Promise.race([
|
|
21
|
+
client.core.getChainIdentifier().then((result) => result.chainIdentifier),
|
|
22
|
+
new Promise((_, reject) => {
|
|
23
|
+
timeout = setTimeout(() => {
|
|
24
|
+
reject(new SuiEndpointError("endpoint_timeout", `Sui gRPC endpoint verification timed out after ${timeoutMs}ms`, { timeoutMs, url }));
|
|
25
|
+
}, timeoutMs);
|
|
26
|
+
})
|
|
27
|
+
]);
|
|
28
|
+
if (chainIdentifier !== expectedChainIdentifier) {
|
|
29
|
+
throw new SuiEndpointError("chain_identifier_mismatch", `Sui chain identifier mismatch: expected mainnet, got ${chainIdentifier}`, { expectedChainIdentifier, chainIdentifier, url });
|
|
30
|
+
}
|
|
31
|
+
return { client, chainIdentifier };
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof SuiEndpointError) {
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
throw new SuiEndpointError("endpoint_unreachable", "Could not verify Sui mainnet gRPC endpoint", {
|
|
38
|
+
error: error instanceof Error ? error.message : String(error),
|
|
39
|
+
url
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
if (timeout) {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function verifyMainnetGraphqlEndpoint(input) {
|
|
49
|
+
const url = parseGraphqlUrl(input.url);
|
|
50
|
+
const expectedChainIdentifier = input.expectedChainIdentifier ?? SUI_MAINNET_CHAIN_IDENTIFIER;
|
|
51
|
+
const timeoutMs = input.timeoutMs ?? DEFAULT_SUI_GRAPHQL_ENDPOINT_VERIFY_TIMEOUT_MS;
|
|
52
|
+
const client = new SuiGraphQLClient({
|
|
53
|
+
url,
|
|
54
|
+
network: "mainnet",
|
|
55
|
+
...(input.fetch ? { fetch: input.fetch } : {})
|
|
56
|
+
});
|
|
57
|
+
let timeout;
|
|
58
|
+
try {
|
|
59
|
+
const chainIdentifier = await Promise.race([
|
|
60
|
+
client.query({
|
|
61
|
+
query: "query SayUrIntentChainIdentifier { chainIdentifier }",
|
|
62
|
+
variables: {}
|
|
63
|
+
}).then((result) => {
|
|
64
|
+
const value = result.data?.chainIdentifier;
|
|
65
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
66
|
+
throw new SuiEndpointError("endpoint_unreachable", "Sui GraphQL endpoint did not return chainIdentifier", {
|
|
67
|
+
url
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}),
|
|
72
|
+
new Promise((_, reject) => {
|
|
73
|
+
timeout = setTimeout(() => {
|
|
74
|
+
reject(new SuiEndpointError("endpoint_timeout", `Sui GraphQL endpoint verification timed out after ${timeoutMs}ms`, { timeoutMs, url }));
|
|
75
|
+
}, timeoutMs);
|
|
76
|
+
})
|
|
77
|
+
]);
|
|
78
|
+
if (chainIdentifier !== expectedChainIdentifier) {
|
|
79
|
+
throw new SuiEndpointError("chain_identifier_mismatch", `Sui chain identifier mismatch: expected mainnet, got ${chainIdentifier}`, { expectedChainIdentifier, chainIdentifier, url });
|
|
80
|
+
}
|
|
81
|
+
return { client, chainIdentifier };
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error instanceof SuiEndpointError) {
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
throw new SuiEndpointError("endpoint_unreachable", "Could not verify Sui mainnet GraphQL endpoint", {
|
|
88
|
+
error: error instanceof Error ? error.message : String(error),
|
|
89
|
+
url
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
if (timeout) {
|
|
94
|
+
clearTimeout(timeout);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { TransactionActivitySourceError } from "../core/activity/transactionActivityTypes.js";
|
|
2
|
+
import { parseSuiAddress } from "../core/suiAddress.js";
|
|
3
|
+
export function transactionFactFromNode(node) {
|
|
4
|
+
if (typeof node.digest !== "string" || node.digest.length === 0) {
|
|
5
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL transaction was missing digest");
|
|
6
|
+
}
|
|
7
|
+
const sender = typeof node.sender?.address === "string" ? parseSuiAddress(node.sender.address) : undefined;
|
|
8
|
+
const checkpointValue = node.effects?.checkpoint?.sequenceNumber;
|
|
9
|
+
const checkpoint = typeof checkpointValue === "number" || typeof checkpointValue === "string"
|
|
10
|
+
? String(checkpointValue)
|
|
11
|
+
: undefined;
|
|
12
|
+
const timestamp = typeof node.effects?.timestamp === "string" ? normalizeGraphqlTimestamp(node.effects.timestamp) : undefined;
|
|
13
|
+
return {
|
|
14
|
+
digest: node.digest,
|
|
15
|
+
sender,
|
|
16
|
+
checkpoint,
|
|
17
|
+
timestamp,
|
|
18
|
+
status: executionStatusFromGraphql(node.effects?.status),
|
|
19
|
+
details: transactionDetailsFromNode(node)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function transactionDetailsFromNode(node) {
|
|
23
|
+
return {
|
|
24
|
+
transactionKind: stringValue(node.kind?.__typename),
|
|
25
|
+
moveCalls: moveCallFactsFromKind(node.kind),
|
|
26
|
+
balanceChanges: balanceChangeFactsFromEffects(node.effects),
|
|
27
|
+
objectChanges: objectChangeFactsFromEffects(node.effects),
|
|
28
|
+
events: eventFactsFromEffects(node.effects),
|
|
29
|
+
gas: gasFactFromEffects(node.effects),
|
|
30
|
+
executionError: executionErrorFactFromEffects(node.effects),
|
|
31
|
+
truncation: {
|
|
32
|
+
moveCalls: node.kind?.commands?.pageInfo?.hasNextPage === true,
|
|
33
|
+
balanceChanges: node.effects?.balanceChanges?.pageInfo?.hasNextPage === true,
|
|
34
|
+
objectChanges: node.effects?.objectChanges?.pageInfo?.hasNextPage === true,
|
|
35
|
+
events: node.effects?.events?.pageInfo?.hasNextPage === true
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function moveCallFactsFromKind(kind) {
|
|
40
|
+
const nodes = Array.isArray(kind?.commands?.nodes) ? kind.commands.nodes : [];
|
|
41
|
+
return nodes.flatMap((command, commandIndex) => {
|
|
42
|
+
if (command.__typename !== "MoveCallCommand" || !command.function) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const target = stringValue(command.function.fullyQualifiedName);
|
|
46
|
+
const packageId = stringValue(command.function.module?.package?.address);
|
|
47
|
+
const module = stringValue(command.function.module?.name);
|
|
48
|
+
const fn = stringValue(command.function.name);
|
|
49
|
+
if (!target || !packageId || !module || !fn) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
return [{
|
|
53
|
+
commandIndex,
|
|
54
|
+
package: packageId,
|
|
55
|
+
module,
|
|
56
|
+
function: fn,
|
|
57
|
+
target
|
|
58
|
+
}];
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function balanceChangeFactsFromEffects(effects) {
|
|
62
|
+
const nodes = Array.isArray(effects?.balanceChanges?.nodes) ? effects.balanceChanges.nodes : [];
|
|
63
|
+
return nodes.flatMap((change, index) => {
|
|
64
|
+
const amountRaw = unsignedOrSignedIntegerString(change.amount);
|
|
65
|
+
const coinType = stringValue(change.coinType?.repr);
|
|
66
|
+
if (!amountRaw || !coinType) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return [{
|
|
70
|
+
index,
|
|
71
|
+
owner: stringValue(change.owner?.address),
|
|
72
|
+
coinType,
|
|
73
|
+
amountRaw,
|
|
74
|
+
direction: balanceChangeDirection(amountRaw)
|
|
75
|
+
}];
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function objectChangeFactsFromEffects(effects) {
|
|
79
|
+
const nodes = Array.isArray(effects?.objectChanges?.nodes) ? effects.objectChanges.nodes : [];
|
|
80
|
+
return nodes.flatMap((change, index) => {
|
|
81
|
+
const objectId = stringValue(change.address);
|
|
82
|
+
if (!objectId) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
return [{
|
|
86
|
+
index,
|
|
87
|
+
objectId,
|
|
88
|
+
changeKind: objectChangeKind(change),
|
|
89
|
+
inputType: stringValue(change.inputState?.asMoveObject?.contents?.type?.repr),
|
|
90
|
+
outputType: stringValue(change.outputState?.asMoveObject?.contents?.type?.repr)
|
|
91
|
+
}];
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function eventFactsFromEffects(effects) {
|
|
95
|
+
const nodes = Array.isArray(effects?.events?.nodes) ? effects.events.nodes : [];
|
|
96
|
+
return nodes.flatMap((event) => {
|
|
97
|
+
const sequenceNumber = unsignedOrSignedIntegerString(event.sequenceNumber);
|
|
98
|
+
if (!sequenceNumber) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return [{
|
|
102
|
+
sequenceNumber,
|
|
103
|
+
sender: stringValue(event.sender?.address),
|
|
104
|
+
package: stringValue(event.transactionModule?.package?.address),
|
|
105
|
+
module: stringValue(event.transactionModule?.name),
|
|
106
|
+
eventType: stringValue(event.contents?.type?.repr)
|
|
107
|
+
}];
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function gasFactFromEffects(effects) {
|
|
111
|
+
const summary = effects?.gasEffects?.gasSummary;
|
|
112
|
+
if (!summary) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const computationCostRaw = unsignedOrSignedIntegerString(summary.computationCost);
|
|
116
|
+
const storageCostRaw = unsignedOrSignedIntegerString(summary.storageCost);
|
|
117
|
+
const storageRebateRaw = unsignedOrSignedIntegerString(summary.storageRebate);
|
|
118
|
+
const nonRefundableStorageFeeRaw = unsignedOrSignedIntegerString(summary.nonRefundableStorageFee);
|
|
119
|
+
return {
|
|
120
|
+
gasObjectId: stringValue(effects?.gasEffects?.gasObject?.address),
|
|
121
|
+
computationCostRaw,
|
|
122
|
+
storageCostRaw,
|
|
123
|
+
storageRebateRaw,
|
|
124
|
+
nonRefundableStorageFeeRaw,
|
|
125
|
+
netGasCostRaw: netGasCostRaw({ computationCostRaw, storageCostRaw, storageRebateRaw })
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function executionErrorFactFromEffects(effects) {
|
|
129
|
+
const error = effects?.executionError;
|
|
130
|
+
const message = stringValue(error?.message);
|
|
131
|
+
if (!error || !message) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
const fn = error.function;
|
|
135
|
+
const module = error.module ?? fn?.module;
|
|
136
|
+
return {
|
|
137
|
+
message,
|
|
138
|
+
abortCodeRaw: unsignedOrSignedIntegerString(error.abortCode),
|
|
139
|
+
identifier: stringValue(error.identifier),
|
|
140
|
+
instructionOffset: integerNumber(error.instructionOffset),
|
|
141
|
+
sourceLineNumber: integerNumber(error.sourceLineNumber),
|
|
142
|
+
package: stringValue(module?.package?.address),
|
|
143
|
+
module: stringValue(module?.name),
|
|
144
|
+
function: stringValue(fn?.name)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function objectChangeKind(change) {
|
|
148
|
+
if (change.idCreated === true)
|
|
149
|
+
return "created";
|
|
150
|
+
if (change.idDeleted === true)
|
|
151
|
+
return "deleted";
|
|
152
|
+
return "mutated";
|
|
153
|
+
}
|
|
154
|
+
function balanceChangeDirection(amountRaw) {
|
|
155
|
+
if (amountRaw.startsWith("-"))
|
|
156
|
+
return "decrease";
|
|
157
|
+
if (amountRaw === "0")
|
|
158
|
+
return "zero";
|
|
159
|
+
return "increase";
|
|
160
|
+
}
|
|
161
|
+
function netGasCostRaw(input) {
|
|
162
|
+
if (input.computationCostRaw === undefined || input.storageCostRaw === undefined || input.storageRebateRaw === undefined) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
return String(BigInt(input.computationCostRaw) + BigInt(input.storageCostRaw) - BigInt(input.storageRebateRaw));
|
|
166
|
+
}
|
|
167
|
+
function stringValue(value) {
|
|
168
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
169
|
+
}
|
|
170
|
+
function unsignedOrSignedIntegerString(value) {
|
|
171
|
+
if (typeof value === "number" && Number.isInteger(value)) {
|
|
172
|
+
return String(value);
|
|
173
|
+
}
|
|
174
|
+
if (typeof value === "bigint") {
|
|
175
|
+
return String(value);
|
|
176
|
+
}
|
|
177
|
+
if (typeof value === "string" && /^-?\d+$/.test(value)) {
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
function integerNumber(value) {
|
|
183
|
+
return typeof value === "number" && Number.isInteger(value) ? value : undefined;
|
|
184
|
+
}
|
|
185
|
+
function executionStatusFromGraphql(value) {
|
|
186
|
+
if (value === "SUCCESS")
|
|
187
|
+
return "success";
|
|
188
|
+
if (value === "FAILURE")
|
|
189
|
+
return "failure";
|
|
190
|
+
// Null status can occur before checkpointed effects are available; unmapped
|
|
191
|
+
// provider enum values stay unknown until explicitly mapped.
|
|
192
|
+
return "unknown";
|
|
193
|
+
}
|
|
194
|
+
function normalizeGraphqlTimestamp(value) {
|
|
195
|
+
const parsed = new Date(value);
|
|
196
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
197
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL transaction timestamp was invalid");
|
|
198
|
+
}
|
|
199
|
+
return parsed.toISOString();
|
|
200
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { TransactionActivitySourceError } from "../core/activity/transactionActivityTypes.js";
|
|
2
|
+
const LOCAL_TRANSACTION_DETAIL_LIMITS = {
|
|
3
|
+
moveCallLimit: 50,
|
|
4
|
+
balanceChangeLimit: 100,
|
|
5
|
+
objectChangeLimit: 100,
|
|
6
|
+
eventLimit: 50
|
|
7
|
+
};
|
|
8
|
+
const TRANSACTION_DETAIL_FRAGMENT = `
|
|
9
|
+
fragment SayUrIntentTransactionDetail on Transaction {
|
|
10
|
+
digest
|
|
11
|
+
sender { address }
|
|
12
|
+
kind {
|
|
13
|
+
__typename
|
|
14
|
+
... on ProgrammableTransaction {
|
|
15
|
+
commands(first: $moveCallLimit) {
|
|
16
|
+
nodes {
|
|
17
|
+
__typename
|
|
18
|
+
... on MoveCallCommand {
|
|
19
|
+
function {
|
|
20
|
+
fullyQualifiedName
|
|
21
|
+
name
|
|
22
|
+
module {
|
|
23
|
+
name
|
|
24
|
+
package { address }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
pageInfo { hasNextPage }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
effects {
|
|
34
|
+
status
|
|
35
|
+
timestamp
|
|
36
|
+
checkpoint { sequenceNumber }
|
|
37
|
+
executionError {
|
|
38
|
+
message
|
|
39
|
+
abortCode
|
|
40
|
+
identifier
|
|
41
|
+
instructionOffset
|
|
42
|
+
sourceLineNumber
|
|
43
|
+
module {
|
|
44
|
+
name
|
|
45
|
+
package { address }
|
|
46
|
+
}
|
|
47
|
+
function {
|
|
48
|
+
name
|
|
49
|
+
module {
|
|
50
|
+
name
|
|
51
|
+
package { address }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
gasEffects {
|
|
56
|
+
gasObject { address }
|
|
57
|
+
gasSummary {
|
|
58
|
+
computationCost
|
|
59
|
+
storageCost
|
|
60
|
+
storageRebate
|
|
61
|
+
nonRefundableStorageFee
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
balanceChanges(first: $balanceChangeLimit) {
|
|
65
|
+
nodes {
|
|
66
|
+
amount
|
|
67
|
+
coinType { repr }
|
|
68
|
+
owner { address }
|
|
69
|
+
}
|
|
70
|
+
pageInfo { hasNextPage }
|
|
71
|
+
}
|
|
72
|
+
objectChanges(first: $objectChangeLimit) {
|
|
73
|
+
nodes {
|
|
74
|
+
address
|
|
75
|
+
idCreated
|
|
76
|
+
idDeleted
|
|
77
|
+
inputState { asMoveObject { contents { type { repr } } } }
|
|
78
|
+
outputState { asMoveObject { contents { type { repr } } } }
|
|
79
|
+
}
|
|
80
|
+
pageInfo { hasNextPage }
|
|
81
|
+
}
|
|
82
|
+
events(first: $eventLimit) {
|
|
83
|
+
nodes {
|
|
84
|
+
sequenceNumber
|
|
85
|
+
sender { address }
|
|
86
|
+
transactionModule {
|
|
87
|
+
name
|
|
88
|
+
package { address }
|
|
89
|
+
}
|
|
90
|
+
contents { type { repr } }
|
|
91
|
+
}
|
|
92
|
+
pageInfo { hasNextPage }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
export async function queryInspectTransaction(client, input) {
|
|
98
|
+
return queryGraphql(client, {
|
|
99
|
+
query: `
|
|
100
|
+
query SayUrIntentInspectTransaction(
|
|
101
|
+
$digest: String!
|
|
102
|
+
$moveCallLimit: Int!
|
|
103
|
+
$balanceChangeLimit: Int!
|
|
104
|
+
$objectChangeLimit: Int!
|
|
105
|
+
$eventLimit: Int!
|
|
106
|
+
) {
|
|
107
|
+
transaction(digest: $digest) {
|
|
108
|
+
...SayUrIntentTransactionDetail
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
${TRANSACTION_DETAIL_FRAGMENT}
|
|
112
|
+
`,
|
|
113
|
+
variables: transactionDetailVariables({ digest: input.digest }, input.limits)
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
export async function queryScanAccountTransactions(client, input) {
|
|
117
|
+
return queryGraphql(client, {
|
|
118
|
+
query: `
|
|
119
|
+
query SayUrIntentScanAccountActivity(
|
|
120
|
+
$last: Int!
|
|
121
|
+
$before: String
|
|
122
|
+
$filter: TransactionFilter!
|
|
123
|
+
$moveCallLimit: Int!
|
|
124
|
+
$balanceChangeLimit: Int!
|
|
125
|
+
$objectChangeLimit: Int!
|
|
126
|
+
$eventLimit: Int!
|
|
127
|
+
) {
|
|
128
|
+
transactions(last: $last, before: $before, filter: $filter) {
|
|
129
|
+
nodes {
|
|
130
|
+
...SayUrIntentTransactionDetail
|
|
131
|
+
}
|
|
132
|
+
pageInfo {
|
|
133
|
+
hasPreviousPage
|
|
134
|
+
startCursor
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
${TRANSACTION_DETAIL_FRAGMENT}
|
|
139
|
+
`,
|
|
140
|
+
variables: {
|
|
141
|
+
...transactionDetailVariables({}, input.limits),
|
|
142
|
+
last: input.last,
|
|
143
|
+
before: input.before ?? null,
|
|
144
|
+
filter: input.filter
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
export function cursorFromPageInfo(pageInfo) {
|
|
149
|
+
if (pageInfo.hasPreviousPage !== true) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
if (typeof pageInfo.startCursor !== "string" || pageInfo.startCursor.length === 0) {
|
|
153
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL transaction scan reported another page without a continuation cursor");
|
|
154
|
+
}
|
|
155
|
+
return pageInfo.startCursor;
|
|
156
|
+
}
|
|
157
|
+
export async function loadGraphqlServiceLimits(client) {
|
|
158
|
+
const result = await queryGraphql(client, {
|
|
159
|
+
query: `
|
|
160
|
+
query SayUrIntentGraphqlServiceLimits {
|
|
161
|
+
serviceConfig {
|
|
162
|
+
transactionPageLimit: maxPageSize(type: "Query", field: "transactions")
|
|
163
|
+
moveCallLimit: maxPageSize(type: "ProgrammableTransaction", field: "commands")
|
|
164
|
+
balanceChangeLimit: maxPageSize(type: "TransactionEffects", field: "balanceChanges")
|
|
165
|
+
objectChangeLimit: maxPageSize(type: "TransactionEffects", field: "objectChanges")
|
|
166
|
+
eventLimit: maxPageSize(type: "TransactionEffects", field: "events")
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
`
|
|
170
|
+
});
|
|
171
|
+
const config = result.serviceConfig;
|
|
172
|
+
if (!config) {
|
|
173
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL serviceConfig was unavailable");
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
transactionPageLimit: positiveIntFromServiceConfig(config.transactionPageLimit, "Query.transactions"),
|
|
177
|
+
moveCallLimit: Math.min(positiveIntFromServiceConfig(config.moveCallLimit, "ProgrammableTransaction.commands"), LOCAL_TRANSACTION_DETAIL_LIMITS.moveCallLimit),
|
|
178
|
+
balanceChangeLimit: Math.min(positiveIntFromServiceConfig(config.balanceChangeLimit, "TransactionEffects.balanceChanges"), LOCAL_TRANSACTION_DETAIL_LIMITS.balanceChangeLimit),
|
|
179
|
+
objectChangeLimit: Math.min(positiveIntFromServiceConfig(config.objectChangeLimit, "TransactionEffects.objectChanges"), LOCAL_TRANSACTION_DETAIL_LIMITS.objectChangeLimit),
|
|
180
|
+
eventLimit: Math.min(positiveIntFromServiceConfig(config.eventLimit, "TransactionEffects.events"), LOCAL_TRANSACTION_DETAIL_LIMITS.eventLimit)
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
export function graphQlAfterCheckpointForInclusiveLowerBound(value) {
|
|
184
|
+
const checkpoint = BigInt(value);
|
|
185
|
+
return checkpoint === 0n ? undefined : Number(checkpoint - 1n);
|
|
186
|
+
}
|
|
187
|
+
export function graphQlBeforeCheckpointForInclusiveUpperBound(value) {
|
|
188
|
+
const checkpoint = BigInt(value);
|
|
189
|
+
return checkpoint >= BigInt(Number.MAX_SAFE_INTEGER) ? undefined : Number(checkpoint + 1n);
|
|
190
|
+
}
|
|
191
|
+
function transactionDetailVariables(extra, limits) {
|
|
192
|
+
return {
|
|
193
|
+
...extra,
|
|
194
|
+
moveCallLimit: limits.moveCallLimit,
|
|
195
|
+
balanceChangeLimit: limits.balanceChangeLimit,
|
|
196
|
+
objectChangeLimit: limits.objectChangeLimit,
|
|
197
|
+
eventLimit: limits.eventLimit
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function positiveIntFromServiceConfig(value, field) {
|
|
201
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
|
|
202
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL serviceConfig returned an invalid page size", {
|
|
203
|
+
field,
|
|
204
|
+
value
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
209
|
+
async function queryGraphql(client, options) {
|
|
210
|
+
try {
|
|
211
|
+
const result = await client.query({ query: options.query, variables: options.variables ?? {} });
|
|
212
|
+
if (result.errors && result.errors.length > 0) {
|
|
213
|
+
const message = result.errors.map((error) => error.message).join("; ");
|
|
214
|
+
// The pinned SDK exposes provider GraphQL errors as messages here. Cursor
|
|
215
|
+
// rejection is intentionally detected conservatively and otherwise falls
|
|
216
|
+
// back to provider_error.
|
|
217
|
+
throw new TransactionActivitySourceError(message.toLowerCase().includes("cursor") ? "cursor_invalid" : "provider_error", "Sui GraphQL query returned errors", { message });
|
|
218
|
+
}
|
|
219
|
+
if (!result.data) {
|
|
220
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL query returned no data");
|
|
221
|
+
}
|
|
222
|
+
return result.data;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (error instanceof TransactionActivitySourceError) {
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
229
|
+
throw new TransactionActivitySourceError(message.toLowerCase().includes("cursor") ? "cursor_invalid" : "provider_error", "Sui GraphQL query failed", { message });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { TransactionActivitySourceError } from "../core/activity/transactionActivityTypes.js";
|
|
2
|
+
import { SuiEndpointError, parseGraphqlUrl } from "../core/suiEndpoint.js";
|
|
3
|
+
import { DEFAULT_SUI_GRAPHQL_ENDPOINT_VERIFY_TIMEOUT_MS, SUI_MAINNET_CHAIN_IDENTIFIER, verifyMainnetGraphqlEndpoint } from "./suiEndpoint.js";
|
|
4
|
+
import { transactionFactFromNode } from "./suiTransactionGraphqlMapping.js";
|
|
5
|
+
import { cursorFromPageInfo, graphQlAfterCheckpointForInclusiveLowerBound, graphQlBeforeCheckpointForInclusiveUpperBound, loadGraphqlServiceLimits, queryInspectTransaction, queryScanAccountTransactions } from "./suiTransactionGraphqlQueries.js";
|
|
6
|
+
export class GraphqlSuiTransactionActivitySource {
|
|
7
|
+
url;
|
|
8
|
+
expectedChainIdentifier;
|
|
9
|
+
fetch;
|
|
10
|
+
verifyTimeoutMs;
|
|
11
|
+
verified;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.url = parseGraphqlUrl(options.url);
|
|
14
|
+
this.expectedChainIdentifier = options.expectedChainIdentifier ?? SUI_MAINNET_CHAIN_IDENTIFIER;
|
|
15
|
+
this.fetch = options.fetch;
|
|
16
|
+
this.verifyTimeoutMs = options.verifyTimeoutMs ?? DEFAULT_SUI_GRAPHQL_ENDPOINT_VERIFY_TIMEOUT_MS;
|
|
17
|
+
}
|
|
18
|
+
async verifyMainnet() {
|
|
19
|
+
if (!this.verified) {
|
|
20
|
+
const verified = await verifyMainnetGraphqlEndpoint({
|
|
21
|
+
url: this.url,
|
|
22
|
+
expectedChainIdentifier: this.expectedChainIdentifier,
|
|
23
|
+
timeoutMs: this.verifyTimeoutMs,
|
|
24
|
+
...(this.fetch ? { fetch: this.fetch } : {})
|
|
25
|
+
});
|
|
26
|
+
const limits = await loadGraphqlServiceLimits(verified.client);
|
|
27
|
+
this.verified = {
|
|
28
|
+
client: verified.client,
|
|
29
|
+
limits,
|
|
30
|
+
info: {
|
|
31
|
+
transport: "graphql",
|
|
32
|
+
endpointHost: new URL(this.url).host,
|
|
33
|
+
chainIdentifier: verified.chainIdentifier
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return this.verified.info;
|
|
38
|
+
}
|
|
39
|
+
async getTransaction(digest) {
|
|
40
|
+
const { client, limits } = await this.runtime();
|
|
41
|
+
const result = await queryInspectTransaction(client, { digest, limits });
|
|
42
|
+
return result.transaction ? transactionFactFromNode(result.transaction) : null;
|
|
43
|
+
}
|
|
44
|
+
async scanAccount(input) {
|
|
45
|
+
const { client, limits } = await this.runtime();
|
|
46
|
+
const filter = input.relationship === "sent"
|
|
47
|
+
? { sentAddress: input.account }
|
|
48
|
+
: { affectedAddress: input.account };
|
|
49
|
+
if (input.fromCheckpoint !== undefined) {
|
|
50
|
+
const afterCheckpoint = graphQlAfterCheckpointForInclusiveLowerBound(input.fromCheckpoint);
|
|
51
|
+
if (afterCheckpoint !== undefined) {
|
|
52
|
+
filter.afterCheckpoint = afterCheckpoint;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (input.toCheckpoint !== undefined) {
|
|
56
|
+
const beforeCheckpoint = graphQlBeforeCheckpointForInclusiveUpperBound(input.toCheckpoint);
|
|
57
|
+
if (beforeCheckpoint !== undefined) {
|
|
58
|
+
filter.beforeCheckpoint = beforeCheckpoint;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return this.scanTransactions({
|
|
62
|
+
filter,
|
|
63
|
+
limit: input.limit,
|
|
64
|
+
cursor: input.cursor
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async scanFunction(input) {
|
|
68
|
+
const filter = {
|
|
69
|
+
function: input.functionTarget,
|
|
70
|
+
sentAddress: input.sentAddress
|
|
71
|
+
};
|
|
72
|
+
if (input.fromCheckpoint !== undefined) {
|
|
73
|
+
const afterCheckpoint = graphQlAfterCheckpointForInclusiveLowerBound(input.fromCheckpoint);
|
|
74
|
+
if (afterCheckpoint !== undefined) {
|
|
75
|
+
filter.afterCheckpoint = afterCheckpoint;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (input.toCheckpoint !== undefined) {
|
|
79
|
+
const beforeCheckpoint = graphQlBeforeCheckpointForInclusiveUpperBound(input.toCheckpoint);
|
|
80
|
+
if (beforeCheckpoint !== undefined) {
|
|
81
|
+
filter.beforeCheckpoint = beforeCheckpoint;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
return await this.scanTransactions({
|
|
86
|
+
filter,
|
|
87
|
+
limit: input.limit,
|
|
88
|
+
cursor: input.cursor
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (isFunctionFilterGraphqlRejection(error)) {
|
|
93
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL function activity scan rejected a verified filter combination", {
|
|
94
|
+
reason: "function_filter_rejected_by_graphql_validation",
|
|
95
|
+
providerReason: "provider_error",
|
|
96
|
+
message: error.details.message
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async scanTransactions(input) {
|
|
103
|
+
const { client, limits } = await this.runtime();
|
|
104
|
+
const transactions = [];
|
|
105
|
+
let cursor = input.cursor;
|
|
106
|
+
let hasMore = false;
|
|
107
|
+
do {
|
|
108
|
+
const pageLimit = Math.min(input.limit - transactions.length, limits.transactionPageLimit);
|
|
109
|
+
const result = await queryScanAccountTransactions(client, {
|
|
110
|
+
last: pageLimit,
|
|
111
|
+
before: cursor,
|
|
112
|
+
filter: input.filter,
|
|
113
|
+
limits
|
|
114
|
+
});
|
|
115
|
+
const connection = result.transactions;
|
|
116
|
+
if (!connection || !Array.isArray(connection.nodes) || !connection.pageInfo) {
|
|
117
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL transaction scan returned an unexpected shape");
|
|
118
|
+
}
|
|
119
|
+
if (connection.nodes.length === 0 && connection.pageInfo.hasPreviousPage === true) {
|
|
120
|
+
throw new TransactionActivitySourceError("provider_error", "Sui GraphQL transaction scan reported another page without returning transactions");
|
|
121
|
+
}
|
|
122
|
+
transactions.push(...connection.nodes.map(transactionFactFromNode));
|
|
123
|
+
hasMore = connection.pageInfo.hasPreviousPage === true;
|
|
124
|
+
cursor = cursorFromPageInfo(connection.pageInfo);
|
|
125
|
+
} while (hasMore && transactions.length < input.limit);
|
|
126
|
+
return {
|
|
127
|
+
transactions,
|
|
128
|
+
hasMore,
|
|
129
|
+
cursor
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async runtime() {
|
|
133
|
+
await this.verifyMainnet();
|
|
134
|
+
if (!this.verified) {
|
|
135
|
+
throw new SuiEndpointError("endpoint_unreachable", "Sui GraphQL endpoint was not verified");
|
|
136
|
+
}
|
|
137
|
+
return { client: this.verified.client, limits: this.verified.limits };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function isFunctionFilterGraphqlRejection(error) {
|
|
141
|
+
if (!(error instanceof TransactionActivitySourceError) || error.reason !== "provider_error") {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const message = error.details.message;
|
|
145
|
+
return typeof message === "string" &&
|
|
146
|
+
/Failed to parse\s+"TransactionFilter"/i.test(message) &&
|
|
147
|
+
/At most one of\s+\[affectedAddress,\s+affectedObject,\s+function,\s+kind\]\s+can be specified/i.test(message);
|
|
148
|
+
}
|
package/docs/AGENT_BEHAVIOR.md
CHANGED
|
@@ -11,7 +11,7 @@ It is not the contributor rulebook and it is not enforcement. Development rules
|
|
|
11
11
|
| Surface | Status | Behavior |
|
|
12
12
|
| --- | --- | --- |
|
|
13
13
|
| Sui mainnet state reads | Current | Use read tools for supported balances, DeepBook pools, token registry metadata, mid-price snapshots, orderbook context, raw-quantity quotes, and DeepBook account inventory. |
|
|
14
|
-
| DeepBook review sessions | Digest-gated handoff; user-controlled signing on the review page | A review URL can be created. The review URL displays the proposal and local review evidence. The account-bound review can build local unsigned DeepBook swap transaction material inside the review server, internally bind a Sui transaction digest to that stored material, and derive object ownership, quote/policy provenance, human-readable review facts, and review-time simulation evidence from the same private review artifacts. This release does not provide a sign action, signing data, MCP-visible transaction bytes, or signing readiness. The local review page requests a digest-gated byte handoff for a `ready_for_wallet_review` state, then the user signs in their own wallet and the page records the execution receipt. |
|
|
14
|
+
| DeepBook and FlowX swap review sessions | Digest-gated handoff; user-controlled signing on the review page | A review URL can be created. The review URL displays the proposal and local review evidence. The account-bound review can build local unsigned DeepBook or FlowX swap transaction material inside the review server, internally bind a Sui transaction digest to that stored material, and derive object ownership, quote/policy provenance, human-readable review facts, and review-time simulation evidence from the same private review artifacts. This release does not provide a sign action, signing data, MCP-visible transaction bytes, or signing readiness. The local review page requests a digest-gated byte handoff for a `ready_for_wallet_review` state, then the user signs in their own wallet and the page records the execution receipt. |
|
|
15
15
|
| External proposal review sessions | Non-signable review in the current release | `action.prepare_external_proposal_review` can create a review URL from a structured external payment or Sui action proposal. Treat the proposal as untrusted display and review context only. It does not build, verify, simulate, sign, or execute transaction material. |
|
|
16
16
|
| Wallet signing | User-controlled on the local review page | MCP tools do not return signing readiness, signing data, or executable transaction bytes. Signing and execution happen only in the user's wallet from the local review page after the digest-gated handoff; results are recorded as execution receipts keyed by the review session. |
|
|
17
17
|
| PTB visualization | Rendered with emitted wallet review contracts | `reviewState.ptbVisualization` can accompany an emitted wallet review contract as a Mermaid flowchart of the stored local transaction shape. Treat it as visualization evidence only, not transaction-building input, wallet authorization, signing data, signing readiness, payment execution readiness, or route recommendation. |
|