@zktx.io/sui-mcp 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/LICENSES/@mysten-bcs-2.0.5-Apache-2.0.txt +205 -0
- package/LICENSES/@mysten-sui-2.17.0-Apache-2.0.txt +201 -0
- package/LICENSES/@mysten-utils-0.3.3-Apache-2.0.txt +205 -0
- package/LICENSES/fancy-canvas-2.1.0-MIT.txt +7 -0
- package/LICENSES/lightweight-charts-5.2.0-NOTICE.txt +2 -0
- package/README.md +288 -0
- package/dist/adapters/adapterLifecycleValidators.js +5 -0
- package/dist/adapters/adapterPromptSurfaces.js +60 -0
- package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +178 -0
- package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
- package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
- package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
- package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
- package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +254 -0
- package/dist/adapters/intentPlanFactories.js +51 -0
- package/dist/adapters/reviewAdapters.js +47 -0
- package/dist/core/action/adapterLifecycleValidation.js +12 -0
- package/dist/core/action/contractNameRegistry.js +151 -0
- package/dist/core/action/forbiddenFields.js +43 -0
- package/dist/core/action/humanReadableReviewEvidence.js +193 -0
- package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
- package/dist/core/action/ptbVisualizationProducer.js +71 -0
- package/dist/core/action/receiptPtbGraph.js +29 -0
- package/dist/core/action/reviewCheckResults.js +6 -0
- package/dist/core/action/reviewStateValidation.js +11 -0
- package/dist/core/action/reviewTimeSimulationEvidence.js +470 -0
- package/dist/core/action/schemas.js +491 -0
- package/dist/core/action/signableAdapterContract.js +998 -0
- package/dist/core/action/suiChainReceiptEvidence.js +106 -0
- package/dist/core/action/suiChainReceiptMapping.js +107 -0
- package/dist/core/action/suiChainReceiptReader.js +211 -0
- package/dist/core/action/suiChainReceiptVerifier.js +178 -0
- package/dist/core/action/swapHumanReadableReviewProjection.js +130 -0
- package/dist/core/action/swapQuotePolicyEvidence.js +271 -0
- package/dist/core/action/transactionFunding.js +244 -0
- package/dist/core/action/transactionObjectOwnershipEvidence.js +256 -0
- package/dist/core/action/transactionObjectOwnershipProducer.js +323 -0
- package/dist/core/action/types.js +33 -0
- package/dist/core/action/walletReviewContractAssembler.js +286 -0
- package/dist/core/activity/accountAssetTimeline.js +157 -0
- package/dist/core/activity/accountAssetTimelineUsdcReferences.js +120 -0
- package/dist/core/activity/activityStore.js +16 -0
- package/dist/core/activity/externalActivityCoverage.js +76 -0
- package/dist/core/activity/externalActivityTransactionStream.js +82 -0
- package/dist/core/activity/localDataService.js +296 -0
- package/dist/core/activity/localDataTypes.js +11 -0
- package/dist/core/activity/localDataValidation.js +439 -0
- package/dist/core/activity/schemaVersion.js +2 -0
- package/dist/core/activity/sqliteActivityStore.js +1068 -0
- package/dist/core/activity/sqliteActivityStoreRows.js +389 -0
- package/dist/core/activity/sqliteActivityStoreSchema.js +367 -0
- package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
- package/dist/core/activity/suiFunctionTarget.js +43 -0
- package/dist/core/activity/transactionActivityAccountEffects.js +186 -0
- package/dist/core/activity/transactionActivityAnalysis.js +295 -0
- package/dist/core/activity/transactionActivityClassifier.js +306 -0
- package/dist/core/activity/transactionActivityDetails.js +229 -0
- package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
- package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
- package/dist/core/activity/transactionActivityService.js +379 -0
- package/dist/core/activity/transactionActivityTypes.js +18 -0
- package/dist/core/eventlog/sink.js +35 -0
- package/dist/core/evidence/settlementFamilies.js +85 -0
- package/dist/core/evidence/userAnswerUse.js +1 -0
- package/dist/core/numeric/rawU64.js +63 -0
- package/dist/core/preferences/preferencesStore.js +26 -0
- package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
- package/dist/core/proposal/externalProposalReview.js +347 -0
- package/dist/core/proposal/schemas.js +208 -0
- package/dist/core/proposal/types.js +35 -0
- package/dist/core/read/amounts.js +14 -0
- package/dist/core/read/coinMetadata.js +60 -0
- package/dist/core/read/deepbookOfficialIndexerSource.js +373 -0
- package/dist/core/read/deepbookRawQuoteClient.js +86 -0
- package/dist/core/read/deepbookReadHelpers.js +321 -0
- package/dist/core/read/deepbookRegistry.js +157 -0
- package/dist/core/read/deepbookSourceOwners.js +159 -0
- package/dist/core/read/deepbookUsdcChartService.js +342 -0
- package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
- package/dist/core/read/readCardInputs.js +19 -0
- package/dist/core/read/readCardResult.js +40 -0
- package/dist/core/read/readResponseGuidance.js +402 -0
- package/dist/core/read/readService.js +1639 -0
- package/dist/core/read/readServiceTypes.js +88 -0
- package/dist/core/read/settlementParityFormatting.js +82 -0
- package/dist/core/read/walletReadHelpers.js +99 -0
- package/dist/core/review/reviewChecks.js +45 -0
- package/dist/core/review/reviewComputation.js +38 -0
- package/dist/core/review/reviewComputationResult.js +87 -0
- package/dist/core/session/cardSession.js +31 -0
- package/dist/core/session/cardSessionStore.js +204 -0
- package/dist/core/session/keyedRecordStore.js +18 -0
- package/dist/core/session/liveReviewSessionContract.js +3 -0
- package/dist/core/session/localSession.js +31 -0
- package/dist/core/session/privateReviewArtifacts.js +75 -0
- package/dist/core/session/reviewValidity.js +49 -0
- package/dist/core/session/sessionErrors.js +9 -0
- package/dist/core/session/sessionRecordStore.js +40 -0
- package/dist/core/session/sessionStore.js +655 -0
- package/dist/core/session/settingsSession.js +1 -0
- package/dist/core/session/settingsSessions.js +45 -0
- package/dist/core/session/sqliteCardStore.js +88 -0
- package/dist/core/session/sqliteSessionStore.js +213 -0
- package/dist/core/session/sqliteTransactionMaterialStore.js +64 -0
- package/dist/core/session/sqliteWalletWorkflowStore.js +821 -0
- package/dist/core/session/status.js +60 -0
- package/dist/core/session/transactionMaterialStore.js +221 -0
- package/dist/core/session/transactionRequest.js +72 -0
- package/dist/core/session/wait.js +79 -0
- package/dist/core/session/walletConnection.js +92 -0
- package/dist/core/session/walletRuntime.js +38 -0
- package/dist/core/session/walletWorkflow.js +608 -0
- package/dist/core/session/workflowState.js +80 -0
- package/dist/core/session/workflowView.js +150 -0
- package/dist/core/suiAddress.js +18 -0
- package/dist/core/suiEndpoint.js +72 -0
- package/dist/mcp/activeAccountResponse.js +24 -0
- package/dist/mcp/prompts.js +146 -0
- package/dist/mcp/registerTool.js +19 -0
- package/dist/mcp/resources.js +72 -0
- package/dist/mcp/responseGuidance.js +326 -0
- package/dist/mcp/result.js +17 -0
- package/dist/mcp/schemas.js +11 -0
- package/dist/mcp/server.js +32 -0
- package/dist/mcp/serverInfo.js +131 -0
- package/dist/mcp/toolErrors.js +118 -0
- package/dist/mcp/toolNames.js +58 -0
- package/dist/mcp/tools/account/index.js +44 -0
- package/dist/mcp/tools/action/prepareSuiActionReview.js +126 -0
- package/dist/mcp/tools/read/commonSchemas.js +43 -0
- package/dist/mcp/tools/read/deepbookReadTools.js +686 -0
- package/dist/mcp/tools/read/index.js +14 -0
- package/dist/mcp/tools/read/readToolHelpers.js +97 -0
- package/dist/mcp/tools/read/reviewActivityTools.js +179 -0
- package/dist/mcp/tools/read/serverStatusTools.js +100 -0
- package/dist/mcp/tools/read/transactionActivityOutput.js +371 -0
- package/dist/mcp/tools/read/transactionActivityTools.js +858 -0
- package/dist/mcp/tools/read/walletReadTools.js +734 -0
- package/dist/mcp/tools/session/executionResultTools.js +44 -0
- package/dist/mcp/tools/session/index.js +8 -0
- package/dist/mcp/tools/session/shared.js +36 -0
- package/dist/mcp/tools/session/statusTools.js +69 -0
- package/dist/mcp/tools/session/walletConnectionTools.js +69 -0
- package/dist/mcp/tools/settings/index.js +66 -0
- package/dist/mcp-app/account.html +3133 -0
- package/dist/mcp-app/account.notices.txt +973 -0
- package/dist/mcp-app/chart.html +3354 -0
- package/dist/mcp-app/chart.notices.txt +1194 -0
- package/dist/mcp-app/connect.html +3179 -0
- package/dist/mcp-app/connect.notices.txt +1012 -0
- package/dist/mcp-app/receipt.html +8430 -0
- package/dist/mcp-app/receipt.notices.txt +3549 -0
- package/dist/mcp-app/review.html +8430 -0
- package/dist/mcp-app/review.notices.txt +3549 -0
- package/dist/mcp-ui/contracts.js +31 -0
- package/dist/mcp-ui/readCards.js +28 -0
- package/dist/mcp-ui/tools.js +134 -0
- package/dist/review-app/brand-dark.svg +1 -0
- package/dist/review-app/brand-light.svg +1 -0
- package/dist/review-app/favicon.svg +1 -0
- package/dist/review-app/settings.css +1 -0
- package/dist/review-app/settings.js +1 -0
- package/dist/review-app/ui.css +809 -0
- package/dist/review-server/assets.js +48 -0
- package/dist/review-server/html.js +45 -0
- package/dist/review-server/http.js +47 -0
- package/dist/review-server/middleware/hostOrigin.js +48 -0
- package/dist/review-server/middleware/reviewToken.js +7 -0
- package/dist/review-server/reviewServerPolicy.js +1 -0
- package/dist/review-server/server.js +72 -0
- package/dist/review-server/settingsApi.js +158 -0
- package/dist/runtime/application.js +203 -0
- package/dist/runtime/config.js +97 -0
- package/dist/runtime/localSettingsService.js +198 -0
- package/dist/runtime/logger.js +66 -0
- package/dist/runtime/reviewDependencies.js +58 -0
- package/dist/runtime/reviewServerAcquire.js +93 -0
- package/dist/runtime/runtimeTermination.js +60 -0
- package/dist/runtime/shared/authenticatedFetch.js +127 -0
- package/dist/runtime/shared/control.js +93 -0
- package/dist/runtime/shared/dataAccess.js +66 -0
- package/dist/runtime/shared/mcpHttp.js +84 -0
- package/dist/runtime/shared/ownerLease.js +50 -0
- package/dist/runtime/shared/server.js +98 -0
- package/dist/runtime/shared/stdio.js +113 -0
- package/dist/runtime/smokeMainnetRead.js +391 -0
- package/dist/runtime/smokeMainnetReadAssertions.js +306 -0
- package/dist/runtime/smokeReview.js +40 -0
- package/dist/runtime/start.js +90 -0
- package/dist/runtime/suiEndpoint.js +97 -0
- package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
- package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
- package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
- package/dist/runtime/walletConnectConfig.js +3 -0
- package/dist/runtime/walletConnectStorage.js +99 -0
- package/dist/runtime/walletConnectTransport.js +128 -0
- package/dist/runtime/walletSdkChild.js +3 -0
- package/dist/runtime/walletSdkChildRuntime.js +160 -0
- package/dist/runtime/walletSdkIpc.js +26 -0
- package/dist/runtime/walletSdkProcess.js +358 -0
- package/docs/AGENT_BEHAVIOR.md +524 -0
- package/docs/AGENT_DEVELOPMENT_POLICY.md +815 -0
- package/docs/FRONTEND_POLICY.md +534 -0
- package/docs/LOCAL_DB_ARCHITECTURE.md +381 -0
- package/docs/MCP_SETUP.md +742 -0
- package/docs/MCP_TOOLS.md +1318 -0
- package/docs/SDK_API.md +206 -0
- package/docs/SIGNABLE_ADAPTER_CONTRACT.md +396 -0
- package/docs/TRANSACTION_ACTIVITY_LOG.md +194 -0
- package/docs/UTILITY_INDEX.md +275 -0
- package/docs/WALLET_CONNECTION.md +238 -0
- package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +166 -0
- package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +161 -0
- package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +259 -0
- package/package.json +79 -0
- package/protocols/deepbook-margin.md +15 -0
- package/protocols/deepbook-v3.md +24 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const EXTERNAL_PROPOSAL_CONTRACT_VERSION = "external-proposal-alpha-2026-05-25";
|
|
2
|
+
export const PROPOSAL_REVIEW_MODEL_VERSION = "proposal-review-model-alpha-2026-05-25";
|
|
3
|
+
export const EXTERNAL_PROPOSAL_SETTLEMENT_TOKEN_SELECTION_UNSUPPORTED_CLAIM_ID = "settlement_token_selection";
|
|
4
|
+
export const EXTERNAL_PROPOSAL_REVIEW_ALWAYS_UNSUPPORTED_CLAIMS = [
|
|
5
|
+
{
|
|
6
|
+
id: "external_transaction_material_trusted_authority",
|
|
7
|
+
label: "External transaction material",
|
|
8
|
+
reason: "External proposal data is not accepted as transaction-building input, signing data, or wallet authorization."
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "signing_readiness",
|
|
12
|
+
label: "Signing readiness",
|
|
13
|
+
reason: "This review session is read-only and has no reviewed signable adapter, transaction regeneration, or wallet handoff."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "route_recommendation",
|
|
17
|
+
label: "Route recommendation",
|
|
18
|
+
reason: "The review model does not rank venues, choose routes, or make best-price recommendations."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: EXTERNAL_PROPOSAL_SETTLEMENT_TOKEN_SELECTION_UNSUPPORTED_CLAIM_ID,
|
|
22
|
+
label: "Settlement-token selection",
|
|
23
|
+
reason: "External proposal review can display declared asset fields, but it does not verify that the user selected a settlement token and does not choose one for the user."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "fiat_usd_cash_out",
|
|
27
|
+
label: "Fiat USD cash-out",
|
|
28
|
+
reason: "Settlement assets are not treated as fiat USD, bank cash-out amounts, or peg guarantees."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "profit_or_pnl",
|
|
32
|
+
label: "P&L, tax, and cost basis",
|
|
33
|
+
reason: "The current release does not compute profit, loss, tax, or cost basis."
|
|
34
|
+
}
|
|
35
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReadServiceInputError } from "./readServiceTypes.js";
|
|
2
|
+
export function sumRawAmounts(rawAmounts, field = "balance") {
|
|
3
|
+
let total = 0n;
|
|
4
|
+
for (const rawAmount of rawAmounts) {
|
|
5
|
+
if (!/^\d+$/.test(rawAmount)) {
|
|
6
|
+
throw new ReadServiceInputError("input_invalid", "raw amount must be an unsigned integer string", {
|
|
7
|
+
field,
|
|
8
|
+
value: rawAmount
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
total += BigInt(rawAmount);
|
|
12
|
+
}
|
|
13
|
+
return total.toString();
|
|
14
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { normalizeStructTag, parseToUnits } from "@mysten/sui/utils";
|
|
2
|
+
export const COIN_METADATA_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
3
|
+
export const SUI_METADATA_UNIT_SOURCE = "sui_core_getCoinMetadata";
|
|
4
|
+
export const DEEPBOOK_SCALAR_UNIT_SOURCE = "deepbook_mainnetCoins_scalar";
|
|
5
|
+
export const DISPLAY_AMOUNT_SOURCE = "raw_balance_with_verified_decimals";
|
|
6
|
+
export function normalizeCoinType(coinType) {
|
|
7
|
+
return normalizeStructTag(coinType);
|
|
8
|
+
}
|
|
9
|
+
export function decimalsFromScalar(scalar) {
|
|
10
|
+
if (!Number.isSafeInteger(scalar) || scalar < 1) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
let value = scalar;
|
|
14
|
+
let decimals = 0;
|
|
15
|
+
while (value > 1 && value % 10 === 0) {
|
|
16
|
+
value /= 10;
|
|
17
|
+
decimals += 1;
|
|
18
|
+
}
|
|
19
|
+
return value === 1 ? decimals : undefined;
|
|
20
|
+
}
|
|
21
|
+
export function assertValidDecimals(decimals) {
|
|
22
|
+
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
|
|
23
|
+
throw new Error("Coin metadata decimals must be an integer from 0 to 255");
|
|
24
|
+
}
|
|
25
|
+
return decimals;
|
|
26
|
+
}
|
|
27
|
+
export function formatRawAmount(rawAmount, decimals) {
|
|
28
|
+
assertValidDecimals(decimals);
|
|
29
|
+
if (!/^\d+$/.test(rawAmount)) {
|
|
30
|
+
throw new Error("raw amount must be an unsigned integer string");
|
|
31
|
+
}
|
|
32
|
+
const normalizedRaw = rawAmount.replace(/^0+(?=\d)/, "");
|
|
33
|
+
if (decimals === 0) {
|
|
34
|
+
return normalizedRaw;
|
|
35
|
+
}
|
|
36
|
+
const padded = normalizedRaw.padStart(decimals + 1, "0");
|
|
37
|
+
const integerPart = padded.slice(0, -decimals).replace(/^0+(?=\d)/, "");
|
|
38
|
+
const fractionalPart = padded.slice(-decimals).replace(/0+$/, "");
|
|
39
|
+
return fractionalPart.length === 0 ? integerPart : `${integerPart}.${fractionalPart}`;
|
|
40
|
+
}
|
|
41
|
+
export function assertDisplayAmountSyntax(displayAmount) {
|
|
42
|
+
if (!/^\d+(?:\.\d+)?$/.test(displayAmount)) {
|
|
43
|
+
throw new Error("display amount must be an unsigned decimal string");
|
|
44
|
+
}
|
|
45
|
+
return displayAmount;
|
|
46
|
+
}
|
|
47
|
+
export function parseDisplayAmountToRaw(displayAmount, decimals) {
|
|
48
|
+
assertValidDecimals(decimals);
|
|
49
|
+
assertDisplayAmountSyntax(displayAmount);
|
|
50
|
+
try {
|
|
51
|
+
return parseToUnits(displayAmount, decimals).toString();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const message = error instanceof Error ? error.message : "unknown";
|
|
55
|
+
if (message.startsWith("Too many decimal places")) {
|
|
56
|
+
throw new Error("display amount has more fractional digits than verified decimals");
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { normalizeCoinType } from "./coinMetadata.js";
|
|
3
|
+
export const DEFAULT_DEEPBOOK_OFFICIAL_INDEXER_BASE_URL = "https://deepbook-indexer.mainnet.mystenlabs.com";
|
|
4
|
+
export const DEFAULT_DEEPBOOK_OFFICIAL_INDEXER_FETCH_TIMEOUT_MS = 10_000;
|
|
5
|
+
export const DEEPBOOK_OFFICIAL_INDEXER_CANONICAL_USDC_COIN_TYPE = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC";
|
|
6
|
+
export const DEEPBOOK_OFFICIAL_INDEXER_PRICE_CONVENTION = "USDC_PER_BASE";
|
|
7
|
+
export const DEEPBOOK_OFFICIAL_INDEXER_SOURCE_STATEMENT = "Sui MCP read DeepBookV3 official Indexer candle data for this response.";
|
|
8
|
+
export const DEEPBOOK_OFFICIAL_INDEXER_CANDLE_TIMESTAMP_BOUNDARY = "open";
|
|
9
|
+
export const DEEPBOOK_OFFICIAL_INDEXER_INTERVALS = ["1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w"];
|
|
10
|
+
export const DEFAULT_DEEPBOOK_OFFICIAL_INDEXER_INTERVAL = "15m";
|
|
11
|
+
const SUI_OBJECT_ID_PATTERN = /^0x[0-9a-f]{64}$/i;
|
|
12
|
+
const DECIMAL_STRING_PATTERN = /^(0|[1-9][0-9]*)(\.[0-9]+)?$/;
|
|
13
|
+
export class DeepbookOfficialIndexerSourceError extends Error {
|
|
14
|
+
reason;
|
|
15
|
+
details;
|
|
16
|
+
constructor(reason, message, details = {}) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.reason = reason;
|
|
19
|
+
this.details = details;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const finiteNonNegativeNumberSchema = z.number().refine((value) => Number.isFinite(value) && value >= 0, {
|
|
23
|
+
message: "expected a finite non-negative number"
|
|
24
|
+
});
|
|
25
|
+
const timestampMsSchema = z.number().int().refine((value) => Number.isSafeInteger(value) && value >= 0, {
|
|
26
|
+
message: "expected a finite non-negative integer millisecond timestamp"
|
|
27
|
+
});
|
|
28
|
+
const decimalStringSchema = z.string().regex(DECIMAL_STRING_PATTERN);
|
|
29
|
+
const isoDateStringSchema = z.string().refine((value) => {
|
|
30
|
+
const timestamp = Date.parse(value);
|
|
31
|
+
return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
|
|
32
|
+
}, "expected an ISO timestamp string");
|
|
33
|
+
const coinTypeSchema = z.string().min(1).refine((value) => {
|
|
34
|
+
try {
|
|
35
|
+
normalizeCoinType(value);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}, "expected a valid Sui coin type");
|
|
42
|
+
export const deepbookOfficialIndexerIntervalSchema = z.enum(DEEPBOOK_OFFICIAL_INDEXER_INTERVALS);
|
|
43
|
+
export const deepbookOfficialIndexerPoolSchema = z.object({
|
|
44
|
+
pool_id: z.string().regex(SUI_OBJECT_ID_PATTERN),
|
|
45
|
+
pool_name: z.string().min(1),
|
|
46
|
+
base_asset_id: coinTypeSchema,
|
|
47
|
+
base_asset_symbol: z.string().min(1),
|
|
48
|
+
base_asset_decimals: z.number().int().min(0).max(255),
|
|
49
|
+
quote_asset_id: coinTypeSchema,
|
|
50
|
+
quote_asset_symbol: z.string().min(1),
|
|
51
|
+
quote_asset_decimals: z.number().int().min(0).max(255)
|
|
52
|
+
});
|
|
53
|
+
export const deepbookOfficialIndexerPoolsSchema = z.array(deepbookOfficialIndexerPoolSchema).min(1);
|
|
54
|
+
const deepbookOfficialIndexerWireCandleSchema = z.tuple([
|
|
55
|
+
timestampMsSchema,
|
|
56
|
+
finiteNonNegativeNumberSchema,
|
|
57
|
+
finiteNonNegativeNumberSchema,
|
|
58
|
+
finiteNonNegativeNumberSchema,
|
|
59
|
+
finiteNonNegativeNumberSchema,
|
|
60
|
+
finiteNonNegativeNumberSchema
|
|
61
|
+
]).superRefine((candle, context) => {
|
|
62
|
+
const [, open, high, low, close] = candle;
|
|
63
|
+
if (high < open || high < low || high < close) {
|
|
64
|
+
context.addIssue({
|
|
65
|
+
code: "custom",
|
|
66
|
+
message: "candle high must be greater than or equal to open low and close",
|
|
67
|
+
path: [2]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (low > open || low > high || low > close) {
|
|
71
|
+
context.addIssue({
|
|
72
|
+
code: "custom",
|
|
73
|
+
message: "candle low must be less than or equal to open high and close",
|
|
74
|
+
path: [3]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
export const deepbookOfficialIndexerOhclvResponseSchema = z.object({
|
|
79
|
+
candles: z.array(deepbookOfficialIndexerWireCandleSchema)
|
|
80
|
+
}).superRefine((response, context) => {
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
for (const [index, candle] of response.candles.entries()) {
|
|
83
|
+
const timestampMs = candle[0];
|
|
84
|
+
if (seen.has(timestampMs)) {
|
|
85
|
+
context.addIssue({
|
|
86
|
+
code: "custom",
|
|
87
|
+
message: "duplicate candle timestamp",
|
|
88
|
+
path: ["candles", index, 0]
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
seen.add(timestampMs);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
export const deepbookOfficialIndexerCandleSchema = z.object({
|
|
96
|
+
timestampMs: timestampMsSchema,
|
|
97
|
+
start: isoDateStringSchema,
|
|
98
|
+
end: isoDateStringSchema,
|
|
99
|
+
open: decimalStringSchema,
|
|
100
|
+
high: decimalStringSchema,
|
|
101
|
+
low: decimalStringSchema,
|
|
102
|
+
close: decimalStringSchema,
|
|
103
|
+
volume: decimalStringSchema
|
|
104
|
+
}).strict().superRefine((candle, context) => {
|
|
105
|
+
if (Date.parse(candle.end) <= Date.parse(candle.start)) {
|
|
106
|
+
context.addIssue({ code: "custom", message: "candle end must be after start", path: ["end"] });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
export class DeepbookOfficialIndexerSource {
|
|
110
|
+
baseUrl;
|
|
111
|
+
fetchImpl;
|
|
112
|
+
timeoutMs;
|
|
113
|
+
now;
|
|
114
|
+
constructor(options = {}) {
|
|
115
|
+
this.baseUrl = normalizeDeepbookOfficialIndexerBaseUrl(options.baseUrl ?? DEFAULT_DEEPBOOK_OFFICIAL_INDEXER_BASE_URL);
|
|
116
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
117
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_DEEPBOOK_OFFICIAL_INDEXER_FETCH_TIMEOUT_MS;
|
|
118
|
+
this.now = options.now ?? (() => new Date());
|
|
119
|
+
}
|
|
120
|
+
poolsUrl() {
|
|
121
|
+
return new URL("get_pools", `${this.baseUrl}/`).toString();
|
|
122
|
+
}
|
|
123
|
+
candlesUrl(input) {
|
|
124
|
+
const interval = parseDeepbookOfficialIndexerInterval(input.interval);
|
|
125
|
+
assertPoolName(input.poolName);
|
|
126
|
+
const url = new URL(`ohclv/${encodeURIComponent(input.poolName)}`, `${this.baseUrl}/`);
|
|
127
|
+
url.searchParams.set("interval", interval);
|
|
128
|
+
if (input.startTimeMs !== undefined) {
|
|
129
|
+
assertTimestampMs(input.startTimeMs, "startTimeMs");
|
|
130
|
+
url.searchParams.set("start_time", input.startTimeMs.toString());
|
|
131
|
+
}
|
|
132
|
+
if (input.endTimeMs !== undefined) {
|
|
133
|
+
assertTimestampMs(input.endTimeMs, "endTimeMs");
|
|
134
|
+
url.searchParams.set("end_time", input.endTimeMs.toString());
|
|
135
|
+
}
|
|
136
|
+
if (input.limit !== undefined) {
|
|
137
|
+
assertPositiveInteger(input.limit, "limit");
|
|
138
|
+
url.searchParams.set("limit", input.limit.toString());
|
|
139
|
+
}
|
|
140
|
+
return url.toString();
|
|
141
|
+
}
|
|
142
|
+
async fetchPools() {
|
|
143
|
+
const response = await this.fetchJson("get_pools", this.poolsUrl());
|
|
144
|
+
const pools = parseOfficialIndexerPayload(deepbookOfficialIndexerPoolsSchema, response.json, response.source);
|
|
145
|
+
return { source: response.source, pools };
|
|
146
|
+
}
|
|
147
|
+
async fetchCandles(input) {
|
|
148
|
+
const interval = parseDeepbookOfficialIndexerInterval(input.interval);
|
|
149
|
+
const url = this.candlesUrl({ ...input, interval });
|
|
150
|
+
const response = await this.fetchJson("ohclv", url, { ...input, interval });
|
|
151
|
+
const payload = parseOfficialIndexerPayload(deepbookOfficialIndexerOhclvResponseSchema, response.json, response.source);
|
|
152
|
+
const candles = payload.candles
|
|
153
|
+
.map((wireCandle) => officialWireCandleToCandle(wireCandle, interval))
|
|
154
|
+
.sort((left, right) => left.timestampMs - right.timestampMs);
|
|
155
|
+
return {
|
|
156
|
+
source: response.source,
|
|
157
|
+
candles
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async fetchJson(endpoint, url, input = {}) {
|
|
161
|
+
const controller = new AbortController();
|
|
162
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
163
|
+
let response;
|
|
164
|
+
try {
|
|
165
|
+
response = await this.fetchImpl(url, {
|
|
166
|
+
signal: controller.signal,
|
|
167
|
+
headers: {
|
|
168
|
+
accept: "application/json",
|
|
169
|
+
"user-agent": "sui-mcp"
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
throw officialIndexerFetchError(error, endpoint, url);
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
clearTimeout(timeout);
|
|
178
|
+
}
|
|
179
|
+
if (!response.ok) {
|
|
180
|
+
throw new DeepbookOfficialIndexerSourceError("source_http_error", "DeepBook official Indexer source returned an error", {
|
|
181
|
+
endpoint,
|
|
182
|
+
url,
|
|
183
|
+
httpStatus: response.status
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
let json;
|
|
187
|
+
try {
|
|
188
|
+
json = await response.json();
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", "DeepBook official Indexer source returned invalid JSON", {
|
|
192
|
+
endpoint,
|
|
193
|
+
url,
|
|
194
|
+
message: error instanceof Error ? error.message : String(error)
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
source: {
|
|
199
|
+
baseUrl: this.baseUrl,
|
|
200
|
+
endpoint,
|
|
201
|
+
url,
|
|
202
|
+
fetchedAt: this.now().toISOString(),
|
|
203
|
+
sourceStatement: DEEPBOOK_OFFICIAL_INDEXER_SOURCE_STATEMENT,
|
|
204
|
+
poolName: input.poolName,
|
|
205
|
+
interval: input.interval,
|
|
206
|
+
startTimeMs: input.startTimeMs,
|
|
207
|
+
endTimeMs: input.endTimeMs,
|
|
208
|
+
limit: input.limit
|
|
209
|
+
},
|
|
210
|
+
json
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export function normalizeDeepbookOfficialIndexerBaseUrl(value) {
|
|
215
|
+
let parsed;
|
|
216
|
+
try {
|
|
217
|
+
parsed = new URL(value);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_source_url", "DeepBook official Indexer base URL must be a valid URL");
|
|
221
|
+
}
|
|
222
|
+
if (parsed.protocol !== "https:") {
|
|
223
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_source_url", "DeepBook official Indexer base URL must use https");
|
|
224
|
+
}
|
|
225
|
+
if (!parsed.hostname) {
|
|
226
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_source_url", "DeepBook official Indexer base URL must include a host");
|
|
227
|
+
}
|
|
228
|
+
if (parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
229
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_source_url", "DeepBook official Indexer base URL must not include credentials, query, or fragment");
|
|
230
|
+
}
|
|
231
|
+
const pathname = parsed.pathname.replace(/\/+$/, "");
|
|
232
|
+
return `${parsed.origin}${pathname}`;
|
|
233
|
+
}
|
|
234
|
+
export function parseDeepbookOfficialIndexerInterval(value) {
|
|
235
|
+
const parsed = deepbookOfficialIndexerIntervalSchema.safeParse(value);
|
|
236
|
+
if (!parsed.success) {
|
|
237
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", "DeepBook official Indexer interval is not supported", { interval: value });
|
|
238
|
+
}
|
|
239
|
+
return parsed.data;
|
|
240
|
+
}
|
|
241
|
+
export function deepbookOfficialIndexerIntervalDurationMs(interval) {
|
|
242
|
+
switch (interval) {
|
|
243
|
+
case "1m":
|
|
244
|
+
return 60_000;
|
|
245
|
+
case "5m":
|
|
246
|
+
return 5 * 60_000;
|
|
247
|
+
case "15m":
|
|
248
|
+
return 15 * 60_000;
|
|
249
|
+
case "30m":
|
|
250
|
+
return 30 * 60_000;
|
|
251
|
+
case "1h":
|
|
252
|
+
return 60 * 60_000;
|
|
253
|
+
case "4h":
|
|
254
|
+
return 4 * 60 * 60_000;
|
|
255
|
+
case "1d":
|
|
256
|
+
return 24 * 60 * 60_000;
|
|
257
|
+
case "1w":
|
|
258
|
+
return 7 * 24 * 60 * 60_000;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
export function isDeepbookOfficialIndexerCanonicalUsdcPool(pool) {
|
|
262
|
+
return normalizeCoinType(pool.quote_asset_id) === normalizeCoinType(DEEPBOOK_OFFICIAL_INDEXER_CANONICAL_USDC_COIN_TYPE);
|
|
263
|
+
}
|
|
264
|
+
export function selectDeepbookOfficialIndexerCanonicalUsdcPools(pools) {
|
|
265
|
+
return pools.filter(isDeepbookOfficialIndexerCanonicalUsdcPool);
|
|
266
|
+
}
|
|
267
|
+
function officialWireCandleToCandle([timestampMs, open, high, low, close, volume], interval) {
|
|
268
|
+
const start = new Date(timestampMs).toISOString();
|
|
269
|
+
const end = new Date(timestampMs + deepbookOfficialIndexerIntervalDurationMs(interval)).toISOString();
|
|
270
|
+
return deepbookOfficialIndexerCandleSchema.parse({
|
|
271
|
+
timestampMs,
|
|
272
|
+
start,
|
|
273
|
+
end,
|
|
274
|
+
open: decimalStringFromNumber(open),
|
|
275
|
+
high: decimalStringFromNumber(high),
|
|
276
|
+
low: decimalStringFromNumber(low),
|
|
277
|
+
close: decimalStringFromNumber(close),
|
|
278
|
+
volume: decimalStringFromNumber(volume)
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function decimalStringFromNumber(value) {
|
|
282
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
283
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", "DeepBook official Indexer number is invalid", {
|
|
284
|
+
value
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
const asString = value.toString();
|
|
288
|
+
if (!/[eE]/.test(asString)) {
|
|
289
|
+
return asString;
|
|
290
|
+
}
|
|
291
|
+
const [coefficient, exponentText] = asString.toLowerCase().split("e");
|
|
292
|
+
const exponent = Number(exponentText);
|
|
293
|
+
if (coefficient === undefined || exponentText === undefined || !Number.isInteger(exponent)) {
|
|
294
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", "DeepBook official Indexer number is invalid", {
|
|
295
|
+
value
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const [rawIntegerPart, fractionalPart = ""] = coefficient.split(".");
|
|
299
|
+
const integerPart = rawIntegerPart ?? "";
|
|
300
|
+
const digits = `${integerPart}${fractionalPart}`;
|
|
301
|
+
const decimalPosition = integerPart.length + exponent;
|
|
302
|
+
let decimal;
|
|
303
|
+
if (decimalPosition <= 0) {
|
|
304
|
+
decimal = `0.${"0".repeat(-decimalPosition)}${digits}`;
|
|
305
|
+
}
|
|
306
|
+
else if (decimalPosition >= digits.length) {
|
|
307
|
+
decimal = `${digits}${"0".repeat(decimalPosition - digits.length)}`;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
decimal = `${digits.slice(0, decimalPosition)}.${digits.slice(decimalPosition)}`;
|
|
311
|
+
}
|
|
312
|
+
return normalizeDecimalString(decimal);
|
|
313
|
+
}
|
|
314
|
+
function normalizeDecimalString(value) {
|
|
315
|
+
const unsigned = value.replace(/^0+(?=\d)/, "");
|
|
316
|
+
const trimmed = unsigned.includes(".") ? unsigned.replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "") : unsigned;
|
|
317
|
+
return trimmed === "" ? "0" : trimmed;
|
|
318
|
+
}
|
|
319
|
+
function parseOfficialIndexerPayload(schema, json, source) {
|
|
320
|
+
const parsed = schema.safeParse(json);
|
|
321
|
+
if (!parsed.success) {
|
|
322
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", "DeepBook official Indexer payload failed schema validation", {
|
|
323
|
+
endpoint: source.endpoint,
|
|
324
|
+
url: source.url,
|
|
325
|
+
issues: parsed.error.issues.map((issue) => ({
|
|
326
|
+
path: issue.path.join("."),
|
|
327
|
+
message: issue.message
|
|
328
|
+
}))
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return parsed.data;
|
|
332
|
+
}
|
|
333
|
+
function assertPoolName(poolName) {
|
|
334
|
+
if (!/^[A-Z0-9]+_[A-Z0-9]+$/.test(poolName)) {
|
|
335
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", "DeepBook official Indexer pool name is invalid", {
|
|
336
|
+
poolName
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function assertTimestampMs(value, field) {
|
|
341
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
342
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", `DeepBook official Indexer ${field} is invalid`, {
|
|
343
|
+
[field]: value
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function assertPositiveInteger(value, field) {
|
|
348
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
349
|
+
throw new DeepbookOfficialIndexerSourceError("invalid_payload", `DeepBook official Indexer ${field} is invalid`, {
|
|
350
|
+
[field]: value
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function officialIndexerFetchError(error, endpoint, url) {
|
|
355
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
356
|
+
if (isAbortError(error)) {
|
|
357
|
+
return new DeepbookOfficialIndexerSourceError("source_timeout", "DeepBook official Indexer source timed out", {
|
|
358
|
+
endpoint,
|
|
359
|
+
url,
|
|
360
|
+
message
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return new DeepbookOfficialIndexerSourceError("source_unavailable", "DeepBook official Indexer source could not be read", {
|
|
364
|
+
endpoint,
|
|
365
|
+
url,
|
|
366
|
+
message
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
function isAbortError(error) {
|
|
370
|
+
return error instanceof DOMException
|
|
371
|
+
? error.name === "AbortError"
|
|
372
|
+
: error instanceof Error && error.name === "AbortError";
|
|
373
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { bcs } from "@mysten/sui/bcs";
|
|
2
|
+
import { Transaction } from "@mysten/sui/transactions";
|
|
3
|
+
import { DeepBookClient } from "@mysten/deepbook-v3";
|
|
4
|
+
import { ReadServiceInputError } from "./readServiceTypes.js";
|
|
5
|
+
export function createDeepBookReadClient(input) {
|
|
6
|
+
const sdkClient = new DeepBookClient({
|
|
7
|
+
client: input.client,
|
|
8
|
+
address: input.simulationSender,
|
|
9
|
+
network: input.network,
|
|
10
|
+
...(input.balanceManagers === undefined ? {} : { balanceManagers: input.balanceManagers })
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
midPrice: (poolKey) => sdkClient.midPrice(poolKey),
|
|
14
|
+
poolBookParams: (poolKey) => sdkClient.poolBookParams(poolKey),
|
|
15
|
+
getLevel2TicksFromMid: (poolKey, ticks) => sdkClient.getLevel2TicksFromMid(poolKey, ticks),
|
|
16
|
+
getQuoteQuantityOutRaw: (poolKey, baseQuantity) => simulateDeepbookRawQuote({
|
|
17
|
+
client: input.client,
|
|
18
|
+
simulationSender: input.simulationSender,
|
|
19
|
+
poolKey,
|
|
20
|
+
command: () => sdkClient.deepBook.getQuoteQuantityOut(poolKey, baseQuantity)
|
|
21
|
+
}),
|
|
22
|
+
getBaseQuantityOutRaw: (poolKey, quoteQuantity) => simulateDeepbookRawQuote({
|
|
23
|
+
client: input.client,
|
|
24
|
+
simulationSender: input.simulationSender,
|
|
25
|
+
poolKey,
|
|
26
|
+
command: () => sdkClient.deepBook.getBaseQuantityOut(poolKey, quoteQuantity)
|
|
27
|
+
}),
|
|
28
|
+
getQuoteQuantityOutInputFeeRaw: (poolKey, baseQuantity) => simulateDeepbookRawQuote({
|
|
29
|
+
client: input.client,
|
|
30
|
+
simulationSender: input.simulationSender,
|
|
31
|
+
poolKey,
|
|
32
|
+
command: () => sdkClient.deepBook.getQuoteQuantityOutInputFee(poolKey, baseQuantity)
|
|
33
|
+
}),
|
|
34
|
+
getBaseQuantityOutInputFeeRaw: (poolKey, quoteQuantity) => simulateDeepbookRawQuote({
|
|
35
|
+
client: input.client,
|
|
36
|
+
simulationSender: input.simulationSender,
|
|
37
|
+
poolKey,
|
|
38
|
+
command: () => sdkClient.deepBook.getBaseQuantityOutInputFee(poolKey, quoteQuantity)
|
|
39
|
+
}),
|
|
40
|
+
getBalanceManagerIds: (owner) => sdkClient.getBalanceManagerIds(owner),
|
|
41
|
+
accountExists: (poolKey, managerKey) => sdkClient.accountExists(poolKey, managerKey),
|
|
42
|
+
account: (poolKey, managerKey) => sdkClient.account(poolKey, managerKey),
|
|
43
|
+
lockedBalance: (poolKey, balanceManagerKey) => sdkClient.lockedBalance(poolKey, balanceManagerKey),
|
|
44
|
+
accountOpenOrders: (poolKey, managerKey) => sdkClient.accountOpenOrders(poolKey, managerKey)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function simulateDeepbookRawQuote(input) {
|
|
48
|
+
const tx = new Transaction();
|
|
49
|
+
tx.setSender(input.simulationSender);
|
|
50
|
+
tx.add(input.command());
|
|
51
|
+
const result = await input.client.core.simulateTransaction({
|
|
52
|
+
transaction: tx,
|
|
53
|
+
include: { commandResults: true, effects: true }
|
|
54
|
+
});
|
|
55
|
+
const returnValues = result.commandResults?.[0]?.returnValues;
|
|
56
|
+
if (!Array.isArray(returnValues) || returnValues.length < 3) {
|
|
57
|
+
throw new ReadServiceInputError("quote_unavailable", "DeepBook raw quote return values are unavailable", {
|
|
58
|
+
poolKey: input.poolKey,
|
|
59
|
+
expectedReturnValues: ["base_quantity_out", "quote_quantity_out", "deep_quantity_required"]
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
baseOutRaw: parseU64ReturnValue(returnValues[0], "baseOutRaw", input.poolKey),
|
|
64
|
+
quoteOutRaw: parseU64ReturnValue(returnValues[1], "quoteOutRaw", input.poolKey),
|
|
65
|
+
deepRequiredRaw: parseU64ReturnValue(returnValues[2], "deepRequiredRaw", input.poolKey)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function parseU64ReturnValue(value, field, poolKey) {
|
|
69
|
+
const bcsValue = typeof value === "object" && value !== null && "bcs" in value ? value.bcs : undefined;
|
|
70
|
+
if (!(bcsValue instanceof Uint8Array)) {
|
|
71
|
+
throw new ReadServiceInputError("quote_unavailable", "DeepBook raw quote return value is unavailable", {
|
|
72
|
+
poolKey,
|
|
73
|
+
field
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
return bcs.U64.parse(bcsValue).toString();
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new ReadServiceInputError("quote_unavailable", "DeepBook raw quote return value is not a u64", {
|
|
81
|
+
poolKey,
|
|
82
|
+
field,
|
|
83
|
+
reason: error instanceof Error ? error.message : "unknown"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|