hive-intelligence 1.13.1 → 1.15.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/README.md +5 -3
- package/agent-skills/packs/hive/SKILL.md +2 -2
- package/agent-skills/packs/hive-prediction-markets/SKILL.md +9 -8
- package/build/{chunk-V7TBL47R.js → chunk-762ZUJ4Q.js} +1 -1
- package/build/{chunk-ZRAW5MXW.js → chunk-GNZGLVFE.js} +2688 -592
- package/build/cli.js +5 -5
- package/build/{doctor-DUXJWV3F.js → doctor-25WPHR4J.js} +1 -1
- package/build/{mcpServer-NWKQ4LCN.js → mcpServer-O4KXRE62.js} +1 -1
- package/build/monitor-worker.js +3 -154
- package/build/release.json +4 -4
- package/build/{serve-GVD5SX46.js → serve-V2KPPJCD.js} +2 -2
- package/build/server.js +9 -3
- package/build/stdio.js +1 -1
- package/package.json +5 -5
|
@@ -8841,8 +8841,8 @@ Don't use when:
|
|
|
8841
8841
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8842
8842
|
};
|
|
8843
8843
|
var handler91 = async (client2, args) => {
|
|
8844
|
-
const
|
|
8845
|
-
if (
|
|
8844
|
+
const invalid3 = walletWindowError(args);
|
|
8845
|
+
if (invalid3) return invalid3;
|
|
8846
8846
|
const network = encodeURIComponent(String(args?.network ?? "").trim());
|
|
8847
8847
|
const address = encodeURIComponent(String(args?.address ?? "").trim());
|
|
8848
8848
|
const query = {
|
|
@@ -8926,8 +8926,8 @@ Don't use when:
|
|
|
8926
8926
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8927
8927
|
};
|
|
8928
8928
|
var handler92 = async (client2, args) => {
|
|
8929
|
-
const
|
|
8930
|
-
if (
|
|
8929
|
+
const invalid3 = walletWindowError(args);
|
|
8930
|
+
if (invalid3) return invalid3;
|
|
8931
8931
|
const network = encodeURIComponent(String(args?.network ?? "").trim());
|
|
8932
8932
|
const address = encodeURIComponent(String(args?.address ?? "").trim());
|
|
8933
8933
|
const query = {
|
|
@@ -9017,7 +9017,7 @@ Use when:
|
|
|
9017
9017
|
|
|
9018
9018
|
Don't use when:
|
|
9019
9019
|
- You need prices or market caps (use coingecko_get_rwa_markets, which returns spot market data in one call)
|
|
9020
|
-
- You need RWA perpetual futures, open interest, or funding rates on Hyperliquid,
|
|
9020
|
+
- You need RWA perpetual futures, open interest, or funding rates on Hyperliquid, Lighter, or Extended (use rwa_get_markets or rwa_get_funding_rates)
|
|
9021
9021
|
- You need the underlying token contracts for an asset (use coingecko_get_rwa_asset with tokens=true)
|
|
9022
9022
|
- You want a regular crypto coin ID (use coingecko_get_coins_list)
|
|
9023
9023
|
|
|
@@ -9115,7 +9115,7 @@ Use when:
|
|
|
9115
9115
|
- You want to size an issuer such as Ondo, Backed, or Paxos by the assets it wraps
|
|
9116
9116
|
|
|
9117
9117
|
Don't use when:
|
|
9118
|
-
- You need RWA perpetual markets or funding rates on Hyperliquid,
|
|
9118
|
+
- You need RWA perpetual markets or funding rates on Hyperliquid, Lighter, or Extended (use rwa_get_markets or rwa_get_funding_rates)
|
|
9119
9119
|
- You need per-exchange or per-pair trades for one asset (use coingecko_get_rwa_tickers)
|
|
9120
9120
|
- You need price history rather than a snapshot (use coingecko_get_rwa_market_chart)
|
|
9121
9121
|
- You need the token contract addresses behind an asset (use coingecko_get_rwa_asset with tokens=true)
|
|
@@ -9351,7 +9351,7 @@ Use when:
|
|
|
9351
9351
|
|
|
9352
9352
|
Don't use when:
|
|
9353
9353
|
- You need a single aggregated price or market cap (use coingecko_get_rwa_markets or coingecko_get_rwa_asset)
|
|
9354
|
-
- You need RWA perpetual futures venues, open interest, or funding rates on Hyperliquid,
|
|
9354
|
+
- You need RWA perpetual futures venues, open interest, or funding rates on Hyperliquid, Lighter, or Extended (use rwa_get_markets or rwa_get_funding_rates)
|
|
9355
9355
|
- You need historical prices (use coingecko_get_rwa_market_chart)
|
|
9356
9356
|
- You want tickers for a regular crypto coin (use coingecko_get_coin_tickers)
|
|
9357
9357
|
|
|
@@ -9696,7 +9696,7 @@ var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
9696
9696
|
"GOPLUS_API_KEY"
|
|
9697
9697
|
]);
|
|
9698
9698
|
function isSensitiveEnvironmentKey(key) {
|
|
9699
|
-
return /(?:api[_-]?(?:key|id)|key|secret|password|token|auth|credential|private|bearer|redis.*url|
|
|
9699
|
+
return /(?:api[_-]?(?:key|id)|key|secret|password|token|auth|credential|private|bearer|redis.*url|clickhouse_url)/i.test(
|
|
9700
9700
|
key
|
|
9701
9701
|
);
|
|
9702
9702
|
}
|
|
@@ -12509,14 +12509,14 @@ function validateFeedbackInput(input) {
|
|
|
12509
12509
|
`Invalid arguments: message is ${message.length} characters; the limit is ${FEEDBACK_MAX_MESSAGE_CHARS}.`
|
|
12510
12510
|
);
|
|
12511
12511
|
}
|
|
12512
|
-
let
|
|
12512
|
+
let tool101 = null;
|
|
12513
12513
|
if (input.tool !== void 0 && input.tool !== null) {
|
|
12514
12514
|
if (typeof input.tool !== "string" || !isToolNameShaped(input.tool)) {
|
|
12515
12515
|
throw new FeedbackValidationError(
|
|
12516
12516
|
"Invalid arguments: tool must be a Hive tool name (lowercase letters, digits and underscores, 3 to 128 characters) or omitted."
|
|
12517
12517
|
);
|
|
12518
12518
|
}
|
|
12519
|
-
|
|
12519
|
+
tool101 = input.tool;
|
|
12520
12520
|
}
|
|
12521
12521
|
let receiptId = null;
|
|
12522
12522
|
if (input.receiptId !== void 0 && input.receiptId !== null) {
|
|
@@ -12527,7 +12527,7 @@ function validateFeedbackInput(input) {
|
|
|
12527
12527
|
}
|
|
12528
12528
|
receiptId = input.receiptId.toLowerCase();
|
|
12529
12529
|
}
|
|
12530
|
-
return { message: scrubFeedbackText(message), tool:
|
|
12530
|
+
return { message: scrubFeedbackText(message), tool: tool101, receiptId };
|
|
12531
12531
|
}
|
|
12532
12532
|
function withTimeout(promise, ms) {
|
|
12533
12533
|
return new Promise((resolve, reject) => {
|
|
@@ -20416,8 +20416,8 @@ Error Handling:
|
|
|
20416
20416
|
},
|
|
20417
20417
|
async execute(args) {
|
|
20418
20418
|
const MAX_SAFE_LIMIT = 50;
|
|
20419
|
-
const
|
|
20420
|
-
const limit = Math.min(args?.limit ||
|
|
20419
|
+
const DEFAULT_LIMIT5 = 30;
|
|
20420
|
+
const limit = Math.min(args?.limit || DEFAULT_LIMIT5, MAX_SAFE_LIMIT);
|
|
20421
20421
|
const offset = args?.offset || 0;
|
|
20422
20422
|
try {
|
|
20423
20423
|
const data = await apiClient.getProtocols();
|
|
@@ -21525,8 +21525,8 @@ Error Handling:
|
|
|
21525
21525
|
},
|
|
21526
21526
|
async execute(args) {
|
|
21527
21527
|
const MAX_SAFE_LIMIT = 100;
|
|
21528
|
-
const
|
|
21529
|
-
const limit = Math.min(args?.limit ||
|
|
21528
|
+
const DEFAULT_LIMIT5 = 50;
|
|
21529
|
+
const limit = Math.min(args?.limit || DEFAULT_LIMIT5, MAX_SAFE_LIMIT);
|
|
21530
21530
|
const offset = args?.offset || 0;
|
|
21531
21531
|
const minTvl = args?.minTvl || 0;
|
|
21532
21532
|
try {
|
|
@@ -23642,8 +23642,8 @@ Error Handling:
|
|
|
23642
23642
|
},
|
|
23643
23643
|
async execute(args) {
|
|
23644
23644
|
const MAX_SAFE_LIMIT = 100;
|
|
23645
|
-
const
|
|
23646
|
-
const limit = Math.min(args.limit ||
|
|
23645
|
+
const DEFAULT_LIMIT5 = 50;
|
|
23646
|
+
const limit = Math.min(args.limit || DEFAULT_LIMIT5, MAX_SAFE_LIMIT);
|
|
23647
23647
|
const offset = args.offset || 0;
|
|
23648
23648
|
const minFees = args.minFees || 0;
|
|
23649
23649
|
try {
|
|
@@ -27197,10 +27197,10 @@ function setupTools(apiClient) {
|
|
|
27197
27197
|
...derivativesVolumesTools(apiClient),
|
|
27198
27198
|
...metadataTools(apiClient)
|
|
27199
27199
|
];
|
|
27200
|
-
toolCategories.forEach((
|
|
27201
|
-
const execute =
|
|
27202
|
-
tools4[
|
|
27203
|
-
...
|
|
27200
|
+
toolCategories.forEach((tool101) => {
|
|
27201
|
+
const execute = tool101.execute.bind(tool101);
|
|
27202
|
+
tools4[tool101.name] = {
|
|
27203
|
+
...tool101,
|
|
27204
27204
|
execute: async (args) => normalizeMcpErrorResult(await execute(args))
|
|
27205
27205
|
};
|
|
27206
27206
|
});
|
|
@@ -31435,8 +31435,8 @@ var CCXT_REQUIRED_BATCH_FILTERS = {
|
|
|
31435
31435
|
ccxt_fetch_borrow_rate_histories: "codes",
|
|
31436
31436
|
ccxt_fetch_all_greeks: "symbols"
|
|
31437
31437
|
};
|
|
31438
|
-
function withCcxtBounds(
|
|
31439
|
-
const schema =
|
|
31438
|
+
function withCcxtBounds(tool101) {
|
|
31439
|
+
const schema = tool101.inputSchema;
|
|
31440
31440
|
const properties = Object.fromEntries(
|
|
31441
31441
|
Object.entries(schema.properties || {}).map(
|
|
31442
31442
|
([name, rawProperty]) => {
|
|
@@ -31455,13 +31455,13 @@ function withCcxtBounds(tool100) {
|
|
|
31455
31455
|
)
|
|
31456
31456
|
);
|
|
31457
31457
|
const required = new Set(schema.required || []);
|
|
31458
|
-
const explicitFilter = CCXT_REQUIRED_BATCH_FILTERS[
|
|
31458
|
+
const explicitFilter = CCXT_REQUIRED_BATCH_FILTERS[tool101.name];
|
|
31459
31459
|
if (explicitFilter) required.add(explicitFilter);
|
|
31460
31460
|
return {
|
|
31461
|
-
...
|
|
31462
|
-
description: explicitFilter ? `${
|
|
31461
|
+
...tool101,
|
|
31462
|
+
description: explicitFilter ? `${tool101.description}
|
|
31463
31463
|
|
|
31464
|
-
Hive safety bound: ${explicitFilter} is required and accepts at most 100 entries; unbounded all-market reads are rejected.` :
|
|
31464
|
+
Hive safety bound: ${explicitFilter} is required and accepts at most 100 entries; unbounded all-market reads are rejected.` : tool101.description,
|
|
31465
31465
|
inputSchema: {
|
|
31466
31466
|
...schema,
|
|
31467
31467
|
type: "object",
|
|
@@ -31472,7 +31472,7 @@ Hive safety bound: ${explicitFilter} is required and accepts at most 100 entries
|
|
|
31472
31472
|
};
|
|
31473
31473
|
}
|
|
31474
31474
|
var tools = Object.fromEntries(
|
|
31475
|
-
Object.entries(rawTools).map(([key,
|
|
31475
|
+
Object.entries(rawTools).map(([key, tool101]) => [key, withCcxtBounds(tool101)])
|
|
31476
31476
|
);
|
|
31477
31477
|
var exportedHandlers = handlers2;
|
|
31478
31478
|
var ccxt_default = { metadata: metadata100, tools, handlers: exportedHandlers };
|
|
@@ -31587,6 +31587,7 @@ var ResilientHttpClient = class {
|
|
|
31587
31587
|
this.axiosInstance = axios2.create({
|
|
31588
31588
|
baseURL: this.config.baseURL,
|
|
31589
31589
|
timeout: this.config.timeout,
|
|
31590
|
+
...config2.maxContentLength ? { maxContentLength: config2.maxContentLength } : {},
|
|
31590
31591
|
headers: {
|
|
31591
31592
|
"Content-Type": "application/json",
|
|
31592
31593
|
Accept: "application/json",
|
|
@@ -31728,7 +31729,8 @@ var ResilientHttpClient = class {
|
|
|
31728
31729
|
} else {
|
|
31729
31730
|
if (error instanceof AxiosError) {
|
|
31730
31731
|
const status = error.response?.status ?? 0;
|
|
31731
|
-
|
|
31732
|
+
const oversized = error.message.includes("maxContentLength");
|
|
31733
|
+
if (!status && !oversized || status >= 500 || status === 429) {
|
|
31732
31734
|
this.recordFailure(error);
|
|
31733
31735
|
}
|
|
31734
31736
|
}
|
|
@@ -32588,9 +32590,9 @@ function handleSimulateBundle(clientParam, args) {
|
|
|
32588
32590
|
save: sim.save ?? false,
|
|
32589
32591
|
estimate_gas: sim.estimate_gas ?? true
|
|
32590
32592
|
};
|
|
32591
|
-
const
|
|
32592
|
-
if (typeof
|
|
32593
|
-
simulation.block_number =
|
|
32593
|
+
const blockNumber2 = sim.block_number ?? args?.block_number;
|
|
32594
|
+
if (typeof blockNumber2 === "number") {
|
|
32595
|
+
simulation.block_number = blockNumber2;
|
|
32594
32596
|
} else {
|
|
32595
32597
|
delete simulation.block_number;
|
|
32596
32598
|
}
|
|
@@ -32984,7 +32986,7 @@ function makeGatewayTool(key, name, method, description, properties, required, b
|
|
|
32984
32986
|
httpPath: "/{network}.gateway.tenderly.co/{accessKey}",
|
|
32985
32987
|
operationId: `tenderly-${method.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`
|
|
32986
32988
|
};
|
|
32987
|
-
const
|
|
32989
|
+
const tool101 = {
|
|
32988
32990
|
name,
|
|
32989
32991
|
title: name.replace(/^tenderly_/, "").replace(/_/g, " ").replace(/\b\w/g, (character) => character.toUpperCase()),
|
|
32990
32992
|
description: `${description}
|
|
@@ -33041,7 +33043,7 @@ Requires TENDERLY_NODE_ACCESS_KEY (or TENDERLY_GATEWAY_ACCESS_KEY). This is a re
|
|
|
33041
33043
|
);
|
|
33042
33044
|
}
|
|
33043
33045
|
};
|
|
33044
|
-
return { key, tool:
|
|
33046
|
+
return { key, tool: tool101, metadata: metadata103, handler: handler100 };
|
|
33045
33047
|
}
|
|
33046
33048
|
var accessListProp = {
|
|
33047
33049
|
type: "array",
|
|
@@ -36797,7 +36799,7 @@ function meta(resource, path, method) {
|
|
|
36797
36799
|
};
|
|
36798
36800
|
}
|
|
36799
36801
|
function dataPost(key, name, description, path, properties, bodyBuilder, required = [], schemaExtensions = {}) {
|
|
36800
|
-
const
|
|
36802
|
+
const tool101 = makeTool(
|
|
36801
36803
|
name,
|
|
36802
36804
|
description,
|
|
36803
36805
|
properties,
|
|
@@ -36807,7 +36809,7 @@ function dataPost(key, name, description, path, properties, bodyBuilder, require
|
|
|
36807
36809
|
);
|
|
36808
36810
|
return {
|
|
36809
36811
|
key,
|
|
36810
|
-
tool:
|
|
36812
|
+
tool: tool101,
|
|
36811
36813
|
metadata: meta(key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`), path, "post"),
|
|
36812
36814
|
handler: async (clientParam, args = {}) => {
|
|
36813
36815
|
const client2 = resolveClient3(clientParam);
|
|
@@ -36822,7 +36824,7 @@ function dataPost(key, name, description, path, properties, bodyBuilder, require
|
|
|
36822
36824
|
};
|
|
36823
36825
|
}
|
|
36824
36826
|
function dataGet(key, name, description, path, properties, queryBuilder, required = [], schemaExtensions = {}) {
|
|
36825
|
-
const
|
|
36827
|
+
const tool101 = makeTool(
|
|
36826
36828
|
name,
|
|
36827
36829
|
description,
|
|
36828
36830
|
properties,
|
|
@@ -36832,7 +36834,7 @@ function dataGet(key, name, description, path, properties, queryBuilder, require
|
|
|
36832
36834
|
);
|
|
36833
36835
|
return {
|
|
36834
36836
|
key,
|
|
36835
|
-
tool:
|
|
36837
|
+
tool: tool101,
|
|
36836
36838
|
metadata: meta(key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`), path, "get"),
|
|
36837
36839
|
handler: async (clientParam, args = {}) => {
|
|
36838
36840
|
const client2 = resolveClient3(clientParam);
|
|
@@ -36847,7 +36849,7 @@ function dataGet(key, name, description, path, properties, queryBuilder, require
|
|
|
36847
36849
|
};
|
|
36848
36850
|
}
|
|
36849
36851
|
function nftGet(key, name, description, path, properties, queryBuilder, required = [], annotations = volatileAnnotations3, schemaExtensions = {}) {
|
|
36850
|
-
const
|
|
36852
|
+
const tool101 = makeTool(
|
|
36851
36853
|
name,
|
|
36852
36854
|
description,
|
|
36853
36855
|
properties,
|
|
@@ -36857,7 +36859,7 @@ function nftGet(key, name, description, path, properties, queryBuilder, required
|
|
|
36857
36859
|
);
|
|
36858
36860
|
return {
|
|
36859
36861
|
key,
|
|
36860
|
-
tool:
|
|
36862
|
+
tool: tool101,
|
|
36861
36863
|
metadata: meta(`nft.${key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)}`, path, "get"),
|
|
36862
36864
|
handler: async (clientParam, args = {}) => {
|
|
36863
36865
|
const client2 = resolveClient3(clientParam);
|
|
@@ -36877,7 +36879,7 @@ function nftGet(key, name, description, path, properties, queryBuilder, required
|
|
|
36877
36879
|
};
|
|
36878
36880
|
}
|
|
36879
36881
|
function nftPost(key, name, description, path, properties, bodyBuilder, required = [], annotations = volatileAnnotations3, schemaExtensions = {}) {
|
|
36880
|
-
const
|
|
36882
|
+
const tool101 = makeTool(
|
|
36881
36883
|
name,
|
|
36882
36884
|
description,
|
|
36883
36885
|
properties,
|
|
@@ -36887,7 +36889,7 @@ function nftPost(key, name, description, path, properties, bodyBuilder, required
|
|
|
36887
36889
|
);
|
|
36888
36890
|
return {
|
|
36889
36891
|
key,
|
|
36890
|
-
tool:
|
|
36892
|
+
tool: tool101,
|
|
36891
36893
|
metadata: meta(`nft.${key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)}`, path, "post"),
|
|
36892
36894
|
handler: async (clientParam, args = {}) => {
|
|
36893
36895
|
const client2 = resolveClient3(clientParam);
|
|
@@ -36907,7 +36909,7 @@ function nftPost(key, name, description, path, properties, bodyBuilder, required
|
|
|
36907
36909
|
};
|
|
36908
36910
|
}
|
|
36909
36911
|
function rpcTool(key, name, description, method, properties, paramsBuilder, required = [], schemaExtensions = {}, defaultNetwork = "eth-mainnet") {
|
|
36910
|
-
const
|
|
36912
|
+
const tool101 = makeTool(
|
|
36911
36913
|
name,
|
|
36912
36914
|
description,
|
|
36913
36915
|
properties,
|
|
@@ -36917,7 +36919,7 @@ function rpcTool(key, name, description, method, properties, paramsBuilder, requ
|
|
|
36917
36919
|
);
|
|
36918
36920
|
return {
|
|
36919
36921
|
key,
|
|
36920
|
-
tool:
|
|
36922
|
+
tool: tool101,
|
|
36921
36923
|
metadata: meta(`rpc.${key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)}`, "/v2/{apiKey}", "post"),
|
|
36922
36924
|
handler: async (clientParam, args = {}) => {
|
|
36923
36925
|
const client2 = resolveClient3(clientParam);
|
|
@@ -38691,8 +38693,8 @@ var ALCHEMY_ARRAY_LIMITS = {
|
|
|
38691
38693
|
networks: 20,
|
|
38692
38694
|
category: 10
|
|
38693
38695
|
};
|
|
38694
|
-
function withAlchemyBounds(
|
|
38695
|
-
const schema =
|
|
38696
|
+
function withAlchemyBounds(tool101) {
|
|
38697
|
+
const schema = tool101.inputSchema;
|
|
38696
38698
|
const properties = Object.fromEntries(
|
|
38697
38699
|
Object.entries(schema.properties || {}).map(
|
|
38698
38700
|
([name, rawProperty]) => {
|
|
@@ -38714,7 +38716,7 @@ function withAlchemyBounds(tool100) {
|
|
|
38714
38716
|
}
|
|
38715
38717
|
)
|
|
38716
38718
|
);
|
|
38717
|
-
return { ...
|
|
38719
|
+
return { ...tool101, inputSchema: { ...schema, properties } };
|
|
38718
38720
|
}
|
|
38719
38721
|
var activeDefinitions = definitions.map((definition) => ({
|
|
38720
38722
|
...definition,
|
|
@@ -38743,6 +38745,24 @@ function getAllAlchemyEndpoints() {
|
|
|
38743
38745
|
return endpoints;
|
|
38744
38746
|
}
|
|
38745
38747
|
|
|
38748
|
+
// src/config/removedVenues.ts
|
|
38749
|
+
var REMOVED_CCXT_EXCHANGES = /* @__PURE__ */ new Set([
|
|
38750
|
+
"bitmex",
|
|
38751
|
+
"coinex",
|
|
38752
|
+
"coinbaseinternational",
|
|
38753
|
+
"dydx",
|
|
38754
|
+
"woofipro"
|
|
38755
|
+
]);
|
|
38756
|
+
var REMOVED_HYPERLIQUID_DEXES = /* @__PURE__ */ new Set([
|
|
38757
|
+
"km",
|
|
38758
|
+
"mkts",
|
|
38759
|
+
"para",
|
|
38760
|
+
"io"
|
|
38761
|
+
]);
|
|
38762
|
+
function removedVenueMessage(venue) {
|
|
38763
|
+
return `Invalid arguments: Hive does not serve the venue "${venue}" (removed 2026-10-26). Pick another venue.`;
|
|
38764
|
+
}
|
|
38765
|
+
|
|
38746
38766
|
// src/tools/hyperliquid/api/hyperliquid.ts
|
|
38747
38767
|
import axios3 from "axios";
|
|
38748
38768
|
var HYPERLIQUID_API_URL = "https://api.hyperliquid.xyz";
|
|
@@ -38773,6 +38793,13 @@ var HyperliquidAPI = class {
|
|
|
38773
38793
|
"User-Agent": "hive-mcp-server/1.0.0"
|
|
38774
38794
|
}
|
|
38775
38795
|
});
|
|
38796
|
+
this.client.interceptors.request.use((config2) => {
|
|
38797
|
+
const dex = config2.data?.dex;
|
|
38798
|
+
if (typeof dex === "string" && REMOVED_HYPERLIQUID_DEXES.has(dex)) {
|
|
38799
|
+
throw new Error(removedVenueMessage(`hyperliquid:${dex}`));
|
|
38800
|
+
}
|
|
38801
|
+
return config2;
|
|
38802
|
+
});
|
|
38776
38803
|
this.client.interceptors.response.use(
|
|
38777
38804
|
(response) => response,
|
|
38778
38805
|
(error) => {
|
|
@@ -38838,7 +38865,9 @@ var HyperliquidAPI = class {
|
|
|
38838
38865
|
name: String(d.name ?? ""),
|
|
38839
38866
|
fullName: d.full_name ?? d.fullName ?? null,
|
|
38840
38867
|
deployer: d.deployer ?? null
|
|
38841
|
-
})).filter(
|
|
38868
|
+
})).filter(
|
|
38869
|
+
(d) => d.name.length > 0 && !REMOVED_HYPERLIQUID_DEXES.has(d.name)
|
|
38870
|
+
);
|
|
38842
38871
|
return { builderDexCount: builderDexs.length, builderDexs };
|
|
38843
38872
|
}
|
|
38844
38873
|
/**
|
|
@@ -39103,17 +39132,17 @@ var HEADROOM_TOOL_PATTERNS = [
|
|
|
39103
39132
|
function toolNeedsResponseHeadroom(toolName) {
|
|
39104
39133
|
return HEADROOM_TOOL_PATTERNS.some((pattern) => pattern.test(toolName));
|
|
39105
39134
|
}
|
|
39106
|
-
function getAnnotatedMaxResultSizeChars(
|
|
39107
|
-
const meta2 =
|
|
39135
|
+
function getAnnotatedMaxResultSizeChars(tool101) {
|
|
39136
|
+
const meta2 = tool101?._meta;
|
|
39108
39137
|
const value = meta2?.[MAX_RESULT_SIZE_META_KEY];
|
|
39109
39138
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
39110
39139
|
}
|
|
39111
|
-
function withResponseHeadroomMeta(
|
|
39112
|
-
if (!toolNeedsResponseHeadroom(
|
|
39113
|
-
const existing =
|
|
39114
|
-
if (existing && existing[MAX_RESULT_SIZE_META_KEY] !== void 0) return
|
|
39140
|
+
function withResponseHeadroomMeta(tool101) {
|
|
39141
|
+
if (!toolNeedsResponseHeadroom(tool101.name)) return tool101;
|
|
39142
|
+
const existing = tool101._meta;
|
|
39143
|
+
if (existing && existing[MAX_RESULT_SIZE_META_KEY] !== void 0) return tool101;
|
|
39115
39144
|
return {
|
|
39116
|
-
...
|
|
39145
|
+
...tool101,
|
|
39117
39146
|
_meta: {
|
|
39118
39147
|
...existing ?? {},
|
|
39119
39148
|
[MAX_RESULT_SIZE_META_KEY]: ANNOTATED_MAX_RESULT_SIZE_CHARS
|
|
@@ -39720,7 +39749,7 @@ var HYPERLIQUID_INFO_DESCRIPTORS = [
|
|
|
39720
39749
|
"You need outcome identifiers for hyperliquid_get_settled_outcome"
|
|
39721
39750
|
],
|
|
39722
39751
|
dontUseWhen: [
|
|
39723
|
-
"You need Polymarket markets (use the polymarket_ tools) or Kalshi markets (
|
|
39752
|
+
"You need Polymarket markets (use the polymarket_ tools) or Kalshi markets (use the kalshi_ tools)"
|
|
39724
39753
|
],
|
|
39725
39754
|
params: []
|
|
39726
39755
|
},
|
|
@@ -40480,8 +40509,6 @@ function nextIntervalBoundaryIso(intervalHours, now = Date.now()) {
|
|
|
40480
40509
|
return new Date(Math.ceil(now / ms) * ms).toISOString();
|
|
40481
40510
|
}
|
|
40482
40511
|
var HOURS_PER_YEAR = 24 * 365;
|
|
40483
|
-
var ARBITRUM_BLOCKS_PER_YEAR = 365.25 * 24 * 3600 / 0.25;
|
|
40484
|
-
var OSTIUM_PRECISION_18 = 1e18;
|
|
40485
40512
|
function annualizeFundingRate(ratePerInterval, intervalHours) {
|
|
40486
40513
|
if (ratePerInterval == null || !Number.isFinite(ratePerInterval)) {
|
|
40487
40514
|
return null;
|
|
@@ -40489,16 +40516,6 @@ function annualizeFundingRate(ratePerInterval, intervalHours) {
|
|
|
40489
40516
|
if (!Number.isFinite(intervalHours) || intervalHours <= 0) return null;
|
|
40490
40517
|
return ratePerInterval * (HOURS_PER_YEAR / intervalHours);
|
|
40491
40518
|
}
|
|
40492
|
-
function ostiumRolloverAnnualFraction(rolloverFeePerBlockRaw) {
|
|
40493
|
-
if (!rolloverFeePerBlockRaw) return null;
|
|
40494
|
-
const perBlock = Number(rolloverFeePerBlockRaw) / OSTIUM_PRECISION_18;
|
|
40495
|
-
if (!Number.isFinite(perBlock) || perBlock < 0) return null;
|
|
40496
|
-
return perBlock * ARBITRUM_BLOCKS_PER_YEAR;
|
|
40497
|
-
}
|
|
40498
|
-
function avantisMarginFeeAnnualFraction(percentPerHour) {
|
|
40499
|
-
if (percentPerHour == null || !Number.isFinite(percentPerHour)) return null;
|
|
40500
|
-
return percentPerHour * HOURS_PER_YEAR / 100;
|
|
40501
|
-
}
|
|
40502
40519
|
var ETFS = /* @__PURE__ */ new Set([
|
|
40503
40520
|
"SPY",
|
|
40504
40521
|
"QQQ",
|
|
@@ -40747,117 +40764,6 @@ function stampPriceSuspect(rows) {
|
|
|
40747
40764
|
return rows;
|
|
40748
40765
|
}
|
|
40749
40766
|
|
|
40750
|
-
// src/tools/rwa/venues/avantis.ts
|
|
40751
|
-
var AVANTIS_DATA_URL = "https://socket-api-pub.avantisfi.com/socket-api/v1/data";
|
|
40752
|
-
var AVANTIS_EQUITIES_GROUP_NAME = "EQUITIES";
|
|
40753
|
-
var AVANTIS_EQUITY_ASSET_TYPE = "equity";
|
|
40754
|
-
var AVANTIS_QUOTE_INTERVAL_HOURS = 1;
|
|
40755
|
-
function resolveEquitiesGroupIndex(groupInfo) {
|
|
40756
|
-
for (const [index, group] of Object.entries(groupInfo ?? {})) {
|
|
40757
|
-
if (group?.name?.toUpperCase() === AVANTIS_EQUITIES_GROUP_NAME) {
|
|
40758
|
-
const parsed = parseFiniteNumber(index);
|
|
40759
|
-
if (parsed != null) return parsed;
|
|
40760
|
-
}
|
|
40761
|
-
}
|
|
40762
|
-
return null;
|
|
40763
|
-
}
|
|
40764
|
-
async function fetchAvantisRows(fetchJson = defaultRwaFetchJson) {
|
|
40765
|
-
const payload = await fetchJson(
|
|
40766
|
-
AVANTIS_DATA_URL,
|
|
40767
|
-
void 0,
|
|
40768
|
-
"avantis socket-api data"
|
|
40769
|
-
);
|
|
40770
|
-
const data = payload?.data;
|
|
40771
|
-
const pairInfos = data?.pairInfos;
|
|
40772
|
-
if (!pairInfos || typeof pairInfos !== "object") {
|
|
40773
|
-
throw new Error("avantis socket-api returned unexpected shape");
|
|
40774
|
-
}
|
|
40775
|
-
const equitiesGroupIndex = resolveEquitiesGroupIndex(data?.groupInfo);
|
|
40776
|
-
if (equitiesGroupIndex == null) {
|
|
40777
|
-
throw new Error("avantis socket-api exposed no EQUITIES group");
|
|
40778
|
-
}
|
|
40779
|
-
const equities = [];
|
|
40780
|
-
let equitiesGroupPairCount = 0;
|
|
40781
|
-
for (const [key, pair] of Object.entries(pairInfos)) {
|
|
40782
|
-
if (pair?.groupIndex !== equitiesGroupIndex) continue;
|
|
40783
|
-
equitiesGroupPairCount += 1;
|
|
40784
|
-
const attributes = pair.feed?.attributes;
|
|
40785
|
-
const assetType = attributes?.assetType ?? attributes?.asset_type;
|
|
40786
|
-
if (assetType !== AVANTIS_EQUITY_ASSET_TYPE) continue;
|
|
40787
|
-
equities.push({ key, pair });
|
|
40788
|
-
}
|
|
40789
|
-
if (equitiesGroupPairCount > 0 && equities.length === 0) {
|
|
40790
|
-
throw new Error(
|
|
40791
|
-
`avantis EQUITIES group has ${equitiesGroupPairCount} pairs but none matched the equity identity rule \u2014 upstream schema drift`
|
|
40792
|
-
);
|
|
40793
|
-
}
|
|
40794
|
-
const rows = [];
|
|
40795
|
-
for (const { key, pair } of equities) {
|
|
40796
|
-
const rawSymbol = pair.from;
|
|
40797
|
-
if (typeof rawSymbol !== "string" || rawSymbol.trim() === "") continue;
|
|
40798
|
-
const symbol = rawSymbol.toUpperCase().replace(/[^A-Z0-9]/g, "");
|
|
40799
|
-
if (symbol === "") continue;
|
|
40800
|
-
const marginFeeLong = parseFiniteNumber(pair.marginFee?.long);
|
|
40801
|
-
const marginFeeShort = parseFiniteNumber(pair.marginFee?.short);
|
|
40802
|
-
const borrowAprAnnual = avantisMarginFeeAnnualFraction(marginFeeLong);
|
|
40803
|
-
const attributes = pair.feed?.attributes;
|
|
40804
|
-
const feedId = pair.feed?.feedId;
|
|
40805
|
-
const quote = typeof pair.to === "string" ? pair.to : "USD";
|
|
40806
|
-
rows.push({
|
|
40807
|
-
venue: "avantis",
|
|
40808
|
-
market: `${symbol}/${quote}`,
|
|
40809
|
-
symbol,
|
|
40810
|
-
underlying: canonicalRwaUnderlying(symbol),
|
|
40811
|
-
asset_class: classifyRwaAssetClass(symbol),
|
|
40812
|
-
funding_mechanism: "funding_plus_borrow",
|
|
40813
|
-
funding_rate: null,
|
|
40814
|
-
funding_interval_hours: AVANTIS_QUOTE_INTERVAL_HOURS,
|
|
40815
|
-
funding_annualized: null,
|
|
40816
|
-
borrow_apr_annual: borrowAprAnnual,
|
|
40817
|
-
// Avantis publishes no prices; no Pyth integration exists here, so the
|
|
40818
|
-
// honest mark from the venue's own API is null.
|
|
40819
|
-
mark_price: null,
|
|
40820
|
-
oracle_price: null,
|
|
40821
|
-
// `pairOI` is ALREADY USD notional — do not multiply by price
|
|
40822
|
-
// (verified: summing pairOI across the group matches the venue's own
|
|
40823
|
-
// groupOI to the cent).
|
|
40824
|
-
open_interest_usd: parseFiniteNumber(pair.pairOI),
|
|
40825
|
-
// Rollover venue with no published 24h traded notional.
|
|
40826
|
-
volume_24h_usd: null,
|
|
40827
|
-
next_funding_time: null,
|
|
40828
|
-
is_delisted: false,
|
|
40829
|
-
is_price_suspect: false,
|
|
40830
|
-
price_suspect_basis: "insufficient_venues",
|
|
40831
|
-
price_deviation_pct: null,
|
|
40832
|
-
meta: {
|
|
40833
|
-
// The rollover is charged on COLLATERAL, not notional: ~10%/yr at
|
|
40834
|
-
// 5x max leverage is only ~2%/yr on notional. Not comparable to a
|
|
40835
|
-
// notional-based funding rate without this caveat.
|
|
40836
|
-
charged_on: "collateral",
|
|
40837
|
-
applies_to: "both_sides",
|
|
40838
|
-
pair_index: parseFiniteNumber(pair.index) ?? parseFiniteNumber(key),
|
|
40839
|
-
margin_fee_percent_per_hour: marginFeeLong,
|
|
40840
|
-
// Long and short match on all but one pair; when they diverge both
|
|
40841
|
-
// sides are recorded.
|
|
40842
|
-
margin_fee_short_percent_per_hour: marginFeeShort !== marginFeeLong ? marginFeeShort : null,
|
|
40843
|
-
max_leverage: parseFiniteNumber(pair.leverages?.maxLeverage),
|
|
40844
|
-
min_borrow_fee_annual_percent: parseFiniteNumber(
|
|
40845
|
-
pair.storagePairParams?.minBorrowFee
|
|
40846
|
-
),
|
|
40847
|
-
long_oi_usd: parseFiniteNumber(pair.openInterest?.long),
|
|
40848
|
-
short_oi_usd: parseFiniteNumber(pair.openInterest?.short),
|
|
40849
|
-
is_open: attributes?.isOpen ?? null,
|
|
40850
|
-
next_open: attributes?.nextOpen ?? null,
|
|
40851
|
-
schedule: attributes?.schedule ?? null,
|
|
40852
|
-
// For agents with their own oracle access (e.g. Pyth Hermes).
|
|
40853
|
-
pyth_feed_id: typeof feedId === "string" ? feedId : null,
|
|
40854
|
-
identified_by: "group-index+pyth-asset-type"
|
|
40855
|
-
}
|
|
40856
|
-
});
|
|
40857
|
-
}
|
|
40858
|
-
return rows;
|
|
40859
|
-
}
|
|
40860
|
-
|
|
40861
40767
|
// src/tools/rwa/venues/extended.ts
|
|
40862
40768
|
var EXTENDED_BASE = "https://api.starknet.extended.exchange/api/v1";
|
|
40863
40769
|
var EXTENDED_INTERVAL_HOURS = 1;
|
|
@@ -41175,89 +41081,9 @@ async function fetchLighterRows(fetchJson = defaultRwaFetchJson) {
|
|
|
41175
41081
|
return rows;
|
|
41176
41082
|
}
|
|
41177
41083
|
|
|
41178
|
-
// src/tools/rwa/venues/ostium.ts
|
|
41179
|
-
var OSTIUM_SUBGRAPH_URL = "https://api.subgraph.ormilabs.com/api/public/67a599d5-c8d2-4cc4-9c4d-2975a97bc5d8/subgraphs/ost-prod/live/gn";
|
|
41180
|
-
var OSTIUM_PRECISION_182 = 1e18;
|
|
41181
|
-
var OSTIUM_PAIRS_QUERY = "{ pairs(first: 300) { id from group { name } rolloverFeePerBlock lastTradePrice longOI shortOI } }";
|
|
41182
|
-
var GROUP_ASSET_CLASS = {
|
|
41183
|
-
stocks: "equity",
|
|
41184
|
-
indices: "index",
|
|
41185
|
-
commodities: "commodity",
|
|
41186
|
-
forex: "fx",
|
|
41187
|
-
etf: "etf",
|
|
41188
|
-
crypto: "crypto"
|
|
41189
|
-
};
|
|
41190
|
-
async function fetchOstiumRows(fetchJson = defaultRwaFetchJson) {
|
|
41191
|
-
const payload = await fetchJson(
|
|
41192
|
-
OSTIUM_SUBGRAPH_URL,
|
|
41193
|
-
{
|
|
41194
|
-
method: "POST",
|
|
41195
|
-
headers: { "Content-Type": "application/json" },
|
|
41196
|
-
body: JSON.stringify({ query: OSTIUM_PAIRS_QUERY })
|
|
41197
|
-
},
|
|
41198
|
-
"ostium subgraph pairs"
|
|
41199
|
-
);
|
|
41200
|
-
const pairs = payload?.data?.pairs;
|
|
41201
|
-
if (!Array.isArray(pairs)) {
|
|
41202
|
-
throw new Error("ostium subgraph returned unexpected shape");
|
|
41203
|
-
}
|
|
41204
|
-
const rows = [];
|
|
41205
|
-
for (const pair of pairs) {
|
|
41206
|
-
const symbol = pair.from?.toUpperCase();
|
|
41207
|
-
if (!symbol) continue;
|
|
41208
|
-
const groupName = pair.group?.name?.toLowerCase() ?? "";
|
|
41209
|
-
const venueClass = GROUP_ASSET_CLASS[groupName];
|
|
41210
|
-
const borrowAprAnnual = ostiumRolloverAnnualFraction(
|
|
41211
|
-
pair.rolloverFeePerBlock
|
|
41212
|
-
);
|
|
41213
|
-
if (borrowAprAnnual == null) continue;
|
|
41214
|
-
const markPrice = (() => {
|
|
41215
|
-
const scaled = parseFiniteNumber(pair.lastTradePrice);
|
|
41216
|
-
if (scaled == null) return null;
|
|
41217
|
-
const price = scaled / OSTIUM_PRECISION_182;
|
|
41218
|
-
return price > 0 && price < 1e6 ? price : null;
|
|
41219
|
-
})();
|
|
41220
|
-
const longUnits = parseFiniteNumber(pair.longOI);
|
|
41221
|
-
const shortUnits = parseFiniteNumber(pair.shortOI);
|
|
41222
|
-
const openInterestUsd = markPrice != null && longUnits != null && shortUnits != null ? (longUnits + shortUnits) / OSTIUM_PRECISION_182 * markPrice : null;
|
|
41223
|
-
rows.push({
|
|
41224
|
-
venue: "ostium",
|
|
41225
|
-
market: symbol,
|
|
41226
|
-
symbol,
|
|
41227
|
-
underlying: canonicalRwaUnderlying(symbol),
|
|
41228
|
-
asset_class: classifyRwaAssetClass(symbol, venueClass),
|
|
41229
|
-
funding_mechanism: "funding_plus_borrow",
|
|
41230
|
-
funding_rate: null,
|
|
41231
|
-
funding_interval_hours: 1,
|
|
41232
|
-
funding_annualized: null,
|
|
41233
|
-
borrow_apr_annual: borrowAprAnnual,
|
|
41234
|
-
mark_price: markPrice,
|
|
41235
|
-
oracle_price: null,
|
|
41236
|
-
open_interest_usd: openInterestUsd,
|
|
41237
|
-
// Ostium is a rollover venue with no published 24h traded notional.
|
|
41238
|
-
volume_24h_usd: null,
|
|
41239
|
-
next_funding_time: null,
|
|
41240
|
-
is_delisted: false,
|
|
41241
|
-
is_price_suspect: false,
|
|
41242
|
-
price_suspect_basis: "insufficient_venues",
|
|
41243
|
-
price_deviation_pct: null,
|
|
41244
|
-
meta: {
|
|
41245
|
-
pair_id: pair.id ?? null,
|
|
41246
|
-
group: groupName,
|
|
41247
|
-
rollover_fee_per_block_raw: pair.rolloverFeePerBlock ?? null,
|
|
41248
|
-
// Rollover accrues per block and is charged to BOTH sides.
|
|
41249
|
-
applies_to: "both_sides"
|
|
41250
|
-
}
|
|
41251
|
-
});
|
|
41252
|
-
}
|
|
41253
|
-
return rows;
|
|
41254
|
-
}
|
|
41255
|
-
|
|
41256
41084
|
// src/tools/rwa/venues/index.ts
|
|
41257
41085
|
var RWA_VENUE_IDS = [
|
|
41258
41086
|
"hyperliquid_dexes",
|
|
41259
|
-
"ostium",
|
|
41260
|
-
"avantis",
|
|
41261
41087
|
"lighter",
|
|
41262
41088
|
"extended"
|
|
41263
41089
|
];
|
|
@@ -41279,10 +41105,6 @@ async function fetchRwaVenueRows(venue, deps) {
|
|
|
41279
41105
|
note: noteParts.length > 0 ? noteParts.join(" | ") : void 0
|
|
41280
41106
|
};
|
|
41281
41107
|
}
|
|
41282
|
-
case "ostium":
|
|
41283
|
-
return { rows: await fetchOstiumRows(deps.fetchJson) };
|
|
41284
|
-
case "avantis":
|
|
41285
|
-
return { rows: await fetchAvantisRows(deps.fetchJson) };
|
|
41286
41108
|
case "lighter":
|
|
41287
41109
|
return { rows: await fetchLighterRows(deps.fetchJson) };
|
|
41288
41110
|
case "extended":
|
|
@@ -41419,7 +41241,7 @@ var FILTER_PARAM_PROPERTIES = {
|
|
|
41419
41241
|
},
|
|
41420
41242
|
venue: {
|
|
41421
41243
|
type: "string",
|
|
41422
|
-
description: 'Venue filter: "
|
|
41244
|
+
description: 'Venue filter: "lighter", "extended", "hyperliquid" (all builder dexes), or a specific builder dex like "hyperliquid:xyz".'
|
|
41423
41245
|
},
|
|
41424
41246
|
asset_class: {
|
|
41425
41247
|
type: "string",
|
|
@@ -41541,7 +41363,7 @@ function getAllRwaEndpoints() {
|
|
|
41541
41363
|
},
|
|
41542
41364
|
tool: {
|
|
41543
41365
|
name: "rwa_get_funding_rates",
|
|
41544
|
-
description: `Get current funding for tokenized real-world-asset perps (stocks, pre-IPO names, ETFs, indices, commodities, FX) normalized across all
|
|
41366
|
+
description: `Get current funding for tokenized real-world-asset perps (stocks, pre-IPO names, ETFs, indices, commodities, FX) normalized across all three RWA venues in ONE call: Hyperliquid HIP-3 builder dexes (each dex its own venue), Lighter, and Extended.
|
|
41545
41367
|
|
|
41546
41368
|
Venues are queried with per-venue error isolation: every venue reports its own runtime_status, so one failing venue never hides the others \u2014 partial results always flow.
|
|
41547
41369
|
|
|
@@ -41549,7 +41371,7 @@ ${ROW_CONTRACT}
|
|
|
41549
41371
|
|
|
41550
41372
|
Args:
|
|
41551
41373
|
- underlying (string, optional): filter to one canonical underlying (e.g. "TSLA").
|
|
41552
|
-
- venue (string, optional): filter to one venue ("
|
|
41374
|
+
- venue (string, optional): filter to one venue ("lighter", "extended", "hyperliquid", or "hyperliquid:xyz").
|
|
41553
41375
|
- asset_class (string, optional): filter to one asset class (equity, pre_ipo, etf, index, commodity, fx, crypto).
|
|
41554
41376
|
- limit (integer, optional): max rows (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}), deepest open interest first.
|
|
41555
41377
|
- offset (integer, optional): rows to skip; pass next_offset to page.
|
|
@@ -41614,7 +41436,7 @@ Don't use when:
|
|
|
41614
41436
|
},
|
|
41615
41437
|
tool: {
|
|
41616
41438
|
name: "rwa_get_markets",
|
|
41617
|
-
description: `Get the tokenized real-world-asset perp UNIVERSE across all
|
|
41439
|
+
description: `Get the tokenized real-world-asset perp UNIVERSE across all three RWA venues (Hyperliquid HIP-3 builder dexes, Lighter, Extended): every listed market with asset_class, open interest where the venue exposes it, mark/oracle price, and the delisted/price-suspect flags. Funding columns are omitted \u2014 use rwa_get_funding_rates for carry.
|
|
41618
41440
|
|
|
41619
41441
|
Venues are queried with per-venue error isolation (per-venue runtime_status; partial results always flow).
|
|
41620
41442
|
|
|
@@ -41689,7 +41511,7 @@ Don't use when:
|
|
|
41689
41511
|
returnsNote: 'A "note" field reports partial dex failures. Delisted HIP-3 markets ship with is_delisted: true and a FROZEN mark price \u2014 never trust their price.',
|
|
41690
41512
|
useWhen: [
|
|
41691
41513
|
"You want the full HIP-3 equity/pre-IPO perp landscape in one normalized call",
|
|
41692
|
-
"You are checking builder-set funding on a specific dex (xyz,
|
|
41514
|
+
"You are checking builder-set funding on a specific dex (xyz, flx, cash, ...)"
|
|
41693
41515
|
],
|
|
41694
41516
|
dontUseWhen: [
|
|
41695
41517
|
"You need main-dex Hyperliquid crypto perps (use hyperliquid_get_meta / hyperliquid_get_perp_volume_leaderboard)",
|
|
@@ -41697,38 +41519,6 @@ Don't use when:
|
|
|
41697
41519
|
],
|
|
41698
41520
|
deps
|
|
41699
41521
|
}),
|
|
41700
|
-
venueRawEndpoint({
|
|
41701
|
-
toolName: "rwa_get_ostium_markets",
|
|
41702
|
-
resource: "rwa-ostium-markets",
|
|
41703
|
-
venueId: "ostium",
|
|
41704
|
-
tags: ["rwa", "ostium", "equity-perps", "rollover", "commodities", "fx"],
|
|
41705
|
-
summary: `Get Ostium's RWA perp markets (stocks, indices, commodities, FX; crypto pairs stamped "crypto") from the official public subgraph. Ostium charges NO long/short funding: the holding cost is a carry-anchored rollover accrued per Arbitrum block and charged to BOTH sides, so rows carry funding_mechanism "funding_plus_borrow" with the cost in borrow_apr_annual and funding_rate null.`,
|
|
41706
|
-
returnsNote: "borrow_apr_annual is annualized from the on-chain per-block rollover (PRECISION_18, 250ms Arbitrum blocks). It is a both-sides cost \u2014 never compare its sign against a funding transfer.",
|
|
41707
|
-
useWhen: [
|
|
41708
|
-
"You need Ostium's rollover (holding cost) for stock/commodity/FX pairs",
|
|
41709
|
-
"You are auditing the raw per-block rollover behind the annualized number"
|
|
41710
|
-
],
|
|
41711
|
-
dontUseWhen: [
|
|
41712
|
-
"You want a cross-venue funding comparison (use rwa_get_funding_rates \u2014 Ostium's both-sides rollover is excluded from funding spreads by design)"
|
|
41713
|
-
],
|
|
41714
|
-
deps
|
|
41715
|
-
}),
|
|
41716
|
-
venueRawEndpoint({
|
|
41717
|
-
toolName: "rwa_get_avantis_markets",
|
|
41718
|
-
resource: "rwa-avantis-markets",
|
|
41719
|
-
venueId: "avantis",
|
|
41720
|
-
tags: ["rwa", "avantis", "equity-perps", "rollover", "open-interest"],
|
|
41721
|
-
summary: `Get Avantis's equity perp markets (Base) from the official public data API: listing, USD open interest, both-sides rollover fees, and market hours. Avantis has NO funding and publishes NO prices (pairs name a Pyth feed instead), so rows carry funding_mechanism "funding_plus_borrow", the rollover in borrow_apr_annual, and mark_price null with the pyth_feed_id in meta.`,
|
|
41722
|
-
returnsNote: "The rollover is charged on COLLATERAL, not notional (meta.charged_on) \u2014 at 5x max leverage a 10%/yr collateral charge is ~2%/yr on notional. open_interest_usd comes from the venue's own USD pairOI field.",
|
|
41723
|
-
useWhen: [
|
|
41724
|
-
"You need Avantis equity-perp open interest, fees, or market hours",
|
|
41725
|
-
"You have your own Pyth access and want the feed ids to price these pairs"
|
|
41726
|
-
],
|
|
41727
|
-
dontUseWhen: [
|
|
41728
|
-
"You need a mark price from this venue (Avantis publishes none; cross-venue marks live in rwa_get_funding_rates rows from other venues)"
|
|
41729
|
-
],
|
|
41730
|
-
deps
|
|
41731
|
-
}),
|
|
41732
41522
|
venueRawEndpoint({
|
|
41733
41523
|
toolName: "rwa_get_lighter_markets",
|
|
41734
41524
|
resource: "rwa-lighter-markets",
|
|
@@ -41765,55 +41555,51 @@ Don't use when:
|
|
|
41765
41555
|
}
|
|
41766
41556
|
|
|
41767
41557
|
// src/tools/archive/client.ts
|
|
41768
|
-
|
|
41769
|
-
var
|
|
41770
|
-
var
|
|
41771
|
-
var
|
|
41772
|
-
var
|
|
41558
|
+
var ARCHIVE_URL_ENV = "CLICKHOUSE_URL";
|
|
41559
|
+
var ARCHIVE_PASSWORD_ENV = "CLICKHOUSE_READ_PASSWORD";
|
|
41560
|
+
var ARCHIVE_USER = "hive_read";
|
|
41561
|
+
var REQUEST_TIMEOUT_MS = 2e4;
|
|
41562
|
+
var ARCHIVE_MISSING_CREDENTIALS_MESSAGE = `Hive Archive credentials not configured \u2014 set ${ARCHIVE_URL_ENV} and ${ARCHIVE_PASSWORD_ENV} to enable archive_ history tools. Until then use the live derivatives tools (ccxt_fetch_funding_rates_multi, hyperliquid_get_funding_history) for current data.`;
|
|
41773
41563
|
function isArchiveConfigured() {
|
|
41774
|
-
return hasEnvVars(ARCHIVE_URL_ENV,
|
|
41564
|
+
return hasEnvVars(ARCHIVE_URL_ENV, ARCHIVE_PASSWORD_ENV);
|
|
41775
41565
|
}
|
|
41776
41566
|
function getArchiveClient() {
|
|
41777
41567
|
if (!isArchiveConfigured()) return null;
|
|
41778
|
-
|
|
41779
|
-
|
|
41780
|
-
|
|
41781
|
-
|
|
41782
|
-
|
|
41783
|
-
);
|
|
41784
|
-
|
|
41785
|
-
|
|
41568
|
+
const base = getEnvVar(ARCHIVE_URL_ENV);
|
|
41569
|
+
const auth = Buffer.from(
|
|
41570
|
+
`${ARCHIVE_USER}:${getEnvVar(ARCHIVE_PASSWORD_ENV)}`
|
|
41571
|
+
).toString("base64");
|
|
41572
|
+
return async (sql, params = {}) => {
|
|
41573
|
+
const url = new URL(base);
|
|
41574
|
+
url.searchParams.set("date_time_output_format", "iso");
|
|
41575
|
+
for (const [name, value] of Object.entries(params)) {
|
|
41576
|
+
url.searchParams.set(`param_${name}`, String(value));
|
|
41577
|
+
}
|
|
41578
|
+
const response = await fetch(url, {
|
|
41579
|
+
method: "POST",
|
|
41580
|
+
headers: { Authorization: `Basic ${auth}` },
|
|
41581
|
+
body: `${sql} FORMAT JSONEachRow`,
|
|
41582
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
41583
|
+
});
|
|
41584
|
+
const body = await response.text();
|
|
41585
|
+
if (!response.ok) {
|
|
41586
|
+
throw new Error(`ClickHouse ${response.status}: ${body.slice(0, 500)}`);
|
|
41587
|
+
}
|
|
41588
|
+
return body.split("\n").filter((line) => line !== "").map((line) => JSON.parse(line));
|
|
41589
|
+
};
|
|
41786
41590
|
}
|
|
41787
41591
|
|
|
41788
41592
|
// src/tools/archive/api.ts
|
|
41789
41593
|
var DEFAULT_LIMIT2 = 100;
|
|
41790
41594
|
var MAX_LIMIT2 = 500;
|
|
41791
|
-
var
|
|
41792
|
-
funding: "2023-11",
|
|
41793
|
-
open_interest: "2025-04",
|
|
41794
|
-
basis: "2025-03",
|
|
41795
|
-
long_short: "2026-03",
|
|
41796
|
-
liquidations: "2025-04"
|
|
41797
|
-
};
|
|
41798
|
-
var ARCHIVE_SINCE_NOTE = `archive since ${ARCHIVE_SINCE.funding}`;
|
|
41799
|
-
var QUERY_TIMEOUT_MS = 1e4;
|
|
41800
|
-
var VENUE_SCAN_MAX_STEPS = 64;
|
|
41595
|
+
var VENUE_LIST_MAX = 64;
|
|
41801
41596
|
var COVERAGE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
41802
41597
|
var COVERAGE_CACHE_MAX_ENTRIES = 16;
|
|
41803
|
-
var HOURS_PER_YEAR2 = 8760;
|
|
41804
|
-
function annualizeRate(rate, intervalHours) {
|
|
41805
|
-
if (!Number.isFinite(rate)) return null;
|
|
41806
|
-
if (!Number.isFinite(intervalHours) || intervalHours <= 0) return null;
|
|
41807
|
-
return rate * (HOURS_PER_YEAR2 / intervalHours);
|
|
41808
|
-
}
|
|
41809
41598
|
function clampLimit3(value) {
|
|
41810
41599
|
const parsed = typeof value === "number" ? Math.floor(value) : NaN;
|
|
41811
41600
|
if (!Number.isFinite(parsed)) return DEFAULT_LIMIT2;
|
|
41812
41601
|
return Math.min(Math.max(parsed, 1), MAX_LIMIT2);
|
|
41813
41602
|
}
|
|
41814
|
-
function escapeLikeLiteral(value) {
|
|
41815
|
-
return value.replace(/[\\%_]/g, (match) => `\\${match}`);
|
|
41816
|
-
}
|
|
41817
41603
|
var ArchiveInputError = class extends Error {
|
|
41818
41604
|
};
|
|
41819
41605
|
function normalizeBaseCoin(value) {
|
|
@@ -41856,96 +41642,129 @@ function parseHistoryArgs(args) {
|
|
|
41856
41642
|
};
|
|
41857
41643
|
}
|
|
41858
41644
|
function archiveQueryError(context, error) {
|
|
41859
|
-
const detail = error.message
|
|
41860
|
-
|
|
41861
|
-
if (error.code === "57014" || /timeout/i.test(detail)) {
|
|
41645
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
41646
|
+
if (/Code: (158|159|160|396)\b|TimeoutError|aborted|timeout/i.test(detail) || error instanceof Error && error.name === "TimeoutError") {
|
|
41862
41647
|
return new Error(
|
|
41863
|
-
`Hive Archive query timed out while ${context}
|
|
41648
|
+
`Hive Archive query timed out or hit its read limit while ${context}. Narrow the time range with from/to or lower limit, then retry.`
|
|
41864
41649
|
);
|
|
41865
41650
|
}
|
|
41866
|
-
if (/
|
|
41651
|
+
if (/ClickHouse (401|403)\b|Code: 516\b|AUTHENTICATION_FAILED/.test(detail)) {
|
|
41867
41652
|
return new Error(
|
|
41868
|
-
`Hive Archive rejected the configured credentials while ${context}
|
|
41653
|
+
`Hive Archive rejected the configured credentials while ${context}. The archive password needs rotation \u2014 this is a server configuration issue, not a query problem.`
|
|
41869
41654
|
);
|
|
41870
41655
|
}
|
|
41871
41656
|
return new Error(
|
|
41872
|
-
`Hive Archive query failed while ${context}
|
|
41657
|
+
`Hive Archive query failed while ${context}: ${detail}. Retry once; if it persists the archive is degraded \u2014 use live derivatives tools instead.`
|
|
41873
41658
|
);
|
|
41874
41659
|
}
|
|
41875
41660
|
var HISTORY_TABLES = {
|
|
41876
41661
|
funding: {
|
|
41877
|
-
table: "
|
|
41878
|
-
columns: "exchange,symbol,base_coin,rate,interval_hours,settled_at",
|
|
41879
|
-
tsColumn: "settled_at"
|
|
41662
|
+
table: "core.funding_settlements",
|
|
41663
|
+
columns: "venue AS exchange, venue_symbol AS symbol, base_coin, rate, interval_hours, settled_at, apr AS funding_annualized",
|
|
41664
|
+
tsColumn: "settled_at",
|
|
41665
|
+
numeric: ["rate", "funding_annualized"]
|
|
41880
41666
|
},
|
|
41881
41667
|
open_interest: {
|
|
41882
|
-
table: "
|
|
41883
|
-
columns: "exchange,base_coin,symbol,open_interest_value,timestamp",
|
|
41884
|
-
tsColumn: "
|
|
41668
|
+
table: "core.open_interest_hourly",
|
|
41669
|
+
columns: "venue AS exchange, base_coin, venue_symbol AS symbol, oi_usd AS open_interest_value, hour AS timestamp",
|
|
41670
|
+
tsColumn: "hour",
|
|
41671
|
+
numeric: ["open_interest_value"]
|
|
41885
41672
|
},
|
|
41886
41673
|
basis: {
|
|
41887
|
-
table: "
|
|
41888
|
-
columns: "exchange,base_coin,spot_price,futures_price,basis_rate,
|
|
41889
|
-
tsColumn: "
|
|
41674
|
+
table: "core.basis",
|
|
41675
|
+
columns: "venue AS exchange, base_coin, spot_price, futures_price, basis_rate, basis_pct, ts AS timestamp",
|
|
41676
|
+
tsColumn: "ts",
|
|
41677
|
+
numeric: ["spot_price", "futures_price", "basis_rate", "basis_pct"]
|
|
41890
41678
|
},
|
|
41891
41679
|
long_short: {
|
|
41892
|
-
table: "
|
|
41893
|
-
columns: "exchange,base_coin,symbol,long_ratio,short_ratio,timestamp",
|
|
41894
|
-
tsColumn: "
|
|
41680
|
+
table: "core.long_short_ratio",
|
|
41681
|
+
columns: "venue AS exchange, base_coin, venue_symbol AS symbol, ratio_kind, long_ratio, short_ratio, ts AS timestamp",
|
|
41682
|
+
tsColumn: "ts",
|
|
41683
|
+
numeric: ["long_ratio", "short_ratio"]
|
|
41895
41684
|
},
|
|
41896
41685
|
liquidations: {
|
|
41897
|
-
table: "
|
|
41898
|
-
columns: "exchange,base_coin,symbol,side,quantity,price,timestamp",
|
|
41899
|
-
tsColumn: "
|
|
41686
|
+
table: "core.liquidations",
|
|
41687
|
+
columns: "venue AS exchange, base_coin, venue_symbol AS symbol, side, qty_base AS quantity, price, notional_usd, event_time AS timestamp",
|
|
41688
|
+
tsColumn: "event_time",
|
|
41689
|
+
numeric: ["quantity", "price", "notional_usd"]
|
|
41900
41690
|
}
|
|
41901
41691
|
};
|
|
41902
|
-
|
|
41692
|
+
var TOP_TRADER_SUFFIX = /_top_trader$/i;
|
|
41693
|
+
function toNumbers(row, keys) {
|
|
41694
|
+
const out = { ...row };
|
|
41695
|
+
for (const key of keys) {
|
|
41696
|
+
const value = out[key];
|
|
41697
|
+
if (typeof value === "string") out[key] = Number(value);
|
|
41698
|
+
}
|
|
41699
|
+
return out;
|
|
41700
|
+
}
|
|
41701
|
+
async function fetchHistoryRows(query, dataset, filters) {
|
|
41903
41702
|
const spec = HISTORY_TABLES[dataset];
|
|
41904
|
-
|
|
41703
|
+
const where = ["base_coin = {base_coin:String}"];
|
|
41704
|
+
const params = { base_coin: filters.base_coin };
|
|
41905
41705
|
if (filters.exchange) {
|
|
41906
|
-
|
|
41706
|
+
let venue = filters.exchange;
|
|
41707
|
+
if (dataset === "long_short" && TOP_TRADER_SUFFIX.test(venue)) {
|
|
41708
|
+
venue = venue.replace(TOP_TRADER_SUFFIX, "");
|
|
41709
|
+
where.push("ratio_kind != 'accounts'");
|
|
41710
|
+
}
|
|
41711
|
+
where.push("lower(venue) = lower({exchange:String})");
|
|
41712
|
+
params.exchange = venue;
|
|
41907
41713
|
}
|
|
41908
|
-
if (filters.from)
|
|
41909
|
-
|
|
41910
|
-
|
|
41911
|
-
|
|
41714
|
+
if (filters.from) {
|
|
41715
|
+
where.push(`${spec.tsColumn} >= fromUnixTimestamp64Milli({from_ms:Int64})`);
|
|
41716
|
+
params.from_ms = Date.parse(filters.from);
|
|
41717
|
+
}
|
|
41718
|
+
if (filters.to) {
|
|
41719
|
+
where.push(`${spec.tsColumn} <= fromUnixTimestamp64Milli({to_ms:Int64})`);
|
|
41720
|
+
params.to_ms = Date.parse(filters.to);
|
|
41721
|
+
}
|
|
41722
|
+
const sql = `SELECT ${spec.columns} FROM ${spec.table} FINAL WHERE ${where.join(" AND ")} ORDER BY ${spec.tsColumn} DESC LIMIT ${filters.limit}`;
|
|
41723
|
+
try {
|
|
41724
|
+
const rows = await query(sql, params);
|
|
41725
|
+
return rows.map((row) => toNumbers(row, spec.numeric));
|
|
41726
|
+
} catch (error) {
|
|
41912
41727
|
throw archiveQueryError(
|
|
41913
41728
|
`reading ${spec.table} for ${filters.base_coin}`,
|
|
41914
41729
|
error
|
|
41915
41730
|
);
|
|
41916
41731
|
}
|
|
41917
|
-
return data ?? [];
|
|
41918
41732
|
}
|
|
41919
41733
|
var coverageCache = /* @__PURE__ */ new Map();
|
|
41920
|
-
async function
|
|
41921
|
-
|
|
41922
|
-
|
|
41923
|
-
|
|
41924
|
-
|
|
41734
|
+
async function probeExtremes(query, dataset, baseCoin) {
|
|
41735
|
+
const spec = HISTORY_TABLES[dataset];
|
|
41736
|
+
const where = baseCoin ? " WHERE base_coin = {base_coin:String}" : "";
|
|
41737
|
+
try {
|
|
41738
|
+
const [row] = await query(
|
|
41739
|
+
`SELECT minOrNull(${spec.tsColumn}) AS earliest_at, maxOrNull(${spec.tsColumn}) AS latest_at FROM ${spec.table} FINAL${where}`,
|
|
41740
|
+
baseCoin ? { base_coin: baseCoin } : {}
|
|
41741
|
+
);
|
|
41742
|
+
return {
|
|
41743
|
+
dataset,
|
|
41744
|
+
table: spec.table,
|
|
41745
|
+
scope: baseCoin ? "probe_coin" : "all_coins",
|
|
41746
|
+
earliest_at: typeof row?.earliest_at === "string" ? row.earliest_at : null,
|
|
41747
|
+
latest_at: typeof row?.latest_at === "string" ? row.latest_at : null
|
|
41748
|
+
};
|
|
41749
|
+
} catch (error) {
|
|
41925
41750
|
throw archiveQueryError(`probing ${spec.table} coverage`, error);
|
|
41926
41751
|
}
|
|
41927
|
-
const row = (data ?? [])[0];
|
|
41928
|
-
const value = row?.[spec.tsColumn];
|
|
41929
|
-
return typeof value === "string" ? value : null;
|
|
41930
41752
|
}
|
|
41931
|
-
async function listFundingVenues(
|
|
41932
|
-
|
|
41933
|
-
|
|
41934
|
-
|
|
41935
|
-
|
|
41936
|
-
|
|
41937
|
-
|
|
41938
|
-
|
|
41939
|
-
|
|
41940
|
-
|
|
41941
|
-
|
|
41942
|
-
|
|
41943
|
-
venues.push(row.exchange);
|
|
41944
|
-
cursor = row.exchange;
|
|
41753
|
+
async function listFundingVenues(query) {
|
|
41754
|
+
try {
|
|
41755
|
+
const rows = await query(
|
|
41756
|
+
`SELECT DISTINCT venue FROM ${HISTORY_TABLES.funding.table} FINAL ORDER BY venue LIMIT ${VENUE_LIST_MAX + 1}`
|
|
41757
|
+
);
|
|
41758
|
+
const venues = rows.map((row) => row.venue).filter((venue) => typeof venue === "string");
|
|
41759
|
+
return {
|
|
41760
|
+
venues: venues.slice(0, VENUE_LIST_MAX),
|
|
41761
|
+
truncated: venues.length > VENUE_LIST_MAX
|
|
41762
|
+
};
|
|
41763
|
+
} catch (error) {
|
|
41764
|
+
throw archiveQueryError("enumerating archive venues", error);
|
|
41945
41765
|
}
|
|
41946
|
-
return { venues, truncated: true };
|
|
41947
41766
|
}
|
|
41948
|
-
async function fetchCoverage(
|
|
41767
|
+
async function fetchCoverage(query, probeBaseCoin) {
|
|
41949
41768
|
const cached = coverageCache.get(probeBaseCoin);
|
|
41950
41769
|
if (cached && Date.now() - cached.at < COVERAGE_CACHE_TTL_MS) {
|
|
41951
41770
|
return { coverage: cached.payload, cache: "hit" };
|
|
@@ -41956,40 +41775,20 @@ async function fetchCoverage(client2, probeBaseCoin) {
|
|
|
41956
41775
|
"long_short",
|
|
41957
41776
|
"liquidations"
|
|
41958
41777
|
];
|
|
41959
|
-
const [venueScan,
|
|
41960
|
-
listFundingVenues(
|
|
41961
|
-
|
|
41962
|
-
|
|
41963
|
-
|
|
41964
|
-
|
|
41965
|
-
probeTimestamp(client2, HISTORY_TABLES[dataset], "desc", probeBaseCoin)
|
|
41966
|
-
])
|
|
41778
|
+
const [venueScan, funding, ...futures] = await Promise.all([
|
|
41779
|
+
listFundingVenues(query),
|
|
41780
|
+
probeExtremes(query, "funding", null),
|
|
41781
|
+
...futuresDatasets.map(
|
|
41782
|
+
(dataset) => probeExtremes(query, dataset, probeBaseCoin)
|
|
41783
|
+
)
|
|
41967
41784
|
]);
|
|
41968
|
-
const datasets = [
|
|
41969
|
-
{
|
|
41970
|
-
dataset: "funding",
|
|
41971
|
-
table: HISTORY_TABLES.funding.table,
|
|
41972
|
-
scope: "all_coins",
|
|
41973
|
-
earliest_at: fundingEarliest,
|
|
41974
|
-
latest_at: fundingLatest
|
|
41975
|
-
},
|
|
41976
|
-
...futuresDatasets.map((dataset, index) => {
|
|
41977
|
-
return {
|
|
41978
|
-
dataset,
|
|
41979
|
-
table: HISTORY_TABLES[dataset].table,
|
|
41980
|
-
scope: "probe_coin",
|
|
41981
|
-
earliest_at: futuresExtremes[index * 2],
|
|
41982
|
-
latest_at: futuresExtremes[index * 2 + 1]
|
|
41983
|
-
};
|
|
41984
|
-
})
|
|
41985
|
-
];
|
|
41986
41785
|
const coverage = {
|
|
41987
41786
|
venues: venueScan.venues,
|
|
41988
41787
|
venue_count: venueScan.venues.length,
|
|
41989
41788
|
venues_truncated: venueScan.truncated,
|
|
41990
41789
|
probe_base_coin: probeBaseCoin,
|
|
41991
|
-
datasets,
|
|
41992
|
-
note:
|
|
41790
|
+
datasets: [funding, ...futures],
|
|
41791
|
+
note: `Funding extremes cover all coins; the other datasets are probed for probe_base_coin \u2014 pass base_coin to probe another asset. Venues are the distinct funding venues in the archive. Older history is still being loaded, so earliest_at can move back over time.`
|
|
41993
41792
|
};
|
|
41994
41793
|
if (coverageCache.size >= COVERAGE_CACHE_MAX_ENTRIES) {
|
|
41995
41794
|
const oldestKey = coverageCache.keys().next().value;
|
|
@@ -42044,13 +41843,13 @@ function historyInputSchema(baseCoinDescription) {
|
|
|
42044
41843
|
required: ["base_coin"]
|
|
42045
41844
|
};
|
|
42046
41845
|
}
|
|
42047
|
-
function errorResult2(
|
|
41846
|
+
function errorResult2(tool101, error) {
|
|
42048
41847
|
if (error instanceof ArchiveInputError) {
|
|
42049
41848
|
return asTextContentResult(
|
|
42050
41849
|
{
|
|
42051
41850
|
error: error.message,
|
|
42052
41851
|
runtime_status: "invalid_input",
|
|
42053
|
-
tool:
|
|
41852
|
+
tool: tool101
|
|
42054
41853
|
},
|
|
42055
41854
|
true
|
|
42056
41855
|
);
|
|
@@ -42060,36 +41859,36 @@ function errorResult2(tool100, error) {
|
|
|
42060
41859
|
{
|
|
42061
41860
|
error: message,
|
|
42062
41861
|
runtime_status: classifyRuntimeStatusFromError(message),
|
|
42063
|
-
tool:
|
|
41862
|
+
tool: tool101
|
|
42064
41863
|
},
|
|
42065
41864
|
true
|
|
42066
41865
|
);
|
|
42067
41866
|
}
|
|
42068
|
-
function missingCredentialsResult(
|
|
41867
|
+
function missingCredentialsResult(tool101) {
|
|
42069
41868
|
return asTextContentResult(
|
|
42070
41869
|
{
|
|
42071
41870
|
error: ARCHIVE_MISSING_CREDENTIALS_MESSAGE,
|
|
42072
41871
|
runtime_status: "missing_key",
|
|
42073
|
-
tool:
|
|
41872
|
+
tool: tool101
|
|
42074
41873
|
},
|
|
42075
41874
|
true
|
|
42076
41875
|
);
|
|
42077
41876
|
}
|
|
41877
|
+
var HISTORY_NOTE = "History depth varies by dataset and venue and older history is still being loaded \u2014 call archive_get_coverage for the earliest and latest rows.";
|
|
42078
41878
|
function historyHandler(toolName, dataset, options) {
|
|
42079
41879
|
return async (_client, args) => {
|
|
42080
|
-
const
|
|
42081
|
-
if (!
|
|
41880
|
+
const query = getArchiveClient();
|
|
41881
|
+
if (!query) return missingCredentialsResult(toolName);
|
|
42082
41882
|
try {
|
|
42083
41883
|
const filters = parseHistoryArgs(args);
|
|
42084
|
-
const
|
|
42085
|
-
const mapped = options?.mapRow ? rows.map(options.mapRow) : rows;
|
|
41884
|
+
const mapped = await fetchHistoryRows(query, dataset, filters);
|
|
42086
41885
|
const spec = HISTORY_TABLES[dataset];
|
|
42087
41886
|
return asTextContentResult({
|
|
42088
41887
|
provider: PROVIDER_NAME,
|
|
42089
41888
|
dataset: spec.table,
|
|
42090
41889
|
runtime_status: "ok",
|
|
42091
41890
|
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
42092
|
-
note: options?.extraNote ?
|
|
41891
|
+
note: options?.extraNote ? `${options.extraNote} ${HISTORY_NOTE}` : HISTORY_NOTE,
|
|
42093
41892
|
filters,
|
|
42094
41893
|
order: `${spec.tsColumn} desc`,
|
|
42095
41894
|
returnedCount: mapped.length,
|
|
@@ -42111,7 +41910,7 @@ function getAllArchiveEndpoints() {
|
|
|
42111
41910
|
},
|
|
42112
41911
|
tool: {
|
|
42113
41912
|
name: "archive_get_funding_history",
|
|
42114
|
-
description: `Get historical perp funding-rate SETTLEMENTS for one base coin across up to
|
|
41913
|
+
description: `Get historical perp funding-rate SETTLEMENTS for one base coin across up to 35 CEX+DEX venues from the Hive Archive. Each row is one settled funding event: exchange, symbol, base_coin, rate (per-interval fraction, positive = longs pay shorts), interval_hours, settled_at (end of the interval, UTC) \u2014 plus funding_annualized, the simple annual rate from that row's own interval (rate * 8760 / interval_hours, never a guessed 8h default). Call archive_get_coverage for how far back the history reaches.
|
|
42115
41914
|
|
|
42116
41915
|
${SHARED_ARGS_DOC}
|
|
42117
41916
|
|
|
@@ -42132,19 +41931,11 @@ Don't use when:
|
|
|
42132
41931
|
- You need one venue's own raw history endpoint (use get_funding_rate_history or hyperliquid_get_funding_history)
|
|
42133
41932
|
- You need tokenized-stock/RWA perp funding (use rwa_get_funding_rates)`,
|
|
42134
41933
|
inputSchema: historyInputSchema(
|
|
42135
|
-
'Base asset ticker to fetch settlements for, e.g. "BTC". Required
|
|
41934
|
+
'Base asset ticker to fetch settlements for, e.g. "BTC". Required.'
|
|
42136
41935
|
),
|
|
42137
41936
|
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
42138
41937
|
},
|
|
42139
|
-
handler: historyHandler("archive_get_funding_history", "funding"
|
|
42140
|
-
mapRow: (row) => ({
|
|
42141
|
-
...row,
|
|
42142
|
-
funding_annualized: annualizeRate(
|
|
42143
|
-
Number(row.rate),
|
|
42144
|
-
Number(row.interval_hours)
|
|
42145
|
-
)
|
|
42146
|
-
})
|
|
42147
|
-
})
|
|
41938
|
+
handler: historyHandler("archive_get_funding_history", "funding")
|
|
42148
41939
|
},
|
|
42149
41940
|
{
|
|
42150
41941
|
metadata: {
|
|
@@ -42160,7 +41951,7 @@ Don't use when:
|
|
|
42160
41951
|
},
|
|
42161
41952
|
tool: {
|
|
42162
41953
|
name: "archive_get_open_interest_history",
|
|
42163
|
-
description: `Get historical perp open-interest snapshots for one base coin across venues from the Hive Archive
|
|
41954
|
+
description: `Get historical perp open-interest snapshots for one base coin across venues from the Hive Archive. Each row: exchange, base_coin, symbol (venue-native contract), open_interest_value (open interest of that contract in US dollars, one side, at the venue mark price), timestamp (start of the hour; the value is the hour close). Call archive_get_coverage for exact per-dataset depth.
|
|
42164
41955
|
|
|
42165
41956
|
${SHARED_ARGS_DOC}
|
|
42166
41957
|
|
|
@@ -42187,7 +41978,7 @@ Don't use when:
|
|
|
42187
41978
|
"archive_get_open_interest_history",
|
|
42188
41979
|
"open_interest",
|
|
42189
41980
|
{
|
|
42190
|
-
extraNote: "open_interest_value is
|
|
41981
|
+
extraNote: "open_interest_value is US dollars, one side counted, per contract."
|
|
42191
41982
|
}
|
|
42192
41983
|
)
|
|
42193
41984
|
},
|
|
@@ -42199,7 +41990,7 @@ Don't use when:
|
|
|
42199
41990
|
},
|
|
42200
41991
|
tool: {
|
|
42201
41992
|
name: "archive_get_basis_history",
|
|
42202
|
-
description: `Get historical spot-vs-futures BASIS snapshots for one base coin across venues from the Hive Archive. Each row: exchange, base_coin, spot_price, futures_price, basis_rate (fraction),
|
|
41993
|
+
description: `Get historical spot-vs-futures BASIS snapshots for one base coin across venues from the Hive Archive. Each row: exchange, base_coin, spot_price, futures_price, basis_rate ((futures - spot) / spot as a fraction), basis_pct (100 * basis_rate, not annualised), timestamp \u2014 computed at collection time from the venue's own spot and perpetual prices.
|
|
42203
41994
|
|
|
42204
41995
|
${SHARED_ARGS_DOC}
|
|
42205
41996
|
|
|
@@ -42238,7 +42029,7 @@ Don't use when:
|
|
|
42238
42029
|
},
|
|
42239
42030
|
tool: {
|
|
42240
42031
|
name: "archive_get_long_short_history",
|
|
42241
|
-
description: `Get historical long/short-ratio snapshots for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, long_ratio, short_ratio (fractions summing to ~1), timestamp.
|
|
42032
|
+
description: `Get historical long/short-ratio snapshots for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, ratio_kind ("accounts" = all accounts, "top_trader_accounts", "top_trader_positions"), long_ratio, short_ratio (fractions summing to ~1), timestamp. Filter exchange "Binance" for every Binance series, or "Binance_top_trader" for its top-trader series only.
|
|
42242
42033
|
|
|
42243
42034
|
${SHARED_ARGS_DOC}
|
|
42244
42035
|
|
|
@@ -42271,7 +42062,7 @@ Don't use when:
|
|
|
42271
42062
|
},
|
|
42272
42063
|
tool: {
|
|
42273
42064
|
name: "archive_get_liquidations_history",
|
|
42274
|
-
description: `Get historical liquidation events for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, side ("long" = longs liquidated, "short" = shorts liquidated), quantity
|
|
42065
|
+
description: `Get historical liquidation events for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, side ("long" = longs liquidated, "short" = shorts liquidated), quantity (base coins), price (quote per base coin), notional_usd (US dollars), timestamp.
|
|
42275
42066
|
|
|
42276
42067
|
${SHARED_ARGS_DOC}
|
|
42277
42068
|
|
|
@@ -42306,7 +42097,7 @@ Don't use when:
|
|
|
42306
42097
|
},
|
|
42307
42098
|
tool: {
|
|
42308
42099
|
name: "archive_get_coverage",
|
|
42309
|
-
description: `Discover what the Hive Archive holds: the
|
|
42100
|
+
description: `Discover what the Hive Archive holds: the venue list (up to 35 CEX+DEX funding venues) and earliest/latest timestamps per dataset (funding settlements, open interest, basis, long/short ratio, liquidations). Funding extremes are global; the other datasets are probed for one base coin (default "BTC"). Results are cached in-process for 10 minutes.
|
|
42310
42101
|
|
|
42311
42102
|
Args:
|
|
42312
42103
|
- base_coin (string, optional): probe coin for the futures datasets (default "BTC"). Pass another ticker to check archive depth for that asset.
|
|
@@ -42338,11 +42129,11 @@ Don't use when:
|
|
|
42338
42129
|
},
|
|
42339
42130
|
handler: async (_client, args) => {
|
|
42340
42131
|
const toolName = "archive_get_coverage";
|
|
42341
|
-
const
|
|
42342
|
-
if (!
|
|
42132
|
+
const query = getArchiveClient();
|
|
42133
|
+
if (!query) return missingCredentialsResult(toolName);
|
|
42343
42134
|
try {
|
|
42344
42135
|
const probe = typeof args?.base_coin === "string" && args.base_coin.trim() !== "" ? args.base_coin.trim().toUpperCase() : "BTC";
|
|
42345
|
-
const { coverage, cache } = await fetchCoverage(
|
|
42136
|
+
const { coverage, cache } = await fetchCoverage(query, probe);
|
|
42346
42137
|
return asTextContentResult({
|
|
42347
42138
|
provider: PROVIDER_NAME,
|
|
42348
42139
|
runtime_status: "ok",
|
|
@@ -43198,13 +42989,13 @@ function okResult(sourceUrl, body) {
|
|
|
43198
42989
|
may_have_more
|
|
43199
42990
|
});
|
|
43200
42991
|
}
|
|
43201
|
-
function errorResult3(
|
|
42992
|
+
function errorResult3(tool101, error) {
|
|
43202
42993
|
if (error instanceof PolymarketInputError) {
|
|
43203
42994
|
return asTextContentResult(
|
|
43204
42995
|
{
|
|
43205
42996
|
error: error.message,
|
|
43206
42997
|
runtime_status: "invalid_input",
|
|
43207
|
-
tool:
|
|
42998
|
+
tool: tool101
|
|
43208
42999
|
},
|
|
43209
43000
|
true
|
|
43210
43001
|
);
|
|
@@ -43214,7 +43005,7 @@ function errorResult3(tool100, error) {
|
|
|
43214
43005
|
{
|
|
43215
43006
|
error: message,
|
|
43216
43007
|
runtime_status: classifyRuntimeStatusFromError(message),
|
|
43217
|
-
tool:
|
|
43008
|
+
tool: tool101
|
|
43218
43009
|
},
|
|
43219
43010
|
true
|
|
43220
43011
|
);
|
|
@@ -43826,6 +43617,1376 @@ Don't use when:
|
|
|
43826
43617
|
];
|
|
43827
43618
|
}
|
|
43828
43619
|
|
|
43620
|
+
// src/tools/kalshi/client.ts
|
|
43621
|
+
var KALSHI_BASE_URL = "https://api.elections.kalshi.com/trade-api/v2";
|
|
43622
|
+
var KALSHI_USER_AGENT = "HiveIntelligence/1.0 (+https://hiveintelligence.xyz)";
|
|
43623
|
+
var KALSHI_TIMEOUT_MS = 1e4;
|
|
43624
|
+
var KALSHI_NETWORK_RETRIES = 1;
|
|
43625
|
+
var NETWORK_RETRY_DELAY_MS = 150;
|
|
43626
|
+
var UPSTREAM_ERROR_MAX_CHARS2 = 200;
|
|
43627
|
+
var KalshiInputError = class extends Error {
|
|
43628
|
+
};
|
|
43629
|
+
var KalshiNotFoundError = class extends Error {
|
|
43630
|
+
constructor(message, url) {
|
|
43631
|
+
super(message);
|
|
43632
|
+
this.url = url;
|
|
43633
|
+
}
|
|
43634
|
+
url;
|
|
43635
|
+
};
|
|
43636
|
+
function buildUrl2(path, params = {}, base = KALSHI_BASE_URL) {
|
|
43637
|
+
const url = new URL(`${base}${path}`);
|
|
43638
|
+
for (const [key, value] of Object.entries(params)) {
|
|
43639
|
+
if (value === void 0) continue;
|
|
43640
|
+
url.searchParams.set(key, String(value));
|
|
43641
|
+
}
|
|
43642
|
+
return url.toString();
|
|
43643
|
+
}
|
|
43644
|
+
function clip(text) {
|
|
43645
|
+
return text.trim().slice(0, UPSTREAM_ERROR_MAX_CHARS2);
|
|
43646
|
+
}
|
|
43647
|
+
function upstreamErrorText2(bodyText) {
|
|
43648
|
+
try {
|
|
43649
|
+
const parsed = JSON.parse(bodyText);
|
|
43650
|
+
if (parsed && typeof parsed === "object") {
|
|
43651
|
+
const err = parsed.error;
|
|
43652
|
+
if (typeof err === "string" && err.trim()) return clip(err);
|
|
43653
|
+
if (err && typeof err === "object") {
|
|
43654
|
+
const record = err;
|
|
43655
|
+
const parts = [record.code, record.message, record.details].filter(
|
|
43656
|
+
(part) => typeof part === "string" && part.trim() !== ""
|
|
43657
|
+
);
|
|
43658
|
+
if (parts.length > 0) return clip(parts.join(": "));
|
|
43659
|
+
}
|
|
43660
|
+
if (typeof parsed.msg === "string" && parsed.msg.trim()) {
|
|
43661
|
+
return clip(parsed.msg);
|
|
43662
|
+
}
|
|
43663
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
43664
|
+
return clip(parsed.message);
|
|
43665
|
+
}
|
|
43666
|
+
}
|
|
43667
|
+
} catch {
|
|
43668
|
+
}
|
|
43669
|
+
const trimmed = bodyText.trim();
|
|
43670
|
+
return trimmed ? clip(trimmed) : "";
|
|
43671
|
+
}
|
|
43672
|
+
function describeNetworkError(error) {
|
|
43673
|
+
if (!(error instanceof Error)) return String(error);
|
|
43674
|
+
const cause = error.cause;
|
|
43675
|
+
const code = cause && typeof cause === "object" && typeof cause.code === "string" ? cause.code : null;
|
|
43676
|
+
return code ? `${error.message} (${code})` : error.message;
|
|
43677
|
+
}
|
|
43678
|
+
function sleep2(ms) {
|
|
43679
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43680
|
+
}
|
|
43681
|
+
async function fetchKalshiJson(url, label, timeoutMs = KALSHI_TIMEOUT_MS) {
|
|
43682
|
+
let response = null;
|
|
43683
|
+
for (let attempt = 0; attempt <= KALSHI_NETWORK_RETRIES; attempt++) {
|
|
43684
|
+
const controller = new AbortController();
|
|
43685
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
43686
|
+
try {
|
|
43687
|
+
response = await fetch(url, {
|
|
43688
|
+
method: "GET",
|
|
43689
|
+
headers: {
|
|
43690
|
+
Accept: "application/json",
|
|
43691
|
+
"User-Agent": KALSHI_USER_AGENT
|
|
43692
|
+
},
|
|
43693
|
+
signal: controller.signal
|
|
43694
|
+
});
|
|
43695
|
+
break;
|
|
43696
|
+
} catch (error) {
|
|
43697
|
+
if (controller.signal.aborted) {
|
|
43698
|
+
throw new Error(
|
|
43699
|
+
`Kalshi ${label} timed out after ${timeoutMs} ms. Retry once; if it persists the upstream is degraded.`
|
|
43700
|
+
);
|
|
43701
|
+
}
|
|
43702
|
+
if (attempt < KALSHI_NETWORK_RETRIES) {
|
|
43703
|
+
await sleep2(NETWORK_RETRY_DELAY_MS);
|
|
43704
|
+
continue;
|
|
43705
|
+
}
|
|
43706
|
+
throw new Error(
|
|
43707
|
+
`Kalshi ${label} request failed after ${attempt + 1} attempts (${describeNetworkError(error)}); the upstream is temporarily unreachable. Retry shortly.`
|
|
43708
|
+
);
|
|
43709
|
+
} finally {
|
|
43710
|
+
clearTimeout(timer);
|
|
43711
|
+
}
|
|
43712
|
+
}
|
|
43713
|
+
if (!response) {
|
|
43714
|
+
throw new Error(`Kalshi ${label} produced no response.`);
|
|
43715
|
+
}
|
|
43716
|
+
const bodyText = await response.text();
|
|
43717
|
+
if (!response.ok) {
|
|
43718
|
+
const detail = upstreamErrorText2(bodyText);
|
|
43719
|
+
const suffix = detail ? `: ${detail}` : "";
|
|
43720
|
+
const statusText = response.statusText ? ` ${response.statusText}` : "";
|
|
43721
|
+
if (response.status === 404) {
|
|
43722
|
+
throw new KalshiNotFoundError(
|
|
43723
|
+
`Kalshi ${label} responded 404${suffix}`,
|
|
43724
|
+
url
|
|
43725
|
+
);
|
|
43726
|
+
}
|
|
43727
|
+
if (response.status === 400 || response.status === 422) {
|
|
43728
|
+
throw new KalshiInputError(
|
|
43729
|
+
`Invalid arguments: Kalshi ${label} rejected the request (${response.status})${suffix}.`
|
|
43730
|
+
);
|
|
43731
|
+
}
|
|
43732
|
+
if (response.status === 429) {
|
|
43733
|
+
throw new Error(
|
|
43734
|
+
`Kalshi ${label} responded 429 Too Many Requests${suffix}. Kalshi rate-limited this client; back off and retry.`
|
|
43735
|
+
);
|
|
43736
|
+
}
|
|
43737
|
+
throw new Error(
|
|
43738
|
+
`Kalshi ${label} responded ${response.status}${statusText}${suffix}`
|
|
43739
|
+
);
|
|
43740
|
+
}
|
|
43741
|
+
try {
|
|
43742
|
+
return JSON.parse(bodyText);
|
|
43743
|
+
} catch {
|
|
43744
|
+
throw new Error(`Kalshi ${label} returned a non-JSON body.`);
|
|
43745
|
+
}
|
|
43746
|
+
}
|
|
43747
|
+
|
|
43748
|
+
// src/tools/kalshi/api.ts
|
|
43749
|
+
var DEFAULT_LIMIT4 = 10;
|
|
43750
|
+
var MAX_LIMIT4 = 50;
|
|
43751
|
+
var DEFAULT_TRADES_LIMIT = 50;
|
|
43752
|
+
var MAX_TRADES_LIMIT = 200;
|
|
43753
|
+
var DEFAULT_BOOK_DEPTH2 = 10;
|
|
43754
|
+
var MAX_BOOK_DEPTH2 = 100;
|
|
43755
|
+
var SEARCH_PAGE_SIZE = 200;
|
|
43756
|
+
var SEARCH_MAX_PAGES = 3;
|
|
43757
|
+
var EVENT_MARKETS_PREVIEW2 = 25;
|
|
43758
|
+
var MAX_CANDLES = 500;
|
|
43759
|
+
var PERIOD_INTERVALS = [1, 60, 1440];
|
|
43760
|
+
var DEFAULT_PERIOD_INTERVAL = 60;
|
|
43761
|
+
var DEFAULT_WINDOW_CANDLES = {
|
|
43762
|
+
1: 480,
|
|
43763
|
+
60: 480,
|
|
43764
|
+
1440: 365
|
|
43765
|
+
};
|
|
43766
|
+
var MARKET_STATUS_FILTERS = [
|
|
43767
|
+
"unopened",
|
|
43768
|
+
"open",
|
|
43769
|
+
"paused",
|
|
43770
|
+
"closed",
|
|
43771
|
+
"settled"
|
|
43772
|
+
];
|
|
43773
|
+
var EVENT_STATUS_FILTERS = [
|
|
43774
|
+
"unopened",
|
|
43775
|
+
"open",
|
|
43776
|
+
"closed",
|
|
43777
|
+
"settled"
|
|
43778
|
+
];
|
|
43779
|
+
var ANY_STATUS = "all";
|
|
43780
|
+
var TICKER_RE = /^[A-Z0-9][A-Z0-9._-]{0,119}$/;
|
|
43781
|
+
var QUERY_MAX_CHARS2 = 200;
|
|
43782
|
+
var CURSOR_MAX_CHARS = 2e3;
|
|
43783
|
+
var RULES_MAX_CHARS = 1200;
|
|
43784
|
+
var MS_THRESHOLD = 1e11;
|
|
43785
|
+
function invalid2(message) {
|
|
43786
|
+
throw new KalshiInputError(`Invalid arguments: ${message}`);
|
|
43787
|
+
}
|
|
43788
|
+
function parseFiniteNumber3(value) {
|
|
43789
|
+
const num = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
43790
|
+
return Number.isFinite(num) ? num : null;
|
|
43791
|
+
}
|
|
43792
|
+
function asRecord2(value) {
|
|
43793
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
43794
|
+
}
|
|
43795
|
+
function asRecordArray2(value, label) {
|
|
43796
|
+
if (!Array.isArray(value)) {
|
|
43797
|
+
throw new Error(
|
|
43798
|
+
`Kalshi ${label} returned an unexpected payload shape (expected a JSON array).`
|
|
43799
|
+
);
|
|
43800
|
+
}
|
|
43801
|
+
return value.filter((item) => asRecord2(item) !== null);
|
|
43802
|
+
}
|
|
43803
|
+
function optionalString2(value) {
|
|
43804
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
43805
|
+
}
|
|
43806
|
+
function optionalBoolean2(value) {
|
|
43807
|
+
return typeof value === "boolean" ? value : null;
|
|
43808
|
+
}
|
|
43809
|
+
function round4(value) {
|
|
43810
|
+
return Number(value.toFixed(4));
|
|
43811
|
+
}
|
|
43812
|
+
function dollarsToCents(dollars) {
|
|
43813
|
+
return Math.round(dollars * 1e4) / 100;
|
|
43814
|
+
}
|
|
43815
|
+
function readPrice(raw, base) {
|
|
43816
|
+
const dollars = parseFiniteNumber3(raw[`${base}_dollars`]);
|
|
43817
|
+
if (dollars !== null) return { dollars, cents: dollarsToCents(dollars) };
|
|
43818
|
+
const legacyCents = parseFiniteNumber3(raw[base]);
|
|
43819
|
+
if (legacyCents !== null) {
|
|
43820
|
+
return { dollars: round4(legacyCents / 100), cents: legacyCents };
|
|
43821
|
+
}
|
|
43822
|
+
return { dollars: null, cents: null };
|
|
43823
|
+
}
|
|
43824
|
+
function readCount(raw, base) {
|
|
43825
|
+
const fp = parseFiniteNumber3(raw[`${base}_fp`]);
|
|
43826
|
+
return fp !== null ? fp : parseFiniteNumber3(raw[base]);
|
|
43827
|
+
}
|
|
43828
|
+
function unixSecondsToIso2(seconds) {
|
|
43829
|
+
return new Date(seconds * 1e3).toISOString();
|
|
43830
|
+
}
|
|
43831
|
+
function boundedText(value, max) {
|
|
43832
|
+
const text = optionalString2(value);
|
|
43833
|
+
if (!text) return null;
|
|
43834
|
+
return text.length > max ? `${text.slice(0, max)}...` : text;
|
|
43835
|
+
}
|
|
43836
|
+
function parseBoundedInteger2(value, field, min, max, fallback) {
|
|
43837
|
+
if (value === void 0 || value === null) return fallback;
|
|
43838
|
+
const num = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
43839
|
+
if (!Number.isInteger(num) || num < min || num > max) {
|
|
43840
|
+
invalid2(
|
|
43841
|
+
`${field} must be an integer between ${min} and ${max}; got ${JSON.stringify(value)}.`
|
|
43842
|
+
);
|
|
43843
|
+
}
|
|
43844
|
+
return num;
|
|
43845
|
+
}
|
|
43846
|
+
function parseLimit2(value) {
|
|
43847
|
+
return parseBoundedInteger2(value, "limit", 1, MAX_LIMIT4, DEFAULT_LIMIT4);
|
|
43848
|
+
}
|
|
43849
|
+
function parseTradesLimit(value) {
|
|
43850
|
+
return parseBoundedInteger2(
|
|
43851
|
+
value,
|
|
43852
|
+
"limit",
|
|
43853
|
+
1,
|
|
43854
|
+
MAX_TRADES_LIMIT,
|
|
43855
|
+
DEFAULT_TRADES_LIMIT
|
|
43856
|
+
);
|
|
43857
|
+
}
|
|
43858
|
+
function parseBookDepth2(value) {
|
|
43859
|
+
return parseBoundedInteger2(
|
|
43860
|
+
value,
|
|
43861
|
+
"depth",
|
|
43862
|
+
1,
|
|
43863
|
+
MAX_BOOK_DEPTH2,
|
|
43864
|
+
DEFAULT_BOOK_DEPTH2
|
|
43865
|
+
);
|
|
43866
|
+
}
|
|
43867
|
+
function parseOptionalBoolean2(value, field, fallback) {
|
|
43868
|
+
if (value === void 0 || value === null) return fallback;
|
|
43869
|
+
if (typeof value === "boolean") return value;
|
|
43870
|
+
if (value === "true") return true;
|
|
43871
|
+
if (value === "false") return false;
|
|
43872
|
+
return invalid2(`${field} must be a boolean; got ${JSON.stringify(value)}.`);
|
|
43873
|
+
}
|
|
43874
|
+
function parseOptionalQuery2(value) {
|
|
43875
|
+
if (value === void 0 || value === null) return null;
|
|
43876
|
+
if (typeof value !== "string") {
|
|
43877
|
+
invalid2(`query must be a string; got ${JSON.stringify(value)}.`);
|
|
43878
|
+
}
|
|
43879
|
+
const query = value.trim();
|
|
43880
|
+
if (!query) return null;
|
|
43881
|
+
if (query.length > QUERY_MAX_CHARS2) {
|
|
43882
|
+
invalid2(`query must be at most ${QUERY_MAX_CHARS2} characters.`);
|
|
43883
|
+
}
|
|
43884
|
+
return query;
|
|
43885
|
+
}
|
|
43886
|
+
function parseOptionalCursor(value) {
|
|
43887
|
+
if (value === void 0 || value === null) return null;
|
|
43888
|
+
const cursor = typeof value === "string" ? value.trim() : "";
|
|
43889
|
+
if (!cursor) return null;
|
|
43890
|
+
if (cursor.length > CURSOR_MAX_CHARS) {
|
|
43891
|
+
invalid2(
|
|
43892
|
+
`cursor must be the opaque next_cursor string from a previous response (at most ${CURSOR_MAX_CHARS} characters).`
|
|
43893
|
+
);
|
|
43894
|
+
}
|
|
43895
|
+
return cursor;
|
|
43896
|
+
}
|
|
43897
|
+
function parseOptionalTicker(value, field) {
|
|
43898
|
+
if (value === void 0 || value === null) return null;
|
|
43899
|
+
const ticker = typeof value === "string" ? value.trim().toUpperCase() : "";
|
|
43900
|
+
if (ticker === "") return null;
|
|
43901
|
+
if (!TICKER_RE.test(ticker)) {
|
|
43902
|
+
invalid2(
|
|
43903
|
+
`${field} must be a Kalshi ticker (letters, digits, "-", ".", "_"), e.g. "KXBTCD-26SEP1612-T85799.99"; got ${JSON.stringify(value)}.`
|
|
43904
|
+
);
|
|
43905
|
+
}
|
|
43906
|
+
return ticker;
|
|
43907
|
+
}
|
|
43908
|
+
function parseRequiredTicker(value, field = "ticker") {
|
|
43909
|
+
const ticker = parseOptionalTicker(value, field);
|
|
43910
|
+
if (!ticker) {
|
|
43911
|
+
invalid2(
|
|
43912
|
+
`${field} is required. Pass a Kalshi market ticker such as "KXNIREFCALL-30JAN01-YES" (find one with kalshi_search_markets or kalshi_get_events).`
|
|
43913
|
+
);
|
|
43914
|
+
}
|
|
43915
|
+
return ticker;
|
|
43916
|
+
}
|
|
43917
|
+
function parseStatusFilter(value, allowed, fallback) {
|
|
43918
|
+
if (value === void 0 || value === null) return fallback;
|
|
43919
|
+
const status = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
43920
|
+
if (status === ANY_STATUS) return null;
|
|
43921
|
+
if (!allowed.includes(status)) {
|
|
43922
|
+
invalid2(
|
|
43923
|
+
`status must be one of ${[...allowed, ANY_STATUS].map((item) => `"${item}"`).join(", ")}; got ${JSON.stringify(value)}.`
|
|
43924
|
+
);
|
|
43925
|
+
}
|
|
43926
|
+
return status;
|
|
43927
|
+
}
|
|
43928
|
+
function parsePeriodInterval(value) {
|
|
43929
|
+
if (value === void 0 || value === null) return DEFAULT_PERIOD_INTERVAL;
|
|
43930
|
+
const num = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
43931
|
+
if (!PERIOD_INTERVALS.includes(num)) {
|
|
43932
|
+
invalid2(
|
|
43933
|
+
`period_interval must be one of ${PERIOD_INTERVALS.join(", ")} (minutes per candle); got ${JSON.stringify(value)}.`
|
|
43934
|
+
);
|
|
43935
|
+
}
|
|
43936
|
+
return num;
|
|
43937
|
+
}
|
|
43938
|
+
function parseTimestamp(value, field) {
|
|
43939
|
+
if (value === void 0 || value === null) return null;
|
|
43940
|
+
if (typeof value === "string" && value.trim() === "") return null;
|
|
43941
|
+
const numeric = parseFiniteNumber3(value);
|
|
43942
|
+
if (numeric !== null) {
|
|
43943
|
+
if (numeric <= 0) {
|
|
43944
|
+
invalid2(`${field} must be a positive unix timestamp or an ISO 8601 datetime; got ${JSON.stringify(value)}.`);
|
|
43945
|
+
}
|
|
43946
|
+
return Math.floor(numeric >= MS_THRESHOLD ? numeric / 1e3 : numeric);
|
|
43947
|
+
}
|
|
43948
|
+
if (typeof value === "string") {
|
|
43949
|
+
const ms = Date.parse(value);
|
|
43950
|
+
if (Number.isFinite(ms)) return Math.floor(ms / 1e3);
|
|
43951
|
+
}
|
|
43952
|
+
return invalid2(
|
|
43953
|
+
`${field} must be an ISO 8601 datetime (e.g. "2026-09-01T00:00:00Z") or a unix timestamp in seconds; got ${JSON.stringify(value)}.`
|
|
43954
|
+
);
|
|
43955
|
+
}
|
|
43956
|
+
function resolveCandleWindow(args, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
43957
|
+
const interval = parsePeriodInterval(args?.period_interval);
|
|
43958
|
+
const intervalSeconds = interval * 60;
|
|
43959
|
+
const end = parseTimestamp(args?.end, "end") ?? nowSeconds;
|
|
43960
|
+
const requestedStart = parseTimestamp(args?.start, "start");
|
|
43961
|
+
let start = requestedStart ?? end - DEFAULT_WINDOW_CANDLES[interval] * intervalSeconds;
|
|
43962
|
+
if (start >= end) {
|
|
43963
|
+
invalid2(
|
|
43964
|
+
`start (${unixSecondsToIso2(start)}) must be before end (${unixSecondsToIso2(end)}).`
|
|
43965
|
+
);
|
|
43966
|
+
}
|
|
43967
|
+
const maxSpan = MAX_CANDLES * intervalSeconds;
|
|
43968
|
+
let truncated = false;
|
|
43969
|
+
if (end - start > maxSpan) {
|
|
43970
|
+
start = end - maxSpan;
|
|
43971
|
+
truncated = true;
|
|
43972
|
+
}
|
|
43973
|
+
return {
|
|
43974
|
+
start_ts: start,
|
|
43975
|
+
end_ts: end,
|
|
43976
|
+
period_interval: interval,
|
|
43977
|
+
requested_start_ts: requestedStart,
|
|
43978
|
+
window_truncated: truncated,
|
|
43979
|
+
expected_candles: Math.ceil((end - start) / intervalSeconds)
|
|
43980
|
+
};
|
|
43981
|
+
}
|
|
43982
|
+
function matchesQuery2(fields, query) {
|
|
43983
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
43984
|
+
if (terms.length === 0) return true;
|
|
43985
|
+
const text = fields.filter((value) => typeof value === "string").join(" ").toLowerCase();
|
|
43986
|
+
return terms.every((term) => text.includes(term));
|
|
43987
|
+
}
|
|
43988
|
+
function seriesTickerFromTicker(ticker) {
|
|
43989
|
+
const [head] = ticker.split("-");
|
|
43990
|
+
return head || ticker;
|
|
43991
|
+
}
|
|
43992
|
+
function toMarketRow3(raw) {
|
|
43993
|
+
const yesBid = readPrice(raw, "yes_bid");
|
|
43994
|
+
const yesAsk = readPrice(raw, "yes_ask");
|
|
43995
|
+
const last = readPrice(raw, "last_price");
|
|
43996
|
+
return {
|
|
43997
|
+
ticker: optionalString2(raw.ticker) ?? "",
|
|
43998
|
+
event_ticker: optionalString2(raw.event_ticker),
|
|
43999
|
+
title: optionalString2(raw.title),
|
|
44000
|
+
yes_sub_title: optionalString2(raw.yes_sub_title),
|
|
44001
|
+
no_sub_title: optionalString2(raw.no_sub_title),
|
|
44002
|
+
status: optionalString2(raw.status),
|
|
44003
|
+
market_type: optionalString2(raw.market_type),
|
|
44004
|
+
yes_bid: yesBid.dollars,
|
|
44005
|
+
yes_bid_cents: yesBid.cents,
|
|
44006
|
+
yes_ask: yesAsk.dollars,
|
|
44007
|
+
yes_ask_cents: yesAsk.cents,
|
|
44008
|
+
last_price: last.dollars,
|
|
44009
|
+
last_price_cents: last.cents,
|
|
44010
|
+
volume: readCount(raw, "volume"),
|
|
44011
|
+
volume_24h: readCount(raw, "volume_24h"),
|
|
44012
|
+
open_interest: readCount(raw, "open_interest"),
|
|
44013
|
+
open_time: optionalString2(raw.open_time),
|
|
44014
|
+
close_time: optionalString2(raw.close_time)
|
|
44015
|
+
};
|
|
44016
|
+
}
|
|
44017
|
+
function normalizeMarket(raw) {
|
|
44018
|
+
const row = toMarketRow3(raw);
|
|
44019
|
+
const noBid = readPrice(raw, "no_bid");
|
|
44020
|
+
const noAsk = readPrice(raw, "no_ask");
|
|
44021
|
+
const previous = readPrice(raw, "previous_price");
|
|
44022
|
+
const midpoint = row.yes_bid !== null && row.yes_ask !== null ? round4((row.yes_bid + row.yes_ask) / 2) : null;
|
|
44023
|
+
const spreadCents = row.yes_bid_cents !== null && row.yes_ask_cents !== null ? Number((row.yes_ask_cents - row.yes_bid_cents).toFixed(2)) : null;
|
|
44024
|
+
return {
|
|
44025
|
+
...row,
|
|
44026
|
+
series_ticker: seriesTickerFromTicker(row.ticker),
|
|
44027
|
+
subtitle: optionalString2(raw.subtitle),
|
|
44028
|
+
result: optionalString2(raw.result),
|
|
44029
|
+
no_bid: noBid.dollars,
|
|
44030
|
+
no_bid_cents: noBid.cents,
|
|
44031
|
+
no_ask: noAsk.dollars,
|
|
44032
|
+
no_ask_cents: noAsk.cents,
|
|
44033
|
+
yes_midpoint: midpoint,
|
|
44034
|
+
yes_spread_cents: spreadCents,
|
|
44035
|
+
previous_price: previous.dollars,
|
|
44036
|
+
previous_price_cents: previous.cents,
|
|
44037
|
+
previous_yes_bid: readPrice(raw, "previous_yes_bid").dollars,
|
|
44038
|
+
previous_yes_ask: readPrice(raw, "previous_yes_ask").dollars,
|
|
44039
|
+
yes_bid_size: readCount(raw, "yes_bid_size"),
|
|
44040
|
+
yes_ask_size: readCount(raw, "yes_ask_size"),
|
|
44041
|
+
liquidity_dollars: parseFiniteNumber3(raw.liquidity_dollars),
|
|
44042
|
+
notional_value_dollars: parseFiniteNumber3(raw.notional_value_dollars),
|
|
44043
|
+
expiration_time: optionalString2(raw.expiration_time),
|
|
44044
|
+
expected_expiration_time: optionalString2(raw.expected_expiration_time),
|
|
44045
|
+
latest_expiration_time: optionalString2(raw.latest_expiration_time),
|
|
44046
|
+
can_close_early: optionalBoolean2(raw.can_close_early),
|
|
44047
|
+
early_close_condition: optionalString2(raw.early_close_condition),
|
|
44048
|
+
settlement_timer_seconds: parseFiniteNumber3(raw.settlement_timer_seconds),
|
|
44049
|
+
strike_type: optionalString2(raw.strike_type),
|
|
44050
|
+
floor_strike: parseFiniteNumber3(raw.floor_strike),
|
|
44051
|
+
cap_strike: parseFiniteNumber3(raw.cap_strike),
|
|
44052
|
+
custom_strike: asRecord2(raw.custom_strike),
|
|
44053
|
+
is_provisional: optionalBoolean2(raw.is_provisional),
|
|
44054
|
+
mve_collection_ticker: optionalString2(raw.mve_collection_ticker),
|
|
44055
|
+
mve_selected_legs: Array.isArray(raw.mve_selected_legs) ? raw.mve_selected_legs : null,
|
|
44056
|
+
price_level_structure: optionalString2(raw.price_level_structure),
|
|
44057
|
+
price_ranges: Array.isArray(raw.price_ranges) ? raw.price_ranges : null,
|
|
44058
|
+
rules_primary: boundedText(raw.rules_primary, RULES_MAX_CHARS),
|
|
44059
|
+
rules_secondary: boundedText(raw.rules_secondary, RULES_MAX_CHARS),
|
|
44060
|
+
created_time: optionalString2(raw.created_time),
|
|
44061
|
+
updated_time: optionalString2(raw.updated_time)
|
|
44062
|
+
};
|
|
44063
|
+
}
|
|
44064
|
+
function toEventRow2(raw, withNested, extraMarkets = void 0) {
|
|
44065
|
+
const rawMarkets = Array.isArray(raw.markets) ? raw.markets : Array.isArray(extraMarkets) ? extraMarkets : [];
|
|
44066
|
+
const markets = rawMarkets.map(asRecord2).filter((item) => item !== null).map(toMarketRow3);
|
|
44067
|
+
const sources = Array.isArray(raw.settlement_sources) ? raw.settlement_sources.map((source) => {
|
|
44068
|
+
const record = asRecord2(source);
|
|
44069
|
+
return record ? optionalString2(record.name) : optionalString2(source);
|
|
44070
|
+
}).filter((name) => name !== null) : [];
|
|
44071
|
+
return {
|
|
44072
|
+
event_ticker: optionalString2(raw.event_ticker) ?? "",
|
|
44073
|
+
series_ticker: optionalString2(raw.series_ticker),
|
|
44074
|
+
title: optionalString2(raw.title),
|
|
44075
|
+
sub_title: optionalString2(raw.sub_title),
|
|
44076
|
+
category: optionalString2(raw.category),
|
|
44077
|
+
mutually_exclusive: optionalBoolean2(raw.mutually_exclusive),
|
|
44078
|
+
strike_date: optionalString2(raw.strike_date),
|
|
44079
|
+
strike_period: optionalString2(raw.strike_period),
|
|
44080
|
+
collateral_return_type: optionalString2(raw.collateral_return_type),
|
|
44081
|
+
settlement_sources: sources,
|
|
44082
|
+
last_updated_ts: optionalString2(raw.last_updated_ts),
|
|
44083
|
+
market_count: withNested ? markets.length : null,
|
|
44084
|
+
markets: withNested ? markets.slice(0, EVENT_MARKETS_PREVIEW2) : null,
|
|
44085
|
+
markets_truncated: withNested && markets.length > EVENT_MARKETS_PREVIEW2
|
|
44086
|
+
};
|
|
44087
|
+
}
|
|
44088
|
+
function parseLevels2(value, unit) {
|
|
44089
|
+
if (!Array.isArray(value)) return [];
|
|
44090
|
+
const levels = [];
|
|
44091
|
+
for (const item of value) {
|
|
44092
|
+
if (!Array.isArray(item) || item.length < 2) continue;
|
|
44093
|
+
const rawPrice = parseFiniteNumber3(item[0]);
|
|
44094
|
+
const size = parseFiniteNumber3(item[1]);
|
|
44095
|
+
if (rawPrice === null || size === null) continue;
|
|
44096
|
+
if (unit === "dollars") {
|
|
44097
|
+
levels.push({ price: rawPrice, cents: dollarsToCents(rawPrice), size });
|
|
44098
|
+
} else {
|
|
44099
|
+
levels.push({ price: round4(rawPrice / 100), cents: rawPrice, size });
|
|
44100
|
+
}
|
|
44101
|
+
}
|
|
44102
|
+
return levels.sort((a, b) => b.price - a.price);
|
|
44103
|
+
}
|
|
44104
|
+
function summarizeOrderbook2(raw, ticker, depth) {
|
|
44105
|
+
const fp = asRecord2(raw.orderbook_fp);
|
|
44106
|
+
const legacy = asRecord2(raw.orderbook);
|
|
44107
|
+
const yes = fp ? parseLevels2(fp.yes_dollars, "dollars") : parseLevels2(legacy?.yes, "cents");
|
|
44108
|
+
const no = fp ? parseLevels2(fp.no_dollars, "dollars") : parseLevels2(legacy?.no, "cents");
|
|
44109
|
+
const bestYesBid = yes[0]?.price ?? null;
|
|
44110
|
+
const bestNoBid = no[0]?.price ?? null;
|
|
44111
|
+
const bestYesAsk = bestNoBid !== null ? round4(1 - bestNoBid) : null;
|
|
44112
|
+
const bestNoAsk = bestYesBid !== null ? round4(1 - bestYesBid) : null;
|
|
44113
|
+
const spread = bestYesBid !== null && bestYesAsk !== null ? round4(bestYesAsk - bestYesBid) : null;
|
|
44114
|
+
const midpoint = bestYesBid !== null && bestYesAsk !== null ? round4((bestYesAsk + bestYesBid) / 2) : null;
|
|
44115
|
+
return {
|
|
44116
|
+
ticker,
|
|
44117
|
+
yes_bids: yes.slice(0, depth),
|
|
44118
|
+
no_bids: no.slice(0, depth),
|
|
44119
|
+
yes_levels_total: yes.length,
|
|
44120
|
+
no_levels_total: no.length,
|
|
44121
|
+
best_yes_bid: bestYesBid,
|
|
44122
|
+
best_yes_bid_cents: bestYesBid !== null ? dollarsToCents(bestYesBid) : null,
|
|
44123
|
+
best_yes_ask: bestYesAsk,
|
|
44124
|
+
best_yes_ask_cents: bestYesAsk !== null ? dollarsToCents(bestYesAsk) : null,
|
|
44125
|
+
best_no_bid: bestNoBid,
|
|
44126
|
+
best_no_ask: bestNoAsk,
|
|
44127
|
+
yes_spread: spread,
|
|
44128
|
+
yes_midpoint: midpoint
|
|
44129
|
+
};
|
|
44130
|
+
}
|
|
44131
|
+
function toTradeRow(raw) {
|
|
44132
|
+
const yesPrice2 = readPrice(raw, "yes_price");
|
|
44133
|
+
const noPrice = readPrice(raw, "no_price");
|
|
44134
|
+
return {
|
|
44135
|
+
trade_id: optionalString2(raw.trade_id),
|
|
44136
|
+
ticker: optionalString2(raw.ticker),
|
|
44137
|
+
created_time: optionalString2(raw.created_time),
|
|
44138
|
+
count: readCount(raw, "count"),
|
|
44139
|
+
yes_price: yesPrice2.dollars,
|
|
44140
|
+
yes_price_cents: yesPrice2.cents,
|
|
44141
|
+
no_price: noPrice.dollars,
|
|
44142
|
+
no_price_cents: noPrice.cents,
|
|
44143
|
+
taker_side: optionalString2(raw.taker_side),
|
|
44144
|
+
taker_book_side: optionalString2(raw.taker_book_side),
|
|
44145
|
+
is_block_trade: optionalBoolean2(raw.is_block_trade)
|
|
44146
|
+
};
|
|
44147
|
+
}
|
|
44148
|
+
function toCandle(raw) {
|
|
44149
|
+
const endTs = parseFiniteNumber3(raw.end_period_ts);
|
|
44150
|
+
if (endTs === null) return null;
|
|
44151
|
+
const price = asRecord2(raw.price) ?? {};
|
|
44152
|
+
const yesBid = asRecord2(raw.yes_bid) ?? {};
|
|
44153
|
+
const yesAsk = asRecord2(raw.yes_ask) ?? {};
|
|
44154
|
+
return {
|
|
44155
|
+
end_period_ts: endTs,
|
|
44156
|
+
end_time: unixSecondsToIso2(endTs),
|
|
44157
|
+
open: readPrice(price, "open").dollars,
|
|
44158
|
+
high: readPrice(price, "high").dollars,
|
|
44159
|
+
low: readPrice(price, "low").dollars,
|
|
44160
|
+
close: readPrice(price, "close").dollars,
|
|
44161
|
+
mean: readPrice(price, "mean").dollars,
|
|
44162
|
+
previous: readPrice(price, "previous").dollars,
|
|
44163
|
+
yes_bid_close: readPrice(yesBid, "close").dollars,
|
|
44164
|
+
yes_ask_close: readPrice(yesAsk, "close").dollars,
|
|
44165
|
+
volume: readCount(raw, "volume"),
|
|
44166
|
+
open_interest: readCount(raw, "open_interest")
|
|
44167
|
+
};
|
|
44168
|
+
}
|
|
44169
|
+
function summarizeCandles(candles) {
|
|
44170
|
+
const traded = candles.filter(
|
|
44171
|
+
(candle) => candle.close !== null
|
|
44172
|
+
);
|
|
44173
|
+
if (traded.length === 0) return null;
|
|
44174
|
+
const first = traded[0];
|
|
44175
|
+
const last = traded[traded.length - 1];
|
|
44176
|
+
let minLow = first.close;
|
|
44177
|
+
let maxHigh = first.close;
|
|
44178
|
+
let totalVolume = 0;
|
|
44179
|
+
let lastOpenInterest = null;
|
|
44180
|
+
for (const candle of candles) {
|
|
44181
|
+
const low = candle.low ?? candle.close;
|
|
44182
|
+
const high = candle.high ?? candle.close;
|
|
44183
|
+
if (low !== null && low < minLow) minLow = low;
|
|
44184
|
+
if (high !== null && high > maxHigh) maxHigh = high;
|
|
44185
|
+
if (candle.volume !== null) totalVolume += candle.volume;
|
|
44186
|
+
if (candle.open_interest !== null) lastOpenInterest = candle.open_interest;
|
|
44187
|
+
}
|
|
44188
|
+
const change = round4(last.close - first.close);
|
|
44189
|
+
return {
|
|
44190
|
+
candles: candles.length,
|
|
44191
|
+
candles_with_trades: traded.length,
|
|
44192
|
+
first_at: first.end_time,
|
|
44193
|
+
last_at: last.end_time,
|
|
44194
|
+
first_close: first.close,
|
|
44195
|
+
first_close_cents: dollarsToCents(first.close),
|
|
44196
|
+
last_close: last.close,
|
|
44197
|
+
last_close_cents: dollarsToCents(last.close),
|
|
44198
|
+
min_low: minLow,
|
|
44199
|
+
max_high: maxHigh,
|
|
44200
|
+
change,
|
|
44201
|
+
change_pct: first.close > 0 ? Number((change / first.close * 100).toFixed(2)) : null,
|
|
44202
|
+
total_volume: Number(totalVolume.toFixed(2)),
|
|
44203
|
+
last_open_interest: lastOpenInterest
|
|
44204
|
+
};
|
|
44205
|
+
}
|
|
44206
|
+
function readCursor(record) {
|
|
44207
|
+
const cursor = record ? optionalString2(record.cursor) : null;
|
|
44208
|
+
return cursor;
|
|
44209
|
+
}
|
|
44210
|
+
async function fetchMarketsPage(params) {
|
|
44211
|
+
const url = buildUrl2("/markets", params);
|
|
44212
|
+
const data = await fetchKalshiJson(url, "GET /markets");
|
|
44213
|
+
const record = asRecord2(data);
|
|
44214
|
+
return {
|
|
44215
|
+
url,
|
|
44216
|
+
rows: asRecordArray2(record?.markets, "GET /markets"),
|
|
44217
|
+
nextCursor: readCursor(record)
|
|
44218
|
+
};
|
|
44219
|
+
}
|
|
44220
|
+
async function searchMarkets(filters) {
|
|
44221
|
+
const pageSize = filters.query ? SEARCH_PAGE_SIZE : filters.limit;
|
|
44222
|
+
const maxPages = filters.query ? SEARCH_MAX_PAGES : 1;
|
|
44223
|
+
let cursor = filters.cursor;
|
|
44224
|
+
let firstUrl = "";
|
|
44225
|
+
let scanned = 0;
|
|
44226
|
+
let pagesScanned = 0;
|
|
44227
|
+
const matched = [];
|
|
44228
|
+
for (let page = 0; page < maxPages; page++) {
|
|
44229
|
+
const { url, rows, nextCursor } = await fetchMarketsPage({
|
|
44230
|
+
status: filters.status ?? void 0,
|
|
44231
|
+
series_ticker: filters.seriesTicker ?? void 0,
|
|
44232
|
+
event_ticker: filters.eventTicker ?? void 0,
|
|
44233
|
+
mve_filter: filters.eventTicker ? void 0 : "exclude",
|
|
44234
|
+
limit: pageSize,
|
|
44235
|
+
cursor: cursor ?? void 0
|
|
44236
|
+
});
|
|
44237
|
+
if (!firstUrl) firstUrl = url;
|
|
44238
|
+
pagesScanned += 1;
|
|
44239
|
+
scanned += rows.length;
|
|
44240
|
+
for (const raw of rows) {
|
|
44241
|
+
const row = toMarketRow3(raw);
|
|
44242
|
+
if (!filters.query || matchesQuery2(
|
|
44243
|
+
[
|
|
44244
|
+
row.ticker,
|
|
44245
|
+
row.event_ticker,
|
|
44246
|
+
row.title,
|
|
44247
|
+
row.yes_sub_title,
|
|
44248
|
+
row.no_sub_title
|
|
44249
|
+
],
|
|
44250
|
+
filters.query
|
|
44251
|
+
)) {
|
|
44252
|
+
matched.push(row);
|
|
44253
|
+
}
|
|
44254
|
+
}
|
|
44255
|
+
cursor = nextCursor;
|
|
44256
|
+
if (!cursor) break;
|
|
44257
|
+
if (matched.length >= filters.limit) break;
|
|
44258
|
+
}
|
|
44259
|
+
return {
|
|
44260
|
+
url: firstUrl,
|
|
44261
|
+
rows: matched.slice(0, filters.limit),
|
|
44262
|
+
pageSize,
|
|
44263
|
+
pagesScanned,
|
|
44264
|
+
scanned,
|
|
44265
|
+
matched: matched.length,
|
|
44266
|
+
nextCursor: cursor,
|
|
44267
|
+
mayHaveMore: matched.length > filters.limit || cursor !== null
|
|
44268
|
+
};
|
|
44269
|
+
}
|
|
44270
|
+
async function fetchMarket(ticker) {
|
|
44271
|
+
const url = buildUrl2(`/markets/${encodeURIComponent(ticker)}`);
|
|
44272
|
+
try {
|
|
44273
|
+
const data = await fetchKalshiJson(url, "GET /markets/{ticker}");
|
|
44274
|
+
const record = asRecord2(asRecord2(data)?.market);
|
|
44275
|
+
if (!record || typeof record.ticker !== "string") {
|
|
44276
|
+
throw new Error(
|
|
44277
|
+
"Kalshi GET /markets/{ticker} returned an unexpected payload shape."
|
|
44278
|
+
);
|
|
44279
|
+
}
|
|
44280
|
+
return { url, market: normalizeMarket(record) };
|
|
44281
|
+
} catch (error) {
|
|
44282
|
+
if (error instanceof KalshiNotFoundError) {
|
|
44283
|
+
invalid2(
|
|
44284
|
+
`no Kalshi market has ticker "${ticker}". Tickers are uppercase strings like "KXBTCD-26SEP1612-T85799.99"; find one with kalshi_search_markets or kalshi_get_events.`
|
|
44285
|
+
);
|
|
44286
|
+
}
|
|
44287
|
+
throw error;
|
|
44288
|
+
}
|
|
44289
|
+
}
|
|
44290
|
+
async function fetchEventsPage(params) {
|
|
44291
|
+
const url = buildUrl2("/events", {
|
|
44292
|
+
status: params.status ?? void 0,
|
|
44293
|
+
series_ticker: params.seriesTicker ?? void 0,
|
|
44294
|
+
limit: params.limit,
|
|
44295
|
+
cursor: params.cursor ?? void 0,
|
|
44296
|
+
with_nested_markets: params.withNested ? true : void 0
|
|
44297
|
+
});
|
|
44298
|
+
const data = await fetchKalshiJson(url, "GET /events");
|
|
44299
|
+
const record = asRecord2(data);
|
|
44300
|
+
const rows = asRecordArray2(record?.events, "GET /events").map(
|
|
44301
|
+
(raw) => toEventRow2(raw, params.withNested)
|
|
44302
|
+
);
|
|
44303
|
+
return { url, rows, nextCursor: readCursor(record) };
|
|
44304
|
+
}
|
|
44305
|
+
async function fetchEvent(eventTicker, withNested) {
|
|
44306
|
+
const url = buildUrl2(`/events/${encodeURIComponent(eventTicker)}`, {
|
|
44307
|
+
with_nested_markets: withNested ? true : void 0
|
|
44308
|
+
});
|
|
44309
|
+
try {
|
|
44310
|
+
const data = await fetchKalshiJson(url, "GET /events/{event_ticker}");
|
|
44311
|
+
const record = asRecord2(data);
|
|
44312
|
+
const event = asRecord2(record?.event);
|
|
44313
|
+
if (!event || typeof event.event_ticker !== "string") {
|
|
44314
|
+
throw new Error(
|
|
44315
|
+
"Kalshi GET /events/{event_ticker} returned an unexpected payload shape."
|
|
44316
|
+
);
|
|
44317
|
+
}
|
|
44318
|
+
return { url, event: toEventRow2(event, withNested, record?.markets) };
|
|
44319
|
+
} catch (error) {
|
|
44320
|
+
if (error instanceof KalshiNotFoundError) {
|
|
44321
|
+
invalid2(
|
|
44322
|
+
`no Kalshi event has ticker "${eventTicker}". Event tickers look like "KXBTCD-26SEP1612" (series + date suffix); list them with kalshi_get_events and a series_ticker.`
|
|
44323
|
+
);
|
|
44324
|
+
}
|
|
44325
|
+
throw error;
|
|
44326
|
+
}
|
|
44327
|
+
}
|
|
44328
|
+
async function fetchOrderbook2(ticker, depth) {
|
|
44329
|
+
const url = buildUrl2(`/markets/${encodeURIComponent(ticker)}/orderbook`);
|
|
44330
|
+
try {
|
|
44331
|
+
const data = await fetchKalshiJson(
|
|
44332
|
+
url,
|
|
44333
|
+
"GET /markets/{ticker}/orderbook"
|
|
44334
|
+
);
|
|
44335
|
+
const record = asRecord2(data);
|
|
44336
|
+
if (!record || !asRecord2(record.orderbook_fp) && !asRecord2(record.orderbook)) {
|
|
44337
|
+
throw new Error(
|
|
44338
|
+
"Kalshi GET /markets/{ticker}/orderbook returned an unexpected payload shape."
|
|
44339
|
+
);
|
|
44340
|
+
}
|
|
44341
|
+
return { url, book: summarizeOrderbook2(record, ticker, depth) };
|
|
44342
|
+
} catch (error) {
|
|
44343
|
+
if (error instanceof KalshiNotFoundError) {
|
|
44344
|
+
invalid2(
|
|
44345
|
+
`no Kalshi market has ticker "${ticker}" (order book lookup returned 404). Find tickers with kalshi_search_markets.`
|
|
44346
|
+
);
|
|
44347
|
+
}
|
|
44348
|
+
throw error;
|
|
44349
|
+
}
|
|
44350
|
+
}
|
|
44351
|
+
async function fetchTrades(ticker, limit, cursor) {
|
|
44352
|
+
const url = buildUrl2("/markets/trades", {
|
|
44353
|
+
ticker,
|
|
44354
|
+
limit,
|
|
44355
|
+
cursor: cursor ?? void 0
|
|
44356
|
+
});
|
|
44357
|
+
const data = await fetchKalshiJson(url, "GET /markets/trades");
|
|
44358
|
+
const record = asRecord2(data);
|
|
44359
|
+
return {
|
|
44360
|
+
url,
|
|
44361
|
+
rows: asRecordArray2(record?.trades, "GET /markets/trades").map(toTradeRow),
|
|
44362
|
+
nextCursor: readCursor(record)
|
|
44363
|
+
};
|
|
44364
|
+
}
|
|
44365
|
+
async function assertMarketExists(ticker) {
|
|
44366
|
+
await fetchMarket(ticker);
|
|
44367
|
+
}
|
|
44368
|
+
async function fetchCandlesticks(seriesTicker, ticker, window) {
|
|
44369
|
+
const url = buildUrl2(
|
|
44370
|
+
`/series/${encodeURIComponent(seriesTicker)}/markets/${encodeURIComponent(ticker)}/candlesticks`,
|
|
44371
|
+
{
|
|
44372
|
+
start_ts: window.start_ts,
|
|
44373
|
+
end_ts: window.end_ts,
|
|
44374
|
+
period_interval: window.period_interval
|
|
44375
|
+
}
|
|
44376
|
+
);
|
|
44377
|
+
try {
|
|
44378
|
+
const data = await fetchKalshiJson(url, "GET candlesticks");
|
|
44379
|
+
const record = asRecord2(data);
|
|
44380
|
+
const candles = asRecordArray2(record?.candlesticks, "GET candlesticks").map(toCandle).filter((candle) => candle !== null).sort((a, b) => a.end_period_ts - b.end_period_ts);
|
|
44381
|
+
return { url, candles };
|
|
44382
|
+
} catch (error) {
|
|
44383
|
+
if (error instanceof KalshiNotFoundError) {
|
|
44384
|
+
invalid2(
|
|
44385
|
+
`no Kalshi candlesticks for ticker "${ticker}" under series "${seriesTicker}" (404). Verify the ticker with kalshi_get_market; if its series prefix is unusual, pass series_ticker explicitly.`
|
|
44386
|
+
);
|
|
44387
|
+
}
|
|
44388
|
+
throw error;
|
|
44389
|
+
}
|
|
44390
|
+
}
|
|
44391
|
+
|
|
44392
|
+
// src/tools/kalshi/tools.ts
|
|
44393
|
+
var PROVIDER_NAME3 = "Kalshi";
|
|
44394
|
+
var KALSHI_ODDS_NOTE = "Kalshi prices are market-implied odds (the 0-1 dollar price of a $1-payout contract, roughly the crowd's probability; cents = price x 100), not ground truth.";
|
|
44395
|
+
var KALSHI_ATTRIBUTION = "Source: Kalshi public API";
|
|
44396
|
+
var PRICE_UNITS_DOC = "Prices carry two units for one quote: the 0-1 field is dollars per $1 contract (= implied probability) and the *_cents field is the same quote in cents (sub-penny markets keep decimals, e.g. 99.49).";
|
|
44397
|
+
var ENVELOPE_DOC2 = `Every response carries { "provider": "Kalshi", "runtime_status", "fetched_at", "note" (odds caveat + "${KALSHI_ATTRIBUTION}" + request URL), "source_url", ..., "returnedCount", "may_have_more" }.`;
|
|
44398
|
+
function okResult2(sourceUrl, body) {
|
|
44399
|
+
const { returnedCount, may_have_more, ...rest } = body;
|
|
44400
|
+
return asTextContentResult({
|
|
44401
|
+
provider: PROVIDER_NAME3,
|
|
44402
|
+
runtime_status: "ok",
|
|
44403
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
44404
|
+
note: `${KALSHI_ODDS_NOTE} ${KALSHI_ATTRIBUTION} (${sourceUrl}).`,
|
|
44405
|
+
source_url: sourceUrl,
|
|
44406
|
+
...rest,
|
|
44407
|
+
returnedCount,
|
|
44408
|
+
may_have_more
|
|
44409
|
+
});
|
|
44410
|
+
}
|
|
44411
|
+
function errorResult4(tool101, error) {
|
|
44412
|
+
if (error instanceof KalshiInputError) {
|
|
44413
|
+
return asTextContentResult(
|
|
44414
|
+
{
|
|
44415
|
+
error: error.message,
|
|
44416
|
+
runtime_status: "invalid_input",
|
|
44417
|
+
provider: PROVIDER_NAME3,
|
|
44418
|
+
tool: tool101
|
|
44419
|
+
},
|
|
44420
|
+
true
|
|
44421
|
+
);
|
|
44422
|
+
}
|
|
44423
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44424
|
+
return asTextContentResult(
|
|
44425
|
+
{
|
|
44426
|
+
error: message,
|
|
44427
|
+
runtime_status: classifyRuntimeStatusFromError(message),
|
|
44428
|
+
provider: PROVIDER_NAME3,
|
|
44429
|
+
tool: tool101
|
|
44430
|
+
},
|
|
44431
|
+
true
|
|
44432
|
+
);
|
|
44433
|
+
}
|
|
44434
|
+
var TICKER_SCHEMA = {
|
|
44435
|
+
type: "string",
|
|
44436
|
+
description: 'Kalshi market ticker (uppercase, SERIES-EVENT-MARKET), e.g. "KXNIREFCALL-30JAN01-YES". Find tickers with kalshi_search_markets or kalshi_get_events.'
|
|
44437
|
+
};
|
|
44438
|
+
var SERIES_TICKER_SCHEMA = {
|
|
44439
|
+
type: "string",
|
|
44440
|
+
description: 'Series ticker filter, e.g. "KXBTCD" (hourly BTC price), "KXFEDDECISION" (Fed decisions), "KXHIGHNY" (NYC high temperature). The series is the leading segment of any ticker.'
|
|
44441
|
+
};
|
|
44442
|
+
var CURSOR_SCHEMA2 = {
|
|
44443
|
+
type: "string",
|
|
44444
|
+
description: "Opaque next_cursor from a previous response; pass it to continue paging."
|
|
44445
|
+
};
|
|
44446
|
+
var LIMIT_SCHEMA2 = {
|
|
44447
|
+
type: "integer",
|
|
44448
|
+
minimum: 1,
|
|
44449
|
+
maximum: MAX_LIMIT4,
|
|
44450
|
+
description: `Maximum rows to return (1-${MAX_LIMIT4}, default ${DEFAULT_LIMIT4}).`
|
|
44451
|
+
};
|
|
44452
|
+
function getAllKalshiEndpoints() {
|
|
44453
|
+
return [
|
|
44454
|
+
{
|
|
44455
|
+
metadata: {
|
|
44456
|
+
resource: "kalshi-markets-search",
|
|
44457
|
+
operation: "read",
|
|
44458
|
+
tags: ["kalshi", "prediction_markets", "markets", "search", "odds"]
|
|
44459
|
+
},
|
|
44460
|
+
tool: {
|
|
44461
|
+
name: "kalshi_search_markets",
|
|
44462
|
+
description: `Search Kalshi prediction markets by free text and list their market-implied odds (yes bid/ask, last price), volume, open interest, close time, and status (keyless public API). Kalshi has no server-side text search, so with a query the tool scans up to ${SEARCH_MAX_PAGES} pages of ${SEARCH_PAGE_SIZE} markets (combo/multivariate markets excluded) and keeps rows where every query term appears in the ticker, event ticker, title, or yes/no subtitle. Narrow with series_ticker or event_ticker whenever you can; the unfiltered scan covers only the newest few hundred of tens of thousands of open markets. Without a query it lists the first page of matching markets.
|
|
44463
|
+
|
|
44464
|
+
${PRICE_UNITS_DOC}
|
|
44465
|
+
|
|
44466
|
+
Args:
|
|
44467
|
+
- query (string, optional): free-text terms, all must match (case-insensitive), e.g. "btc" or "fed decision". Titles use Kalshi wording (e.g. "BTC", not "Bitcoin").
|
|
44468
|
+
- status (string, optional): listing filter, one of ${[...MARKET_STATUS_FILTERS, ANY_STATUS].map((item) => `"${item}"`).join(", ")} (default "open"; "all" = every status). Returned rows carry Kalshi's lifecycle status ("active" for open markets).
|
|
44469
|
+
- series_ticker (string, optional): restrict to one series, e.g. "KXBTCD".
|
|
44470
|
+
- event_ticker (string, optional): restrict to one event, e.g. "KXBTCD-26SEP1612".
|
|
44471
|
+
- limit (integer, optional): max rows (1-${MAX_LIMIT4}, default ${DEFAULT_LIMIT4}).
|
|
44472
|
+
- cursor (string, optional): next_cursor from a previous response to continue the scan.
|
|
44473
|
+
|
|
44474
|
+
Returns:
|
|
44475
|
+
${ENVELOPE_DOC2}
|
|
44476
|
+
"filters", "scan" { "page_size", "pages_scanned", "max_pages", "scanned", "matched" }, "next_cursor" (null when the listing is exhausted), and "rows": [{ "ticker", "event_ticker", "title", "yes_sub_title", "no_sub_title", "status", "market_type", "yes_bid", "yes_bid_cents", "yes_ask", "yes_ask_cents", "last_price", "last_price_cents", "volume", "volume_24h", "open_interest" (contracts), "open_time", "close_time" }].
|
|
44477
|
+
may_have_more is true when more rows matched than were returned or unscanned pages remain; pass next_cursor to continue, or add series_ticker / event_ticker.
|
|
44478
|
+
|
|
44479
|
+
Examples:
|
|
44480
|
+
- { "query": "btc", "series_ticker": "KXBTCD", "limit": 5 } \u2014 the live hourly BTC price brackets.
|
|
44481
|
+
- { "series_ticker": "KXFEDDECISION" } \u2014 every open Fed-decision market.
|
|
44482
|
+
- { "query": "referendum", "limit": 10 } \u2014 scan the newest open markets for referendum questions.
|
|
44483
|
+
- { "event_ticker": "KXBTCD-26SEP1612", "status": "all" } \u2014 every bracket of one event regardless of status.
|
|
44484
|
+
|
|
44485
|
+
Use when:
|
|
44486
|
+
- "What are the odds of X on Kalshi?" \u2014 find the market and read yes_bid / yes_ask / last_price
|
|
44487
|
+
- You need a ticker before an order-book, trades, or price-history read
|
|
44488
|
+
- You want every bracket of a series or event with current odds
|
|
44489
|
+
|
|
44490
|
+
Don't use when:
|
|
44491
|
+
- You already have a ticker (use kalshi_get_market)
|
|
44492
|
+
- You want the headline questions grouped by event (use kalshi_get_events)
|
|
44493
|
+
- You need Polymarket or Hyperliquid HIP-4 outcome markets (use their own tools)`,
|
|
44494
|
+
inputSchema: {
|
|
44495
|
+
type: "object",
|
|
44496
|
+
properties: {
|
|
44497
|
+
query: {
|
|
44498
|
+
type: "string",
|
|
44499
|
+
description: 'Free-text search terms; every term must appear in the ticker, event ticker, title, or yes/no subtitle (case-insensitive). Example: "btc".'
|
|
44500
|
+
},
|
|
44501
|
+
status: {
|
|
44502
|
+
type: "string",
|
|
44503
|
+
enum: [...MARKET_STATUS_FILTERS, ANY_STATUS],
|
|
44504
|
+
description: 'Listing status filter (default "open"). "all" returns every status.'
|
|
44505
|
+
},
|
|
44506
|
+
series_ticker: SERIES_TICKER_SCHEMA,
|
|
44507
|
+
event_ticker: {
|
|
44508
|
+
type: "string",
|
|
44509
|
+
description: 'Event ticker filter, e.g. "KXBTCD-26SEP1612" (one event, all of its markets).'
|
|
44510
|
+
},
|
|
44511
|
+
limit: LIMIT_SCHEMA2,
|
|
44512
|
+
cursor: CURSOR_SCHEMA2
|
|
44513
|
+
}
|
|
44514
|
+
},
|
|
44515
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
44516
|
+
},
|
|
44517
|
+
handler: async (_client, args) => {
|
|
44518
|
+
const toolName = "kalshi_search_markets";
|
|
44519
|
+
try {
|
|
44520
|
+
const query = parseOptionalQuery2(args?.query);
|
|
44521
|
+
const status = parseStatusFilter(
|
|
44522
|
+
args?.status,
|
|
44523
|
+
MARKET_STATUS_FILTERS,
|
|
44524
|
+
"open"
|
|
44525
|
+
);
|
|
44526
|
+
const seriesTicker = parseOptionalTicker(
|
|
44527
|
+
args?.series_ticker,
|
|
44528
|
+
"series_ticker"
|
|
44529
|
+
);
|
|
44530
|
+
const eventTicker = parseOptionalTicker(
|
|
44531
|
+
args?.event_ticker,
|
|
44532
|
+
"event_ticker"
|
|
44533
|
+
);
|
|
44534
|
+
const limit = parseLimit2(args?.limit);
|
|
44535
|
+
const cursor = parseOptionalCursor(args?.cursor);
|
|
44536
|
+
const result2 = await searchMarkets({
|
|
44537
|
+
query,
|
|
44538
|
+
status,
|
|
44539
|
+
seriesTicker,
|
|
44540
|
+
eventTicker,
|
|
44541
|
+
limit,
|
|
44542
|
+
cursor
|
|
44543
|
+
});
|
|
44544
|
+
return okResult2(result2.url, {
|
|
44545
|
+
filters: {
|
|
44546
|
+
query,
|
|
44547
|
+
status: status ?? ANY_STATUS,
|
|
44548
|
+
series_ticker: seriesTicker,
|
|
44549
|
+
event_ticker: eventTicker,
|
|
44550
|
+
limit,
|
|
44551
|
+
cursor
|
|
44552
|
+
},
|
|
44553
|
+
scan: {
|
|
44554
|
+
page_size: result2.pageSize,
|
|
44555
|
+
pages_scanned: result2.pagesScanned,
|
|
44556
|
+
max_pages: query ? SEARCH_MAX_PAGES : 1,
|
|
44557
|
+
scanned: result2.scanned,
|
|
44558
|
+
matched: result2.matched
|
|
44559
|
+
},
|
|
44560
|
+
next_cursor: result2.nextCursor,
|
|
44561
|
+
rows: result2.rows,
|
|
44562
|
+
returnedCount: result2.rows.length,
|
|
44563
|
+
may_have_more: result2.mayHaveMore
|
|
44564
|
+
});
|
|
44565
|
+
} catch (error) {
|
|
44566
|
+
return errorResult4(toolName, error);
|
|
44567
|
+
}
|
|
44568
|
+
}
|
|
44569
|
+
},
|
|
44570
|
+
{
|
|
44571
|
+
metadata: {
|
|
44572
|
+
resource: "kalshi-events",
|
|
44573
|
+
operation: "read",
|
|
44574
|
+
tags: ["kalshi", "prediction_markets", "events", "odds"]
|
|
44575
|
+
},
|
|
44576
|
+
tool: {
|
|
44577
|
+
name: "kalshi_get_events",
|
|
44578
|
+
description: `List Kalshi EVENTS (a headline question grouping one or more markets, e.g. "BTC price on Sep 16, 2026 at 12pm EDT?" with one market per price bracket) with their series, category, strike date, and settlement sources (keyless public API). Filter by status and series_ticker, or fetch one event by event_ticker. Set with_nested_markets to include each event's market_count and up to ${EVENT_MARKETS_PREVIEW2} compact market rows with current odds.
|
|
44579
|
+
|
|
44580
|
+
${PRICE_UNITS_DOC}
|
|
44581
|
+
|
|
44582
|
+
Args:
|
|
44583
|
+
- event_ticker (string, optional): one event's ticker (exact), e.g. "KXBTCD-26SEP1612"; other filters are ignored.
|
|
44584
|
+
- status (string, optional): one of ${[...EVENT_STATUS_FILTERS, ANY_STATUS].map((item) => `"${item}"`).join(", ")} (default "open"; "all" = every status).
|
|
44585
|
+
- series_ticker (string, optional): restrict to one series, e.g. "KXFEDDECISION".
|
|
44586
|
+
- with_nested_markets (boolean, optional): include nested markets (default false). Slower for large pages.
|
|
44587
|
+
- limit (integer, optional): max events (1-${MAX_LIMIT4}, default ${DEFAULT_LIMIT4}).
|
|
44588
|
+
- cursor (string, optional): next_cursor from a previous response.
|
|
44589
|
+
|
|
44590
|
+
Returns:
|
|
44591
|
+
${ENVELOPE_DOC2}
|
|
44592
|
+
"filters", "next_cursor", and "rows": [{ "event_ticker", "series_ticker", "title", "sub_title", "category", "mutually_exclusive", "strike_date", "strike_period", "collateral_return_type", "settlement_sources" (names), "last_updated_ts", "market_count" (null unless nested), "markets" (compact market rows as in kalshi_search_markets, or null), "markets_truncated" }].
|
|
44593
|
+
|
|
44594
|
+
Examples:
|
|
44595
|
+
- { "series_ticker": "KXFEDDECISION", "with_nested_markets": true, "limit": 3 } \u2014 upcoming Fed meetings with their rate brackets.
|
|
44596
|
+
- { "event_ticker": "KXNIREFCALL-30JAN01", "with_nested_markets": true } \u2014 one event and its markets.
|
|
44597
|
+
- { "status": "open", "limit": 20 } \u2014 a page of open events across every category.
|
|
44598
|
+
|
|
44599
|
+
Use when:
|
|
44600
|
+
- "What is Kalshi pricing for the election / the Fed / a game?" \u2014 multi-market questions
|
|
44601
|
+
- You want to browse a series' upcoming events before picking a market
|
|
44602
|
+
- You need every bracket ticker of one event
|
|
44603
|
+
|
|
44604
|
+
Don't use when:
|
|
44605
|
+
- You want free-text search across markets (use kalshi_search_markets)
|
|
44606
|
+
- You need one market's full detail (use kalshi_get_market)`,
|
|
44607
|
+
inputSchema: {
|
|
44608
|
+
type: "object",
|
|
44609
|
+
properties: {
|
|
44610
|
+
event_ticker: {
|
|
44611
|
+
type: "string",
|
|
44612
|
+
description: 'Exact event ticker, e.g. "KXBTCD-26SEP1612". When set, other filters are ignored.'
|
|
44613
|
+
},
|
|
44614
|
+
status: {
|
|
44615
|
+
type: "string",
|
|
44616
|
+
enum: [...EVENT_STATUS_FILTERS, ANY_STATUS],
|
|
44617
|
+
description: 'Event status filter (default "open"). "all" returns every status.'
|
|
44618
|
+
},
|
|
44619
|
+
series_ticker: SERIES_TICKER_SCHEMA,
|
|
44620
|
+
with_nested_markets: {
|
|
44621
|
+
type: "boolean",
|
|
44622
|
+
description: `Include each event's markets (market_count plus up to ${EVENT_MARKETS_PREVIEW2} compact rows). Default false.`
|
|
44623
|
+
},
|
|
44624
|
+
limit: LIMIT_SCHEMA2,
|
|
44625
|
+
cursor: CURSOR_SCHEMA2
|
|
44626
|
+
}
|
|
44627
|
+
},
|
|
44628
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
44629
|
+
},
|
|
44630
|
+
handler: async (_client, args) => {
|
|
44631
|
+
const toolName = "kalshi_get_events";
|
|
44632
|
+
try {
|
|
44633
|
+
const eventTicker = parseOptionalTicker(
|
|
44634
|
+
args?.event_ticker,
|
|
44635
|
+
"event_ticker"
|
|
44636
|
+
);
|
|
44637
|
+
const withNested = parseOptionalBoolean2(
|
|
44638
|
+
args?.with_nested_markets,
|
|
44639
|
+
"with_nested_markets",
|
|
44640
|
+
false
|
|
44641
|
+
);
|
|
44642
|
+
if (eventTicker) {
|
|
44643
|
+
const { url: url2, event } = await fetchEvent(eventTicker, withNested);
|
|
44644
|
+
return okResult2(url2, {
|
|
44645
|
+
filters: { event_ticker: eventTicker, with_nested_markets: withNested },
|
|
44646
|
+
next_cursor: null,
|
|
44647
|
+
rows: [event],
|
|
44648
|
+
returnedCount: 1,
|
|
44649
|
+
may_have_more: false
|
|
44650
|
+
});
|
|
44651
|
+
}
|
|
44652
|
+
const status = parseStatusFilter(
|
|
44653
|
+
args?.status,
|
|
44654
|
+
EVENT_STATUS_FILTERS,
|
|
44655
|
+
"open"
|
|
44656
|
+
);
|
|
44657
|
+
const seriesTicker = parseOptionalTicker(
|
|
44658
|
+
args?.series_ticker,
|
|
44659
|
+
"series_ticker"
|
|
44660
|
+
);
|
|
44661
|
+
const limit = parseLimit2(args?.limit);
|
|
44662
|
+
const cursor = parseOptionalCursor(args?.cursor);
|
|
44663
|
+
const { url, rows, nextCursor } = await fetchEventsPage({
|
|
44664
|
+
status,
|
|
44665
|
+
seriesTicker,
|
|
44666
|
+
limit,
|
|
44667
|
+
cursor,
|
|
44668
|
+
withNested
|
|
44669
|
+
});
|
|
44670
|
+
return okResult2(url, {
|
|
44671
|
+
filters: {
|
|
44672
|
+
status: status ?? ANY_STATUS,
|
|
44673
|
+
series_ticker: seriesTicker,
|
|
44674
|
+
with_nested_markets: withNested,
|
|
44675
|
+
limit,
|
|
44676
|
+
cursor
|
|
44677
|
+
},
|
|
44678
|
+
next_cursor: nextCursor,
|
|
44679
|
+
rows,
|
|
44680
|
+
returnedCount: rows.length,
|
|
44681
|
+
may_have_more: nextCursor !== null
|
|
44682
|
+
});
|
|
44683
|
+
} catch (error) {
|
|
44684
|
+
return errorResult4(toolName, error);
|
|
44685
|
+
}
|
|
44686
|
+
}
|
|
44687
|
+
},
|
|
44688
|
+
{
|
|
44689
|
+
metadata: {
|
|
44690
|
+
resource: "kalshi-market",
|
|
44691
|
+
operation: "read",
|
|
44692
|
+
tags: ["kalshi", "prediction_markets", "market", "odds"]
|
|
44693
|
+
},
|
|
44694
|
+
tool: {
|
|
44695
|
+
name: "kalshi_get_market",
|
|
44696
|
+
description: `Get one Kalshi market in full by ticker: title and yes/no subtitles, lifecycle status and result, yes/no bid and ask, last and previous price, midpoint and spread, volume, open interest, resting sizes, open/close/expiration times, strike fields, early-close terms, and the settlement rules (keyless public API).
|
|
44697
|
+
|
|
44698
|
+
${PRICE_UNITS_DOC}
|
|
44699
|
+
|
|
44700
|
+
Args:
|
|
44701
|
+
- ticker (string, required): market ticker, e.g. "KXNIREFCALL-30JAN01-YES".
|
|
44702
|
+
|
|
44703
|
+
Returns:
|
|
44704
|
+
${ENVELOPE_DOC2}
|
|
44705
|
+
"market": { "ticker", "event_ticker", "series_ticker" (derived from the ticker prefix), "title", "subtitle", "yes_sub_title", "no_sub_title", "status" (initialized|inactive|active|closed|determined|disputed|amended|finalized), "result" ("yes" | "no" | "" while open), "market_type" (binary|scalar), "yes_bid", "yes_bid_cents", "yes_ask", "yes_ask_cents", "no_bid", "no_bid_cents", "no_ask", "no_ask_cents", "last_price", "last_price_cents", "yes_midpoint", "yes_spread_cents", "previous_price", "previous_price_cents", "previous_yes_bid", "previous_yes_ask", "yes_bid_size", "yes_ask_size", "volume", "volume_24h", "open_interest", "liquidity_dollars", "notional_value_dollars", "open_time", "close_time", "expiration_time", "expected_expiration_time", "latest_expiration_time", "can_close_early", "early_close_condition", "settlement_timer_seconds", "strike_type", "floor_strike", "cap_strike", "custom_strike", "is_provisional", "mve_collection_ticker", "mve_selected_legs", "price_level_structure", "price_ranges", "rules_primary", "rules_secondary" (each cut at 1200 chars), "created_time", "updated_time" }.
|
|
44706
|
+
|
|
44707
|
+
Examples:
|
|
44708
|
+
- { "ticker": "KXNIREFCALL-30JAN01-YES" }
|
|
44709
|
+
- { "ticker": "KXBTCD-26SEP1612-T85799.99" }
|
|
44710
|
+
|
|
44711
|
+
Use when:
|
|
44712
|
+
- You have a ticker and need its full state, resolution rules, or close/expiration times
|
|
44713
|
+
- You are checking whether a market is still active before quoting its odds
|
|
44714
|
+
|
|
44715
|
+
Don't use when:
|
|
44716
|
+
- You only know the topic (use kalshi_search_markets)
|
|
44717
|
+
- You need bid/ask depth or the trade tape (use kalshi_get_orderbook / kalshi_get_trades)`,
|
|
44718
|
+
inputSchema: {
|
|
44719
|
+
type: "object",
|
|
44720
|
+
properties: { ticker: TICKER_SCHEMA },
|
|
44721
|
+
required: ["ticker"]
|
|
44722
|
+
},
|
|
44723
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
44724
|
+
},
|
|
44725
|
+
handler: async (_client, args) => {
|
|
44726
|
+
const toolName = "kalshi_get_market";
|
|
44727
|
+
try {
|
|
44728
|
+
const ticker = parseRequiredTicker(args?.ticker);
|
|
44729
|
+
const { url, market } = await fetchMarket(ticker);
|
|
44730
|
+
return okResult2(url, {
|
|
44731
|
+
market,
|
|
44732
|
+
returnedCount: 1,
|
|
44733
|
+
may_have_more: false
|
|
44734
|
+
});
|
|
44735
|
+
} catch (error) {
|
|
44736
|
+
return errorResult4(toolName, error);
|
|
44737
|
+
}
|
|
44738
|
+
}
|
|
44739
|
+
},
|
|
44740
|
+
{
|
|
44741
|
+
metadata: {
|
|
44742
|
+
resource: "kalshi-orderbook",
|
|
44743
|
+
operation: "read",
|
|
44744
|
+
tags: ["kalshi", "prediction_markets", "orderbook", "liquidity", "odds"]
|
|
44745
|
+
},
|
|
44746
|
+
tool: {
|
|
44747
|
+
name: "kalshi_get_orderbook",
|
|
44748
|
+
description: `Get the live order book of one Kalshi market (keyless public API): resting YES bids and NO bids best-first to the requested depth, level totals, and the derived best yes/no ask, spread, and midpoint. Kalshi serves bids only because a NO bid at price p is a YES ask at 1 - p (binary contracts sum to $1); the derived ask fields apply that identity. Sizes are contracts (each pays $1 if its side wins).
|
|
44749
|
+
|
|
44750
|
+
${PRICE_UNITS_DOC}
|
|
44751
|
+
|
|
44752
|
+
Args:
|
|
44753
|
+
- ticker (string, required): market ticker.
|
|
44754
|
+
- depth (integer, optional): levels per side (1-${MAX_BOOK_DEPTH2}, default ${DEFAULT_BOOK_DEPTH2}).
|
|
44755
|
+
|
|
44756
|
+
Returns:
|
|
44757
|
+
${ENVELOPE_DOC2}
|
|
44758
|
+
"ticker", "depth", "yes_bids": [{ "price", "cents", "size" }] (highest first), "no_bids": [...] (highest first), "yes_levels_total", "no_levels_total", "best_yes_bid", "best_yes_bid_cents", "best_yes_ask" (= 1 - best_no_bid), "best_yes_ask_cents", "best_no_bid", "best_no_ask" (= 1 - best_yes_bid), "yes_spread", "yes_midpoint". returnedCount is the number of levels returned across both sides. An empty book on a real market returns zero levels with runtime_status ok; an unknown ticker is rejected as invalid_input.
|
|
44759
|
+
|
|
44760
|
+
Examples:
|
|
44761
|
+
- { "ticker": "KXNIREFCALL-30JAN01-YES", "depth": 5 }
|
|
44762
|
+
|
|
44763
|
+
Use when:
|
|
44764
|
+
- You need executable odds (bid/ask) and depth, not just the last trade, or want to size a hypothetical position
|
|
44765
|
+
- You are checking spread and depth before trusting a thin market's implied probability
|
|
44766
|
+
|
|
44767
|
+
Don't use when:
|
|
44768
|
+
- You want the market's headline odds and metadata (use kalshi_get_market)
|
|
44769
|
+
- You want a CEX or Hyperliquid order book (use get_orderbook / hyperliquid_get_l2_book)`,
|
|
44770
|
+
inputSchema: {
|
|
44771
|
+
type: "object",
|
|
44772
|
+
properties: {
|
|
44773
|
+
ticker: TICKER_SCHEMA,
|
|
44774
|
+
depth: {
|
|
44775
|
+
type: "integer",
|
|
44776
|
+
minimum: 1,
|
|
44777
|
+
maximum: MAX_BOOK_DEPTH2,
|
|
44778
|
+
description: `Levels per side (1-${MAX_BOOK_DEPTH2}, default ${DEFAULT_BOOK_DEPTH2}).`
|
|
44779
|
+
}
|
|
44780
|
+
},
|
|
44781
|
+
required: ["ticker"]
|
|
44782
|
+
},
|
|
44783
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
44784
|
+
},
|
|
44785
|
+
handler: async (_client, args) => {
|
|
44786
|
+
const toolName = "kalshi_get_orderbook";
|
|
44787
|
+
try {
|
|
44788
|
+
const ticker = parseRequiredTicker(args?.ticker);
|
|
44789
|
+
const depth = parseBookDepth2(args?.depth);
|
|
44790
|
+
const { url, book } = await fetchOrderbook2(ticker, depth);
|
|
44791
|
+
if (book.yes_levels_total === 0 && book.no_levels_total === 0) {
|
|
44792
|
+
await assertMarketExists(ticker);
|
|
44793
|
+
}
|
|
44794
|
+
return okResult2(url, {
|
|
44795
|
+
...book,
|
|
44796
|
+
depth,
|
|
44797
|
+
returnedCount: book.yes_bids.length + book.no_bids.length,
|
|
44798
|
+
may_have_more: book.yes_levels_total > depth || book.no_levels_total > depth
|
|
44799
|
+
});
|
|
44800
|
+
} catch (error) {
|
|
44801
|
+
return errorResult4(toolName, error);
|
|
44802
|
+
}
|
|
44803
|
+
}
|
|
44804
|
+
},
|
|
44805
|
+
{
|
|
44806
|
+
metadata: {
|
|
44807
|
+
resource: "kalshi-trades",
|
|
44808
|
+
operation: "read",
|
|
44809
|
+
tags: ["kalshi", "prediction_markets", "trades", "tape", "odds"]
|
|
44810
|
+
},
|
|
44811
|
+
tool: {
|
|
44812
|
+
name: "kalshi_get_trades",
|
|
44813
|
+
description: `Get the recent trade tape of one Kalshi market (keyless public API), newest first: contracts traded, the yes and no price of each fill, the taker side, and whether it was a block trade. Paginate with next_cursor.
|
|
44814
|
+
|
|
44815
|
+
${PRICE_UNITS_DOC}
|
|
44816
|
+
|
|
44817
|
+
Args:
|
|
44818
|
+
- ticker (string, required): market ticker.
|
|
44819
|
+
- limit (integer, optional): max trades (1-${MAX_TRADES_LIMIT}, default ${DEFAULT_TRADES_LIMIT}).
|
|
44820
|
+
- cursor (string, optional): next_cursor from a previous response for older trades.
|
|
44821
|
+
|
|
44822
|
+
Returns:
|
|
44823
|
+
${ENVELOPE_DOC2}
|
|
44824
|
+
"ticker", "next_cursor", and "rows": [{ "trade_id", "ticker", "created_time", "count" (contracts), "yes_price", "yes_price_cents", "no_price", "no_price_cents", "taker_side" ("yes" | "no"), "taker_book_side" ("bid" | "ask"), "is_block_trade" }]. An empty tape on a real market returns zero rows with runtime_status ok; an unknown ticker is rejected as invalid_input.
|
|
44825
|
+
|
|
44826
|
+
Examples:
|
|
44827
|
+
- { "ticker": "KXNIREFCALL-30JAN01-YES", "limit": 20 }
|
|
44828
|
+
- { "ticker": "KXNIREFCALL-30JAN01-YES", "limit": 100, "cursor": "<next_cursor>" } \u2014 the next page of older fills.
|
|
44829
|
+
|
|
44830
|
+
Use when:
|
|
44831
|
+
- "Who is hitting the bid / lifting the offer on this market?" \u2014 recent flow and taker direction
|
|
44832
|
+
- You want fill prices and sizes rather than resting quotes
|
|
44833
|
+
|
|
44834
|
+
Don't use when:
|
|
44835
|
+
- You need resting liquidity (use kalshi_get_orderbook)
|
|
44836
|
+
- You need an OHLC series over time (use kalshi_get_price_history)`,
|
|
44837
|
+
inputSchema: {
|
|
44838
|
+
type: "object",
|
|
44839
|
+
properties: {
|
|
44840
|
+
ticker: TICKER_SCHEMA,
|
|
44841
|
+
limit: {
|
|
44842
|
+
type: "integer",
|
|
44843
|
+
minimum: 1,
|
|
44844
|
+
maximum: MAX_TRADES_LIMIT,
|
|
44845
|
+
description: `Maximum trades to return (1-${MAX_TRADES_LIMIT}, default ${DEFAULT_TRADES_LIMIT}).`
|
|
44846
|
+
},
|
|
44847
|
+
cursor: CURSOR_SCHEMA2
|
|
44848
|
+
},
|
|
44849
|
+
required: ["ticker"]
|
|
44850
|
+
},
|
|
44851
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
44852
|
+
},
|
|
44853
|
+
handler: async (_client, args) => {
|
|
44854
|
+
const toolName = "kalshi_get_trades";
|
|
44855
|
+
try {
|
|
44856
|
+
const ticker = parseRequiredTicker(args?.ticker);
|
|
44857
|
+
const limit = parseTradesLimit(args?.limit);
|
|
44858
|
+
const cursor = parseOptionalCursor(args?.cursor);
|
|
44859
|
+
const { url, rows, nextCursor } = await fetchTrades(
|
|
44860
|
+
ticker,
|
|
44861
|
+
limit,
|
|
44862
|
+
cursor
|
|
44863
|
+
);
|
|
44864
|
+
if (rows.length === 0 && cursor === null) {
|
|
44865
|
+
await assertMarketExists(ticker);
|
|
44866
|
+
}
|
|
44867
|
+
return okResult2(url, {
|
|
44868
|
+
ticker,
|
|
44869
|
+
filters: { limit, cursor },
|
|
44870
|
+
next_cursor: nextCursor,
|
|
44871
|
+
rows,
|
|
44872
|
+
returnedCount: rows.length,
|
|
44873
|
+
may_have_more: nextCursor !== null
|
|
44874
|
+
});
|
|
44875
|
+
} catch (error) {
|
|
44876
|
+
return errorResult4(toolName, error);
|
|
44877
|
+
}
|
|
44878
|
+
}
|
|
44879
|
+
},
|
|
44880
|
+
{
|
|
44881
|
+
metadata: {
|
|
44882
|
+
resource: "kalshi-price-history",
|
|
44883
|
+
operation: "read",
|
|
44884
|
+
tags: [
|
|
44885
|
+
"kalshi",
|
|
44886
|
+
"prediction_markets",
|
|
44887
|
+
"price-history",
|
|
44888
|
+
"candlesticks",
|
|
44889
|
+
"odds",
|
|
44890
|
+
"timeseries"
|
|
44891
|
+
]
|
|
44892
|
+
},
|
|
44893
|
+
tool: {
|
|
44894
|
+
name: "kalshi_get_price_history",
|
|
44895
|
+
description: `Get candlestick price history for one Kalshi market (keyless public API): per period the trade-price open/high/low/close/mean, the previous close carried through quiet periods, the closing yes bid and ask, volume, and open interest, plus a summary (first/last close, range, change, total volume). Candles are 1-minute, 1-hour, or 1-day. At most ${MAX_CANDLES} candles are returned: a wider window is clamped to the most recent ${MAX_CANDLES} and flagged (window.truncated), so page back by passing an earlier end.
|
|
44896
|
+
|
|
44897
|
+
Candle prices are 0-1 dollars per $1 contract (= implied probability); the summary also carries cents. Periods without trades have null OHLC and a non-null "previous".
|
|
44898
|
+
|
|
44899
|
+
Args:
|
|
44900
|
+
- ticker (string, required): market ticker.
|
|
44901
|
+
- period_interval (integer, optional): minutes per candle, one of ${PERIOD_INTERVALS.join(", ")} (default ${DEFAULT_PERIOD_INTERVAL}).
|
|
44902
|
+
- start (string | integer, optional): ISO 8601 datetime or unix seconds. Defaults to a window of 480 candles (1m, 60m) or 365 candles (1440m) before end.
|
|
44903
|
+
- end (string | integer, optional): ISO 8601 datetime or unix seconds (default now).
|
|
44904
|
+
- series_ticker (string, optional): the market's series. Kalshi's candlestick route is keyed by series; by default it is derived from the ticker's leading segment (e.g. "KXBTCD" for "KXBTCD-26SEP1612-T85799.99"). Pass it only when that convention does not hold.
|
|
44905
|
+
|
|
44906
|
+
Returns:
|
|
44907
|
+
${ENVELOPE_DOC2}
|
|
44908
|
+
"ticker", "series_ticker", "series_ticker_source" ("argument" | "derived_from_ticker"), "period_interval", "window": { "start", "end", "requested_start", "truncated", "max_candles" }, "summary": { "candles", "candles_with_trades", "first_at", "last_at", "first_close", "first_close_cents", "last_close", "last_close_cents", "min_low", "max_high", "change", "change_pct", "total_volume", "last_open_interest" } (null when no candle traded), "candles": [{ "end_period_ts", "end_time", "open", "high", "low", "close", "mean", "previous", "yes_bid_close", "yes_ask_close", "volume", "open_interest" }] (oldest first). may_have_more is true when the window was clamped.
|
|
44909
|
+
|
|
44910
|
+
Examples:
|
|
44911
|
+
- { "ticker": "KXNIREFCALL-30JAN01-YES", "period_interval": 1440 } \u2014 one year of daily candles.
|
|
44912
|
+
- { "ticker": "KXBTCD-26SEP1612-T85799.99", "period_interval": 1, "start": "2026-09-16T12:00:00Z", "end": "2026-09-16T16:00:00Z" } \u2014 minute candles for one session.
|
|
44913
|
+
|
|
44914
|
+
Use when:
|
|
44915
|
+
- "How have the odds of X moved this week/month?" \u2014 trend, drawdown, or repricing questions
|
|
44916
|
+
- You want to time-align a market's odds with news or on-chain events
|
|
44917
|
+
|
|
44918
|
+
Don't use when:
|
|
44919
|
+
- You need the current bid/ask depth (use kalshi_get_orderbook)
|
|
44920
|
+
- You need crypto token price history (use get_coin_market_chart_range or CCXT OHLCV tools)`,
|
|
44921
|
+
inputSchema: {
|
|
44922
|
+
type: "object",
|
|
44923
|
+
properties: {
|
|
44924
|
+
ticker: TICKER_SCHEMA,
|
|
44925
|
+
period_interval: {
|
|
44926
|
+
type: "integer",
|
|
44927
|
+
enum: [...PERIOD_INTERVALS],
|
|
44928
|
+
description: `Minutes per candle: 1, 60, or 1440 (default ${DEFAULT_PERIOD_INTERVAL}).`
|
|
44929
|
+
},
|
|
44930
|
+
start: {
|
|
44931
|
+
type: "string",
|
|
44932
|
+
description: 'Window start as ISO 8601 (e.g. "2026-09-01T00:00:00Z") or unix seconds. Defaults to 480 candles (1m/60m) or 365 candles (1440m) before end.'
|
|
44933
|
+
},
|
|
44934
|
+
end: {
|
|
44935
|
+
type: "string",
|
|
44936
|
+
description: "Window end as ISO 8601 or unix seconds (default now). Pass an earlier end to page back in time."
|
|
44937
|
+
},
|
|
44938
|
+
series_ticker: {
|
|
44939
|
+
type: "string",
|
|
44940
|
+
description: `Series ticker override for the candlestick route. Defaults to the ticker's leading segment (e.g. "KXBTCD").`
|
|
44941
|
+
}
|
|
44942
|
+
},
|
|
44943
|
+
required: ["ticker"]
|
|
44944
|
+
},
|
|
44945
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
44946
|
+
},
|
|
44947
|
+
handler: async (_client, args) => {
|
|
44948
|
+
const toolName = "kalshi_get_price_history";
|
|
44949
|
+
try {
|
|
44950
|
+
const ticker = parseRequiredTicker(args?.ticker);
|
|
44951
|
+
const explicitSeries = parseOptionalTicker(
|
|
44952
|
+
args?.series_ticker,
|
|
44953
|
+
"series_ticker"
|
|
44954
|
+
);
|
|
44955
|
+
const window = resolveCandleWindow(args);
|
|
44956
|
+
const seriesTicker = explicitSeries ?? seriesTickerFromTicker(ticker);
|
|
44957
|
+
const { url, candles } = await fetchCandlesticks(
|
|
44958
|
+
seriesTicker,
|
|
44959
|
+
ticker,
|
|
44960
|
+
window
|
|
44961
|
+
);
|
|
44962
|
+
const returned = candles.slice(-MAX_CANDLES);
|
|
44963
|
+
const clamped = window.window_truncated || returned.length < candles.length;
|
|
44964
|
+
return okResult2(url, {
|
|
44965
|
+
ticker,
|
|
44966
|
+
series_ticker: seriesTicker,
|
|
44967
|
+
series_ticker_source: explicitSeries ? "argument" : "derived_from_ticker",
|
|
44968
|
+
period_interval: window.period_interval,
|
|
44969
|
+
window: {
|
|
44970
|
+
start: new Date(window.start_ts * 1e3).toISOString(),
|
|
44971
|
+
end: new Date(window.end_ts * 1e3).toISOString(),
|
|
44972
|
+
requested_start: window.requested_start_ts !== null ? new Date(window.requested_start_ts * 1e3).toISOString() : null,
|
|
44973
|
+
truncated: clamped,
|
|
44974
|
+
max_candles: MAX_CANDLES
|
|
44975
|
+
},
|
|
44976
|
+
summary: summarizeCandles(returned),
|
|
44977
|
+
candles_total: candles.length,
|
|
44978
|
+
candles: returned,
|
|
44979
|
+
returnedCount: returned.length,
|
|
44980
|
+
may_have_more: clamped
|
|
44981
|
+
});
|
|
44982
|
+
} catch (error) {
|
|
44983
|
+
return errorResult4(toolName, error);
|
|
44984
|
+
}
|
|
44985
|
+
}
|
|
44986
|
+
}
|
|
44987
|
+
];
|
|
44988
|
+
}
|
|
44989
|
+
|
|
43829
44990
|
// src/tools/signals/http.ts
|
|
43830
44991
|
var SIGNALS_FETCH_TIMEOUT_MS = 1e4;
|
|
43831
44992
|
var SignalsInputError = class extends Error {
|
|
@@ -43884,13 +45045,13 @@ async function fetchJsonWithTimeout(url, label, timeoutMs = SIGNALS_FETCH_TIMEOU
|
|
|
43884
45045
|
clearTimeout(timer);
|
|
43885
45046
|
}
|
|
43886
45047
|
}
|
|
43887
|
-
function
|
|
45048
|
+
function parseFiniteNumber4(value) {
|
|
43888
45049
|
const num = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
43889
45050
|
return Number.isFinite(num) ? num : null;
|
|
43890
45051
|
}
|
|
43891
45052
|
function parseIntegerArg(value, name, min, max, fallback) {
|
|
43892
45053
|
if (value === void 0 || value === null) return fallback;
|
|
43893
|
-
const parsed =
|
|
45054
|
+
const parsed = parseFiniteNumber4(value);
|
|
43894
45055
|
if (parsed === null || !Number.isInteger(parsed) || parsed < min || parsed > max) {
|
|
43895
45056
|
throw new SignalsInputError(
|
|
43896
45057
|
`${name} must be an integer between ${min} and ${max}; got ${JSON.stringify(value)}.`
|
|
@@ -43900,7 +45061,7 @@ function parseIntegerArg(value, name, min, max, fallback) {
|
|
|
43900
45061
|
}
|
|
43901
45062
|
function parseNumberArg(value, name, min, max, fallback) {
|
|
43902
45063
|
if (value === void 0 || value === null) return fallback;
|
|
43903
|
-
const parsed =
|
|
45064
|
+
const parsed = parseFiniteNumber4(value);
|
|
43904
45065
|
if (parsed === null || parsed < min || parsed > max) {
|
|
43905
45066
|
throw new SignalsInputError(
|
|
43906
45067
|
`${name} must be a number between ${min} and ${max}; got ${JSON.stringify(value)}.`
|
|
@@ -43998,8 +45159,8 @@ function normalizeFearGreed(raw) {
|
|
|
43998
45159
|
let newestSeen = -Infinity;
|
|
43999
45160
|
for (const entry2 of body.data) {
|
|
44000
45161
|
const item = entry2 ?? {};
|
|
44001
|
-
const value =
|
|
44002
|
-
const unixTimestamp =
|
|
45162
|
+
const value = parseFiniteNumber4(item.value);
|
|
45163
|
+
const unixTimestamp = parseFiniteNumber4(item.timestamp);
|
|
44003
45164
|
if (value === null || unixTimestamp === null) continue;
|
|
44004
45165
|
history.push({
|
|
44005
45166
|
value,
|
|
@@ -44009,7 +45170,7 @@ function normalizeFearGreed(raw) {
|
|
|
44009
45170
|
});
|
|
44010
45171
|
if (unixTimestamp > newestSeen) {
|
|
44011
45172
|
newestSeen = unixTimestamp;
|
|
44012
|
-
timeUntilUpdate =
|
|
45173
|
+
timeUntilUpdate = parseFiniteNumber4(item.time_until_update);
|
|
44013
45174
|
}
|
|
44014
45175
|
}
|
|
44015
45176
|
history.sort((a, b) => b.unix_timestamp - a.unix_timestamp);
|
|
@@ -44212,7 +45373,7 @@ var COIN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,99}$/;
|
|
|
44212
45373
|
var VS_CURRENCY_RE = /^[a-z0-9]{2,10}$/;
|
|
44213
45374
|
function parseDays(value) {
|
|
44214
45375
|
if (value === void 0 || value === null) return DEFAULT_DAYS;
|
|
44215
|
-
const parsed =
|
|
45376
|
+
const parsed = parseFiniteNumber4(value);
|
|
44216
45377
|
const match = OHLC_DAYS.find((days) => days === parsed);
|
|
44217
45378
|
if (match === void 0) {
|
|
44218
45379
|
throw new SignalsInputError(
|
|
@@ -44320,7 +45481,7 @@ function parseCandles(raw) {
|
|
|
44320
45481
|
const byTime = /* @__PURE__ */ new Map();
|
|
44321
45482
|
for (const entry2 of raw) {
|
|
44322
45483
|
if (!Array.isArray(entry2) || entry2.length < 5) continue;
|
|
44323
|
-
const [time, open, high, low, close] = entry2.slice(0, 5).map(
|
|
45484
|
+
const [time, open, high, low, close] = entry2.slice(0, 5).map(parseFiniteNumber4);
|
|
44324
45485
|
if (time === null || open === null || high === null || low === null || close === null) {
|
|
44325
45486
|
continue;
|
|
44326
45487
|
}
|
|
@@ -44570,8 +45731,8 @@ function parseChart(raw, chart) {
|
|
|
44570
45731
|
const points = [];
|
|
44571
45732
|
for (const entry2 of body.values) {
|
|
44572
45733
|
const point = entry2 ?? {};
|
|
44573
|
-
const x =
|
|
44574
|
-
const y =
|
|
45734
|
+
const x = parseFiniteNumber4(point.x);
|
|
45735
|
+
const y = parseFiniteNumber4(point.y);
|
|
44575
45736
|
if (x === null || y === null) continue;
|
|
44576
45737
|
points.push({ day: new Date(x * 1e3).toISOString().slice(0, 10), value: y });
|
|
44577
45738
|
}
|
|
@@ -44663,11 +45824,11 @@ function parseHacksArgs(args) {
|
|
|
44663
45824
|
sinceMs
|
|
44664
45825
|
};
|
|
44665
45826
|
}
|
|
44666
|
-
function
|
|
45827
|
+
function optionalString3(value) {
|
|
44667
45828
|
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
44668
45829
|
}
|
|
44669
45830
|
function idString(value) {
|
|
44670
|
-
if (typeof value === "string") return
|
|
45831
|
+
if (typeof value === "string") return optionalString3(value);
|
|
44671
45832
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
44672
45833
|
return null;
|
|
44673
45834
|
}
|
|
@@ -44681,25 +45842,25 @@ function normalizeHackRows(raw) {
|
|
|
44681
45842
|
for (const entry2 of raw) {
|
|
44682
45843
|
if (!entry2 || typeof entry2 !== "object") continue;
|
|
44683
45844
|
const item = entry2;
|
|
44684
|
-
const timestamp =
|
|
44685
|
-
const name =
|
|
45845
|
+
const timestamp = parseFiniteNumber4(item.date);
|
|
45846
|
+
const name = optionalString3(item.name);
|
|
44686
45847
|
if (timestamp === null || name === null) continue;
|
|
44687
45848
|
const chains = Array.isArray(item.chain) ? item.chain.filter((chain) => typeof chain === "string") : typeof item.chain === "string" ? [item.chain] : [];
|
|
44688
45849
|
rows.push({
|
|
44689
45850
|
date: new Date(timestamp * 1e3).toISOString(),
|
|
44690
45851
|
timestamp,
|
|
44691
45852
|
name,
|
|
44692
|
-
classification:
|
|
44693
|
-
technique:
|
|
44694
|
-
amount_usd:
|
|
45853
|
+
classification: optionalString3(item.classification),
|
|
45854
|
+
technique: optionalString3(item.technique),
|
|
45855
|
+
amount_usd: parseFiniteNumber4(item.amount),
|
|
44695
45856
|
chains,
|
|
44696
45857
|
bridge_hack: item.bridgeHack === true,
|
|
44697
|
-
target_type:
|
|
44698
|
-
source:
|
|
44699
|
-
returned_funds_usd:
|
|
45858
|
+
target_type: optionalString3(item.targetType),
|
|
45859
|
+
source: optionalString3(item.source),
|
|
45860
|
+
returned_funds_usd: parseFiniteNumber4(item.returnedFunds),
|
|
44700
45861
|
defillama_id: idString(item.defillamaId),
|
|
44701
45862
|
parent_protocol_id: idString(item.parentProtocolId),
|
|
44702
|
-
language:
|
|
45863
|
+
language: optionalString3(item.language)
|
|
44703
45864
|
});
|
|
44704
45865
|
}
|
|
44705
45866
|
rows.sort(
|
|
@@ -44720,10 +45881,10 @@ async function fetchHackRows() {
|
|
|
44720
45881
|
}
|
|
44721
45882
|
|
|
44722
45883
|
// src/tools/signals/tools.ts
|
|
44723
|
-
function
|
|
45884
|
+
function errorResult5(tool101, error, extra = {}) {
|
|
44724
45885
|
if (error instanceof SignalsInputError) {
|
|
44725
45886
|
return asTextContentResult(
|
|
44726
|
-
{ error: error.message, runtime_status: "invalid_input", tool:
|
|
45887
|
+
{ error: error.message, runtime_status: "invalid_input", tool: tool101, ...extra },
|
|
44727
45888
|
true
|
|
44728
45889
|
);
|
|
44729
45890
|
}
|
|
@@ -44732,7 +45893,7 @@ function errorResult4(tool100, error, extra = {}) {
|
|
|
44732
45893
|
{
|
|
44733
45894
|
error: message,
|
|
44734
45895
|
runtime_status: classifyRuntimeStatusFromError(message),
|
|
44735
|
-
tool:
|
|
45896
|
+
tool: tool101,
|
|
44736
45897
|
...extra
|
|
44737
45898
|
},
|
|
44738
45899
|
true
|
|
@@ -44798,7 +45959,7 @@ Don't use when:
|
|
|
44798
45959
|
may_have_more: history.length >= limit
|
|
44799
45960
|
});
|
|
44800
45961
|
} catch (error) {
|
|
44801
|
-
return
|
|
45962
|
+
return errorResult5(toolName, error);
|
|
44802
45963
|
}
|
|
44803
45964
|
}
|
|
44804
45965
|
},
|
|
@@ -44917,7 +46078,7 @@ Don't use when:
|
|
|
44917
46078
|
...computeTechnicalIndicators(candles, parsed)
|
|
44918
46079
|
});
|
|
44919
46080
|
} catch (error) {
|
|
44920
|
-
return
|
|
46081
|
+
return errorResult5(toolName, error);
|
|
44921
46082
|
}
|
|
44922
46083
|
}
|
|
44923
46084
|
},
|
|
@@ -45014,7 +46175,7 @@ Don't use when:
|
|
|
45014
46175
|
rows
|
|
45015
46176
|
});
|
|
45016
46177
|
} catch (error) {
|
|
45017
|
-
return
|
|
46178
|
+
return errorResult5(toolName, error);
|
|
45018
46179
|
}
|
|
45019
46180
|
}
|
|
45020
46181
|
},
|
|
@@ -45097,7 +46258,7 @@ Don't use when:
|
|
|
45097
46258
|
rows: page
|
|
45098
46259
|
});
|
|
45099
46260
|
} catch (error) {
|
|
45100
|
-
return
|
|
46261
|
+
return errorResult5(toolName, error);
|
|
45101
46262
|
}
|
|
45102
46263
|
}
|
|
45103
46264
|
}
|
|
@@ -45237,7 +46398,7 @@ function blockscoutGet(chain, path) {
|
|
|
45237
46398
|
}
|
|
45238
46399
|
var ownCoinGecko = null;
|
|
45239
46400
|
async function resolvePrice(chain, address, token) {
|
|
45240
|
-
const blockscoutRate =
|
|
46401
|
+
const blockscoutRate = parseFiniteNumber4(token.exchange_rate);
|
|
45241
46402
|
if (blockscoutRate !== null && blockscoutRate > 0) {
|
|
45242
46403
|
return { price: blockscoutRate, source: "blockscout_exchange_rate" };
|
|
45243
46404
|
}
|
|
@@ -45249,7 +46410,7 @@ async function resolvePrice(chain, address, token) {
|
|
|
45249
46410
|
{ network: chain.gecko }
|
|
45250
46411
|
);
|
|
45251
46412
|
const prices = result2?.data?.attributes?.token_prices ?? {};
|
|
45252
|
-
const price =
|
|
46413
|
+
const price = parseFiniteNumber4(
|
|
45253
46414
|
prices[address.toLowerCase()] ?? prices[address]
|
|
45254
46415
|
);
|
|
45255
46416
|
if (price !== null && price > 0) {
|
|
@@ -45268,11 +46429,11 @@ async function getTokenHolderDistribution(args, now = Date.now) {
|
|
|
45268
46429
|
`this address is a ${String(token.type)} token; holder tiers are computed for ERC-20 tokens only.`
|
|
45269
46430
|
);
|
|
45270
46431
|
}
|
|
45271
|
-
const decimals =
|
|
46432
|
+
const decimals = parseFiniteNumber4(token.decimals);
|
|
45272
46433
|
if (decimals === null) {
|
|
45273
46434
|
throw new Error("Blockscout returned no decimals for this token");
|
|
45274
46435
|
}
|
|
45275
|
-
const totalHolders =
|
|
46436
|
+
const totalHolders = parseFiniteNumber4(token.holders_count ?? token.holders);
|
|
45276
46437
|
const { price, source: priceSource } = priceOverride !== null ? { price: priceOverride, source: "caller" } : await resolvePrice(chain, address, token);
|
|
45277
46438
|
const base = {
|
|
45278
46439
|
provider: BLOCKSCOUT_PROVIDER,
|
|
@@ -45320,7 +46481,7 @@ async function getTokenHolderDistribution(args, now = Date.now) {
|
|
|
45320
46481
|
let belowFloor = false;
|
|
45321
46482
|
for (const item of items) {
|
|
45322
46483
|
const holder = item ?? {};
|
|
45323
|
-
const units =
|
|
46484
|
+
const units = parseFiniteNumber4(holder.value);
|
|
45324
46485
|
if (units === null) continue;
|
|
45325
46486
|
const usd2 = units / 10 ** decimals * price;
|
|
45326
46487
|
rows.push({ usd: usd2, is_contract: holder.address?.is_contract === true });
|
|
@@ -45437,6 +46598,822 @@ Don't use when:
|
|
|
45437
46598
|
];
|
|
45438
46599
|
}
|
|
45439
46600
|
|
|
46601
|
+
// src/tools/goldsky/tools.ts
|
|
46602
|
+
var GOLDSKY_PROVIDER = "Goldsky";
|
|
46603
|
+
var EDGE_BASE_URL = "https://edge.goldsky.com";
|
|
46604
|
+
var MAX_LOG_BLOCK_SPAN = 999;
|
|
46605
|
+
var MAX_UNFILTERED_LOG_BLOCK_SPAN = 9;
|
|
46606
|
+
var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
46607
|
+
var MAX_TRACE_FILTER_BLOCK_SPAN = 1e3;
|
|
46608
|
+
var CHAIN_IDS = {
|
|
46609
|
+
ethereum: 1,
|
|
46610
|
+
mainnet: 1,
|
|
46611
|
+
eth: 1,
|
|
46612
|
+
base: 8453,
|
|
46613
|
+
arbitrum: 42161,
|
|
46614
|
+
arbitrum_one: 42161,
|
|
46615
|
+
optimism: 10,
|
|
46616
|
+
polygon: 137,
|
|
46617
|
+
bsc: 56,
|
|
46618
|
+
bnb: 56,
|
|
46619
|
+
hyperevm: 999,
|
|
46620
|
+
avalanche: 43114,
|
|
46621
|
+
linea: 59144,
|
|
46622
|
+
scroll: 534352,
|
|
46623
|
+
zksync: 324,
|
|
46624
|
+
unichain: 130,
|
|
46625
|
+
sonic: 146,
|
|
46626
|
+
berachain: 80094,
|
|
46627
|
+
mantle: 5e3,
|
|
46628
|
+
blast: 81457,
|
|
46629
|
+
monad: 143,
|
|
46630
|
+
worldchain: 480,
|
|
46631
|
+
plasma: 9745,
|
|
46632
|
+
megaeth: 4326,
|
|
46633
|
+
sei: 1329,
|
|
46634
|
+
gnosis: 100,
|
|
46635
|
+
celo: 42220,
|
|
46636
|
+
ink: 57073,
|
|
46637
|
+
katana: 747474,
|
|
46638
|
+
abstract: 2741,
|
|
46639
|
+
robinhood: 4663,
|
|
46640
|
+
tron: 728126428
|
|
46641
|
+
};
|
|
46642
|
+
var FEEDS_CHAINS = {
|
|
46643
|
+
ethereum: "ethereum",
|
|
46644
|
+
eth: "ethereum",
|
|
46645
|
+
mainnet: "ethereum",
|
|
46646
|
+
base: "base",
|
|
46647
|
+
arbitrum: "arbitrum_one",
|
|
46648
|
+
arbitrum_one: "arbitrum_one",
|
|
46649
|
+
optimism: "optimism",
|
|
46650
|
+
polygon: "polygon",
|
|
46651
|
+
bsc: "bsc",
|
|
46652
|
+
bnb: "bsc",
|
|
46653
|
+
robinhood: "robinhood",
|
|
46654
|
+
arc: "arc"
|
|
46655
|
+
};
|
|
46656
|
+
var READ_METHODS = [
|
|
46657
|
+
"eth_blockNumber",
|
|
46658
|
+
"eth_chainId",
|
|
46659
|
+
"eth_gasPrice",
|
|
46660
|
+
"eth_maxPriorityFeePerGas",
|
|
46661
|
+
"eth_blobBaseFee",
|
|
46662
|
+
"eth_feeHistory",
|
|
46663
|
+
"eth_syncing",
|
|
46664
|
+
"eth_getBalance",
|
|
46665
|
+
"eth_getCode",
|
|
46666
|
+
"eth_getStorageAt",
|
|
46667
|
+
"eth_getTransactionCount",
|
|
46668
|
+
"eth_getProof",
|
|
46669
|
+
"eth_getTransactionByHash",
|
|
46670
|
+
"eth_getTransactionByBlockNumberAndIndex",
|
|
46671
|
+
"eth_getTransactionByBlockHashAndIndex",
|
|
46672
|
+
"eth_getTransactionReceipt",
|
|
46673
|
+
"eth_getBlockByNumber",
|
|
46674
|
+
"eth_getBlockByHash",
|
|
46675
|
+
"eth_getBlockReceipts",
|
|
46676
|
+
"eth_getBlockTransactionCountByNumber",
|
|
46677
|
+
"eth_getBlockTransactionCountByHash",
|
|
46678
|
+
"eth_call",
|
|
46679
|
+
"eth_estimateGas",
|
|
46680
|
+
"eth_createAccessList",
|
|
46681
|
+
"net_version",
|
|
46682
|
+
"web3_clientVersion"
|
|
46683
|
+
];
|
|
46684
|
+
var ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
46685
|
+
var HEX_DATA = /^0x(?:[0-9a-fA-F]{2})*$/;
|
|
46686
|
+
var TX_HASH = /^0x[0-9a-fA-F]{64}$/;
|
|
46687
|
+
var HEX_QUANTITY = /^0x[0-9a-fA-F]+$/;
|
|
46688
|
+
var BLOCK_TAGS = ["latest", "safe", "finalized", "earliest", "pending"];
|
|
46689
|
+
var chainProp = {
|
|
46690
|
+
oneOf: [{ type: "string" }, { type: "integer", minimum: 1 }],
|
|
46691
|
+
default: "ethereum",
|
|
46692
|
+
description: `EVM chain: a name (${Object.keys(CHAIN_IDS).filter((name) => !["mainnet", "eth", "arbitrum_one", "bnb"].includes(name)).join(
|
|
46693
|
+
", "
|
|
46694
|
+
)}) or any numeric chain id Goldsky Edge serves (about 150 mainnets).`
|
|
46695
|
+
};
|
|
46696
|
+
var blockProp = {
|
|
46697
|
+
type: "string",
|
|
46698
|
+
default: "latest",
|
|
46699
|
+
description: "Block tag (latest, safe, finalized, earliest) or a hex block number. Historical blocks are served from archive nodes."
|
|
46700
|
+
};
|
|
46701
|
+
var edgeClient = null;
|
|
46702
|
+
function getEdgeClient() {
|
|
46703
|
+
if (!edgeClient) {
|
|
46704
|
+
edgeClient = createHttpClient({
|
|
46705
|
+
baseURL: EDGE_BASE_URL,
|
|
46706
|
+
serviceName: "GoldskyEdge",
|
|
46707
|
+
timeout: 3e4,
|
|
46708
|
+
headers: {
|
|
46709
|
+
Accept: "application/json",
|
|
46710
|
+
"Content-Type": "application/json"
|
|
46711
|
+
},
|
|
46712
|
+
maxRetries: 2,
|
|
46713
|
+
retryBaseDelay: 500,
|
|
46714
|
+
maxContentLength: MAX_RESPONSE_BYTES
|
|
46715
|
+
});
|
|
46716
|
+
}
|
|
46717
|
+
return edgeClient;
|
|
46718
|
+
}
|
|
46719
|
+
function requireKey(name) {
|
|
46720
|
+
const key = getEnvVar(name);
|
|
46721
|
+
if (!key)
|
|
46722
|
+
throw new Error(`Goldsky API key not configured (${name} is not set).`);
|
|
46723
|
+
return key;
|
|
46724
|
+
}
|
|
46725
|
+
function resolveChainId(value) {
|
|
46726
|
+
const raw = String(value ?? "ethereum").trim().toLowerCase();
|
|
46727
|
+
if (/^\d+$/.test(raw)) {
|
|
46728
|
+
const id2 = Number(raw);
|
|
46729
|
+
if (Number.isSafeInteger(id2) && id2 > 0) return id2;
|
|
46730
|
+
}
|
|
46731
|
+
const id = CHAIN_IDS[raw.replace(/[\s-]+/g, "_")];
|
|
46732
|
+
if (id) return id;
|
|
46733
|
+
throw new SignalsInputError(
|
|
46734
|
+
`unknown chain "${value}". Use one of ${Object.keys(CHAIN_IDS).join(", ")}, or a numeric chain id.`
|
|
46735
|
+
);
|
|
46736
|
+
}
|
|
46737
|
+
function requirePattern(value, pattern, field, hint) {
|
|
46738
|
+
if (typeof value !== "string" || !pattern.test(value)) {
|
|
46739
|
+
throw new SignalsInputError(`${field} must be ${hint}.`);
|
|
46740
|
+
}
|
|
46741
|
+
return value;
|
|
46742
|
+
}
|
|
46743
|
+
function blockArg(value) {
|
|
46744
|
+
const block = value === void 0 ? "latest" : String(value);
|
|
46745
|
+
if (BLOCK_TAGS.includes(block) || HEX_QUANTITY.test(block)) return block;
|
|
46746
|
+
throw new SignalsInputError(
|
|
46747
|
+
"block must be a block tag (latest, safe, finalized, earliest) or a hex block number such as 0x1312d00."
|
|
46748
|
+
);
|
|
46749
|
+
}
|
|
46750
|
+
function blockNumber(value, field) {
|
|
46751
|
+
return BigInt(
|
|
46752
|
+
requirePattern(
|
|
46753
|
+
value,
|
|
46754
|
+
HEX_QUANTITY,
|
|
46755
|
+
field,
|
|
46756
|
+
"a hex block number such as 0x1312d00"
|
|
46757
|
+
)
|
|
46758
|
+
);
|
|
46759
|
+
}
|
|
46760
|
+
function boundedInt(value, fallback, min, max, field) {
|
|
46761
|
+
if (value === void 0) return fallback;
|
|
46762
|
+
const n = Number(value);
|
|
46763
|
+
if (!Number.isInteger(n) || n < min || n > max) {
|
|
46764
|
+
throw new SignalsInputError(
|
|
46765
|
+
`${field} must be an integer from ${min} to ${max}.`
|
|
46766
|
+
);
|
|
46767
|
+
}
|
|
46768
|
+
return n;
|
|
46769
|
+
}
|
|
46770
|
+
async function edgeRpc(chainId, method, params) {
|
|
46771
|
+
const key = requireKey("GOLDSKY_EDGE_API_KEY");
|
|
46772
|
+
let response;
|
|
46773
|
+
try {
|
|
46774
|
+
response = await getEdgeClient().post(
|
|
46775
|
+
`/standard/evm/${chainId}`,
|
|
46776
|
+
{
|
|
46777
|
+
body: { jsonrpc: "2.0", id: 1, method, params },
|
|
46778
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
46779
|
+
}
|
|
46780
|
+
);
|
|
46781
|
+
} catch (error) {
|
|
46782
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46783
|
+
if (message.includes("maxContentLength")) {
|
|
46784
|
+
throw new SignalsInputError(
|
|
46785
|
+
`${method} returned more than ${MAX_RESPONSE_BYTES / 1024 / 1024} MB. Narrow the block range, add an address or topic filter, or lower the count.`
|
|
46786
|
+
);
|
|
46787
|
+
}
|
|
46788
|
+
if (/network 'evm:\d+'/i.test(message)) {
|
|
46789
|
+
throw new SignalsInputError(
|
|
46790
|
+
`chain id ${chainId} is not served by Goldsky Edge. Pass a supported chain name or id (https://docs.goldsky.com/chains/supported-networks).`
|
|
46791
|
+
);
|
|
46792
|
+
}
|
|
46793
|
+
throw error;
|
|
46794
|
+
}
|
|
46795
|
+
if (response?.error) {
|
|
46796
|
+
const { code, message = "unknown error" } = response.error;
|
|
46797
|
+
if (code === -32602 || code === -32600 || code === -32012 || code === 3 || /revert/i.test(message)) {
|
|
46798
|
+
throw new SignalsInputError(
|
|
46799
|
+
`Goldsky Edge rejected ${method} (${code}): ${message}`
|
|
46800
|
+
);
|
|
46801
|
+
}
|
|
46802
|
+
throw new Error(`Goldsky Edge ${method} failed (${code}): ${message}`);
|
|
46803
|
+
}
|
|
46804
|
+
return response?.result ?? null;
|
|
46805
|
+
}
|
|
46806
|
+
async function feedsGet(path, query) {
|
|
46807
|
+
const key = requireKey("GOLDSKY_DATA_API_KEY");
|
|
46808
|
+
const params = Object.fromEntries(
|
|
46809
|
+
Object.entries(query).filter(([, v]) => v !== void 0)
|
|
46810
|
+
);
|
|
46811
|
+
return getEdgeClient().get(
|
|
46812
|
+
`/data/feeds/wallets/${path}`,
|
|
46813
|
+
{
|
|
46814
|
+
params,
|
|
46815
|
+
headers: { "X-API-Key": key }
|
|
46816
|
+
}
|
|
46817
|
+
);
|
|
46818
|
+
}
|
|
46819
|
+
function feedsChains(value) {
|
|
46820
|
+
if (value === void 0 || value === "") return void 0;
|
|
46821
|
+
const names = Array.isArray(value) ? value : String(value).split(",");
|
|
46822
|
+
return names.map((name) => {
|
|
46823
|
+
const slug = FEEDS_CHAINS[String(name).trim().toLowerCase()];
|
|
46824
|
+
if (!slug) {
|
|
46825
|
+
throw new SignalsInputError(
|
|
46826
|
+
`Feeds does not cover chain "${name}". Supported: ethereum, base, arbitrum, optimism, polygon, bsc, robinhood, arc.`
|
|
46827
|
+
);
|
|
46828
|
+
}
|
|
46829
|
+
return slug;
|
|
46830
|
+
}).join(",");
|
|
46831
|
+
}
|
|
46832
|
+
function walletAddress(args) {
|
|
46833
|
+
return requirePattern(
|
|
46834
|
+
args.address,
|
|
46835
|
+
ADDRESS,
|
|
46836
|
+
"address",
|
|
46837
|
+
"an EVM address (0x + 40 hex digits)"
|
|
46838
|
+
);
|
|
46839
|
+
}
|
|
46840
|
+
function tool100(name, resource, tags, description, properties, required, run) {
|
|
46841
|
+
return {
|
|
46842
|
+
metadata: {
|
|
46843
|
+
resource,
|
|
46844
|
+
operation: "read",
|
|
46845
|
+
tags: ["goldsky", "onchain", ...tags]
|
|
46846
|
+
},
|
|
46847
|
+
tool: {
|
|
46848
|
+
name,
|
|
46849
|
+
description,
|
|
46850
|
+
inputSchema: {
|
|
46851
|
+
type: "object",
|
|
46852
|
+
properties,
|
|
46853
|
+
required,
|
|
46854
|
+
additionalProperties: false
|
|
46855
|
+
},
|
|
46856
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
46857
|
+
},
|
|
46858
|
+
handler: async (_client, args) => {
|
|
46859
|
+
try {
|
|
46860
|
+
return asTextContentResult({
|
|
46861
|
+
provider: GOLDSKY_PROVIDER,
|
|
46862
|
+
...await run(args ?? {})
|
|
46863
|
+
});
|
|
46864
|
+
} catch (error) {
|
|
46865
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46866
|
+
return asTextContentResult(
|
|
46867
|
+
{
|
|
46868
|
+
error: message,
|
|
46869
|
+
runtime_status: error instanceof SignalsInputError ? "invalid_input" : classifyRuntimeStatusFromError(message),
|
|
46870
|
+
tool: name,
|
|
46871
|
+
provider: GOLDSKY_PROVIDER
|
|
46872
|
+
},
|
|
46873
|
+
true
|
|
46874
|
+
);
|
|
46875
|
+
}
|
|
46876
|
+
}
|
|
46877
|
+
};
|
|
46878
|
+
}
|
|
46879
|
+
function getAllGoldskyEndpoints() {
|
|
46880
|
+
return [
|
|
46881
|
+
tool100(
|
|
46882
|
+
"goldsky_rpc_call",
|
|
46883
|
+
"goldsky.rpc.call",
|
|
46884
|
+
["rpc", "evm", "network", "blocks", "transactions"],
|
|
46885
|
+
`Run one read-only EVM JSON-RPC method on any chain Goldsky Edge serves (about 150 mainnets, including HyperEVM, Monad, Sonic, Berachain, Plasma and Tron), with archive history. Writes are refused.
|
|
46886
|
+
|
|
46887
|
+
Args:
|
|
46888
|
+
- chain (string or integer, default ethereum): chain name or numeric chain id.
|
|
46889
|
+
- method (string, required): one of ${READ_METHODS.join(", ")}.
|
|
46890
|
+
- params (array, default []): the method's positional JSON-RPC params, e.g. ["0xd8dA...6045", "latest"] for eth_getBalance.
|
|
46891
|
+
|
|
46892
|
+
Returns: { "provider": "Goldsky", "chain_id", "method", "result" } with the raw JSON-RPC result (hex quantities stay hex).
|
|
46893
|
+
|
|
46894
|
+
Examples:
|
|
46895
|
+
- { "chain": "hyperevm", "method": "eth_blockNumber" }
|
|
46896
|
+
- { "chain": 8453, "method": "eth_getTransactionReceipt", "params": ["0x<64 hex>"] }
|
|
46897
|
+
|
|
46898
|
+
Use when: you need a node read on a chain other tools do not cover, or a method like eth_getProof, eth_getStorageAt or eth_feeHistory.
|
|
46899
|
+
Don't use when: you need logs (goldsky_get_logs), traces (goldsky_trace_transaction) or a contract read (goldsky_call_contract).`,
|
|
46900
|
+
{
|
|
46901
|
+
chain: chainProp,
|
|
46902
|
+
method: {
|
|
46903
|
+
type: "string",
|
|
46904
|
+
enum: [...READ_METHODS],
|
|
46905
|
+
description: "Read-only JSON-RPC method."
|
|
46906
|
+
},
|
|
46907
|
+
params: {
|
|
46908
|
+
type: "array",
|
|
46909
|
+
maxItems: 8,
|
|
46910
|
+
items: {},
|
|
46911
|
+
default: [],
|
|
46912
|
+
description: "Positional JSON-RPC params."
|
|
46913
|
+
}
|
|
46914
|
+
},
|
|
46915
|
+
["method"],
|
|
46916
|
+
async (args) => {
|
|
46917
|
+
const chainId = resolveChainId(args.chain);
|
|
46918
|
+
const method = String(args.method);
|
|
46919
|
+
if (!READ_METHODS.includes(method)) {
|
|
46920
|
+
throw new SignalsInputError(
|
|
46921
|
+
`method must be one of ${READ_METHODS.join(", ")}.`
|
|
46922
|
+
);
|
|
46923
|
+
}
|
|
46924
|
+
const params = args.params ?? [];
|
|
46925
|
+
if (!Array.isArray(params))
|
|
46926
|
+
throw new SignalsInputError("params must be an array.");
|
|
46927
|
+
return {
|
|
46928
|
+
chain_id: chainId,
|
|
46929
|
+
method,
|
|
46930
|
+
result: await edgeRpc(chainId, method, params)
|
|
46931
|
+
};
|
|
46932
|
+
}
|
|
46933
|
+
),
|
|
46934
|
+
tool100(
|
|
46935
|
+
"goldsky_call_contract",
|
|
46936
|
+
"goldsky.rpc.eth_call",
|
|
46937
|
+
["rpc", "evm", "contracts", "state"],
|
|
46938
|
+
`Read a smart contract's state with eth_call on any Goldsky Edge chain, at the latest or any historical block: pool reserves, vault balances, lending positions, token balanceOf, oracle prices. Read-only; nothing is signed or sent.
|
|
46939
|
+
|
|
46940
|
+
Args:
|
|
46941
|
+
- chain (string or integer, default ethereum): chain name or numeric chain id.
|
|
46942
|
+
- to (string, required): contract address.
|
|
46943
|
+
- data (string, required): ABI-encoded calldata (4-byte selector plus arguments), e.g. 0x18160ddd for totalSupply().
|
|
46944
|
+
- from (string, optional): caller address, for contracts that branch on msg.sender.
|
|
46945
|
+
- block (string, default latest): block tag or hex block number.
|
|
46946
|
+
|
|
46947
|
+
Returns: { "provider": "Goldsky", "chain_id", "to", "block", "result" } where result is the ABI-encoded return value as hex. A revert returns invalid_input with the reason.
|
|
46948
|
+
|
|
46949
|
+
Example: { "chain": "ethereum", "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "data": "0x18160ddd" }
|
|
46950
|
+
|
|
46951
|
+
Use when: the answer lives in contract storage and no typed tool exposes it.
|
|
46952
|
+
Don't use when: you want to simulate a state-changing transaction (use tenderly_simulate_transaction).`,
|
|
46953
|
+
{
|
|
46954
|
+
chain: chainProp,
|
|
46955
|
+
to: {
|
|
46956
|
+
type: "string",
|
|
46957
|
+
pattern: ADDRESS.source,
|
|
46958
|
+
description: "Contract address."
|
|
46959
|
+
},
|
|
46960
|
+
data: {
|
|
46961
|
+
type: "string",
|
|
46962
|
+
pattern: HEX_DATA.source,
|
|
46963
|
+
maxLength: 2e4,
|
|
46964
|
+
description: "ABI-encoded calldata (0x...)."
|
|
46965
|
+
},
|
|
46966
|
+
from: {
|
|
46967
|
+
type: "string",
|
|
46968
|
+
pattern: ADDRESS.source,
|
|
46969
|
+
description: "Optional caller address."
|
|
46970
|
+
},
|
|
46971
|
+
block: blockProp
|
|
46972
|
+
},
|
|
46973
|
+
["to", "data"],
|
|
46974
|
+
async (args) => {
|
|
46975
|
+
const chainId = resolveChainId(args.chain);
|
|
46976
|
+
const to = requirePattern(
|
|
46977
|
+
args.to,
|
|
46978
|
+
ADDRESS,
|
|
46979
|
+
"to",
|
|
46980
|
+
"a contract address (0x + 40 hex digits)"
|
|
46981
|
+
);
|
|
46982
|
+
const data = requirePattern(
|
|
46983
|
+
args.data,
|
|
46984
|
+
HEX_DATA,
|
|
46985
|
+
"data",
|
|
46986
|
+
"hex calldata (0x + an even number of hex digits)"
|
|
46987
|
+
);
|
|
46988
|
+
const call = { to, data };
|
|
46989
|
+
if (args.from !== void 0)
|
|
46990
|
+
call.from = requirePattern(
|
|
46991
|
+
args.from,
|
|
46992
|
+
ADDRESS,
|
|
46993
|
+
"from",
|
|
46994
|
+
"an EVM address"
|
|
46995
|
+
);
|
|
46996
|
+
const block = blockArg(args.block);
|
|
46997
|
+
return {
|
|
46998
|
+
chain_id: chainId,
|
|
46999
|
+
to,
|
|
47000
|
+
block,
|
|
47001
|
+
result: await edgeRpc(chainId, "eth_call", [call, block])
|
|
47002
|
+
};
|
|
47003
|
+
}
|
|
47004
|
+
),
|
|
47005
|
+
tool100(
|
|
47006
|
+
"goldsky_get_logs",
|
|
47007
|
+
"goldsky.rpc.eth_getLogs",
|
|
47008
|
+
["rpc", "evm", "logs", "events"],
|
|
47009
|
+
`Fetch EVM event logs (eth_getLogs) on any Goldsky Edge chain for one block hash or a block range of at most ${MAX_LOG_BLOCK_SPAN + 1} blocks, filtered by contract address and topics. Archive history included.
|
|
47010
|
+
|
|
47011
|
+
Args:
|
|
47012
|
+
- chain (string or integer, default ethereum): chain name or numeric chain id.
|
|
47013
|
+
- from_block / to_block (hex strings): inclusive range, to_block - from_block at most ${MAX_LOG_BLOCK_SPAN} with an address or topic0 filter, or ${MAX_UNFILTERED_LOG_BLOCK_SPAN} without one. Or pass block_hash instead. Responses over ${MAX_RESPONSE_BYTES / 1024 / 1024} MB are refused; narrow the query.
|
|
47014
|
+
- address (string or array of up to 20 strings, optional): emitting contracts.
|
|
47015
|
+
- topics (array, optional): up to 4 topic filters (topic0 is the event signature hash; null matches anything).
|
|
47016
|
+
- limit (integer, default 100, max 1000): logs returned; total_logs reports how many matched.
|
|
47017
|
+
|
|
47018
|
+
Returns: { "provider": "Goldsky", "chain_id", "from_block", "to_block", "block_hash", "total_logs", "returned", "truncated", "logs": [raw log objects] }.
|
|
47019
|
+
|
|
47020
|
+
Example: { "chain": "base", "from_block": "0x3270000", "to_block": "0x32703e7", "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"], "limit": 20 }
|
|
47021
|
+
|
|
47022
|
+
Use when: you need raw events from a specific contract or block range.
|
|
47023
|
+
Don't use when: you want a wallet's transfer history (use goldsky_get_wallet_transfers or get_wallet_transfers).`,
|
|
47024
|
+
{
|
|
47025
|
+
chain: chainProp,
|
|
47026
|
+
from_block: {
|
|
47027
|
+
type: "string",
|
|
47028
|
+
pattern: HEX_QUANTITY.source,
|
|
47029
|
+
description: "Inclusive start block (hex)."
|
|
47030
|
+
},
|
|
47031
|
+
to_block: {
|
|
47032
|
+
type: "string",
|
|
47033
|
+
pattern: HEX_QUANTITY.source,
|
|
47034
|
+
description: "Inclusive end block (hex)."
|
|
47035
|
+
},
|
|
47036
|
+
block_hash: {
|
|
47037
|
+
type: "string",
|
|
47038
|
+
pattern: TX_HASH.source,
|
|
47039
|
+
description: "Single block hash, instead of a range."
|
|
47040
|
+
},
|
|
47041
|
+
address: {
|
|
47042
|
+
oneOf: [
|
|
47043
|
+
{ type: "string", pattern: ADDRESS.source },
|
|
47044
|
+
{
|
|
47045
|
+
type: "array",
|
|
47046
|
+
minItems: 1,
|
|
47047
|
+
maxItems: 20,
|
|
47048
|
+
items: { type: "string", pattern: ADDRESS.source }
|
|
47049
|
+
}
|
|
47050
|
+
],
|
|
47051
|
+
description: "Emitting contract address or addresses."
|
|
47052
|
+
},
|
|
47053
|
+
topics: {
|
|
47054
|
+
type: "array",
|
|
47055
|
+
maxItems: 4,
|
|
47056
|
+
items: {},
|
|
47057
|
+
description: "Topic filters; null matches any."
|
|
47058
|
+
},
|
|
47059
|
+
limit: {
|
|
47060
|
+
type: "integer",
|
|
47061
|
+
minimum: 1,
|
|
47062
|
+
maximum: 1e3,
|
|
47063
|
+
default: 100,
|
|
47064
|
+
description: "Logs to return."
|
|
47065
|
+
}
|
|
47066
|
+
},
|
|
47067
|
+
[],
|
|
47068
|
+
async (args) => {
|
|
47069
|
+
const chainId = resolveChainId(args.chain);
|
|
47070
|
+
const limit = boundedInt(args.limit, 100, 1, 1e3, "limit");
|
|
47071
|
+
const filter = {};
|
|
47072
|
+
if (args.block_hash !== void 0) {
|
|
47073
|
+
filter.blockHash = requirePattern(
|
|
47074
|
+
args.block_hash,
|
|
47075
|
+
TX_HASH,
|
|
47076
|
+
"block_hash",
|
|
47077
|
+
"a block hash (0x + 64 hex digits)"
|
|
47078
|
+
);
|
|
47079
|
+
} else {
|
|
47080
|
+
const from = blockNumber(args.from_block, "from_block");
|
|
47081
|
+
const to = blockNumber(args.to_block, "to_block");
|
|
47082
|
+
const filtered = args.address !== void 0 || Array.isArray(args.topics) && args.topics[0] != null;
|
|
47083
|
+
const span = filtered ? MAX_LOG_BLOCK_SPAN : MAX_UNFILTERED_LOG_BLOCK_SPAN;
|
|
47084
|
+
if (to < from || to - from > BigInt(span)) {
|
|
47085
|
+
throw new SignalsInputError(
|
|
47086
|
+
`from_block..to_block must be ordered and span at most ${span + 1} blocks${filtered ? "" : " without an address or topic0 filter"}; split wider ranges into windows.`
|
|
47087
|
+
);
|
|
47088
|
+
}
|
|
47089
|
+
filter.fromBlock = args.from_block;
|
|
47090
|
+
filter.toBlock = args.to_block;
|
|
47091
|
+
}
|
|
47092
|
+
if (args.address !== void 0) filter.address = args.address;
|
|
47093
|
+
if (args.topics !== void 0) filter.topics = args.topics;
|
|
47094
|
+
const logs = await edgeRpc(chainId, "eth_getLogs", [filter]);
|
|
47095
|
+
const rows = Array.isArray(logs) ? logs : [];
|
|
47096
|
+
return {
|
|
47097
|
+
chain_id: chainId,
|
|
47098
|
+
from_block: filter.fromBlock ?? null,
|
|
47099
|
+
to_block: filter.toBlock ?? null,
|
|
47100
|
+
block_hash: filter.blockHash ?? null,
|
|
47101
|
+
total_logs: rows.length,
|
|
47102
|
+
returned: Math.min(rows.length, limit),
|
|
47103
|
+
truncated: rows.length > limit,
|
|
47104
|
+
logs: rows.slice(0, limit)
|
|
47105
|
+
};
|
|
47106
|
+
}
|
|
47107
|
+
),
|
|
47108
|
+
tool100(
|
|
47109
|
+
"goldsky_trace_transaction",
|
|
47110
|
+
"goldsky.rpc.trace_transaction",
|
|
47111
|
+
["rpc", "evm", "traces", "transactions", "internal"],
|
|
47112
|
+
`Trace an EVM transaction on any Goldsky Edge chain: every internal call, value transfer, contract creation and revert. Archive history included.
|
|
47113
|
+
|
|
47114
|
+
Args:
|
|
47115
|
+
- chain (string or integer, default ethereum): chain name or numeric chain id.
|
|
47116
|
+
- tx_hash (string, required): transaction hash.
|
|
47117
|
+
- format (string, default call_tree): "call_tree" returns a nested call tree (debug_traceTransaction with callTracer: type, from, to, value, gas, input, output, error, calls); "flat" returns Parity-style trace rows (trace_transaction, with traceAddress and subtraces).
|
|
47118
|
+
|
|
47119
|
+
Returns: { "provider": "Goldsky", "chain_id", "tx_hash", "format", "trace" }.
|
|
47120
|
+
|
|
47121
|
+
Example: { "chain": "ethereum", "tx_hash": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060" }
|
|
47122
|
+
|
|
47123
|
+
Use when: you need internal ETH movements or the call path behind a transaction.
|
|
47124
|
+
Don't use when: you need decoded calldata only (use tenderly_decode_calldata).`,
|
|
47125
|
+
{
|
|
47126
|
+
chain: chainProp,
|
|
47127
|
+
tx_hash: {
|
|
47128
|
+
type: "string",
|
|
47129
|
+
pattern: TX_HASH.source,
|
|
47130
|
+
description: "Transaction hash."
|
|
47131
|
+
},
|
|
47132
|
+
format: {
|
|
47133
|
+
type: "string",
|
|
47134
|
+
enum: ["call_tree", "flat"],
|
|
47135
|
+
default: "call_tree",
|
|
47136
|
+
description: "Nested call tree or flat trace rows."
|
|
47137
|
+
}
|
|
47138
|
+
},
|
|
47139
|
+
["tx_hash"],
|
|
47140
|
+
async (args) => {
|
|
47141
|
+
const chainId = resolveChainId(args.chain);
|
|
47142
|
+
const txHash = requirePattern(
|
|
47143
|
+
args.tx_hash,
|
|
47144
|
+
TX_HASH,
|
|
47145
|
+
"tx_hash",
|
|
47146
|
+
"a transaction hash (0x + 64 hex digits)"
|
|
47147
|
+
);
|
|
47148
|
+
const format = args.format === "flat" ? "flat" : "call_tree";
|
|
47149
|
+
const trace = format === "flat" ? await edgeRpc(chainId, "trace_transaction", [txHash]) : await edgeRpc(chainId, "debug_traceTransaction", [
|
|
47150
|
+
txHash,
|
|
47151
|
+
{ tracer: "callTracer" }
|
|
47152
|
+
]);
|
|
47153
|
+
return { chain_id: chainId, tx_hash: txHash, format, trace };
|
|
47154
|
+
}
|
|
47155
|
+
),
|
|
47156
|
+
tool100(
|
|
47157
|
+
"goldsky_trace_filter",
|
|
47158
|
+
"goldsky.rpc.trace_filter",
|
|
47159
|
+
["rpc", "evm", "traces", "internal", "wallets"],
|
|
47160
|
+
`Find internal calls and value transfers to or from given addresses over a block range (trace_filter) on any Goldsky Edge chain that supports Parity traces. Catches internal ETH movements that transfer logs miss, such as who funded a wallet through a contract.
|
|
47161
|
+
|
|
47162
|
+
Args:
|
|
47163
|
+
- chain (string or integer, default ethereum): chain name or numeric chain id.
|
|
47164
|
+
- from_block / to_block (hex strings, required): inclusive range, at most ${MAX_TRACE_FILTER_BLOCK_SPAN.toLocaleString("en-US")} blocks (about 3 hours on Ethereum); walk wider windows one range at a time.
|
|
47165
|
+
- from_address / to_address (arrays of up to 10 addresses): at least one of the two is required.
|
|
47166
|
+
- count (integer, default 50, max 200): traces returned.
|
|
47167
|
+
- after (integer, default 0): traces to skip, for paging.
|
|
47168
|
+
|
|
47169
|
+
Returns: { "provider": "Goldsky", "chain_id", "from_block", "to_block", "returned", "after", "next_after", "traces": [Parity trace rows] }. next_after is null when the page came back short.
|
|
47170
|
+
|
|
47171
|
+
Example: { "chain": "ethereum", "from_block": "0x1900000", "to_block": "0x19003e7", "to_address": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"], "count": 20 }
|
|
47172
|
+
|
|
47173
|
+
Use when: you trace funding or payouts through contracts.
|
|
47174
|
+
Don't use when: one transaction is enough (use goldsky_trace_transaction).`,
|
|
47175
|
+
{
|
|
47176
|
+
chain: chainProp,
|
|
47177
|
+
from_block: {
|
|
47178
|
+
type: "string",
|
|
47179
|
+
pattern: HEX_QUANTITY.source,
|
|
47180
|
+
description: "Inclusive start block (hex)."
|
|
47181
|
+
},
|
|
47182
|
+
to_block: {
|
|
47183
|
+
type: "string",
|
|
47184
|
+
pattern: HEX_QUANTITY.source,
|
|
47185
|
+
description: "Inclusive end block (hex)."
|
|
47186
|
+
},
|
|
47187
|
+
from_address: {
|
|
47188
|
+
type: "array",
|
|
47189
|
+
minItems: 1,
|
|
47190
|
+
maxItems: 10,
|
|
47191
|
+
items: { type: "string", pattern: ADDRESS.source },
|
|
47192
|
+
description: "Senders to match."
|
|
47193
|
+
},
|
|
47194
|
+
to_address: {
|
|
47195
|
+
type: "array",
|
|
47196
|
+
minItems: 1,
|
|
47197
|
+
maxItems: 10,
|
|
47198
|
+
items: { type: "string", pattern: ADDRESS.source },
|
|
47199
|
+
description: "Recipients to match."
|
|
47200
|
+
},
|
|
47201
|
+
count: {
|
|
47202
|
+
type: "integer",
|
|
47203
|
+
minimum: 1,
|
|
47204
|
+
maximum: 200,
|
|
47205
|
+
default: 50,
|
|
47206
|
+
description: "Traces to return."
|
|
47207
|
+
},
|
|
47208
|
+
after: {
|
|
47209
|
+
type: "integer",
|
|
47210
|
+
minimum: 0,
|
|
47211
|
+
default: 0,
|
|
47212
|
+
description: "Traces to skip."
|
|
47213
|
+
}
|
|
47214
|
+
},
|
|
47215
|
+
["from_block", "to_block"],
|
|
47216
|
+
async (args) => {
|
|
47217
|
+
const chainId = resolveChainId(args.chain);
|
|
47218
|
+
const from = blockNumber(args.from_block, "from_block");
|
|
47219
|
+
const to = blockNumber(args.to_block, "to_block");
|
|
47220
|
+
if (to < from || to - from >= BigInt(MAX_TRACE_FILTER_BLOCK_SPAN)) {
|
|
47221
|
+
throw new SignalsInputError(
|
|
47222
|
+
`from_block..to_block must be ordered and span at most ${MAX_TRACE_FILTER_BLOCK_SPAN} blocks.`
|
|
47223
|
+
);
|
|
47224
|
+
}
|
|
47225
|
+
if (args.from_address === void 0 && args.to_address === void 0) {
|
|
47226
|
+
throw new SignalsInputError(
|
|
47227
|
+
"pass from_address or to_address (or both)."
|
|
47228
|
+
);
|
|
47229
|
+
}
|
|
47230
|
+
const count = boundedInt(args.count, 50, 1, 200, "count");
|
|
47231
|
+
const after = boundedInt(args.after, 0, 0, 1e6, "after");
|
|
47232
|
+
const filter = {
|
|
47233
|
+
fromBlock: args.from_block,
|
|
47234
|
+
toBlock: args.to_block,
|
|
47235
|
+
count,
|
|
47236
|
+
after
|
|
47237
|
+
};
|
|
47238
|
+
if (args.from_address !== void 0)
|
|
47239
|
+
filter.fromAddress = args.from_address;
|
|
47240
|
+
if (args.to_address !== void 0) filter.toAddress = args.to_address;
|
|
47241
|
+
const traces = await edgeRpc(chainId, "trace_filter", [filter]);
|
|
47242
|
+
const rows = (Array.isArray(traces) ? traces : []).slice(0, count);
|
|
47243
|
+
return {
|
|
47244
|
+
chain_id: chainId,
|
|
47245
|
+
from_block: args.from_block,
|
|
47246
|
+
to_block: args.to_block,
|
|
47247
|
+
returned: rows.length,
|
|
47248
|
+
after,
|
|
47249
|
+
next_after: rows.length === count ? after + count : null,
|
|
47250
|
+
traces: rows
|
|
47251
|
+
};
|
|
47252
|
+
}
|
|
47253
|
+
),
|
|
47254
|
+
tool100(
|
|
47255
|
+
"goldsky_get_wallet_balances",
|
|
47256
|
+
"goldsky.feeds.wallets.balances",
|
|
47257
|
+
["feeds", "wallets", "balances", "portfolio"],
|
|
47258
|
+
`Get the latest token balances of one EVM wallet with USD prices and a wallet total, from Goldsky Feeds, across ethereum, base, arbitrum, optimism, polygon, bsc, robinhood and arc in one call. Native balances are read live; ERC-20 balances come from Goldsky's index; prices older than 4 hours are not served.
|
|
47259
|
+
|
|
47260
|
+
Args:
|
|
47261
|
+
- address (string, required): EVM wallet address.
|
|
47262
|
+
- chains (string or array, optional): subset of the chains above; default all.
|
|
47263
|
+
- min_value_usd (number, optional): drop rows worth less (and unpriced rows).
|
|
47264
|
+
- include_unknown_price (boolean, default false): also return tokens Goldsky cannot price.
|
|
47265
|
+
- page_size (integer, default 100, max 1000) and page_token (string): paging.
|
|
47266
|
+
|
|
47267
|
+
Returns: { "provider": "Goldsky", "address", "total_value_usd", "data": [{ "chain", "token_address", "token_symbol", "token_name", "token_decimals", "balance_raw", "price_usd", "last_priced_at", "value_usd", "block_number", "block_timestamp" }], "pagination": { "next_page_token" } }.
|
|
47268
|
+
|
|
47269
|
+
Example: { "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "chains": ["ethereum", "base"], "min_value_usd": 10 }
|
|
47270
|
+
|
|
47271
|
+
Use when: you want one priced multi-chain balance sheet for an EVM wallet.
|
|
47272
|
+
Don't use when: the wallet is on Solana (use helius_get_wallet_balances) or you need PnL (use get_wallet_pnl).`,
|
|
47273
|
+
{
|
|
47274
|
+
address: {
|
|
47275
|
+
type: "string",
|
|
47276
|
+
pattern: ADDRESS.source,
|
|
47277
|
+
description: "EVM wallet address."
|
|
47278
|
+
},
|
|
47279
|
+
chains: {
|
|
47280
|
+
oneOf: [
|
|
47281
|
+
{ type: "string" },
|
|
47282
|
+
{
|
|
47283
|
+
type: "array",
|
|
47284
|
+
minItems: 1,
|
|
47285
|
+
maxItems: 8,
|
|
47286
|
+
items: { type: "string" }
|
|
47287
|
+
}
|
|
47288
|
+
],
|
|
47289
|
+
description: "Chains to include; default all eight."
|
|
47290
|
+
},
|
|
47291
|
+
min_value_usd: {
|
|
47292
|
+
type: "number",
|
|
47293
|
+
minimum: 0,
|
|
47294
|
+
description: "Dust filter in USD."
|
|
47295
|
+
},
|
|
47296
|
+
include_unknown_price: {
|
|
47297
|
+
type: "boolean",
|
|
47298
|
+
default: false,
|
|
47299
|
+
description: "Include unpriced tokens."
|
|
47300
|
+
},
|
|
47301
|
+
page_size: {
|
|
47302
|
+
type: "integer",
|
|
47303
|
+
minimum: 1,
|
|
47304
|
+
maximum: 1e3,
|
|
47305
|
+
default: 100,
|
|
47306
|
+
description: "Rows per page."
|
|
47307
|
+
},
|
|
47308
|
+
page_token: {
|
|
47309
|
+
type: "string",
|
|
47310
|
+
maxLength: 2048,
|
|
47311
|
+
description: "next_page_token from a previous page."
|
|
47312
|
+
}
|
|
47313
|
+
},
|
|
47314
|
+
["address"],
|
|
47315
|
+
async (args) => feedsGet("balances", {
|
|
47316
|
+
address: walletAddress(args),
|
|
47317
|
+
chains: feedsChains(args.chains),
|
|
47318
|
+
min_value_usd: args.min_value_usd,
|
|
47319
|
+
include_unknown_price: args.include_unknown_price,
|
|
47320
|
+
page_size: boundedInt(args.page_size, 100, 1, 1e3, "page_size"),
|
|
47321
|
+
page_token: args.page_token
|
|
47322
|
+
})
|
|
47323
|
+
),
|
|
47324
|
+
tool100(
|
|
47325
|
+
"goldsky_get_wallet_transfers",
|
|
47326
|
+
"goldsky.feeds.wallets.transfers",
|
|
47327
|
+
["feeds", "wallets", "transfers", "history"],
|
|
47328
|
+
`Get one EVM wallet's native and ERC-20 transfers, newest first, with the USD value at transfer time, from Goldsky Feeds across ethereum, base, arbitrum, optimism, polygon, bsc, robinhood and arc. NFT transfers are not covered.
|
|
47329
|
+
|
|
47330
|
+
Args:
|
|
47331
|
+
- address (string, required): EVM wallet address.
|
|
47332
|
+
- chains (string or array, optional): subset of the chains above; default all.
|
|
47333
|
+
- direction (string, optional): "in" or "out" relative to address.
|
|
47334
|
+
- transfer_type (string, optional): "native" or "erc20".
|
|
47335
|
+
- token_address (string, optional): comma-separated token contracts; the zero address selects natives.
|
|
47336
|
+
- from / to (RFC 3339 strings, optional): inclusive block-time window.
|
|
47337
|
+
- page_size (integer, default 100, max 1000) and page_token (string): paging.
|
|
47338
|
+
|
|
47339
|
+
Returns: { "provider": "Goldsky", "data": [{ "chain", "block_number", "block_timestamp", "transaction_hash", "from_address", "to_address", "token_address", "token_symbol", "amount_raw", "price_usd", "amount_usd" }], "pagination": { "next_page_token" } }.
|
|
47340
|
+
|
|
47341
|
+
Example: { "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "chains": "base", "direction": "in", "page_size": 20 }
|
|
47342
|
+
|
|
47343
|
+
Use when: you need priced transfer history for an EVM wallet across several chains at once.
|
|
47344
|
+
Don't use when: you need NFT or internal transfers (use alchemy_get_asset_transfers or goldsky_trace_filter).`,
|
|
47345
|
+
{
|
|
47346
|
+
address: {
|
|
47347
|
+
type: "string",
|
|
47348
|
+
pattern: ADDRESS.source,
|
|
47349
|
+
description: "EVM wallet address."
|
|
47350
|
+
},
|
|
47351
|
+
chains: {
|
|
47352
|
+
oneOf: [
|
|
47353
|
+
{ type: "string" },
|
|
47354
|
+
{
|
|
47355
|
+
type: "array",
|
|
47356
|
+
minItems: 1,
|
|
47357
|
+
maxItems: 8,
|
|
47358
|
+
items: { type: "string" }
|
|
47359
|
+
}
|
|
47360
|
+
],
|
|
47361
|
+
description: "Chains to include; default all eight."
|
|
47362
|
+
},
|
|
47363
|
+
direction: {
|
|
47364
|
+
type: "string",
|
|
47365
|
+
enum: ["in", "out"],
|
|
47366
|
+
description: "Relative to address."
|
|
47367
|
+
},
|
|
47368
|
+
transfer_type: {
|
|
47369
|
+
type: "string",
|
|
47370
|
+
enum: ["native", "erc20"],
|
|
47371
|
+
description: "Transfer kind."
|
|
47372
|
+
},
|
|
47373
|
+
token_address: {
|
|
47374
|
+
type: "string",
|
|
47375
|
+
maxLength: 4300,
|
|
47376
|
+
description: "Comma-separated token contracts."
|
|
47377
|
+
},
|
|
47378
|
+
from: {
|
|
47379
|
+
type: "string",
|
|
47380
|
+
format: "date-time",
|
|
47381
|
+
description: "Inclusive lower bound on block time (RFC 3339)."
|
|
47382
|
+
},
|
|
47383
|
+
to: {
|
|
47384
|
+
type: "string",
|
|
47385
|
+
format: "date-time",
|
|
47386
|
+
description: "Inclusive upper bound on block time (RFC 3339)."
|
|
47387
|
+
},
|
|
47388
|
+
page_size: {
|
|
47389
|
+
type: "integer",
|
|
47390
|
+
minimum: 1,
|
|
47391
|
+
maximum: 1e3,
|
|
47392
|
+
default: 100,
|
|
47393
|
+
description: "Rows per page."
|
|
47394
|
+
},
|
|
47395
|
+
page_token: {
|
|
47396
|
+
type: "string",
|
|
47397
|
+
maxLength: 2048,
|
|
47398
|
+
description: "next_page_token from a previous page."
|
|
47399
|
+
}
|
|
47400
|
+
},
|
|
47401
|
+
["address"],
|
|
47402
|
+
async (args) => feedsGet("transfers", {
|
|
47403
|
+
address: walletAddress(args),
|
|
47404
|
+
chains: feedsChains(args.chains),
|
|
47405
|
+
direction: args.direction,
|
|
47406
|
+
transfer_type: args.transfer_type,
|
|
47407
|
+
token_address: args.token_address,
|
|
47408
|
+
from: args.from,
|
|
47409
|
+
to: args.to,
|
|
47410
|
+
page_size: boundedInt(args.page_size, 100, 1, 1e3, "page_size"),
|
|
47411
|
+
page_token: args.page_token
|
|
47412
|
+
})
|
|
47413
|
+
)
|
|
47414
|
+
];
|
|
47415
|
+
}
|
|
47416
|
+
|
|
45440
47417
|
// src/freeWriteEndpoints.ts
|
|
45441
47418
|
var FREE_WRITE_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
45442
47419
|
// Feedback is a bounded, rate-limited insert into Hive's own table. It
|
|
@@ -45572,7 +47549,7 @@ function calculateBackoffDelay2(attempt, baseDelay) {
|
|
|
45572
47549
|
const jitter = Math.random() * 500;
|
|
45573
47550
|
return exponentialDelay + jitter;
|
|
45574
47551
|
}
|
|
45575
|
-
function
|
|
47552
|
+
function sleep3(ms) {
|
|
45576
47553
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
45577
47554
|
}
|
|
45578
47555
|
async function fetchWithTimeout(url, options, timeout) {
|
|
@@ -45638,7 +47615,7 @@ async function resilientFetch(url, fetchOptions = {}, resilientOptions = {}) {
|
|
|
45638
47615
|
delayMs: Math.round(delay2),
|
|
45639
47616
|
error: lastError.message
|
|
45640
47617
|
});
|
|
45641
|
-
await
|
|
47618
|
+
await sleep3(delay2);
|
|
45642
47619
|
continue;
|
|
45643
47620
|
}
|
|
45644
47621
|
}
|
|
@@ -45834,7 +47811,7 @@ async function assertHostResolvesPublic(host) {
|
|
|
45834
47811
|
}
|
|
45835
47812
|
}
|
|
45836
47813
|
}
|
|
45837
|
-
function
|
|
47814
|
+
function buildUrl3(entry2, path, query) {
|
|
45838
47815
|
if (/^[a-z]+:\/\//i.test(path) || path.includes("@")) {
|
|
45839
47816
|
throw new FetchInputError(
|
|
45840
47817
|
`Invalid argument: path "${path}" must be a path on the host, not a full URL.`
|
|
@@ -45867,7 +47844,7 @@ async function fetchPublicApi(params) {
|
|
|
45867
47844
|
`Method "${method}" is not supported for host "${entry2.host}" (allowed: ${entry2.methods.join(", ")}).`
|
|
45868
47845
|
);
|
|
45869
47846
|
}
|
|
45870
|
-
const url =
|
|
47847
|
+
const url = buildUrl3(entry2, params.path, params.query);
|
|
45871
47848
|
await assertHostResolvesPublic(entry2.host);
|
|
45872
47849
|
const headers = { Accept: "application/json" };
|
|
45873
47850
|
let payload;
|
|
@@ -45925,7 +47902,7 @@ async function fetchPublicApi(params) {
|
|
|
45925
47902
|
}
|
|
45926
47903
|
|
|
45927
47904
|
// src/tools/fetch/tools.ts
|
|
45928
|
-
function
|
|
47905
|
+
function asRecord3(value) {
|
|
45929
47906
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
45930
47907
|
}
|
|
45931
47908
|
function getAllFetchEndpoints() {
|
|
@@ -45996,7 +47973,7 @@ Don't use when:
|
|
|
45996
47973
|
host: String(a.host ?? ""),
|
|
45997
47974
|
path: String(a.path ?? ""),
|
|
45998
47975
|
method: a.method === "POST" ? "POST" : "GET",
|
|
45999
|
-
query:
|
|
47976
|
+
query: asRecord3(a.query),
|
|
46000
47977
|
body: a.body
|
|
46001
47978
|
});
|
|
46002
47979
|
return asTextContentResult(data);
|
|
@@ -47267,7 +49244,7 @@ var PROVIDER_CATALOG = [
|
|
|
47267
49244
|
id: "rwa",
|
|
47268
49245
|
name: "RWA Perps",
|
|
47269
49246
|
prefix: "rwa_",
|
|
47270
|
-
type: "Tokenized real-world-asset perps: cross-venue funding, markets, and open interest across Hyperliquid HIP-3 builder dexes,
|
|
49247
|
+
type: "Tokenized real-world-asset perps: cross-venue funding, markets, and open interest across Hyperliquid HIP-3 builder dexes, Lighter, and Extended (keyless official APIs)",
|
|
47271
49248
|
clientStrategy: "internal",
|
|
47272
49249
|
alwaysAvailable: true,
|
|
47273
49250
|
matches: (toolName, metadata103) => matchesProvider("rwa", toolName, metadata103)
|
|
@@ -47291,11 +49268,8 @@ var PROVIDER_CATALOG = [
|
|
|
47291
49268
|
type: "Cross-venue crypto derivatives history from Hive's own archive store: funding-rate settlements across 33 CEX+DEX venues since 2023-11, basis since 2025-03, open interest and liquidations since 2025-04, and long/short ratio since 2026-03",
|
|
47292
49269
|
clientStrategy: "internal",
|
|
47293
49270
|
credentialRequirement: {
|
|
47294
|
-
label: "
|
|
47295
|
-
isConfigured: () => hasAllEnvVars(
|
|
47296
|
-
"SHARPE_ARCHIVE_SUPABASE_URL",
|
|
47297
|
-
"SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY"
|
|
47298
|
-
)
|
|
49271
|
+
label: "CLICKHOUSE_URL and CLICKHOUSE_READ_PASSWORD",
|
|
49272
|
+
isConfigured: () => hasAllEnvVars("CLICKHOUSE_URL", "CLICKHOUSE_READ_PASSWORD")
|
|
47299
49273
|
},
|
|
47300
49274
|
matches: (toolName, metadata103) => matchesProvider("archive", toolName, metadata103)
|
|
47301
49275
|
},
|
|
@@ -47308,6 +49282,15 @@ var PROVIDER_CATALOG = [
|
|
|
47308
49282
|
alwaysAvailable: true,
|
|
47309
49283
|
matches: (toolName, metadata103) => matchesProvider("polymarket", toolName, metadata103)
|
|
47310
49284
|
},
|
|
49285
|
+
{
|
|
49286
|
+
id: "kalshi",
|
|
49287
|
+
name: "Kalshi",
|
|
49288
|
+
prefix: "kalshi_",
|
|
49289
|
+
type: "Kalshi prediction markets: market and event search, market-implied odds, order books, recent trades and candlestick price history (keyless public Trade API v2)",
|
|
49290
|
+
clientStrategy: "internal",
|
|
49291
|
+
alwaysAvailable: true,
|
|
49292
|
+
matches: (toolName, metadata103) => matchesProvider("kalshi", toolName, metadata103)
|
|
49293
|
+
},
|
|
47311
49294
|
{
|
|
47312
49295
|
id: "alternative",
|
|
47313
49296
|
name: "Alternative.me",
|
|
@@ -47379,6 +49362,18 @@ var PROVIDER_CATALOG = [
|
|
|
47379
49362
|
isConfigured: () => hasEnvVar("ALCHEMY_API_KEY")
|
|
47380
49363
|
},
|
|
47381
49364
|
matches: (toolName, metadata103) => matchesProvider("alchemy", toolName, metadata103)
|
|
49365
|
+
},
|
|
49366
|
+
{
|
|
49367
|
+
id: "goldsky",
|
|
49368
|
+
name: "Goldsky",
|
|
49369
|
+
prefix: "goldsky_",
|
|
49370
|
+
type: "EVM JSON-RPC on ~150 chains (eth_call, logs, traces, archive, HyperEVM) and priced wallet balances and transfers (Feeds)",
|
|
49371
|
+
clientStrategy: "internal",
|
|
49372
|
+
credentialRequirement: {
|
|
49373
|
+
label: "GOLDSKY_EDGE_API_KEY and GOLDSKY_DATA_API_KEY",
|
|
49374
|
+
isConfigured: () => hasAllEnvVars("GOLDSKY_EDGE_API_KEY", "GOLDSKY_DATA_API_KEY")
|
|
49375
|
+
},
|
|
49376
|
+
matches: (toolName, metadata103) => matchesProvider("goldsky", toolName, metadata103)
|
|
47382
49377
|
}
|
|
47383
49378
|
];
|
|
47384
49379
|
function getProviderDescriptorForTool(toolName, metadata103) {
|
|
@@ -47397,6 +49392,13 @@ function getProviderRuntimeStatus(provider) {
|
|
|
47397
49392
|
if (rest || node) return "degraded";
|
|
47398
49393
|
return "missing_key";
|
|
47399
49394
|
}
|
|
49395
|
+
if (provider.id === "goldsky") {
|
|
49396
|
+
const edge = hasEnvVar("GOLDSKY_EDGE_API_KEY");
|
|
49397
|
+
const data = hasEnvVar("GOLDSKY_DATA_API_KEY");
|
|
49398
|
+
if (edge && data) return "ok";
|
|
49399
|
+
if (edge || data) return "degraded";
|
|
49400
|
+
return "missing_key";
|
|
49401
|
+
}
|
|
47400
49402
|
return runtimeStatusForConfiguredKey(
|
|
47401
49403
|
provider.credentialRequirement?.isConfigured() ?? true
|
|
47402
49404
|
);
|
|
@@ -47424,6 +49426,10 @@ function getCredentialRequirementForTool(toolName, metadata103) {
|
|
|
47424
49426
|
isConfigured: hasTenderlyNodeCredentials
|
|
47425
49427
|
};
|
|
47426
49428
|
}
|
|
49429
|
+
if (toolName.startsWith("goldsky_")) {
|
|
49430
|
+
const key = toolName.startsWith("goldsky_get_wallet_") ? "GOLDSKY_DATA_API_KEY" : "GOLDSKY_EDGE_API_KEY";
|
|
49431
|
+
return { label: key, isConfigured: () => hasEnvVar(key) };
|
|
49432
|
+
}
|
|
47427
49433
|
return getProviderDescriptorForTool(toolName, metadata103).credentialRequirement ?? null;
|
|
47428
49434
|
}
|
|
47429
49435
|
|
|
@@ -47455,14 +49461,14 @@ var RESPONSE_FORMAT_SCHEMA = {
|
|
|
47455
49461
|
enum: ["json", "markdown"],
|
|
47456
49462
|
description: "Output format: 'json' for structured data (default) or 'markdown' for human-readable text."
|
|
47457
49463
|
};
|
|
47458
|
-
function normalizeToolSchema(
|
|
49464
|
+
function normalizeToolSchema(tool101) {
|
|
47459
49465
|
const normalized = {
|
|
47460
|
-
...
|
|
47461
|
-
inputSchema:
|
|
47462
|
-
title:
|
|
49466
|
+
...tool101,
|
|
49467
|
+
inputSchema: tool101.inputSchema ? JSON.parse(JSON.stringify(tool101.inputSchema)) : tool101.inputSchema,
|
|
49468
|
+
title: tool101.title ?? getToolTitle(tool101.name),
|
|
47463
49469
|
annotations: {
|
|
47464
|
-
...getAnnotationsForTool(
|
|
47465
|
-
...
|
|
49470
|
+
...getAnnotationsForTool(tool101.name),
|
|
49471
|
+
...tool101.annotations ?? {}
|
|
47466
49472
|
}
|
|
47467
49473
|
};
|
|
47468
49474
|
if (normalized.inputSchema && typeof normalized.inputSchema === "object" && normalized.inputSchema.type === "object") {
|
|
@@ -47551,7 +49557,7 @@ var RETIRED_TOOL_REPLACEMENTS = {
|
|
|
47551
49557
|
get_community_notes: "No direct replacement; use get_categories_market_data or get_categories for category data.",
|
|
47552
49558
|
get_event_labels: "No direct replacement; use get_categories_market_data or get_categories for category data."
|
|
47553
49559
|
};
|
|
47554
|
-
var PREDICTION_REPLACEMENT = 'Prediction markets are served by first-party Polymarket tools since 1.7.0: polymarket_search_markets, polymarket_get_events, polymarket_get_market, polymarket_get_price_history, polymarket_get_orderbook, polymarket_get_wallet_positions, polymarket_get_wallet_activity. Kalshi
|
|
49560
|
+
var PREDICTION_REPLACEMENT = 'Prediction markets are served by first-party Polymarket tools since 1.7.0: polymarket_search_markets, polymarket_get_events, polymarket_get_market, polymarket_get_price_history, polymarket_get_orderbook, polymarket_get_wallet_positions, polymarket_get_wallet_activity. Kalshi markets are served by the kalshi_ tools (kalshi_search_markets, kalshi_get_market). Start with search_tools({"query": "prediction markets"}) or hive://toolsets/prediction_markets.';
|
|
47555
49561
|
var UNSERVABLE_TOOLS_DATE = "2026-09-24";
|
|
47556
49562
|
var COINGECKO_ENTERPRISE = "because it needs a CoinGecko Enterprise plan, which Hive does not have";
|
|
47557
49563
|
var COINGECKO_SUPPLY_REPLACEMENT = "Use get_coin_supply_breakdown for current circulating, total, and max supply, or get_coin_market_chart_range for a market-cap and price series.";
|
|
@@ -47583,6 +49589,7 @@ var MORALIS_WALLET_CHAINS = "Use get_wallet_balances across networks, or alchemy
|
|
|
47583
49589
|
var CCXT_PRIVATE = "because every exchange that serves it requires an authenticated exchange account";
|
|
47584
49590
|
var CCXT_BORROW_REPLACEMENT = "Use ccxt_fetch_borrow_rate_history or ccxt_fetch_borrow_rate_histories (public) and read the latest point.";
|
|
47585
49591
|
var CCXT_WRONG_DATA_DATE = "2026-10-22";
|
|
49592
|
+
var RWA_REMAINING_VENUES = "Use rwa_get_funding_rates or rwa_get_markets for the remaining RWA venues (Hyperliquid HIP-3 builder dexes, Lighter, Extended).";
|
|
47586
49593
|
var UNSERVABLE_TOOLS = {
|
|
47587
49594
|
get_coin_circulating_supply_chart: {
|
|
47588
49595
|
reason: COINGECKO_ENTERPRISE,
|
|
@@ -47960,7 +49967,18 @@ var UNSERVABLE_TOOLS = {
|
|
|
47960
49967
|
"Use alchemy_get_block_number for the latest block on an EVM network."
|
|
47961
49968
|
),
|
|
47962
49969
|
moralis_resolve_ens_domain: moralisRetired(MORALIS_ENS_NONE),
|
|
47963
|
-
moralis_resolve_address: moralisRetired(MORALIS_ENS_NONE)
|
|
49970
|
+
moralis_resolve_address: moralisRetired(MORALIS_ENS_NONE),
|
|
49971
|
+
// Removed venues (founder, 2026-10-26): the venue clients are deleted.
|
|
49972
|
+
rwa_get_ostium_markets: {
|
|
49973
|
+
reason: "because Hive no longer serves the Ostium venue",
|
|
49974
|
+
replacement: RWA_REMAINING_VENUES,
|
|
49975
|
+
date: "2026-10-26"
|
|
49976
|
+
},
|
|
49977
|
+
rwa_get_avantis_markets: {
|
|
49978
|
+
reason: "because Hive no longer serves the Avantis venue",
|
|
49979
|
+
replacement: RWA_REMAINING_VENUES,
|
|
49980
|
+
date: "2026-10-26"
|
|
49981
|
+
}
|
|
47964
49982
|
};
|
|
47965
49983
|
function isUnservableTool(toolName) {
|
|
47966
49984
|
return Object.prototype.hasOwnProperty.call(UNSERVABLE_TOOLS, toolName);
|
|
@@ -48315,9 +50333,9 @@ function withExecMarker(result2, marker) {
|
|
|
48315
50333
|
function humanizePropertyName(propName) {
|
|
48316
50334
|
return propName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim().toLowerCase();
|
|
48317
50335
|
}
|
|
48318
|
-
function fillMissingInputDescriptions(
|
|
48319
|
-
const schema =
|
|
48320
|
-
if (!schema?.properties) return
|
|
50336
|
+
function fillMissingInputDescriptions(tool101) {
|
|
50337
|
+
const schema = tool101.inputSchema;
|
|
50338
|
+
if (!schema?.properties) return tool101;
|
|
48321
50339
|
let changed = false;
|
|
48322
50340
|
const properties = {};
|
|
48323
50341
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
@@ -48328,14 +50346,14 @@ function fillMissingInputDescriptions(tool100) {
|
|
|
48328
50346
|
changed = true;
|
|
48329
50347
|
properties[propName] = {
|
|
48330
50348
|
...propSchema,
|
|
48331
|
-
description: `${humanizePropertyName(propName)} parameter for ${
|
|
50349
|
+
description: `${humanizePropertyName(propName)} parameter for ${tool101.name}. See the tool Args section for accepted values and examples.`
|
|
48332
50350
|
};
|
|
48333
50351
|
}
|
|
48334
|
-
if (!changed) return
|
|
50352
|
+
if (!changed) return tool101;
|
|
48335
50353
|
return {
|
|
48336
|
-
...
|
|
50354
|
+
...tool101,
|
|
48337
50355
|
inputSchema: {
|
|
48338
|
-
...
|
|
50356
|
+
...tool101.inputSchema,
|
|
48339
50357
|
properties
|
|
48340
50358
|
}
|
|
48341
50359
|
};
|
|
@@ -48633,16 +50651,18 @@ function getAllWrappedEndpoints() {
|
|
|
48633
50651
|
...getAllRwaEndpoints(),
|
|
48634
50652
|
...getAllArchiveEndpoints(),
|
|
48635
50653
|
...getAllPolymarketEndpoints(),
|
|
50654
|
+
...getAllKalshiEndpoints(),
|
|
48636
50655
|
...getAllSignalsEndpoints(),
|
|
48637
50656
|
...getAllBlockscoutEndpoints(),
|
|
50657
|
+
...getAllGoldskyEndpoints(),
|
|
48638
50658
|
...getAllFetchEndpoints()
|
|
48639
50659
|
];
|
|
48640
50660
|
const allEndpointsJSON = wrapper;
|
|
48641
50661
|
const updatedTools = [];
|
|
48642
50662
|
for (const endpoint of allTool) {
|
|
48643
|
-
let { tool:
|
|
50663
|
+
let { tool: tool101 } = endpoint;
|
|
48644
50664
|
let matchedItem = null;
|
|
48645
|
-
const originalName =
|
|
50665
|
+
const originalName = tool101.name;
|
|
48646
50666
|
const strippedName = stripProviderPrefix(originalName);
|
|
48647
50667
|
for (const endpoints of Object.values(allEndpointsJSON)) {
|
|
48648
50668
|
for (const item of endpoints) {
|
|
@@ -48668,10 +50688,10 @@ function getAllWrappedEndpoints() {
|
|
|
48668
50688
|
}
|
|
48669
50689
|
if (matchedItem) {
|
|
48670
50690
|
let toolInstanse = {
|
|
48671
|
-
...
|
|
50691
|
+
...tool101,
|
|
48672
50692
|
name: matchedItem.tool.name,
|
|
48673
|
-
title:
|
|
48674
|
-
description: matchedItem.tool.description ??
|
|
50693
|
+
title: tool101.title ?? getToolTitle(matchedItem.tool.name),
|
|
50694
|
+
description: matchedItem.tool.description ?? tool101.description
|
|
48675
50695
|
};
|
|
48676
50696
|
if (!toolInstanse.annotations) {
|
|
48677
50697
|
toolInstanse.annotations = getAnnotationsForTool(toolInstanse.name);
|
|
@@ -48710,13 +50730,15 @@ function getAllWrappedEndpoints() {
|
|
|
48710
50730
|
"rwa_",
|
|
48711
50731
|
"archive_",
|
|
48712
50732
|
"polymarket_",
|
|
50733
|
+
"kalshi_",
|
|
48713
50734
|
"signals_",
|
|
48714
|
-
"fetch_"
|
|
50735
|
+
"fetch_",
|
|
50736
|
+
"goldsky_"
|
|
48715
50737
|
];
|
|
48716
|
-
if (selfDescribedPrefixes.some((p) =>
|
|
50738
|
+
if (selfDescribedPrefixes.some((p) => tool101.name.startsWith(p)) || isFreeWriteEndpoint(tool101.name)) {
|
|
48717
50739
|
let toolInstanse = {
|
|
48718
|
-
...
|
|
48719
|
-
title:
|
|
50740
|
+
...tool101,
|
|
50741
|
+
title: tool101.title ?? getToolTitle(tool101.name)
|
|
48720
50742
|
};
|
|
48721
50743
|
if (!toolInstanse.annotations) {
|
|
48722
50744
|
toolInstanse.annotations = getAnnotationsForTool(toolInstanse.name);
|
|
@@ -48742,7 +50764,7 @@ function getAllWrappedEndpoints() {
|
|
|
48742
50764
|
});
|
|
48743
50765
|
} else {
|
|
48744
50766
|
secureLogger.debug(
|
|
48745
|
-
`No match found in allEndpoints for tool: ${
|
|
50767
|
+
`No match found in allEndpoints for tool: ${tool101.name}`
|
|
48746
50768
|
);
|
|
48747
50769
|
}
|
|
48748
50770
|
}
|
|
@@ -48758,9 +50780,9 @@ function getToolByCategory(category) {
|
|
|
48758
50780
|
const allWrappedTools = getAllWrappedEndpoints();
|
|
48759
50781
|
const tools4 = [];
|
|
48760
50782
|
for (const toolName of toolNameList) {
|
|
48761
|
-
const
|
|
48762
|
-
if (
|
|
48763
|
-
tools4.push(
|
|
50783
|
+
const tool101 = allWrappedTools.find((tool102) => tool102.tool.name === toolName);
|
|
50784
|
+
if (tool101) {
|
|
50785
|
+
tools4.push(tool101);
|
|
48764
50786
|
}
|
|
48765
50787
|
}
|
|
48766
50788
|
return tools4;
|
|
@@ -48834,8 +50856,6 @@ var ToolRegistry = [
|
|
|
48834
50856
|
"rwa_get_funding_rates",
|
|
48835
50857
|
"rwa_get_markets",
|
|
48836
50858
|
"rwa_get_hyperliquid_dex_perps",
|
|
48837
|
-
"rwa_get_ostium_markets",
|
|
48838
|
-
"rwa_get_avantis_markets",
|
|
48839
50859
|
"rwa_get_lighter_markets",
|
|
48840
50860
|
"rwa_get_extended_markets",
|
|
48841
50861
|
// Hive Archive cross-venue derivatives history (since 2023-11).
|
|
@@ -48966,7 +50986,7 @@ var ToolRegistry = [
|
|
|
48966
50986
|
{
|
|
48967
50987
|
category: "Portfolio & Wallet",
|
|
48968
50988
|
name: "get_portfolio_wallet_endpoints",
|
|
48969
|
-
description: "Endpoints for tracking user wallet balances, transaction history, portfolio positions across protocols and chains, net asset calculations, token holdings analysis, comprehensive wallet activity monitoring, real-time balance tracking, balance update history over time, multi-token portfolio analysis, balance changes with transaction context, financial auditing capabilities for individual users and addresses, NFT holdings tracking, wallet PnL, and swap history. Provider guide: Use get_wallet_balances, get_wallet_pnl, get_wallet_trades, and get_wallet_transfers (CoinGecko) for EVM balances, PnL, DEX trades, and transfers. Use alchemy_ tools for NFT holdings, raw transfers, and EVM chains CoinGecko does not cover. Use helius_ tools for Solana-native wallet depth (native SOL balance, SPL token accounts, Digital Asset Standard assets owned by a wallet).",
|
|
50989
|
+
description: "Endpoints for tracking user wallet balances, transaction history, portfolio positions across protocols and chains, net asset calculations, token holdings analysis, comprehensive wallet activity monitoring, real-time balance tracking, balance update history over time, multi-token portfolio analysis, balance changes with transaction context, financial auditing capabilities for individual users and addresses, NFT holdings tracking, wallet PnL, and swap history. Provider guide: Use get_wallet_balances, get_wallet_pnl, get_wallet_trades, and get_wallet_transfers (CoinGecko) for EVM balances, PnL, DEX trades, and transfers. Use alchemy_ tools for NFT holdings, raw transfers, and EVM chains CoinGecko does not cover. Use helius_ tools for Solana-native wallet depth (native SOL balance, SPL token accounts, Digital Asset Standard assets owned by a wallet). Use goldsky_get_wallet_balances and goldsky_get_wallet_transfers (Goldsky Feeds) for priced balances and priced transfer history across eight EVM chains in one call.",
|
|
48970
50990
|
tools: [
|
|
48971
50991
|
// CoinGecko wallet data (default for EVM balances, PnL, DEX trades, transfers)
|
|
48972
50992
|
"get_wallet_balances",
|
|
@@ -49046,7 +51066,10 @@ var ToolRegistry = [
|
|
|
49046
51066
|
"helius_get_wallet_history",
|
|
49047
51067
|
"helius_get_wallet_transfers",
|
|
49048
51068
|
"helius_get_transactions_for_address",
|
|
49049
|
-
"helius_get_transfers_by_address"
|
|
51069
|
+
"helius_get_transfers_by_address",
|
|
51070
|
+
// Goldsky Feeds: priced multi-chain EVM balances and transfers
|
|
51071
|
+
"goldsky_get_wallet_balances",
|
|
51072
|
+
"goldsky_get_wallet_transfers"
|
|
49050
51073
|
]
|
|
49051
51074
|
},
|
|
49052
51075
|
{
|
|
@@ -49219,7 +51242,7 @@ var ToolRegistry = [
|
|
|
49219
51242
|
{
|
|
49220
51243
|
category: "Network & Infrastructure",
|
|
49221
51244
|
name: "get_network_infrastructure_endpoints",
|
|
49222
|
-
description: "Endpoints for blockchain network information, network health monitoring, gas price tracking, network statistics, asset platform data, infrastructure metrics across different blockchain networks, real-time mempool monitoring, pending transaction analysis, transaction status simulation, MEV detection and protection, gas fee optimization, arbitrage opportunity identification, comprehensive blockchain activity monitoring, community-contributed insights and annotations, data platform system updates monitoring, centralized exchange (CEX) infrastructure including system status monitoring, server time synchronization, available trading markets/pairs browsing, and supported currencies listing across major exchanges. Provider guide: Use coingecko tools for network lists and stats. Use alchemy_ tools for EVM node reads, logs, blocks, and receipts. Use tenderly_ tools for supported EVM simulation/network reads. Use helius_ tools for Solana chain infrastructure (current slot, epoch info, priority-fee estimation, raw transaction fetches).",
|
|
51245
|
+
description: "Endpoints for blockchain network information, network health monitoring, gas price tracking, network statistics, asset platform data, infrastructure metrics across different blockchain networks, real-time mempool monitoring, pending transaction analysis, transaction status simulation, MEV detection and protection, gas fee optimization, arbitrage opportunity identification, comprehensive blockchain activity monitoring, community-contributed insights and annotations, data platform system updates monitoring, centralized exchange (CEX) infrastructure including system status monitoring, server time synchronization, available trading markets/pairs browsing, and supported currencies listing across major exchanges. Provider guide: Use coingecko tools for network lists and stats. Use alchemy_ tools for EVM node reads, logs, blocks, and receipts. Use goldsky_ tools for contract reads (eth_call), traces, wide log ranges, and node reads on chains Alchemy does not cover (about 150 mainnets, HyperEVM included). Use tenderly_ tools for supported EVM simulation/network reads. Use helius_ tools for Solana chain infrastructure (current slot, epoch info, priority-fee estimation, raw transaction fetches).",
|
|
49223
51246
|
tools: [
|
|
49224
51247
|
"get_networks",
|
|
49225
51248
|
"get_asset_platforms",
|
|
@@ -49245,7 +51268,13 @@ var ToolRegistry = [
|
|
|
49245
51268
|
"helius_get_priority_fee_estimate",
|
|
49246
51269
|
"helius_get_transaction",
|
|
49247
51270
|
"helius_get_token_accounts_by_owner_v2",
|
|
49248
|
-
"helius_get_program_accounts_v2"
|
|
51271
|
+
"helius_get_program_accounts_v2",
|
|
51272
|
+
// Goldsky Edge RPC: eth_call, logs, traces, any-chain node reads
|
|
51273
|
+
"goldsky_rpc_call",
|
|
51274
|
+
"goldsky_call_contract",
|
|
51275
|
+
"goldsky_get_logs",
|
|
51276
|
+
"goldsky_trace_transaction",
|
|
51277
|
+
"goldsky_trace_filter"
|
|
49249
51278
|
]
|
|
49250
51279
|
},
|
|
49251
51280
|
{
|
|
@@ -49271,7 +51300,7 @@ var ToolRegistry = [
|
|
|
49271
51300
|
{
|
|
49272
51301
|
category: "Prediction Markets",
|
|
49273
51302
|
name: "get_prediction_markets_endpoints",
|
|
49274
|
-
description: "Endpoints for prediction-market intelligence on Polymarket: market and event search, market-implied odds per outcome,
|
|
51303
|
+
description: "Endpoints for prediction-market intelligence on Polymarket and Kalshi: market and event search, market-implied odds per outcome, order books, odds history and trades, and Polymarket per-wallet positions and activity. Prices are market-implied odds, not ground truth. Provider guide: polymarket_ tools use the keyless public Gamma, CLOB and Data APIs; kalshi_ tools use the keyless public Kalshi Trade API v2; Hyperliquid HIP-4 outcome markets stay under hyperliquid_get_outcome_meta and hyperliquid_get_settled_outcome.",
|
|
49275
51304
|
tools: [
|
|
49276
51305
|
"polymarket_search_markets",
|
|
49277
51306
|
"polymarket_get_events",
|
|
@@ -49279,7 +51308,13 @@ var ToolRegistry = [
|
|
|
49279
51308
|
"polymarket_get_price_history",
|
|
49280
51309
|
"polymarket_get_orderbook",
|
|
49281
51310
|
"polymarket_get_wallet_positions",
|
|
49282
|
-
"polymarket_get_wallet_activity"
|
|
51311
|
+
"polymarket_get_wallet_activity",
|
|
51312
|
+
"kalshi_search_markets",
|
|
51313
|
+
"kalshi_get_events",
|
|
51314
|
+
"kalshi_get_market",
|
|
51315
|
+
"kalshi_get_orderbook",
|
|
51316
|
+
"kalshi_get_trades",
|
|
51317
|
+
"kalshi_get_price_history"
|
|
49283
51318
|
]
|
|
49284
51319
|
}
|
|
49285
51320
|
];
|
|
@@ -50473,7 +52508,7 @@ function describeErrorCode(code) {
|
|
|
50473
52508
|
// src/utils/agentErrorCopy.ts
|
|
50474
52509
|
function agentErrorCopy(status, context = {}) {
|
|
50475
52510
|
const provider = context.provider || "the provider";
|
|
50476
|
-
const
|
|
52511
|
+
const tool101 = context.tool || "this tool";
|
|
50477
52512
|
switch (status) {
|
|
50478
52513
|
case "ok":
|
|
50479
52514
|
return {
|
|
@@ -50483,12 +52518,12 @@ function agentErrorCopy(status, context = {}) {
|
|
|
50483
52518
|
case "invalid_input":
|
|
50484
52519
|
return {
|
|
50485
52520
|
cause: "The arguments failed validation or named an unsupported venue/method; the provider was not the problem.",
|
|
50486
|
-
next_action: `Fix the arguments: call get_api_endpoint_schema for '${
|
|
52521
|
+
next_action: `Fix the arguments: call get_api_endpoint_schema for '${tool101}', use supported values, then retry. Do not retry unchanged.`
|
|
50487
52522
|
};
|
|
50488
52523
|
case "missing_key":
|
|
50489
52524
|
return {
|
|
50490
|
-
cause: `${provider} is not configured on this deployment (no API credential), so '${
|
|
50491
|
-
next_action: `Do not retry '${
|
|
52525
|
+
cause: `${provider} is not configured on this deployment (no API credential), so '${tool101}' cannot return data here.`,
|
|
52526
|
+
next_action: `Do not retry '${tool101}'. Use search_tools to pick a sibling provider tool for the same data, or ask the operator to configure ${provider} credentials.`
|
|
50492
52527
|
};
|
|
50493
52528
|
case "plan_required":
|
|
50494
52529
|
return {
|
|
@@ -50507,7 +52542,7 @@ function agentErrorCopy(status, context = {}) {
|
|
|
50507
52542
|
};
|
|
50508
52543
|
case "failing":
|
|
50509
52544
|
return {
|
|
50510
|
-
cause: `${provider} returned an unexpected error for '${
|
|
52545
|
+
cause: `${provider} returned an unexpected error for '${tool101}'.`,
|
|
50511
52546
|
next_action: "Do not retry in a loop. Try the route fallback tool or a sibling provider via search_tools, and report the runtime_status to the user."
|
|
50512
52547
|
};
|
|
50513
52548
|
}
|
|
@@ -50605,7 +52640,7 @@ function defaultDependencies2() {
|
|
|
50605
52640
|
flushBatchSize: FLUSH_BATCH_SIZE
|
|
50606
52641
|
};
|
|
50607
52642
|
}
|
|
50608
|
-
function
|
|
52643
|
+
function boundedInt2(value) {
|
|
50609
52644
|
if (!Number.isFinite(value)) return 0;
|
|
50610
52645
|
return Math.max(0, Math.min(MAX_INT4, Math.round(value)));
|
|
50611
52646
|
}
|
|
@@ -50701,7 +52736,7 @@ function createAnalyticsWriter(overrides = {}) {
|
|
|
50701
52736
|
ts: event.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
50702
52737
|
tool_name: event.toolName,
|
|
50703
52738
|
success: event.success,
|
|
50704
|
-
duration_ms:
|
|
52739
|
+
duration_ms: boundedInt2(event.durationMs),
|
|
50705
52740
|
transport: event.transport,
|
|
50706
52741
|
provider: event.provider ?? null,
|
|
50707
52742
|
category: event.category ?? null,
|
|
@@ -50754,7 +52789,7 @@ function createAnalyticsWriter(overrides = {}) {
|
|
|
50754
52789
|
provider: event.provider ?? null,
|
|
50755
52790
|
category: event.category ?? null,
|
|
50756
52791
|
runtime_status: event.runtimeStatus ?? null,
|
|
50757
|
-
seconds_since_consent: event.secondsSinceConsent === null || event.secondsSinceConsent === void 0 ? null :
|
|
52792
|
+
seconds_since_consent: event.secondsSinceConsent === null || event.secondsSinceConsent === void 0 ? null : boundedInt2(event.secondsSinceConsent),
|
|
50758
52793
|
connection_hash: event.connectionHash
|
|
50759
52794
|
});
|
|
50760
52795
|
} catch {
|
|
@@ -51274,7 +53309,8 @@ var PROVIDER_PREFIXES = [
|
|
|
51274
53309
|
["tenderly_", "Tenderly"],
|
|
51275
53310
|
["goplus_", "GoPlus"],
|
|
51276
53311
|
["ccxt_", "CCXT"],
|
|
51277
|
-
["defillama_", "DeFiLlama"]
|
|
53312
|
+
["defillama_", "DeFiLlama"],
|
|
53313
|
+
["goldsky_", "Goldsky"]
|
|
51278
53314
|
];
|
|
51279
53315
|
var replacementAvailability = null;
|
|
51280
53316
|
function setRetiredReplacementAvailabilityProvider(provider) {
|
|
@@ -52078,7 +54114,7 @@ function formatSchemaIssues(error) {
|
|
|
52078
54114
|
}).join(" ");
|
|
52079
54115
|
}
|
|
52080
54116
|
function unknownKeysError(unknownKeys, accepted) {
|
|
52081
|
-
return
|
|
54117
|
+
return errorResult6(
|
|
52082
54118
|
`Invalid arguments: ${unknownKeys.map((key) => `"${key}" is not allowed.`).join(" ")} Accepted fields: ${accepted}.`
|
|
52083
54119
|
);
|
|
52084
54120
|
}
|
|
@@ -52093,11 +54129,11 @@ function looksLikeNameService(address) {
|
|
|
52093
54129
|
return /\.[a-z]{2,}$/i.test(address.trim());
|
|
52094
54130
|
}
|
|
52095
54131
|
function nameServiceError(address) {
|
|
52096
|
-
return
|
|
54132
|
+
return errorResult6(
|
|
52097
54133
|
`Invalid arguments: "${address}" looks like an ENS or name-service name, which isn't supported yet. Pass a 0x address (EVM) or a base58 address (Solana).`
|
|
52098
54134
|
);
|
|
52099
54135
|
}
|
|
52100
|
-
function
|
|
54136
|
+
function errorResult6(message, code = "VALIDATION_ERROR") {
|
|
52101
54137
|
return {
|
|
52102
54138
|
content: [{ type: "text", text: JSON.stringify({ error: message, code }) }],
|
|
52103
54139
|
isError: true
|
|
@@ -52131,7 +54167,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
52131
54167
|
const executeUnderlying = async (toolName, args, parentTool) => {
|
|
52132
54168
|
const lookup = resolveEndpoint(endpoints, toolName);
|
|
52133
54169
|
if (!lookup.ok) {
|
|
52134
|
-
return
|
|
54170
|
+
return errorResult6(
|
|
52135
54171
|
`Underlying endpoint '${toolName}' is not registered. Call search_tools to locate the current tool for this intent.`
|
|
52136
54172
|
);
|
|
52137
54173
|
}
|
|
@@ -52238,7 +54274,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
52238
54274
|
}
|
|
52239
54275
|
const parsed = tokenPriceSchema.safeParse(heroArgs.args);
|
|
52240
54276
|
if (!parsed.success) {
|
|
52241
|
-
return
|
|
54277
|
+
return errorResult6(
|
|
52242
54278
|
`Invalid arguments: ${formatSchemaIssues(parsed.error)}`
|
|
52243
54279
|
);
|
|
52244
54280
|
}
|
|
@@ -52255,7 +54291,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
52255
54291
|
return respond(result3, format, trimOnchainPrice(address));
|
|
52256
54292
|
}
|
|
52257
54293
|
if (!token) {
|
|
52258
|
-
return
|
|
54294
|
+
return errorResult6(
|
|
52259
54295
|
'Invalid arguments: pass token (CoinGecko id or symbol, for example "bitcoin" or "BTC"), or chain + address for a contract.'
|
|
52260
54296
|
);
|
|
52261
54297
|
}
|
|
@@ -52347,7 +54383,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
52347
54383
|
}
|
|
52348
54384
|
const parsed = tokenSafetySchema.safeParse(heroArgs.args);
|
|
52349
54385
|
if (!parsed.success) {
|
|
52350
|
-
return
|
|
54386
|
+
return errorResult6(
|
|
52351
54387
|
`Invalid arguments: ${formatSchemaIssues(parsed.error)}`
|
|
52352
54388
|
);
|
|
52353
54389
|
}
|
|
@@ -52363,7 +54399,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
52363
54399
|
const normalized = normalizeChain(chain);
|
|
52364
54400
|
const chainId = /^\d+$/.test(normalized) ? normalized : GOPLUS_CHAIN_IDS[normalized];
|
|
52365
54401
|
if (!chainId) {
|
|
52366
|
-
return
|
|
54402
|
+
return errorResult6(
|
|
52367
54403
|
`Invalid arguments: Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(GOPLUS_CHAIN_IDS).slice(0, 6).join(", ")}, \u2026), a numeric chain id, or "solana".`
|
|
52368
54404
|
);
|
|
52369
54405
|
}
|
|
@@ -52532,7 +54568,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
52532
54568
|
}
|
|
52533
54569
|
const parsed = walletPortfolioSchema.safeParse(heroArgs.args);
|
|
52534
54570
|
if (!parsed.success) {
|
|
52535
|
-
return
|
|
54571
|
+
return errorResult6(
|
|
52536
54572
|
`Invalid arguments: ${formatSchemaIssues(parsed.error)}`
|
|
52537
54573
|
);
|
|
52538
54574
|
}
|
|
@@ -52571,7 +54607,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
52571
54607
|
fallbackNote = "CoinGecko wallet balances failed for this call; served from Alchemy.";
|
|
52572
54608
|
}
|
|
52573
54609
|
if (!network) {
|
|
52574
|
-
return
|
|
54610
|
+
return errorResult6(
|
|
52575
54611
|
`Invalid arguments: Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(ALCHEMY_NETWORKS).slice(0, 6).join(", ")}, \u2026), a CoinGecko network id (${WALLET_BALANCE_NETWORKS.join(", ")}), or "solana".`
|
|
52576
54612
|
);
|
|
52577
54613
|
}
|
|
@@ -53302,7 +55338,8 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
53302
55338
|
"logs",
|
|
53303
55339
|
"Read bounded logs only when the receipt needs event context.",
|
|
53304
55340
|
"alchemy_get_logs",
|
|
53305
|
-
|
|
55341
|
+
"goldsky_get_logs",
|
|
55342
|
+
PROVIDER_FALLBACK
|
|
53306
55343
|
)
|
|
53307
55344
|
],
|
|
53308
55345
|
stopCondition: "Stop after status and the minimal failure/event evidence answer the question; avoid broad block scans."
|
|
@@ -53414,7 +55451,7 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
53414
55451
|
steps: [
|
|
53415
55452
|
step(
|
|
53416
55453
|
"funding",
|
|
53417
|
-
"Read normalized funding across all
|
|
55454
|
+
"Read normalized funding across all three RWA venues with per-venue runtime status; respect funding_mechanism, since both-sides rollover (borrow_apr_annual) is never sign-comparable with a funding transfer.",
|
|
53418
55455
|
"rwa_get_funding_rates",
|
|
53419
55456
|
"rwa_get_hyperliquid_dex_perps",
|
|
53420
55457
|
`${PROVIDER_FALLBACK} The flagship builder dexes carry the deepest RWA books when the full fan-out is unavailable.`
|
|
@@ -53456,7 +55493,7 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
53456
55493
|
{
|
|
53457
55494
|
id: "rwa_builder_dex_scan",
|
|
53458
55495
|
label: "HIP-3 builder-dex scan",
|
|
53459
|
-
trigger: "Hyperliquid builder-dex, HIP-3, or pre-IPO equity perp questions (xyz,
|
|
55496
|
+
trigger: "Hyperliquid builder-dex, HIP-3, or pre-IPO equity perp questions (xyz, flx, cash, ...).",
|
|
53460
55497
|
exampleQuestion: "What is trading on Hyperliquid's builder dexes and what funding are the pre-IPO perps paying?",
|
|
53461
55498
|
requiredIdentifiers: ["builder dex name or pre-IPO underlying"],
|
|
53462
55499
|
maxMaterialCalls: 2,
|
|
@@ -53889,6 +55926,8 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
53889
55926
|
"hyperliquid_get_portfolio"
|
|
53890
55927
|
],
|
|
53891
55928
|
fallbackTools: [
|
|
55929
|
+
"goldsky_get_wallet_balances",
|
|
55930
|
+
"goldsky_get_wallet_transfers",
|
|
53892
55931
|
"helius_get_parsed_events_transaction_history",
|
|
53893
55932
|
"check_malicious_address",
|
|
53894
55933
|
"get_wallet_approvals",
|
|
@@ -54164,7 +56203,7 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
54164
56203
|
id: "network_infrastructure",
|
|
54165
56204
|
kind: "product",
|
|
54166
56205
|
label: "Network Infrastructure",
|
|
54167
|
-
purpose: "Read chain state, gas, blocks, receipts, logs, transaction status, supported networks, and Solana priority fees.",
|
|
56206
|
+
purpose: "Read chain state, gas, blocks, receipts, logs, contract state, traces, transaction status, supported networks, and Solana priority fees.",
|
|
54168
56207
|
outcome: "A chain/RPC diagnostic answer with exact network, block/transaction fixtures, and execution-adjacent status.",
|
|
54169
56208
|
requiredIdentifiers: ["chain"],
|
|
54170
56209
|
optionalIdentifiers: [
|
|
@@ -54188,7 +56227,12 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
54188
56227
|
"tenderly_get_block_number",
|
|
54189
56228
|
"tenderly_get_gas_price",
|
|
54190
56229
|
"tenderly_suggest_gas_fee",
|
|
54191
|
-
"tenderly_trace_transaction"
|
|
56230
|
+
"tenderly_trace_transaction",
|
|
56231
|
+
"goldsky_rpc_call",
|
|
56232
|
+
"goldsky_call_contract",
|
|
56233
|
+
"goldsky_get_logs",
|
|
56234
|
+
"goldsky_trace_transaction",
|
|
56235
|
+
"goldsky_trace_filter"
|
|
54192
56236
|
],
|
|
54193
56237
|
fallbackTools: [
|
|
54194
56238
|
"get_networks",
|
|
@@ -54299,7 +56343,7 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
54299
56343
|
id: "rwa_perp_analysis",
|
|
54300
56344
|
kind: "product",
|
|
54301
56345
|
label: "RWA Perp Analysis",
|
|
54302
|
-
purpose: "Analyze tokenized real-world-asset perps (stocks, pre-IPO names, ETFs, indices, commodities, FX) across Hyperliquid HIP-3 builder dexes,
|
|
56346
|
+
purpose: "Analyze tokenized real-world-asset perps (stocks, pre-IPO names, ETFs, indices, commodities, FX) across Hyperliquid HIP-3 builder dexes, Lighter, and Extended: cross-venue funding, mechanism-true carry, open interest, and price-suspect checks.",
|
|
54303
56347
|
outcome: "A cross-venue RWA perp report with normalized funding (raw + annualized per venue mechanism), asset-class and delisted/price-suspect flags on every row, per-venue runtime status, and receipts.",
|
|
54304
56348
|
requiredIdentifiers: ["underlying ticker, venue, or asset class"],
|
|
54305
56349
|
optionalIdentifiers: ["builder dex name", "time range"],
|
|
@@ -54307,8 +56351,6 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
54307
56351
|
"rwa_get_funding_rates",
|
|
54308
56352
|
"rwa_get_markets",
|
|
54309
56353
|
"rwa_get_hyperliquid_dex_perps",
|
|
54310
|
-
"rwa_get_ostium_markets",
|
|
54311
|
-
"rwa_get_avantis_markets",
|
|
54312
56354
|
"rwa_get_lighter_markets",
|
|
54313
56355
|
"rwa_get_extended_markets",
|
|
54314
56356
|
"hyperliquid_get_perp_dexs",
|
|
@@ -54364,8 +56406,6 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
54364
56406
|
"carry",
|
|
54365
56407
|
"builder dex",
|
|
54366
56408
|
"hip-3",
|
|
54367
|
-
"ostium",
|
|
54368
|
-
"avantis",
|
|
54369
56409
|
"lighter",
|
|
54370
56410
|
"extended",
|
|
54371
56411
|
"price suspect"
|
|
@@ -54375,9 +56415,11 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
54375
56415
|
id: "prediction_markets",
|
|
54376
56416
|
kind: "product",
|
|
54377
56417
|
label: "Prediction Markets",
|
|
54378
|
-
purpose: "Answer prediction-market questions with market-implied odds from Polymarket: find markets and events, read order books and odds history, and inspect a trader's positions and flow.
|
|
56418
|
+
purpose: "Answer prediction-market questions with market-implied odds from Polymarket and Kalshi: find markets and events, read order books, trades and odds history, and inspect a Polymarket trader's positions and flow.",
|
|
54379
56419
|
outcome: "A sourced odds view (implied probability, liquidity, spread, trend, venue) with the caveat that prices are market-implied, not ground truth, plus per-call runtime status.",
|
|
54380
|
-
requiredIdentifiers: [
|
|
56420
|
+
requiredIdentifiers: [
|
|
56421
|
+
"market topic, Polymarket slug or condition id, or Kalshi ticker"
|
|
56422
|
+
],
|
|
54381
56423
|
optionalIdentifiers: [
|
|
54382
56424
|
"CLOB token id",
|
|
54383
56425
|
"wallet address",
|
|
@@ -54391,7 +56433,13 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
54391
56433
|
"polymarket_get_price_history",
|
|
54392
56434
|
"polymarket_get_orderbook",
|
|
54393
56435
|
"polymarket_get_wallet_positions",
|
|
54394
|
-
"polymarket_get_wallet_activity"
|
|
56436
|
+
"polymarket_get_wallet_activity",
|
|
56437
|
+
"kalshi_search_markets",
|
|
56438
|
+
"kalshi_get_events",
|
|
56439
|
+
"kalshi_get_market",
|
|
56440
|
+
"kalshi_get_orderbook",
|
|
56441
|
+
"kalshi_get_trades",
|
|
56442
|
+
"kalshi_get_price_history"
|
|
54395
56443
|
],
|
|
54396
56444
|
fallbackTools: ["hyperliquid_get_outcome_meta"],
|
|
54397
56445
|
stableFixtures: [
|
|
@@ -56616,7 +58664,7 @@ async function runMcpProtocolHealth(options = {}) {
|
|
|
56616
58664
|
checks.push(
|
|
56617
58665
|
await timedCheck("tools_list_root_surface", async () => {
|
|
56618
58666
|
const listed = await client2.listTools();
|
|
56619
|
-
const names = listed.tools.map((
|
|
58667
|
+
const names = listed.tools.map((tool101) => tool101.name);
|
|
56620
58668
|
const expectedCount = getRootVisibleToolCount();
|
|
56621
58669
|
const expectedTools = [...ROOT_VISIBLE_TOOL_NAMES];
|
|
56622
58670
|
if (listed.tools.length !== expectedCount) {
|
|
@@ -57180,8 +59228,8 @@ var HiveOAuthProvider = class {
|
|
|
57180
59228
|
await this.storage.revokeFamily(record.familyId);
|
|
57181
59229
|
throw error;
|
|
57182
59230
|
}
|
|
57183
|
-
const
|
|
57184
|
-
if (
|
|
59231
|
+
const invalid3 = record.kind !== "refresh" || record.clientId !== client2.client_id || record.expiresAt <= Math.floor(Date.now() / 1e3) || !resourceMatches || !hasOnlySupportedOAuthScopes(requestedScopes) || requestedScopes.some((scope) => !record.scopes.includes(scope));
|
|
59232
|
+
if (invalid3) {
|
|
57185
59233
|
await this.storage.revokeFamily(record.familyId);
|
|
57186
59234
|
throw new InvalidGrantError(
|
|
57187
59235
|
"Refresh token is not valid for this client, resource, or scope set."
|
|
@@ -58012,7 +60060,7 @@ function parsePositiveInteger(value, fallback) {
|
|
|
58012
60060
|
const parsed = Number.parseInt(value || "", 10);
|
|
58013
60061
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
58014
60062
|
}
|
|
58015
|
-
function
|
|
60063
|
+
function sleep4(ms) {
|
|
58016
60064
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
58017
60065
|
}
|
|
58018
60066
|
function chainIdPathSegment(value) {
|
|
@@ -58318,7 +60366,7 @@ var GoPlusClient = class {
|
|
|
58318
60366
|
maxAttempts: this.rateLimitRetryAttempts + 1,
|
|
58319
60367
|
backoffMs
|
|
58320
60368
|
});
|
|
58321
|
-
await
|
|
60369
|
+
await sleep4(backoffMs);
|
|
58322
60370
|
}
|
|
58323
60371
|
if (result2 && typeof result2.code === "number" && result2.code !== 1 && result2.code !== 2) {
|
|
58324
60372
|
const isRateLimit = result2.code === 4029;
|
|
@@ -58769,6 +60817,9 @@ var CCXTClient = class {
|
|
|
58769
60817
|
if (this.exchanges.has(cacheKey2)) {
|
|
58770
60818
|
return this.exchanges.get(cacheKey2);
|
|
58771
60819
|
}
|
|
60820
|
+
if (REMOVED_CCXT_EXCHANGES.has(exchangeId.toLowerCase())) {
|
|
60821
|
+
throw new Error(removedVenueMessage(exchangeId));
|
|
60822
|
+
}
|
|
58772
60823
|
if (!ccxt[exchangeId]) {
|
|
58773
60824
|
throw new Error(
|
|
58774
60825
|
`Exchange '${exchangeId}' is not supported. Use one of: binance, coinbase, kraken, kucoin, etc.`
|
|
@@ -59629,9 +61680,11 @@ var PROVIDER_RESOURCE_PATTERNS = [
|
|
|
59629
61680
|
["rwa", /^rwa(?:[.-]|$)/],
|
|
59630
61681
|
["archive", /^archive(?:[.-]|$)/],
|
|
59631
61682
|
["polymarket", /^polymarket(?:[.-]|$)/],
|
|
61683
|
+
["kalshi", /^kalshi(?:[.-]|$)/],
|
|
59632
61684
|
["alternative", /^signals-fear-greed$/],
|
|
59633
61685
|
["blockchain", /^signals-onchain/],
|
|
59634
61686
|
["blockscout", /^blockscout(?:[.-]|$)/],
|
|
61687
|
+
["goldsky", /^goldsky\./],
|
|
59635
61688
|
["fetch", /^fetch(?:[.-]|$)/],
|
|
59636
61689
|
["helius", /^helius\./],
|
|
59637
61690
|
["tenderly", /^tenderly\./],
|
|
@@ -59654,9 +61707,11 @@ function classifyProvider(endpoint) {
|
|
|
59654
61707
|
if (toolName.startsWith("rwa_")) return "rwa";
|
|
59655
61708
|
if (toolName.startsWith("archive_")) return "archive";
|
|
59656
61709
|
if (toolName.startsWith("polymarket_")) return "polymarket";
|
|
61710
|
+
if (toolName.startsWith("kalshi_")) return "kalshi";
|
|
59657
61711
|
if (toolName === "signals_get_fear_greed") return "alternative";
|
|
59658
61712
|
if (toolName.startsWith("signals_get_onchain")) return "blockchain";
|
|
59659
61713
|
if (toolName.startsWith("fetch_")) return "fetch";
|
|
61714
|
+
if (toolName.startsWith("goldsky_")) return "goldsky";
|
|
59660
61715
|
if (toolName.startsWith("hive_")) return "hive";
|
|
59661
61716
|
return "coingecko";
|
|
59662
61717
|
}
|
|
@@ -59681,8 +61736,8 @@ var QUICK_PROBE_ARGS = {
|
|
|
59681
61736
|
args: { network: "eth-mainnet" }
|
|
59682
61737
|
},
|
|
59683
61738
|
hyperliquid: { toolName: "hyperliquid_get_perp_dexs", args: {} },
|
|
59684
|
-
// Single-request keyless venue read (official
|
|
59685
|
-
rwa: { toolName: "
|
|
61739
|
+
// Single-request keyless venue read (official Extended markets endpoint).
|
|
61740
|
+
rwa: { toolName: "rwa_get_extended_markets", args: { limit: 5 } },
|
|
59686
61741
|
// Cheap coverage probe (index-only extremes, 10-min in-process cache).
|
|
59687
61742
|
// Unset archive credentials classify as missing-key skip, not failure.
|
|
59688
61743
|
archive: { toolName: "archive_get_coverage", args: {} },
|
|
@@ -59701,6 +61756,10 @@ var QUICK_PROBE_ARGS = {
|
|
|
59701
61756
|
toolName: "get_token_holder_distribution",
|
|
59702
61757
|
args: { network: "eth", address: ETH_USDC, max_pages: 1 }
|
|
59703
61758
|
},
|
|
61759
|
+
goldsky: {
|
|
61760
|
+
toolName: "goldsky_rpc_call",
|
|
61761
|
+
args: { chain: "ethereum", method: "eth_blockNumber" }
|
|
61762
|
+
},
|
|
59704
61763
|
fetch: {
|
|
59705
61764
|
toolName: "fetch_public_api",
|
|
59706
61765
|
args: { host: "api.coingecko.com", path: "/api/v3/ping" }
|
|
@@ -59816,6 +61875,32 @@ var TOOL_PROBE_ARGS = {
|
|
|
59816
61875
|
address: ETH_USDC,
|
|
59817
61876
|
max_pages: 1
|
|
59818
61877
|
},
|
|
61878
|
+
// Goldsky fixtures verified live 2026-10-24: USDC totalSupply(), the USDC
|
|
61879
|
+
// Transfer logs of one block, a plain ETH transfer from 2015, and Binance 14
|
|
61880
|
+
// sending inside a 1,000-block window.
|
|
61881
|
+
goldsky_rpc_call: { chain: "hyperevm", method: "eth_blockNumber" },
|
|
61882
|
+
goldsky_call_contract: { chain: "ethereum", to: ETH_USDC, data: "0x18160ddd" },
|
|
61883
|
+
goldsky_get_logs: {
|
|
61884
|
+
chain: "ethereum",
|
|
61885
|
+
from_block: ETH_BLOCK_19000000_HEX,
|
|
61886
|
+
to_block: ETH_BLOCK_19000000_HEX,
|
|
61887
|
+
address: ETH_USDC,
|
|
61888
|
+
topics: [ERC20_TRANSFER_TOPIC],
|
|
61889
|
+
limit: 5
|
|
61890
|
+
},
|
|
61891
|
+
goldsky_trace_transaction: {
|
|
61892
|
+
chain: "ethereum",
|
|
61893
|
+
tx_hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060"
|
|
61894
|
+
},
|
|
61895
|
+
goldsky_trace_filter: {
|
|
61896
|
+
chain: "ethereum",
|
|
61897
|
+
from_block: "0x1900000",
|
|
61898
|
+
to_block: "0x19003e7",
|
|
61899
|
+
from_address: ["0x28C6c06298d514Db089934071355E5743bf21d60"],
|
|
61900
|
+
count: 3
|
|
61901
|
+
},
|
|
61902
|
+
goldsky_get_wallet_balances: { address: ETH_WALLET, chains: "ethereum", page_size: 5 },
|
|
61903
|
+
goldsky_get_wallet_transfers: { address: ETH_WALLET, chains: "base", page_size: 5 },
|
|
59819
61904
|
polymarket_get_events: { slug: "presidential-election-winner-2028" },
|
|
59820
61905
|
polymarket_get_market: {
|
|
59821
61906
|
slug: "will-jd-vance-win-the-2028-us-presidential-election"
|
|
@@ -60816,8 +62901,10 @@ function getClientForProvider(provider, clients) {
|
|
|
60816
62901
|
case "defillama":
|
|
60817
62902
|
case "archive":
|
|
60818
62903
|
case "polymarket":
|
|
62904
|
+
case "kalshi":
|
|
60819
62905
|
case "alternative":
|
|
60820
62906
|
case "blockchain":
|
|
62907
|
+
case "goldsky":
|
|
60821
62908
|
return null;
|
|
60822
62909
|
case "hive":
|
|
60823
62910
|
return null;
|
|
@@ -61085,9 +63172,22 @@ function projectArgsToToolSchema(args, schema) {
|
|
|
61085
63172
|
Object.entries(args).filter(([key]) => key in properties)
|
|
61086
63173
|
);
|
|
61087
63174
|
}
|
|
63175
|
+
var UNPROBED_PROVIDERS = {
|
|
63176
|
+
kalshi: "Not probed: the Kalshi developer agreement (section 3.5) forbids probing its API for availability or performance."
|
|
63177
|
+
};
|
|
61088
63178
|
async function runEndpointProbe(endpoint, provider, clients, args, timeoutMs = PROBE_TIMEOUT) {
|
|
61089
63179
|
const start = Date.now();
|
|
61090
63180
|
const toolName = endpoint.tool.name;
|
|
63181
|
+
const unprobedReason = UNPROBED_PROVIDERS[provider];
|
|
63182
|
+
if (unprobedReason) {
|
|
63183
|
+
return {
|
|
63184
|
+
name: toolName,
|
|
63185
|
+
status: "skipped",
|
|
63186
|
+
duration: 0,
|
|
63187
|
+
error: unprobedReason,
|
|
63188
|
+
runtimeStatus: "ok"
|
|
63189
|
+
};
|
|
63190
|
+
}
|
|
61091
63191
|
const isWriteCapable = endpoint.metadata.operation === "write" || endpoint.tool.annotations?.readOnlyHint === false;
|
|
61092
63192
|
if (isWriteCapable && process.env.HIVE_E2E_ALLOW_WRITE_TOOLS !== "true") {
|
|
61093
63193
|
return {
|
|
@@ -61453,8 +63553,8 @@ var TASK_CANARY_PROBES = [
|
|
|
61453
63553
|
},
|
|
61454
63554
|
{
|
|
61455
63555
|
toolsetId: "rwa_perp_analysis",
|
|
61456
|
-
toolName: "
|
|
61457
|
-
description: "
|
|
63556
|
+
toolName: "rwa_get_extended_markets",
|
|
63557
|
+
description: "Extended RWA markets check (single-request keyless venue read).",
|
|
61458
63558
|
args: { limit: 5 }
|
|
61459
63559
|
},
|
|
61460
63560
|
{
|
|
@@ -63192,8 +65292,6 @@ var TOOLSET_INTENT_HINTS = {
|
|
|
63192
65292
|
"rwa perp",
|
|
63193
65293
|
"pre-ipo",
|
|
63194
65294
|
"pre ipo",
|
|
63195
|
-
"ostium",
|
|
63196
|
-
"avantis",
|
|
63197
65295
|
"rollover"
|
|
63198
65296
|
],
|
|
63199
65297
|
wallet_investigation: [
|
|
@@ -63508,7 +65606,7 @@ var TASK_RESULT_VALIDATION_OUTPUT_SCHEMA = {
|
|
|
63508
65606
|
warnings: { type: "array", items: { type: "string" } }
|
|
63509
65607
|
}
|
|
63510
65608
|
};
|
|
63511
|
-
function
|
|
65609
|
+
function errorResult7(message) {
|
|
63512
65610
|
return {
|
|
63513
65611
|
content: [
|
|
63514
65612
|
{
|
|
@@ -64133,12 +66231,12 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
64133
66231
|
},
|
|
64134
66232
|
handler: async (_client, args) => {
|
|
64135
66233
|
const missingEndpoint = "Invalid arguments: no endpoint provided. Pass an 'endpoint' (or 'endpoint_name') parameter with one tool name as a string.";
|
|
64136
|
-
if (!args) return
|
|
66234
|
+
if (!args) return errorResult7(missingEndpoint);
|
|
64137
66235
|
const parsedArgs = getEndpointSchema.safeParse(args);
|
|
64138
|
-
if (!parsedArgs.success) return
|
|
66236
|
+
if (!parsedArgs.success) return errorResult7(missingEndpoint);
|
|
64139
66237
|
const parsedEndpointArgs = parsedArgs.data;
|
|
64140
66238
|
const requestedEndpoint = parsedEndpointArgs.endpoint ?? parsedEndpointArgs.endpoint_name ?? firstStringArg(args, ENDPOINT_NAME_ALIASES);
|
|
64141
|
-
if (!requestedEndpoint) return
|
|
66239
|
+
if (!requestedEndpoint) return errorResult7(missingEndpoint);
|
|
64142
66240
|
const lookup = resolveEndpoint(endpoints, requestedEndpoint);
|
|
64143
66241
|
const metaEndpoint = lookup.ok ? void 0 : [
|
|
64144
66242
|
searchToolsTool,
|
|
@@ -64186,12 +66284,12 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
64186
66284
|
const invokeWithPolicy = async (invoker, client2, args) => {
|
|
64187
66285
|
const { success, data, error } = invokeEndpointSchema.safeParse(args);
|
|
64188
66286
|
if (!success) {
|
|
64189
|
-
return
|
|
66287
|
+
return errorResult7(
|
|
64190
66288
|
`Invalid arguments: ${JSON.stringify(error?.format())}`
|
|
64191
66289
|
);
|
|
64192
66290
|
}
|
|
64193
66291
|
const invokeArgs = normalizeInvokeArgs(data);
|
|
64194
|
-
if (!invokeArgs.ok) return
|
|
66292
|
+
if (!invokeArgs.ok) return errorResult7(invokeArgs.message);
|
|
64195
66293
|
const lookup = resolveEndpoint(endpoints, invokeArgs.resolvedToolName);
|
|
64196
66294
|
if (!lookup.ok) {
|
|
64197
66295
|
return createToolNotFoundResult(
|
|
@@ -64199,7 +66297,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
64199
66297
|
).mcpResult;
|
|
64200
66298
|
}
|
|
64201
66299
|
if (!endpointAllowedForInvoker(invoker, lookup.endpoint)) {
|
|
64202
|
-
return
|
|
66300
|
+
return errorResult7(invokerPolicyMessage(invoker, lookup.endpoint));
|
|
64203
66301
|
}
|
|
64204
66302
|
const executionContext = {
|
|
64205
66303
|
transport: "dynamic",
|
|
@@ -64273,7 +66371,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
64273
66371
|
handler: async (_client, args) => {
|
|
64274
66372
|
const parsed = validateTaskResultSchema.safeParse(args);
|
|
64275
66373
|
if (!parsed.success) {
|
|
64276
|
-
return
|
|
66374
|
+
return errorResult7(
|
|
64277
66375
|
`Invalid task result validation request: ${JSON.stringify(parsed.error.format())}`
|
|
64278
66376
|
);
|
|
64279
66377
|
}
|
|
@@ -64281,7 +66379,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
64281
66379
|
(entry2) => entry2.id === parsed.data.task_toolset_id
|
|
64282
66380
|
);
|
|
64283
66381
|
if (!toolset) {
|
|
64284
|
-
return
|
|
66382
|
+
return errorResult7(
|
|
64285
66383
|
`Unknown task toolset '${parsed.data.task_toolset_id}'. Use search_tools to select a canonical id.`
|
|
64286
66384
|
);
|
|
64287
66385
|
}
|
|
@@ -64890,7 +66988,7 @@ function getRootToolSet() {
|
|
|
64890
66988
|
tools: Array.from(toolsByName.values()),
|
|
64891
66989
|
visibleTools: ROOT_VISIBLE_TOOL_ORDER.map(
|
|
64892
66990
|
(name) => compatibilityByName.get(name)
|
|
64893
|
-
).filter((
|
|
66991
|
+
).filter((tool101) => Boolean(tool101))
|
|
64894
66992
|
};
|
|
64895
66993
|
secureLogger.info("Root MCP tool catalog cached", {
|
|
64896
66994
|
actualToolCount: actualToolEndpoints.length,
|
|
@@ -65162,21 +67260,21 @@ var HiveMCPServer = class {
|
|
|
65162
67260
|
}
|
|
65163
67261
|
registerAllTools() {
|
|
65164
67262
|
const registeredTools = this.scope.kind === "root" ? this.compatibilityToolEndpoints.map((endpoint) => endpoint.tool) : this.visibleTools;
|
|
65165
|
-
for (const
|
|
65166
|
-
const inputSchema =
|
|
67263
|
+
for (const tool101 of registeredTools) {
|
|
67264
|
+
const inputSchema = tool101.inputSchema;
|
|
65167
67265
|
const inputZodSchema = memoizedJsonSchemaToZodInputSchema(inputSchema);
|
|
65168
|
-
const outputSchema =
|
|
67266
|
+
const outputSchema = tool101.outputSchema;
|
|
65169
67267
|
const outputZodShape = outputSchema?.type === "object" ? memoizedJsonSchemaToZodOutputSchema(outputSchema) : void 0;
|
|
65170
|
-
const toolName =
|
|
65171
|
-
const toolMeta =
|
|
67268
|
+
const toolName = tool101.name;
|
|
67269
|
+
const toolMeta = tool101._meta;
|
|
65172
67270
|
this.server.registerTool(
|
|
65173
67271
|
toolName,
|
|
65174
67272
|
{
|
|
65175
|
-
title:
|
|
65176
|
-
description:
|
|
67273
|
+
title: tool101.title,
|
|
67274
|
+
description: tool101.description,
|
|
65177
67275
|
inputSchema: inputZodSchema,
|
|
65178
67276
|
...outputZodShape && { outputSchema: outputZodShape },
|
|
65179
|
-
annotations:
|
|
67277
|
+
annotations: tool101.annotations,
|
|
65180
67278
|
// Surfaces response-size headroom annotations (plan 1.15) in
|
|
65181
67279
|
// SDK-generated tools/list responses.
|
|
65182
67280
|
...toolMeta && { _meta: toolMeta }
|
|
@@ -65741,8 +67839,6 @@ export {
|
|
|
65741
67839
|
runWithRequestPrincipal,
|
|
65742
67840
|
principalKeyFromRaw,
|
|
65743
67841
|
submitFeedback,
|
|
65744
|
-
HyperliquidAPI,
|
|
65745
|
-
collectRwaRows,
|
|
65746
67842
|
isFreeWriteEndpoint,
|
|
65747
67843
|
trackServerEvent,
|
|
65748
67844
|
trackCliCommand,
|