otto-intel-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/adapter/cdp-signer.d.ts +133 -0
- package/dist/adapter/cdp-signer.js +356 -0
- package/dist/adapter/cdp-signer.js.map +1 -0
- package/dist/adapter/chain.d.ts +36 -0
- package/dist/adapter/chain.js +65 -0
- package/dist/adapter/chain.js.map +1 -0
- package/dist/adapter/erc20.d.ts +39 -0
- package/dist/adapter/erc20.js +17 -0
- package/dist/adapter/erc20.js.map +1 -0
- package/dist/adapter/lifi-decode.d.ts +52 -0
- package/dist/adapter/lifi-decode.js +149 -0
- package/dist/adapter/lifi-decode.js.map +1 -0
- package/dist/adapter/refusal.d.ts +21 -0
- package/dist/adapter/refusal.js +55 -0
- package/dist/adapter/refusal.js.map +1 -0
- package/dist/adapter/sent-step.d.ts +15 -0
- package/dist/adapter/sent-step.js +6 -0
- package/dist/adapter/sent-step.js.map +1 -0
- package/dist/adapter/verify.d.ts +149 -0
- package/dist/adapter/verify.js +432 -0
- package/dist/adapter/verify.js.map +1 -0
- package/dist/adapter-cdp-index.d.ts +6 -0
- package/dist/adapter-cdp-index.js +7 -0
- package/dist/adapter-index.d.ts +15 -0
- package/dist/adapter-index.js +15 -0
- package/dist/artifact-id.d.ts +16 -0
- package/dist/artifact-id.js +60 -0
- package/dist/boot-redaction.d.ts +16 -0
- package/dist/boot-redaction.js +54 -0
- package/dist/data-source.d.ts +7 -0
- package/dist/data-source.js +2 -0
- package/dist/errors.d.ts +70 -0
- package/dist/errors.js +221 -0
- package/dist/execution-config.d.ts +232 -0
- package/dist/execution-config.js +443 -0
- package/dist/execution-delegated-definition.d.ts +165 -0
- package/dist/execution-delegated-definition.js +116 -0
- package/dist/execution-delegation-admin-definition.d.ts +208 -0
- package/dist/execution-delegation-admin-definition.js +170 -0
- package/dist/execution-delegation-admin.d.ts +74 -0
- package/dist/execution-delegation-admin.js +290 -0
- package/dist/execution-delegation-policy.d.ts +257 -0
- package/dist/execution-delegation-policy.js +279 -0
- package/dist/execution-delegation.d.ts +190 -0
- package/dist/execution-delegation.js +545 -0
- package/dist/execution-errors.d.ts +9 -0
- package/dist/execution-errors.js +128 -0
- package/dist/execution-index.d.ts +17 -0
- package/dist/execution-index.js +16 -0
- package/dist/execution-intent.d.ts +14 -0
- package/dist/execution-intent.js +36 -0
- package/dist/execution-registration.d.ts +113 -0
- package/dist/execution-registration.js +161 -0
- package/dist/execution-tool-definitions.d.ts +1103 -0
- package/dist/execution-tool-definitions.js +1051 -0
- package/dist/execution-tools.d.ts +85 -0
- package/dist/execution-tools.js +690 -0
- package/dist/execution-types.d.ts +274 -0
- package/dist/execution-types.js +157 -0
- package/dist/hyperliquid-info-client.d.ts +31 -0
- package/dist/hyperliquid-info-client.js +121 -0
- package/dist/hyperliquid-order-assertions.d.ts +89 -0
- package/dist/hyperliquid-order-assertions.js +325 -0
- package/dist/hyperliquid-order-builder.d.ts +47 -0
- package/dist/hyperliquid-order-builder.js +55 -0
- package/dist/lifi-bridge-assertions.d.ts +49 -0
- package/dist/lifi-bridge-assertions.js +303 -0
- package/dist/lifi-execution-client.d.ts +74 -0
- package/dist/lifi-execution-client.js +165 -0
- package/dist/lifi-fee-assertions.d.ts +32 -0
- package/dist/lifi-fee-assertions.js +103 -0
- package/dist/lifi-swap-assertions.d.ts +32 -0
- package/dist/lifi-swap-assertions.js +214 -0
- package/dist/local-config.d.ts +9 -0
- package/dist/local-config.js +45 -0
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +44 -0
- package/dist/polymarket-clob-client.d.ts +73 -0
- package/dist/polymarket-clob-client.js +186 -0
- package/dist/polymarket-order-assertions.d.ts +60 -0
- package/dist/polymarket-order-assertions.js +273 -0
- package/dist/polymarket-order-builder.d.ts +43 -0
- package/dist/polymarket-order-builder.js +74 -0
- package/dist/prepared-artifacts.d.ts +45 -0
- package/dist/prepared-artifacts.js +92 -0
- package/dist/stdio-main.d.ts +2 -0
- package/dist/stdio-main.js +20 -0
- package/dist/tool-definitions.d.ts +20 -0
- package/dist/tool-definitions.js +162 -0
- package/dist/x402-read-source.d.ts +11 -0
- package/dist/x402-read-source.js +77 -0
- package/package.json +162 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boot-redaction.ts — what the hosted boot may print about a configuration failure.
|
|
3
|
+
*
|
|
4
|
+
* viem embeds the RPC URL in its error messages (`URL: https://…/v2/<key>`) and strips only basic-auth
|
|
5
|
+
* credentials from it — not a key carried in the path or the query. A boot-time RPC failure printed
|
|
6
|
+
* verbatim would therefore write `BASE_RPC_URL` (an API key) to the Railway log. Every configuration or
|
|
7
|
+
* boot error goes through `redactBootMessage` before stderr: URLs become `<url>`, and any configured
|
|
8
|
+
* secret VALUE that survives is replaced by its NAME. The chain-id check itself throws a fixed message
|
|
9
|
+
* (`execution-delegation.ts`); this is the second layer, for every other path that could carry a value.
|
|
10
|
+
*/
|
|
11
|
+
/** The hosted service's secret-bearing variables. Values are never printed; names are. */
|
|
12
|
+
export const REDACTED_ENV_NAMES = Object.freeze([
|
|
13
|
+
'CDP_API_KEY_ID',
|
|
14
|
+
'CDP_API_KEY_SECRET',
|
|
15
|
+
'CDP_WALLET_SECRET',
|
|
16
|
+
'OTTO_DELEGATION_SERVER_SECRET',
|
|
17
|
+
'BASE_RPC_URL',
|
|
18
|
+
'INTERNAL_API_KEY',
|
|
19
|
+
'DATA_ENDPOINTS_INTERNAL_URL',
|
|
20
|
+
'LIFI_API_KEY',
|
|
21
|
+
'LIFI_BASE_URL',
|
|
22
|
+
'POLYMARKET_CLOB_URL',
|
|
23
|
+
'HYPERLIQUID_API_URL',
|
|
24
|
+
// The published STDIO entrypoint's config (c2 fold 08-30): its raw printer was the one
|
|
25
|
+
// remaining unredacted boot path, and its URL/key fields were absent from this list.
|
|
26
|
+
'X402_PRIVATE_KEY',
|
|
27
|
+
'X402_BASE_URL',
|
|
28
|
+
'X402_RPC_URL',
|
|
29
|
+
]);
|
|
30
|
+
const URL_PATTERN = /https?:\/\/\S+/gi;
|
|
31
|
+
/** A value shorter than this is not treated as a secret (it would blank ordinary words). */
|
|
32
|
+
const MIN_VALUE_LENGTH = 8;
|
|
33
|
+
function escapeRegExp(value) {
|
|
34
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
35
|
+
}
|
|
36
|
+
/** Redact URLs and every configured secret value from a message that is about to be printed. */
|
|
37
|
+
export function redactBootMessage(message, env = process.env, names = REDACTED_ENV_NAMES) {
|
|
38
|
+
let out = message.replace(URL_PATTERN, '<url>');
|
|
39
|
+
const values = names
|
|
40
|
+
.map((name) => ({ name, value: env[name]?.trim() ?? '' }))
|
|
41
|
+
.filter(({ value }) => value.length >= MIN_VALUE_LENGTH)
|
|
42
|
+
.sort((a, b) => b.value.length - a.value.length); // longest first: a value that contains another is replaced whole
|
|
43
|
+
for (const { name, value } of values) {
|
|
44
|
+
// A function replacer: a `$` inside the replacement text must never be interpreted as a pattern reference.
|
|
45
|
+
out = out.replace(new RegExp(escapeRegExp(value), 'g'), () => `<${name}>`);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** The one line the boot prints for a failure; never the raw error. */
|
|
50
|
+
export function bootFailureLine(prefix, error, env = process.env) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
return `${prefix}: ${redactBootMessage(message, env)}\n`;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=boot-redaction.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IntelToolDefinition, ToolArguments } from './tool-definitions.js';
|
|
2
|
+
export interface IntelReadOptions {
|
|
3
|
+
readonly signal?: AbortSignal;
|
|
4
|
+
}
|
|
5
|
+
export interface IntelDataSource {
|
|
6
|
+
read(definition: IntelToolDefinition, input: ToolArguments, options?: IntelReadOptions): Promise<unknown>;
|
|
7
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
export declare class WarmReadUnavailableError extends Error {
|
|
3
|
+
readonly retryAfterSeconds?: number;
|
|
4
|
+
constructor(retryAfterSeconds?: number);
|
|
5
|
+
}
|
|
6
|
+
export declare class UpstreamReadError extends Error {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
export declare class X402PaymentError extends Error {
|
|
10
|
+
constructor();
|
|
11
|
+
}
|
|
12
|
+
export declare class PaymentLayerUnavailableError extends Error {
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
15
|
+
export declare class PaymentRequiredError extends Error {
|
|
16
|
+
readonly challenge: Readonly<Record<string, unknown>>;
|
|
17
|
+
constructor(challenge: Readonly<Record<string, unknown>>);
|
|
18
|
+
}
|
|
19
|
+
export declare class FreeReadUnavailableError extends Error {
|
|
20
|
+
constructor();
|
|
21
|
+
}
|
|
22
|
+
export type X402ReadRetryReason = 'route_incomplete' | 'paid_response_invalid' | 'rail_unavailable' | 'rail_rejected' | 'settlement_ambiguous';
|
|
23
|
+
/** What the paid rail's answer PROVES about a presented authorization (2026-08-28, ws-13). */
|
|
24
|
+
export type X402SettlementState = 'recoverable' | 'not_settled' | 'not_applicable';
|
|
25
|
+
export declare class X402ReadRetryableError extends Error {
|
|
26
|
+
readonly paymentPresented: boolean;
|
|
27
|
+
readonly reason: X402ReadRetryReason;
|
|
28
|
+
/**
|
|
29
|
+
* `recoverable` — the rail may have settled (transport failure, 200 without a receipt, 503 after
|
|
30
|
+
* the authorization reached it): the durable rail replays a settled body for the same
|
|
31
|
+
* authorization + input, or completes an uncharged attempt. `not_settled` — the rail answered a
|
|
32
|
+
* non-402 4xx to the request itself, before any settlement: nothing was charged, the
|
|
33
|
+
* authorization is unspent. `not_applicable` — no authorization was presented.
|
|
34
|
+
*/
|
|
35
|
+
readonly settlementState: X402SettlementState;
|
|
36
|
+
readonly upstreamStatus?: number;
|
|
37
|
+
readonly retryAfterSeconds?: number;
|
|
38
|
+
constructor(paymentPresented: boolean, reason?: X402ReadRetryReason, details?: {
|
|
39
|
+
settlementState?: 'recoverable' | 'not_settled';
|
|
40
|
+
upstreamStatus?: number;
|
|
41
|
+
retryAfterSeconds?: number;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The paid rail's PAYWALL answered 402 to a PRESENTED authorization — proven by its own v2
|
|
46
|
+
* PAYMENT-REQUIRED document in the answer, which the paywall emits only at the verify stage, before
|
|
47
|
+
* any settlement. Nothing was settled and nothing was charged. The rail's reason travels verbatim
|
|
48
|
+
* (`Payment required` = it saw no payment; `invalid_exact_evm_insufficient_balance` = transient):
|
|
49
|
+
* the same authorization may be re-presented before it expires when the cause is transient, and the
|
|
50
|
+
* fresh requirement is attached for signing again otherwise. A 402 WITHOUT that document (the SDK's
|
|
51
|
+
* post-handler settlement failure carries settlement headers and no requirement) is NOT this error —
|
|
52
|
+
* it may be settled on-chain and stays recoverable.
|
|
53
|
+
* (2026-08-28, ws-13: the hosted forward used the x402 v1 header name, the v2 paywall answered its
|
|
54
|
+
* unpaid 402, and this case was being reported as "durably owned — retry" — a false claim that made
|
|
55
|
+
* every paying MCP client loop.)
|
|
56
|
+
*/
|
|
57
|
+
export declare class PaymentRejectedError extends Error {
|
|
58
|
+
readonly challenge: Readonly<Record<string, unknown>>;
|
|
59
|
+
readonly upstreamReason: string | undefined;
|
|
60
|
+
constructor(challenge: Readonly<Record<string, unknown>>, upstreamReason?: string);
|
|
61
|
+
}
|
|
62
|
+
export declare class RequestCancelledError extends Error {
|
|
63
|
+
constructor();
|
|
64
|
+
}
|
|
65
|
+
export declare function mapToolError(error: unknown): CallToolResult;
|
|
66
|
+
/**
|
|
67
|
+
* Defense-in-depth for the hosted transport: backend preparation vocabulary must
|
|
68
|
+
* never cross its MCP boundary.
|
|
69
|
+
*/
|
|
70
|
+
export declare function mapHostedToolError(error: unknown): CallToolResult;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
export class WarmReadUnavailableError extends Error {
|
|
2
|
+
retryAfterSeconds;
|
|
3
|
+
constructor(retryAfterSeconds) {
|
|
4
|
+
super('warm data is unavailable');
|
|
5
|
+
this.name = 'WarmReadUnavailableError';
|
|
6
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class UpstreamReadError extends Error {
|
|
10
|
+
constructor() {
|
|
11
|
+
super('upstream read failed');
|
|
12
|
+
this.name = 'UpstreamReadError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class X402PaymentError extends Error {
|
|
16
|
+
constructor() {
|
|
17
|
+
super('x402 data payment failed');
|
|
18
|
+
this.name = 'X402PaymentError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class PaymentLayerUnavailableError extends Error {
|
|
22
|
+
constructor() {
|
|
23
|
+
super('x402 payment verification is unavailable');
|
|
24
|
+
this.name = 'PaymentLayerUnavailableError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class PaymentRequiredError extends Error {
|
|
28
|
+
challenge;
|
|
29
|
+
constructor(challenge) {
|
|
30
|
+
super('x402 payment is required');
|
|
31
|
+
this.name = 'PaymentRequiredError';
|
|
32
|
+
this.challenge = challenge;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class FreeReadUnavailableError extends Error {
|
|
36
|
+
constructor() {
|
|
37
|
+
super('no free result is available for this input');
|
|
38
|
+
this.name = 'FreeReadUnavailableError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class X402ReadRetryableError extends Error {
|
|
42
|
+
paymentPresented;
|
|
43
|
+
reason;
|
|
44
|
+
/**
|
|
45
|
+
* `recoverable` — the rail may have settled (transport failure, 200 without a receipt, 503 after
|
|
46
|
+
* the authorization reached it): the durable rail replays a settled body for the same
|
|
47
|
+
* authorization + input, or completes an uncharged attempt. `not_settled` — the rail answered a
|
|
48
|
+
* non-402 4xx to the request itself, before any settlement: nothing was charged, the
|
|
49
|
+
* authorization is unspent. `not_applicable` — no authorization was presented.
|
|
50
|
+
*/
|
|
51
|
+
settlementState;
|
|
52
|
+
upstreamStatus;
|
|
53
|
+
retryAfterSeconds;
|
|
54
|
+
constructor(paymentPresented, reason = 'route_incomplete', details = {}) {
|
|
55
|
+
super('the x402 route did not complete this read');
|
|
56
|
+
this.name = 'X402ReadRetryableError';
|
|
57
|
+
this.paymentPresented = paymentPresented;
|
|
58
|
+
this.reason = reason;
|
|
59
|
+
this.settlementState = paymentPresented
|
|
60
|
+
? (details.settlementState ?? 'recoverable')
|
|
61
|
+
: 'not_applicable';
|
|
62
|
+
this.upstreamStatus = details.upstreamStatus;
|
|
63
|
+
this.retryAfterSeconds = details.retryAfterSeconds;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The paid rail's PAYWALL answered 402 to a PRESENTED authorization — proven by its own v2
|
|
68
|
+
* PAYMENT-REQUIRED document in the answer, which the paywall emits only at the verify stage, before
|
|
69
|
+
* any settlement. Nothing was settled and nothing was charged. The rail's reason travels verbatim
|
|
70
|
+
* (`Payment required` = it saw no payment; `invalid_exact_evm_insufficient_balance` = transient):
|
|
71
|
+
* the same authorization may be re-presented before it expires when the cause is transient, and the
|
|
72
|
+
* fresh requirement is attached for signing again otherwise. A 402 WITHOUT that document (the SDK's
|
|
73
|
+
* post-handler settlement failure carries settlement headers and no requirement) is NOT this error —
|
|
74
|
+
* it may be settled on-chain and stays recoverable.
|
|
75
|
+
* (2026-08-28, ws-13: the hosted forward used the x402 v1 header name, the v2 paywall answered its
|
|
76
|
+
* unpaid 402, and this case was being reported as "durably owned — retry" — a false claim that made
|
|
77
|
+
* every paying MCP client loop.)
|
|
78
|
+
*/
|
|
79
|
+
export class PaymentRejectedError extends Error {
|
|
80
|
+
challenge;
|
|
81
|
+
upstreamReason;
|
|
82
|
+
constructor(challenge, upstreamReason) {
|
|
83
|
+
super('the paid rail did not accept the presented authorization');
|
|
84
|
+
this.name = 'PaymentRejectedError';
|
|
85
|
+
this.challenge = challenge;
|
|
86
|
+
this.upstreamReason = upstreamReason;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export class RequestCancelledError extends Error {
|
|
90
|
+
constructor() {
|
|
91
|
+
super('request cancelled');
|
|
92
|
+
this.name = 'RequestCancelledError';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function mapToolError(error) {
|
|
96
|
+
if (error instanceof PaymentRequiredError) {
|
|
97
|
+
return {
|
|
98
|
+
isError: true,
|
|
99
|
+
content: [{ type: 'text', text: JSON.stringify(error.challenge) }],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (error instanceof FreeReadUnavailableError) {
|
|
103
|
+
return {
|
|
104
|
+
isError: true,
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: 'text',
|
|
108
|
+
text: 'No free result is available for this input. Your free call was not spent; choose another input or use the paid x402 route.',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (error instanceof PaymentRejectedError) {
|
|
114
|
+
return {
|
|
115
|
+
isError: true,
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: 'text',
|
|
119
|
+
text: JSON.stringify({
|
|
120
|
+
...error.challenge,
|
|
121
|
+
error: 'payment_rejected',
|
|
122
|
+
status: 402,
|
|
123
|
+
retryable: true,
|
|
124
|
+
reuse_x_payment: true,
|
|
125
|
+
payment_presented: true,
|
|
126
|
+
settlement_state: 'not_settled',
|
|
127
|
+
reason: error.upstreamReason ?? 'authorization_not_accepted',
|
|
128
|
+
message: `The paid rail did not accept the presented authorization (${error.upstreamReason ?? 'authorization_not_accepted'}): this attempt was not settled and not charged. If the cause is transient, retry the exact same tool input with the same x_payment before it expires; otherwise sign the accepted requirement in payment_required and retry this tool with the new signed payload.`,
|
|
129
|
+
}),
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (error instanceof X402ReadRetryableError) {
|
|
135
|
+
const message = !error.paymentPresented
|
|
136
|
+
? 'The x402 route was unavailable before a signed authorization was presented. Request a new payment requirement before another attempt.'
|
|
137
|
+
: error.settlementState === 'not_settled'
|
|
138
|
+
? `The paid rail refused this request before any settlement (HTTP ${error.upstreamStatus ?? 'error'}): nothing was charged and the signed authorization is unspent. Retry the exact same tool input with the same x_payment, or sign a fresh requirement if it has expired.`
|
|
139
|
+
: 'Retry the exact same tool input with the same x_payment: the paid rail replays a settled response without a second charge, or completes the attempt if nothing was charged. Do not sign a new authorization for this input while this one is valid — a second authorization can settle on its own.';
|
|
140
|
+
return {
|
|
141
|
+
isError: true,
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: 'text',
|
|
145
|
+
text: JSON.stringify({
|
|
146
|
+
error: 'x402_read_retriable',
|
|
147
|
+
retryable: true,
|
|
148
|
+
reuse_x_payment: error.paymentPresented,
|
|
149
|
+
payment_presented: error.paymentPresented,
|
|
150
|
+
settlement_state: error.settlementState,
|
|
151
|
+
reason: error.reason,
|
|
152
|
+
...(error.upstreamStatus === undefined ? {} : { upstream_status: error.upstreamStatus }),
|
|
153
|
+
...(error.retryAfterSeconds === undefined
|
|
154
|
+
? {}
|
|
155
|
+
: { retry_after_seconds: error.retryAfterSeconds }),
|
|
156
|
+
message,
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (error instanceof PaymentLayerUnavailableError) {
|
|
163
|
+
return {
|
|
164
|
+
isError: true,
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: 'text',
|
|
168
|
+
text: 'Payment verification is temporarily unavailable. No intelligence payload was released.',
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (error instanceof RequestCancelledError) {
|
|
174
|
+
return {
|
|
175
|
+
isError: true,
|
|
176
|
+
content: [{ type: 'text', text: 'The intelligence request was cancelled.' }],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (error instanceof WarmReadUnavailableError) {
|
|
180
|
+
const retry = error.retryAfterSeconds
|
|
181
|
+
? ` Retry after about ${error.retryAfterSeconds} seconds.`
|
|
182
|
+
: ' Retry later.';
|
|
183
|
+
return {
|
|
184
|
+
isError: true,
|
|
185
|
+
content: [
|
|
186
|
+
{
|
|
187
|
+
type: 'text',
|
|
188
|
+
text: `Temporarily unavailable: this scanner's cached data is cold or stale. No paid refresh was triggered.${retry}`,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (error instanceof X402PaymentError) {
|
|
194
|
+
return {
|
|
195
|
+
isError: true,
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: 'The read-only x402 data call could not complete. Check the payer wallet configuration and balance, then retry.',
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
isError: true,
|
|
206
|
+
content: [
|
|
207
|
+
{
|
|
208
|
+
type: 'text',
|
|
209
|
+
text: 'otto intelligence is temporarily unavailable. No order or fund-moving action was attempted.',
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Defense-in-depth for the hosted transport: backend preparation vocabulary must
|
|
216
|
+
* never cross its MCP boundary.
|
|
217
|
+
*/
|
|
218
|
+
export function mapHostedToolError(error) {
|
|
219
|
+
return mapToolError(error instanceof WarmReadUnavailableError ? new FreeReadUnavailableError() : error);
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
export declare const LIFI_INTEGRATOR: "ottowallet";
|
|
3
|
+
export declare const DEFAULT_LIFI_FEE_BPS: 10;
|
|
4
|
+
export declare const DEFAULT_LIFI_AGGREGATE_FEE_CAP_RATE = "0.0025";
|
|
5
|
+
export declare const DEFAULT_LIFI_FEE_CONFIG_VERSION = "lifi-2026-08-16-10bps-25bps-cap-v2";
|
|
6
|
+
export declare const DEFAULT_OTTO_LIFI_FEE_RECIPIENT: `0x${string}`;
|
|
7
|
+
export declare const DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT: `0x${string}`;
|
|
8
|
+
export declare const LIFI_DIAMOND_ADDRESS: `0x${string}`;
|
|
9
|
+
/**
|
|
10
|
+
* Reviewed LI.FI FeeForwarder deployments, per chain, as a LIST — every address a fee-forwarding leg
|
|
11
|
+
* may call. Newest last. A leg calling any address NOT in its chain's list is refused by name (the
|
|
12
|
+
* drift alarm below), never accepted by shape.
|
|
13
|
+
*
|
|
14
|
+
* Review record (both entries verified against lifinance/contracts `deployments/<chain>.json` and
|
|
15
|
+
* on-chain `eth_getCode`; the v2 rollout is what turned the 2026-08-17 constructors inert until this
|
|
16
|
+
* list existed — caught 2026-08-26 by the Model-B proof kit's first dry run; that kit's runbook,
|
|
17
|
+
* `docs/0826_model_b_proof_runbook.md`, ships with the kit and records the finding):
|
|
18
|
+
* - v1 `0xC18D…75e1` (56/137/8453/42161/43114) · `0x6855…E4eD` (1) — reviewed 2026-08-16/17;
|
|
19
|
+
* still deployed (3110 bytes on Base), no longer emitted by li.quest.
|
|
20
|
+
* - v2.0.0 `0xCE40449B773a3E6E5e769ADb4e567179d4828cbd` on ALL six chains — rolled out
|
|
21
|
+
* 2026-08-21 (lifinance/contracts commit 15039b88 "roll out FeeForwarder v2.0.0 to production
|
|
22
|
+
* (EXSC-737) (#2226)"), `src/Periphery/FeeForwarder.sol` `@custom:version 2.0.0`; 3037 bytes,
|
|
23
|
+
* byte-identical on Base and Arbitrum (CREATE2); same `collectTokenFees` selector; live legs
|
|
24
|
+
* carry unchanged distributions (LI.FI platform + Otto). Reviewed 2026-08-26.
|
|
25
|
+
*
|
|
26
|
+
* Adding a deployment here is a REVIEW, not a config edit: confirm the registry entry, read the
|
|
27
|
+
* bytecode, decode one live leg's distributions, then add — never widen to a pattern.
|
|
28
|
+
*/
|
|
29
|
+
export declare const LIFI_FEE_FORWARDER_V1_MAINNET: `0x${string}`;
|
|
30
|
+
export declare const LIFI_FEE_FORWARDER_V1: `0x${string}`;
|
|
31
|
+
export declare const LIFI_FEE_FORWARDER_V2: `0x${string}`;
|
|
32
|
+
export declare const LIFI_FEE_FORWARDERS_BY_CHAIN: Readonly<Record<number, readonly Address[]>>;
|
|
33
|
+
/** The reviewed FeeForwarder list for a chain, or undefined when the chain is outside the reviewed set. */
|
|
34
|
+
export declare function reviewedLiFiFeeForwarders(chainId: number): readonly Address[] | undefined;
|
|
35
|
+
export declare function isReviewedLiFiFeeForwarder(chainId: number, address: Address): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* The drift alarm. When a fee-forwarding leg calls a contract that is not on the reviewed list, the
|
|
38
|
+
* refusal must say so BY ADDRESS and point at where to review it — a silent refusal here is how a
|
|
39
|
+
* shipped rail goes dark for days after an upstream rollout.
|
|
40
|
+
*/
|
|
41
|
+
export declare function unreviewedFeeForwarderMessage(chainId: number, callTo: Address, approveTo: Address): string;
|
|
42
|
+
/**
|
|
43
|
+
* The v1 cross-chain bridge route set, ENUMERATED — not "all LI.FI routes" (spec §7.1). The bridge
|
|
44
|
+
* validator is PolymerCCTP-specific, so a route is supported only when both legs are native Circle
|
|
45
|
+
* USDC on a chain that (a) carries a reviewed LI.FI FeeForwarder above and (b) was live-verified on
|
|
46
|
+
* 2026-08-17 to return the reviewed PolymerCCTP facet under `allowBridges=polymerStandard`. All 20
|
|
47
|
+
* ordered pairs of these five chains were probed and returned selector 0x17917a4e.
|
|
48
|
+
*
|
|
49
|
+
* BSC (56) has a reviewed FeeForwarder but is deliberately absent: it carries only Binance-Peg USDC
|
|
50
|
+
* (18 decimals), not native Circle USDC, so CCTP's burn-and-mint same-token guarantee does not hold.
|
|
51
|
+
* A request naming any chain outside this table is refused by name — the route is the named defect.
|
|
52
|
+
*/
|
|
53
|
+
export declare const LIFI_CCTP_USDC_BY_CHAIN: Readonly<Record<number, Address>>;
|
|
54
|
+
/** LI.FI bridge tool key pinned in the quote request so the reviewed facet is the only one quoted. */
|
|
55
|
+
export declare const LIFI_POLYMER_BRIDGE_TOOL: "polymerStandard";
|
|
56
|
+
/**
|
|
57
|
+
* True only for an ordered chain pair inside the enumerated set whose tokens are the pinned native
|
|
58
|
+
* USDC on each side. Same-token by construction: CCTP burns USDC on the source and mints canonical
|
|
59
|
+
* USDC on the destination, which is what makes the destination asset assertable from source calldata.
|
|
60
|
+
*/
|
|
61
|
+
export declare function isEnumeratedCctpBridgeRoute(fromChainId: number, toChainId: number, fromToken: Address, toToken: Address): boolean;
|
|
62
|
+
/** The only chain Polymarket's CTF exchanges are deployed on; also the EIP-712 domain chain id. */
|
|
63
|
+
export declare const POLYMARKET_CHAIN_ID: 137;
|
|
64
|
+
/** Collateral and conditional-token atomic precision; mirrors the SDK's COLLATERAL_TOKEN_DECIMALS. */
|
|
65
|
+
export declare const POLYMARKET_AMOUNT_DECIMALS: 6;
|
|
66
|
+
/**
|
|
67
|
+
* Otto's registered Polymarket builder code, the bytes32 that lands in the `builder` field INSIDE
|
|
68
|
+
* the signed EIP-712 order (spec §4). Same value the prediction-markets agent already stamps
|
|
69
|
+
* (`services/prediction-markets-agent/src/lib/polymarket-contracts.ts`), live-verified 2026-08-17
|
|
70
|
+
* against `GET https://clob.polymarket.com/fees/builder-fees/<code>`: registered, `enabled: true`,
|
|
71
|
+
* both rates 0 bps.
|
|
72
|
+
*/
|
|
73
|
+
export declare const OTTO_PM_BUILDER_CODE: "0x910082696e647acd3f9486c1a6366840c780a26db5b6aa781892ebf69c48508c";
|
|
74
|
+
export declare const PM_BYTES32_ZERO: `0x${string}`;
|
|
75
|
+
/**
|
|
76
|
+
* RULED builder-attribution values: 0 bps maker, 0 bps taker (`docs/0808_model_b_scope_and_verdict.md`
|
|
77
|
+
* §0.3 — "registered and enabled, with both fee rates at 0 bps"; spec §8 records PM as 0/0 today).
|
|
78
|
+
*
|
|
79
|
+
* The *flip* to a non-zero taker rate is an OPEN Founder item (spec §13, waits on the
|
|
80
|
+
* `/builder/trades` volume pull) and is a Polymarket UI settings change, not a code change. So the
|
|
81
|
+
* constructor never guesses a rate: it stamps the ruled value under a config version and refuses
|
|
82
|
+
* with `POLYMARKET_BUILDER_FEE_UNRULED` when the live registry no longer matches it. Bumping this
|
|
83
|
+
* constant plus the config version below is the one place a ruled flip lands.
|
|
84
|
+
*/
|
|
85
|
+
export declare const PM_RULED_BUILDER_FEE_BPS: Readonly<{
|
|
86
|
+
maker: number;
|
|
87
|
+
taker: number;
|
|
88
|
+
}>;
|
|
89
|
+
export declare const DEFAULT_PM_BUILDER_FEE_CONFIG_VERSION = "pm-2026-08-17-builder-0bps-0bps-v1";
|
|
90
|
+
export declare const POLYMARKET_TICK_SIZES: readonly ["0.1", "0.01", "0.001", "0.0001"];
|
|
91
|
+
export type PolymarketTickSize = (typeof POLYMARKET_TICK_SIZES)[number];
|
|
92
|
+
export interface PolymarketRounding {
|
|
93
|
+
readonly price: number;
|
|
94
|
+
readonly size: number;
|
|
95
|
+
readonly amount: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Decimal precision the SDK rounds price, size, and derived amounts to, per tick size. The SDK's own
|
|
99
|
+
* `ROUNDING_CONFIG` is not reachable from the package root, so it is pinned here and *proved* rather
|
|
100
|
+
* than trusted: the live-captured `polymarket-sdk-created-order-*.json` fixtures are the SDK's real
|
|
101
|
+
* output for a real market, and the amount assertions run against them.
|
|
102
|
+
*/
|
|
103
|
+
export declare const POLYMARKET_ROUNDING_BY_TICK_SIZE: Readonly<Record<PolymarketTickSize, PolymarketRounding>>;
|
|
104
|
+
export declare function isPolymarketTickSize(value: string): value is PolymarketTickSize;
|
|
105
|
+
export declare const POLYMARKET_EIP712_DOMAIN_NAME: "Polymarket CTF Exchange";
|
|
106
|
+
export declare const POLYMARKET_EIP712_DOMAIN_VERSION: "2";
|
|
107
|
+
export declare const POLYMARKET_ORDER_PRIMARY_TYPE: "Order";
|
|
108
|
+
/**
|
|
109
|
+
* The CTF Exchange V2 `Order` struct, pinned field-for-field and in order. This is the shape the
|
|
110
|
+
* user's signer hashes and the exchange re-hashes, so `builder` meaning "builder" depends on this
|
|
111
|
+
* array being exactly right — a reordered or retyped field silently changes what every other field
|
|
112
|
+
* commits to. It is asserted against the live-captured SDK payload rather than trusted: the
|
|
113
|
+
* committed fixtures carry a real signature over a real market, and the recovery check in
|
|
114
|
+
* `polymarket-order-assertions.ts` fails if this array is off by one entry.
|
|
115
|
+
*
|
|
116
|
+
* `expiration` and `taker` are deliberately absent: the V2 exchange does not sign them
|
|
117
|
+
* (`@polymarket/clob-client-v2` ctfExchangeV2TypedData.js), which is why the artifact carries
|
|
118
|
+
* `expiration` as an unsigned POST-time field.
|
|
119
|
+
*/
|
|
120
|
+
export declare const POLYMARKET_ORDER_V2_EIP712_FIELDS: readonly {
|
|
121
|
+
readonly name: string;
|
|
122
|
+
readonly type: string;
|
|
123
|
+
}[];
|
|
124
|
+
/**
|
|
125
|
+
* Hyperliquid requires the EIP-712 domain `chainId` of an L1 action to be exactly 1337, regardless
|
|
126
|
+
* of which EVM chain the signer's wallet is connected to (`@nktkas/hyperliquid` signing/mod.js:175 —
|
|
127
|
+
* "hyperliquid requires chainId to be 1337"). It is a protocol constant, not a network the artifact
|
|
128
|
+
* executes on, which is why the account profile must declare it and an adapter re-checks it.
|
|
129
|
+
*/
|
|
130
|
+
export declare const HYPERLIQUID_L1_SIGNATURE_CHAIN_ID: 1337;
|
|
131
|
+
export declare const HYPERLIQUID_EIP712_DOMAIN_NAME: "Exchange";
|
|
132
|
+
export declare const HYPERLIQUID_EIP712_DOMAIN_VERSION: "1";
|
|
133
|
+
export declare const HYPERLIQUID_AGENT_PRIMARY_TYPE: "Agent";
|
|
134
|
+
/** The L1-action domain has no verifying contract; the venue pins the zero address. */
|
|
135
|
+
export declare const HYPERLIQUID_EIP712_VERIFYING_CONTRACT: "0x0000000000000000000000000000000000000000";
|
|
136
|
+
/** `"a"` on mainnet, `"b"` on testnet (signing/mod.js:186). v1 constructs mainnet actions only. */
|
|
137
|
+
export declare const HYPERLIQUID_MAINNET_AGENT_SOURCE: "a";
|
|
138
|
+
/**
|
|
139
|
+
* The `Agent` struct the user's signer hashes. Pinned field-for-field and in order: the whole action
|
|
140
|
+
* enters the signature only through `connectionId`, so a reordered or retyped field silently changes
|
|
141
|
+
* what the signature commits to. Asserted against the SDK's own types in the live-captured fixture.
|
|
142
|
+
*/
|
|
143
|
+
export declare const HYPERLIQUID_AGENT_EIP712_FIELDS: readonly {
|
|
144
|
+
readonly name: string;
|
|
145
|
+
readonly type: string;
|
|
146
|
+
}[];
|
|
147
|
+
/**
|
|
148
|
+
* `connectionId = keccak256(msgpack(action) ++ nonce_u64_be ++ vault_marker[++ vault_address] ++
|
|
149
|
+
* expires_marker[++ expiresAfter_u64_be])` (`signing/mod.js:88-104`). msgpack encodes object keys in
|
|
150
|
+
* INSERTION order, so the key order below is part of the signed bytes, not a formatting choice: an
|
|
151
|
+
* action carrying the same values in a different key order hashes to a different digest and the venue
|
|
152
|
+
* rejects the signature. The orders below are the exact orders the SDK's own valibot normalizer
|
|
153
|
+
* emits, live-proved against the captured SDK action in `hyperliquid-fixture-capture.ts`.
|
|
154
|
+
*/
|
|
155
|
+
export declare const HYPERLIQUID_ACTION_KEY_ORDER: readonly string[];
|
|
156
|
+
/** `c` (client order id) is appended only when the order carries one. */
|
|
157
|
+
export declare const HYPERLIQUID_ORDER_KEY_ORDER: readonly string[];
|
|
158
|
+
export declare const HYPERLIQUID_TRIGGER_KEY_ORDER: readonly string[];
|
|
159
|
+
/** Perp price precision ceiling: at most `MAX_DECIMALS - szDecimals` fractional digits. */
|
|
160
|
+
export declare const HYPERLIQUID_PERP_MAX_DECIMALS: 6;
|
|
161
|
+
/** …and at most 5 significant digits, unless the price is an integer (always accepted). */
|
|
162
|
+
export declare const HYPERLIQUID_PRICE_MAX_SIGNIFICANT_DIGITS: 5;
|
|
163
|
+
/**
|
|
164
|
+
* Venue time-in-force values a caller may name. `FrontendMarket` and `LiquidationMarket` exist in the
|
|
165
|
+
* venue schema but are Hyperliquid-UI-internal, so they are not offered. A market fill is expressed
|
|
166
|
+
* as `Ioc` at the caller's OWN aggressive limit price: this constructor never invents a price from a
|
|
167
|
+
* mark read, which is what the trade-execution agent's market path does (`hyperliquid-client.ts:1785`
|
|
168
|
+
* — mark × 1.05) and what would let an artifact signed minutes later fill far from the caller's bound.
|
|
169
|
+
*/
|
|
170
|
+
export declare const HYPERLIQUID_TIME_IN_FORCE: readonly ["Gtc", "Ioc", "Alo"];
|
|
171
|
+
export type HyperliquidTimeInForce = (typeof HYPERLIQUID_TIME_IN_FORCE)[number];
|
|
172
|
+
/** `na` for a lone entry; `normalTpsl` binds fixed-size TP/SL legs to the entry (client.ts:1876). */
|
|
173
|
+
export declare const HYPERLIQUID_ORDER_GROUPINGS: readonly ["na", "normalTpsl"];
|
|
174
|
+
export type HyperliquidOrderGrouping = (typeof HYPERLIQUID_ORDER_GROUPINGS)[number];
|
|
175
|
+
/**
|
|
176
|
+
* RULED builder-attribution state (Founder, 2026-08-17; spec §7.2/§12): the Hyperliquid builder field
|
|
177
|
+
* is **spec'd and VALUELESS** until the Step-0 `approveBuilderFee` reversal ships. So the constructed
|
|
178
|
+
* action carries NO `builder` key — an absent key is the only encoding the venue reads as "no builder
|
|
179
|
+
* fee", and inventing an address or a tenths-of-a-bp value here would stamp an unruled fee inside a
|
|
180
|
+
* digest the user signs. The exact `{b, f}` assert lands in a follow-up fold; until then the envelope
|
|
181
|
+
* carries this machine-readable pending marker and the constructor asserts the key's ABSENCE.
|
|
182
|
+
*/
|
|
183
|
+
export declare const HL_BUILDER_ATTRIBUTION_STATUS: "pending_step0_builder_fee_approval";
|
|
184
|
+
export declare const DEFAULT_HL_BUILDER_FEE_CONFIG_VERSION = "hl-2026-08-17-builder-unwired-v1";
|
|
185
|
+
export interface HyperliquidPerpAsset {
|
|
186
|
+
readonly name: string;
|
|
187
|
+
/** Index in the live `meta.universe`; the venue's `a` field. Never a value this repo invents. */
|
|
188
|
+
readonly assetId: number;
|
|
189
|
+
readonly szDecimals: number;
|
|
190
|
+
readonly maxLeverage: number;
|
|
191
|
+
readonly isDelisted: boolean;
|
|
192
|
+
}
|
|
193
|
+
export interface DecimalRate {
|
|
194
|
+
readonly decimal: string;
|
|
195
|
+
readonly numerator: bigint;
|
|
196
|
+
readonly denominator: bigint;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Prepare-only custody boundary: this runtime never receives or retains caller wallet keys,
|
|
200
|
+
* signing secrets, or transaction signatures. It may retain an operational LI.FI quote-service
|
|
201
|
+
* credential, which is isolated from caller schemas and execution envelopes.
|
|
202
|
+
*/
|
|
203
|
+
export interface ExecutionServiceConfig {
|
|
204
|
+
readonly artifactTtlMs: number;
|
|
205
|
+
/** Quote-service authentication only; never caller wallet/signing material or envelope data. */
|
|
206
|
+
readonly lifiApiKey?: string;
|
|
207
|
+
readonly lifiBaseUrl: string;
|
|
208
|
+
readonly lifiAggregateFeeCap: DecimalRate;
|
|
209
|
+
readonly lifiAllowedFeeRecipients: readonly Address[];
|
|
210
|
+
readonly lifiFee: DecimalRate;
|
|
211
|
+
readonly lifiFeeConfigVersion: string;
|
|
212
|
+
readonly lifiFeeRecipient: Address;
|
|
213
|
+
readonly lifiTimeoutMs: number;
|
|
214
|
+
readonly polymarketClobBaseUrl: string;
|
|
215
|
+
/** The bytes32 stamped into the signed order's `builder` field; never caller-supplied. */
|
|
216
|
+
readonly polymarketBuilderCode: Hex;
|
|
217
|
+
readonly polymarketBuilderFeeConfigVersion: string;
|
|
218
|
+
/** The ruled rates the live builder registry must still match; never used as a fee to charge. */
|
|
219
|
+
readonly polymarketRuledBuilderFeeBps: Readonly<{
|
|
220
|
+
maker: number;
|
|
221
|
+
taker: number;
|
|
222
|
+
}>;
|
|
223
|
+
readonly polymarketTimeoutMs: number;
|
|
224
|
+
readonly hyperliquidApiBaseUrl: string;
|
|
225
|
+
readonly hyperliquidBuilderFeeConfigVersion: string;
|
|
226
|
+
/** The ruled pending state of the builder field; never a fee this constructor may charge. */
|
|
227
|
+
readonly hyperliquidBuilderAttributionStatus: typeof HL_BUILDER_ATTRIBUTION_STATUS;
|
|
228
|
+
readonly hyperliquidTimeoutMs: number;
|
|
229
|
+
}
|
|
230
|
+
export declare function parseDecimalRate(raw: string, name?: string): DecimalRate;
|
|
231
|
+
export declare function loadExecutionServiceConfig(env?: NodeJS.ProcessEnv): ExecutionServiceConfig | undefined;
|
|
232
|
+
export declare function attributedFeeAmount(amount: bigint, rate: DecimalRate): bigint;
|