otto-execute 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 +151 -0
- package/dist/cdp-signer.d.ts +2 -0
- package/dist/cdp-signer.js +3 -0
- package/dist/cdp.d.ts +7 -0
- package/dist/cdp.js +8 -0
- package/dist/chain.d.ts +2 -0
- package/dist/chain.js +3 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +400 -0
- package/dist/delegate.d.ts +117 -0
- package/dist/delegate.js +394 -0
- package/dist/eoa-signer.d.ts +94 -0
- package/dist/eoa-signer.js +289 -0
- package/dist/erc20.d.ts +2 -0
- package/dist/erc20.js +3 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +19 -0
- package/dist/lifi-decode.d.ts +2 -0
- package/dist/lifi-decode.js +3 -0
- package/dist/mutations.d.ts +55 -0
- package/dist/mutations.js +363 -0
- package/dist/refusal.d.ts +2 -0
- package/dist/refusal.js +3 -0
- package/dist/vendor/otto-intel-mcp/VENDORED.json +52 -0
- package/dist/vendor/otto-intel-mcp/adapter/cdp-signer.d.ts +133 -0
- package/dist/vendor/otto-intel-mcp/adapter/cdp-signer.js +356 -0
- package/dist/vendor/otto-intel-mcp/adapter/chain.d.ts +36 -0
- package/dist/vendor/otto-intel-mcp/adapter/chain.js +65 -0
- package/dist/vendor/otto-intel-mcp/adapter/erc20.d.ts +39 -0
- package/dist/vendor/otto-intel-mcp/adapter/erc20.js +17 -0
- package/dist/vendor/otto-intel-mcp/adapter/lifi-decode.d.ts +52 -0
- package/dist/vendor/otto-intel-mcp/adapter/lifi-decode.js +149 -0
- package/dist/vendor/otto-intel-mcp/adapter/refusal.d.ts +21 -0
- package/dist/vendor/otto-intel-mcp/adapter/refusal.js +55 -0
- package/dist/vendor/otto-intel-mcp/adapter/sent-step.d.ts +15 -0
- package/dist/vendor/otto-intel-mcp/adapter/sent-step.js +6 -0
- package/dist/vendor/otto-intel-mcp/adapter/verify.d.ts +149 -0
- package/dist/vendor/otto-intel-mcp/adapter/verify.js +432 -0
- package/dist/vendor/otto-intel-mcp/adapter-cdp-index.d.ts +6 -0
- package/dist/vendor/otto-intel-mcp/adapter-cdp-index.js +7 -0
- package/dist/vendor/otto-intel-mcp/adapter-index.d.ts +15 -0
- package/dist/vendor/otto-intel-mcp/adapter-index.js +15 -0
- package/dist/vendor/otto-intel-mcp/artifact-id.d.ts +16 -0
- package/dist/vendor/otto-intel-mcp/artifact-id.js +60 -0
- package/dist/vendor/otto-intel-mcp/execution-config.d.ts +232 -0
- package/dist/vendor/otto-intel-mcp/execution-config.js +443 -0
- package/dist/vendor/otto-intel-mcp/execution-delegated-definition.d.ts +165 -0
- package/dist/vendor/otto-intel-mcp/execution-delegated-definition.js +116 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-admin-definition.d.ts +208 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-admin-definition.js +170 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-policy.d.ts +257 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-policy.js +279 -0
- package/dist/vendor/otto-intel-mcp/execution-errors.d.ts +9 -0
- package/dist/vendor/otto-intel-mcp/execution-errors.js +134 -0
- package/dist/vendor/otto-intel-mcp/execution-index.d.ts +17 -0
- package/dist/vendor/otto-intel-mcp/execution-index.js +16 -0
- package/dist/vendor/otto-intel-mcp/execution-intent.d.ts +14 -0
- package/dist/vendor/otto-intel-mcp/execution-intent.js +36 -0
- package/dist/vendor/otto-intel-mcp/execution-tool-definitions.d.ts +1103 -0
- package/dist/vendor/otto-intel-mcp/execution-tool-definitions.js +1051 -0
- package/dist/vendor/otto-intel-mcp/execution-types.d.ts +274 -0
- package/dist/vendor/otto-intel-mcp/execution-types.js +157 -0
- package/dist/verify.d.ts +2 -0
- package/dist/verify.js +3 -0
- package/dist/x402-table.d.ts +99 -0
- package/dist/x402-table.js +221 -0
- package/package.json +97 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { getAddress } from 'viem';
|
|
2
|
+
import { MAX_ARTIFACT_TTL_MS } from './artifact-id.js';
|
|
3
|
+
export const LIFI_INTEGRATOR = 'ottowallet';
|
|
4
|
+
export const DEFAULT_LIFI_FEE_BPS = 10;
|
|
5
|
+
export const DEFAULT_LIFI_AGGREGATE_FEE_CAP_RATE = '0.0025';
|
|
6
|
+
export const DEFAULT_LIFI_FEE_CONFIG_VERSION = 'lifi-2026-08-16-10bps-25bps-cap-v2';
|
|
7
|
+
export const DEFAULT_OTTO_LIFI_FEE_RECIPIENT = getAddress('0x0E84dDEdAaE6A779c462C22a59F301EC31B6b808');
|
|
8
|
+
export const DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT = getAddress('0xC06eBbeFD94032b85424D51906e2a335Efae264b');
|
|
9
|
+
export const LIFI_DIAMOND_ADDRESS = getAddress('0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE');
|
|
10
|
+
/**
|
|
11
|
+
* Reviewed LI.FI FeeForwarder deployments, per chain, as a LIST — every address a fee-forwarding leg
|
|
12
|
+
* may call. Newest last. A leg calling any address NOT in its chain's list is refused by name (the
|
|
13
|
+
* drift alarm below), never accepted by shape.
|
|
14
|
+
*
|
|
15
|
+
* Review record (both entries verified against lifinance/contracts `deployments/<chain>.json` and
|
|
16
|
+
* on-chain `eth_getCode`; the v2 rollout is what turned the 2026-08-17 constructors inert until this
|
|
17
|
+
* list existed — caught 2026-08-26 by the Model-B proof kit's first dry run; that kit's runbook,
|
|
18
|
+
* `docs/0826_model_b_proof_runbook.md`, ships with the kit and records the finding):
|
|
19
|
+
* - v1 `0xC18D…75e1` (56/137/8453/42161/43114) · `0x6855…E4eD` (1) — reviewed 2026-08-16/17;
|
|
20
|
+
* still deployed (3110 bytes on Base), no longer emitted by li.quest.
|
|
21
|
+
* - v2.0.0 `0xCE40449B773a3E6E5e769ADb4e567179d4828cbd` on ALL six chains — rolled out
|
|
22
|
+
* 2026-08-21 (lifinance/contracts commit 15039b88 "roll out FeeForwarder v2.0.0 to production
|
|
23
|
+
* (EXSC-737) (#2226)"), `src/Periphery/FeeForwarder.sol` `@custom:version 2.0.0`; 3037 bytes,
|
|
24
|
+
* byte-identical on Base and Arbitrum (CREATE2); same `collectTokenFees` selector; live legs
|
|
25
|
+
* carry unchanged distributions (LI.FI platform + Otto). Reviewed 2026-08-26.
|
|
26
|
+
*
|
|
27
|
+
* Adding a deployment here is a REVIEW, not a config edit: confirm the registry entry, read the
|
|
28
|
+
* bytecode, decode one live leg's distributions, then add — never widen to a pattern.
|
|
29
|
+
*/
|
|
30
|
+
export const LIFI_FEE_FORWARDER_V1_MAINNET = getAddress('0x685527c551Cc40ce1f1C9818cD8683307076E4eD');
|
|
31
|
+
export const LIFI_FEE_FORWARDER_V1 = getAddress('0xC18D9E84b8687A2645447A61e52c455Dac1675e1');
|
|
32
|
+
export const LIFI_FEE_FORWARDER_V2 = getAddress('0xCE40449B773a3E6E5e769ADb4e567179d4828cbd');
|
|
33
|
+
export const LIFI_FEE_FORWARDERS_BY_CHAIN = Object.freeze({
|
|
34
|
+
1: Object.freeze([LIFI_FEE_FORWARDER_V1_MAINNET, LIFI_FEE_FORWARDER_V2]),
|
|
35
|
+
56: Object.freeze([LIFI_FEE_FORWARDER_V1, LIFI_FEE_FORWARDER_V2]),
|
|
36
|
+
137: Object.freeze([LIFI_FEE_FORWARDER_V1, LIFI_FEE_FORWARDER_V2]),
|
|
37
|
+
8453: Object.freeze([LIFI_FEE_FORWARDER_V1, LIFI_FEE_FORWARDER_V2]),
|
|
38
|
+
42161: Object.freeze([LIFI_FEE_FORWARDER_V1, LIFI_FEE_FORWARDER_V2]),
|
|
39
|
+
43114: Object.freeze([LIFI_FEE_FORWARDER_V1, LIFI_FEE_FORWARDER_V2]),
|
|
40
|
+
});
|
|
41
|
+
/** The reviewed FeeForwarder list for a chain, or undefined when the chain is outside the reviewed set. */
|
|
42
|
+
export function reviewedLiFiFeeForwarders(chainId) {
|
|
43
|
+
return LIFI_FEE_FORWARDERS_BY_CHAIN[chainId];
|
|
44
|
+
}
|
|
45
|
+
export function isReviewedLiFiFeeForwarder(chainId, address) {
|
|
46
|
+
const reviewed = LIFI_FEE_FORWARDERS_BY_CHAIN[chainId];
|
|
47
|
+
if (!reviewed)
|
|
48
|
+
return false;
|
|
49
|
+
const candidate = getAddress(address);
|
|
50
|
+
return reviewed.some((entry) => entry === candidate);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The drift alarm. When a fee-forwarding leg calls a contract that is not on the reviewed list, the
|
|
54
|
+
* refusal must say so BY ADDRESS and point at where to review it — a silent refusal here is how a
|
|
55
|
+
* shipped rail goes dark for days after an upstream rollout.
|
|
56
|
+
*/
|
|
57
|
+
export function unreviewedFeeForwarderMessage(chainId, callTo, approveTo) {
|
|
58
|
+
const reviewed = LIFI_FEE_FORWARDERS_BY_CHAIN[chainId] ?? [];
|
|
59
|
+
return (`LI.FI fee-forwarding leg calls ${getAddress(callTo)} (approveTo ${getAddress(approveTo)}), which is not a ` +
|
|
60
|
+
`reviewed FeeForwarder deployment on chain ${chainId} (reviewed: ${reviewed.join(', ') || 'none'}). ` +
|
|
61
|
+
`If LI.FI rolled out a new FeeForwarder, review it against lifinance/contracts deployments/<chain>.json ` +
|
|
62
|
+
`and on-chain bytecode, then add it to LIFI_FEE_FORWARDERS_BY_CHAIN — never accept by shape.`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The v1 cross-chain bridge route set, ENUMERATED — not "all LI.FI routes" (spec §7.1). The bridge
|
|
66
|
+
* validator is PolymerCCTP-specific, so a route is supported only when both legs are native Circle
|
|
67
|
+
* USDC on a chain that (a) carries a reviewed LI.FI FeeForwarder above and (b) was live-verified on
|
|
68
|
+
* 2026-08-17 to return the reviewed PolymerCCTP facet under `allowBridges=polymerStandard`. All 20
|
|
69
|
+
* ordered pairs of these five chains were probed and returned selector 0x17917a4e.
|
|
70
|
+
*
|
|
71
|
+
* BSC (56) has a reviewed FeeForwarder but is deliberately absent: it carries only Binance-Peg USDC
|
|
72
|
+
* (18 decimals), not native Circle USDC, so CCTP's burn-and-mint same-token guarantee does not hold.
|
|
73
|
+
* A request naming any chain outside this table is refused by name — the route is the named defect.
|
|
74
|
+
*/
|
|
75
|
+
export const LIFI_CCTP_USDC_BY_CHAIN = Object.freeze({
|
|
76
|
+
1: getAddress('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
|
|
77
|
+
137: getAddress('0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359'),
|
|
78
|
+
8453: getAddress('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'),
|
|
79
|
+
42161: getAddress('0xaf88d065e77c8cC2239327C5EDb3A432268e5831'),
|
|
80
|
+
43114: getAddress('0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E'),
|
|
81
|
+
});
|
|
82
|
+
/** LI.FI bridge tool key pinned in the quote request so the reviewed facet is the only one quoted. */
|
|
83
|
+
export const LIFI_POLYMER_BRIDGE_TOOL = 'polymerStandard';
|
|
84
|
+
/**
|
|
85
|
+
* True only for an ordered chain pair inside the enumerated set whose tokens are the pinned native
|
|
86
|
+
* USDC on each side. Same-token by construction: CCTP burns USDC on the source and mints canonical
|
|
87
|
+
* USDC on the destination, which is what makes the destination asset assertable from source calldata.
|
|
88
|
+
*/
|
|
89
|
+
export function isEnumeratedCctpBridgeRoute(fromChainId, toChainId, fromToken, toToken) {
|
|
90
|
+
if (fromChainId === toChainId)
|
|
91
|
+
return false;
|
|
92
|
+
const source = LIFI_CCTP_USDC_BY_CHAIN[fromChainId];
|
|
93
|
+
const destination = LIFI_CCTP_USDC_BY_CHAIN[toChainId];
|
|
94
|
+
if (!source || !destination)
|
|
95
|
+
return false;
|
|
96
|
+
return getAddress(fromToken) === source && getAddress(toToken) === destination;
|
|
97
|
+
}
|
|
98
|
+
// =============================================================================
|
|
99
|
+
// Polymarket CLOB V2 order construction (spec §7.3)
|
|
100
|
+
// =============================================================================
|
|
101
|
+
/** The only chain Polymarket's CTF exchanges are deployed on; also the EIP-712 domain chain id. */
|
|
102
|
+
export const POLYMARKET_CHAIN_ID = 137;
|
|
103
|
+
/** Collateral and conditional-token atomic precision; mirrors the SDK's COLLATERAL_TOKEN_DECIMALS. */
|
|
104
|
+
export const POLYMARKET_AMOUNT_DECIMALS = 6;
|
|
105
|
+
/**
|
|
106
|
+
* Otto's registered Polymarket builder code, the bytes32 that lands in the `builder` field INSIDE
|
|
107
|
+
* the signed EIP-712 order (spec §4). Same value the prediction-markets agent already stamps
|
|
108
|
+
* (`services/prediction-markets-agent/src/lib/polymarket-contracts.ts`), live-verified 2026-08-17
|
|
109
|
+
* against `GET https://clob.polymarket.com/fees/builder-fees/<code>`: registered, `enabled: true`,
|
|
110
|
+
* both rates 0 bps.
|
|
111
|
+
*/
|
|
112
|
+
export const OTTO_PM_BUILDER_CODE = '0x910082696e647acd3f9486c1a6366840c780a26db5b6aa781892ebf69c48508c';
|
|
113
|
+
export const PM_BYTES32_ZERO = `0x${'00'.repeat(32)}`;
|
|
114
|
+
/**
|
|
115
|
+
* RULED builder-attribution values: 0 bps maker, 0 bps taker (`docs/0808_model_b_scope_and_verdict.md`
|
|
116
|
+
* §0.3 — "registered and enabled, with both fee rates at 0 bps"; spec §8 records PM as 0/0 today).
|
|
117
|
+
*
|
|
118
|
+
* The *flip* to a non-zero taker rate is an OPEN Founder item (spec §13, waits on the
|
|
119
|
+
* `/builder/trades` volume pull) and is a Polymarket UI settings change, not a code change. So the
|
|
120
|
+
* constructor never guesses a rate: it stamps the ruled value under a config version and refuses
|
|
121
|
+
* with `POLYMARKET_BUILDER_FEE_UNRULED` when the live registry no longer matches it. Bumping this
|
|
122
|
+
* constant plus the config version below is the one place a ruled flip lands.
|
|
123
|
+
*/
|
|
124
|
+
export const PM_RULED_BUILDER_FEE_BPS = Object.freeze({
|
|
125
|
+
maker: 0,
|
|
126
|
+
taker: 0,
|
|
127
|
+
});
|
|
128
|
+
export const DEFAULT_PM_BUILDER_FEE_CONFIG_VERSION = 'pm-2026-08-17-builder-0bps-0bps-v1';
|
|
129
|
+
export const POLYMARKET_TICK_SIZES = ['0.1', '0.01', '0.001', '0.0001'];
|
|
130
|
+
/**
|
|
131
|
+
* Decimal precision the SDK rounds price, size, and derived amounts to, per tick size. The SDK's own
|
|
132
|
+
* `ROUNDING_CONFIG` is not reachable from the package root, so it is pinned here and *proved* rather
|
|
133
|
+
* than trusted: the live-captured `polymarket-sdk-created-order-*.json` fixtures are the SDK's real
|
|
134
|
+
* output for a real market, and the amount assertions run against them.
|
|
135
|
+
*/
|
|
136
|
+
export const POLYMARKET_ROUNDING_BY_TICK_SIZE = Object.freeze({
|
|
137
|
+
'0.1': Object.freeze({ price: 1, size: 2, amount: 3 }),
|
|
138
|
+
'0.01': Object.freeze({ price: 2, size: 2, amount: 4 }),
|
|
139
|
+
'0.001': Object.freeze({ price: 3, size: 2, amount: 5 }),
|
|
140
|
+
'0.0001': Object.freeze({ price: 4, size: 2, amount: 6 }),
|
|
141
|
+
});
|
|
142
|
+
export function isPolymarketTickSize(value) {
|
|
143
|
+
return POLYMARKET_TICK_SIZES.includes(value);
|
|
144
|
+
}
|
|
145
|
+
export const POLYMARKET_EIP712_DOMAIN_NAME = 'Polymarket CTF Exchange';
|
|
146
|
+
export const POLYMARKET_EIP712_DOMAIN_VERSION = '2';
|
|
147
|
+
export const POLYMARKET_ORDER_PRIMARY_TYPE = 'Order';
|
|
148
|
+
/**
|
|
149
|
+
* The CTF Exchange V2 `Order` struct, pinned field-for-field and in order. This is the shape the
|
|
150
|
+
* user's signer hashes and the exchange re-hashes, so `builder` meaning "builder" depends on this
|
|
151
|
+
* array being exactly right — a reordered or retyped field silently changes what every other field
|
|
152
|
+
* commits to. It is asserted against the live-captured SDK payload rather than trusted: the
|
|
153
|
+
* committed fixtures carry a real signature over a real market, and the recovery check in
|
|
154
|
+
* `polymarket-order-assertions.ts` fails if this array is off by one entry.
|
|
155
|
+
*
|
|
156
|
+
* `expiration` and `taker` are deliberately absent: the V2 exchange does not sign them
|
|
157
|
+
* (`@polymarket/clob-client-v2` ctfExchangeV2TypedData.js), which is why the artifact carries
|
|
158
|
+
* `expiration` as an unsigned POST-time field.
|
|
159
|
+
*/
|
|
160
|
+
export const POLYMARKET_ORDER_V2_EIP712_FIELDS = Object.freeze([
|
|
161
|
+
Object.freeze({ name: 'salt', type: 'uint256' }),
|
|
162
|
+
Object.freeze({ name: 'maker', type: 'address' }),
|
|
163
|
+
Object.freeze({ name: 'signer', type: 'address' }),
|
|
164
|
+
Object.freeze({ name: 'tokenId', type: 'uint256' }),
|
|
165
|
+
Object.freeze({ name: 'makerAmount', type: 'uint256' }),
|
|
166
|
+
Object.freeze({ name: 'takerAmount', type: 'uint256' }),
|
|
167
|
+
Object.freeze({ name: 'side', type: 'uint8' }),
|
|
168
|
+
Object.freeze({ name: 'signatureType', type: 'uint8' }),
|
|
169
|
+
Object.freeze({ name: 'timestamp', type: 'uint256' }),
|
|
170
|
+
Object.freeze({ name: 'metadata', type: 'bytes32' }),
|
|
171
|
+
Object.freeze({ name: 'builder', type: 'bytes32' }),
|
|
172
|
+
]);
|
|
173
|
+
// =============================================================================
|
|
174
|
+
// Hyperliquid perp L1 action construction (spec §7.2)
|
|
175
|
+
// =============================================================================
|
|
176
|
+
/**
|
|
177
|
+
* Hyperliquid requires the EIP-712 domain `chainId` of an L1 action to be exactly 1337, regardless
|
|
178
|
+
* of which EVM chain the signer's wallet is connected to (`@nktkas/hyperliquid` signing/mod.js:175 —
|
|
179
|
+
* "hyperliquid requires chainId to be 1337"). It is a protocol constant, not a network the artifact
|
|
180
|
+
* executes on, which is why the account profile must declare it and an adapter re-checks it.
|
|
181
|
+
*/
|
|
182
|
+
export const HYPERLIQUID_L1_SIGNATURE_CHAIN_ID = 1337;
|
|
183
|
+
export const HYPERLIQUID_EIP712_DOMAIN_NAME = 'Exchange';
|
|
184
|
+
export const HYPERLIQUID_EIP712_DOMAIN_VERSION = '1';
|
|
185
|
+
export const HYPERLIQUID_AGENT_PRIMARY_TYPE = 'Agent';
|
|
186
|
+
/** The L1-action domain has no verifying contract; the venue pins the zero address. */
|
|
187
|
+
export const HYPERLIQUID_EIP712_VERIFYING_CONTRACT = '0x0000000000000000000000000000000000000000';
|
|
188
|
+
/** `"a"` on mainnet, `"b"` on testnet (signing/mod.js:186). v1 constructs mainnet actions only. */
|
|
189
|
+
export const HYPERLIQUID_MAINNET_AGENT_SOURCE = 'a';
|
|
190
|
+
/**
|
|
191
|
+
* The `Agent` struct the user's signer hashes. Pinned field-for-field and in order: the whole action
|
|
192
|
+
* enters the signature only through `connectionId`, so a reordered or retyped field silently changes
|
|
193
|
+
* what the signature commits to. Asserted against the SDK's own types in the live-captured fixture.
|
|
194
|
+
*/
|
|
195
|
+
export const HYPERLIQUID_AGENT_EIP712_FIELDS = Object.freeze([
|
|
196
|
+
Object.freeze({ name: 'source', type: 'string' }),
|
|
197
|
+
Object.freeze({ name: 'connectionId', type: 'bytes32' }),
|
|
198
|
+
]);
|
|
199
|
+
/**
|
|
200
|
+
* `connectionId = keccak256(msgpack(action) ++ nonce_u64_be ++ vault_marker[++ vault_address] ++
|
|
201
|
+
* expires_marker[++ expiresAfter_u64_be])` (`signing/mod.js:88-104`). msgpack encodes object keys in
|
|
202
|
+
* INSERTION order, so the key order below is part of the signed bytes, not a formatting choice: an
|
|
203
|
+
* action carrying the same values in a different key order hashes to a different digest and the venue
|
|
204
|
+
* rejects the signature. The orders below are the exact orders the SDK's own valibot normalizer
|
|
205
|
+
* emits, live-proved against the captured SDK action in `hyperliquid-fixture-capture.ts`.
|
|
206
|
+
*/
|
|
207
|
+
export const HYPERLIQUID_ACTION_KEY_ORDER = Object.freeze([
|
|
208
|
+
'type',
|
|
209
|
+
'orders',
|
|
210
|
+
'grouping',
|
|
211
|
+
]);
|
|
212
|
+
/** `c` (client order id) is appended only when the order carries one. */
|
|
213
|
+
export const HYPERLIQUID_ORDER_KEY_ORDER = Object.freeze([
|
|
214
|
+
'a',
|
|
215
|
+
'b',
|
|
216
|
+
'p',
|
|
217
|
+
's',
|
|
218
|
+
'r',
|
|
219
|
+
't',
|
|
220
|
+
]);
|
|
221
|
+
export const HYPERLIQUID_TRIGGER_KEY_ORDER = Object.freeze([
|
|
222
|
+
'isMarket',
|
|
223
|
+
'triggerPx',
|
|
224
|
+
'tpsl',
|
|
225
|
+
]);
|
|
226
|
+
/** Perp price precision ceiling: at most `MAX_DECIMALS - szDecimals` fractional digits. */
|
|
227
|
+
export const HYPERLIQUID_PERP_MAX_DECIMALS = 6;
|
|
228
|
+
/** …and at most 5 significant digits, unless the price is an integer (always accepted). */
|
|
229
|
+
export const HYPERLIQUID_PRICE_MAX_SIGNIFICANT_DIGITS = 5;
|
|
230
|
+
/**
|
|
231
|
+
* Venue time-in-force values a caller may name. `FrontendMarket` and `LiquidationMarket` exist in the
|
|
232
|
+
* venue schema but are Hyperliquid-UI-internal, so they are not offered. A market fill is expressed
|
|
233
|
+
* as `Ioc` at the caller's OWN aggressive limit price: this constructor never invents a price from a
|
|
234
|
+
* mark read, which is what the trade-execution agent's market path does (`hyperliquid-client.ts:1785`
|
|
235
|
+
* — mark × 1.05) and what would let an artifact signed minutes later fill far from the caller's bound.
|
|
236
|
+
*/
|
|
237
|
+
export const HYPERLIQUID_TIME_IN_FORCE = ['Gtc', 'Ioc', 'Alo'];
|
|
238
|
+
/** `na` for a lone entry; `normalTpsl` binds fixed-size TP/SL legs to the entry (client.ts:1876). */
|
|
239
|
+
export const HYPERLIQUID_ORDER_GROUPINGS = ['na', 'normalTpsl'];
|
|
240
|
+
/**
|
|
241
|
+
* RULED builder-attribution state (Founder, 2026-08-17; spec §7.2/§12): the Hyperliquid builder field
|
|
242
|
+
* is **spec'd and VALUELESS** until the Step-0 `approveBuilderFee` reversal ships. So the constructed
|
|
243
|
+
* action carries NO `builder` key — an absent key is the only encoding the venue reads as "no builder
|
|
244
|
+
* fee", and inventing an address or a tenths-of-a-bp value here would stamp an unruled fee inside a
|
|
245
|
+
* digest the user signs. The exact `{b, f}` assert lands in a follow-up fold; until then the envelope
|
|
246
|
+
* carries this machine-readable pending marker and the constructor asserts the key's ABSENCE.
|
|
247
|
+
*/
|
|
248
|
+
export const HL_BUILDER_ATTRIBUTION_STATUS = 'pending_step0_builder_fee_approval';
|
|
249
|
+
export const DEFAULT_HL_BUILDER_FEE_CONFIG_VERSION = 'hl-2026-08-17-builder-unwired-v1';
|
|
250
|
+
/** Env names that would carry a builder VALUE. Fenced: see `HL_BUILDER_ATTRIBUTION_STATUS`. */
|
|
251
|
+
const HL_BUILDER_VALUE_ENV_NAMES = [
|
|
252
|
+
'OTTO_HL_BUILDER_ADDRESS',
|
|
253
|
+
'OTTO_HL_BUILDER_FEE_TENTHS_BPS',
|
|
254
|
+
];
|
|
255
|
+
function parseEnabled(raw) {
|
|
256
|
+
if (raw === undefined || raw.trim() === '')
|
|
257
|
+
return false;
|
|
258
|
+
if (raw === 'true')
|
|
259
|
+
return true;
|
|
260
|
+
if (raw === 'false')
|
|
261
|
+
return false;
|
|
262
|
+
throw new Error('OTTO_EXECUTION_MCP_ENABLED must be true or false');
|
|
263
|
+
}
|
|
264
|
+
function positiveInteger(raw, fallback, name) {
|
|
265
|
+
if (raw === undefined || raw.trim() === '')
|
|
266
|
+
return fallback;
|
|
267
|
+
const parsed = Number(raw);
|
|
268
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
269
|
+
throw new Error(`${name} must be a positive integer`);
|
|
270
|
+
}
|
|
271
|
+
return parsed;
|
|
272
|
+
}
|
|
273
|
+
export function parseDecimalRate(raw, name = 'OTTO_LIFI_FEE') {
|
|
274
|
+
const value = raw.trim();
|
|
275
|
+
const match = /^(0|1)\.(\d{1,6})$/.exec(value);
|
|
276
|
+
if (!match)
|
|
277
|
+
throw new Error(`${name} must be a decimal rate with 1-6 fractional digits`);
|
|
278
|
+
const numerator = BigInt(match[1]) * 10n ** BigInt(match[2].length) + BigInt(match[2]);
|
|
279
|
+
const denominator = 10n ** BigInt(match[2].length);
|
|
280
|
+
if (numerator <= 0n || numerator >= denominator) {
|
|
281
|
+
throw new Error(`${name} must be greater than 0 and less than 1`);
|
|
282
|
+
}
|
|
283
|
+
return Object.freeze({ decimal: value, numerator, denominator });
|
|
284
|
+
}
|
|
285
|
+
function checkedBaseUrl(raw) {
|
|
286
|
+
const url = new URL(raw);
|
|
287
|
+
if (url.username || url.password)
|
|
288
|
+
throw new Error('LIFI_BASE_URL must not contain credentials');
|
|
289
|
+
const isOfficial = url.protocol === 'https:' && url.origin === 'https://li.quest';
|
|
290
|
+
const isLoopback = ['http:', 'https:'].includes(url.protocol) &&
|
|
291
|
+
['localhost', '127.0.0.1', '::1', '[::1]'].includes(url.hostname);
|
|
292
|
+
if (!isOfficial && !isLoopback) {
|
|
293
|
+
throw new Error('LIFI_BASE_URL must use https://li.quest or a loopback test server');
|
|
294
|
+
}
|
|
295
|
+
return url.toString().replace(/\/$/, '');
|
|
296
|
+
}
|
|
297
|
+
function checkedConfigVersion(raw) {
|
|
298
|
+
const value = raw?.trim() || DEFAULT_LIFI_FEE_CONFIG_VERSION;
|
|
299
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(value)) {
|
|
300
|
+
throw new Error('OTTO_LIFI_FEE_CONFIG_VERSION is invalid');
|
|
301
|
+
}
|
|
302
|
+
return value;
|
|
303
|
+
}
|
|
304
|
+
function checkedClobBaseUrl(raw) {
|
|
305
|
+
const url = new URL(raw);
|
|
306
|
+
if (url.username || url.password) {
|
|
307
|
+
throw new Error('POLYMARKET_CLOB_URL must not contain credentials');
|
|
308
|
+
}
|
|
309
|
+
const isOfficial = url.protocol === 'https:' && url.origin === 'https://clob.polymarket.com';
|
|
310
|
+
const isLoopback = ['http:', 'https:'].includes(url.protocol) &&
|
|
311
|
+
['localhost', '127.0.0.1', '::1', '[::1]'].includes(url.hostname);
|
|
312
|
+
if (!isOfficial && !isLoopback) {
|
|
313
|
+
throw new Error('POLYMARKET_CLOB_URL must use https://clob.polymarket.com or a loopback test server');
|
|
314
|
+
}
|
|
315
|
+
return url.toString().replace(/\/$/, '');
|
|
316
|
+
}
|
|
317
|
+
function checkedBuilderCode(raw) {
|
|
318
|
+
const value = (raw?.trim() || OTTO_PM_BUILDER_CODE).toLowerCase();
|
|
319
|
+
if (!/^0x[0-9a-f]{64}$/.test(value)) {
|
|
320
|
+
throw new Error('OTTO_PM_BUILDER_CODE must be a 32-byte hex string');
|
|
321
|
+
}
|
|
322
|
+
if (value === PM_BYTES32_ZERO) {
|
|
323
|
+
// A zero builder code is exactly what the CLOB reads as "no builder": the order would be
|
|
324
|
+
// constructed, signed, and filled with Otto's attribution absent rather than refused.
|
|
325
|
+
throw new Error('OTTO_PM_BUILDER_CODE must not be the zero builder code');
|
|
326
|
+
}
|
|
327
|
+
return value;
|
|
328
|
+
}
|
|
329
|
+
function checkedHyperliquidBaseUrl(raw) {
|
|
330
|
+
const url = new URL(raw);
|
|
331
|
+
if (url.username || url.password) {
|
|
332
|
+
throw new Error('HYPERLIQUID_API_URL must not contain credentials');
|
|
333
|
+
}
|
|
334
|
+
const isOfficial = url.protocol === 'https:' && url.origin === 'https://api.hyperliquid.xyz';
|
|
335
|
+
const isLoopback = ['http:', 'https:'].includes(url.protocol) &&
|
|
336
|
+
['localhost', '127.0.0.1', '::1', '[::1]'].includes(url.hostname);
|
|
337
|
+
if (!isOfficial && !isLoopback) {
|
|
338
|
+
throw new Error('HYPERLIQUID_API_URL must use https://api.hyperliquid.xyz or a loopback test server');
|
|
339
|
+
}
|
|
340
|
+
return url.toString().replace(/\/$/, '');
|
|
341
|
+
}
|
|
342
|
+
function checkedFeeBps(raw, fallback, name) {
|
|
343
|
+
if (raw === undefined || raw.trim() === '')
|
|
344
|
+
return fallback;
|
|
345
|
+
const parsed = Number(raw);
|
|
346
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0 || parsed > 10_000) {
|
|
347
|
+
throw new Error(`${name} must be an integer between 0 and 10000`);
|
|
348
|
+
}
|
|
349
|
+
return parsed;
|
|
350
|
+
}
|
|
351
|
+
function basisPointsRate(basisPoints) {
|
|
352
|
+
if (!Number.isSafeInteger(basisPoints) || basisPoints <= 0 || basisPoints >= 10_000) {
|
|
353
|
+
throw new Error('default LI.FI fee must be between 1 and 9,999 basis points');
|
|
354
|
+
}
|
|
355
|
+
const decimalDigits = basisPoints.toString().padStart(4, '0').replace(/0+$/, '');
|
|
356
|
+
return parseDecimalRate(`0.${decimalDigits}`, 'default LI.FI fee');
|
|
357
|
+
}
|
|
358
|
+
export function loadExecutionServiceConfig(env = process.env) {
|
|
359
|
+
if (!parseEnabled(env.OTTO_EXECUTION_MCP_ENABLED))
|
|
360
|
+
return undefined;
|
|
361
|
+
const feeOverride = env.OTTO_LIFI_FEE?.trim();
|
|
362
|
+
const recipientOverride = env.OTTO_LIFI_FEE_RECIPIENT?.trim();
|
|
363
|
+
if ((feeOverride || recipientOverride) && !env.OTTO_LIFI_FEE_CONFIG_VERSION?.trim()) {
|
|
364
|
+
throw new Error('OTTO_LIFI_FEE_CONFIG_VERSION is required when overriding the LI.FI fee or recipient');
|
|
365
|
+
}
|
|
366
|
+
let feeRecipient;
|
|
367
|
+
try {
|
|
368
|
+
feeRecipient = getAddress(recipientOverride || DEFAULT_OTTO_LIFI_FEE_RECIPIENT);
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
throw new Error('OTTO_LIFI_FEE_RECIPIENT must be a valid EVM address');
|
|
372
|
+
}
|
|
373
|
+
if (feeRecipient === getAddress('0x0000000000000000000000000000000000000000')) {
|
|
374
|
+
throw new Error('OTTO_LIFI_FEE_RECIPIENT must not be the zero address');
|
|
375
|
+
}
|
|
376
|
+
if (feeRecipient === DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT) {
|
|
377
|
+
throw new Error('OTTO_LIFI_FEE_RECIPIENT must differ from the LI.FI platform recipient');
|
|
378
|
+
}
|
|
379
|
+
const lifiFee = feeOverride
|
|
380
|
+
? parseDecimalRate(feeOverride)
|
|
381
|
+
: basisPointsRate(DEFAULT_LIFI_FEE_BPS);
|
|
382
|
+
const lifiAggregateFeeCap = parseDecimalRate(DEFAULT_LIFI_AGGREGATE_FEE_CAP_RATE, 'LI.FI aggregate fee cap');
|
|
383
|
+
if (lifiFee.numerator * lifiAggregateFeeCap.denominator >=
|
|
384
|
+
lifiAggregateFeeCap.numerator * lifiFee.denominator) {
|
|
385
|
+
throw new Error('OTTO_LIFI_FEE must be below the LI.FI aggregate fee cap');
|
|
386
|
+
}
|
|
387
|
+
const artifactTtlMs = positiveInteger(env.OTTO_EXECUTION_ARTIFACT_TTL_MS, 300_000, 'OTTO_EXECUTION_ARTIFACT_TTL_MS');
|
|
388
|
+
if (artifactTtlMs > MAX_ARTIFACT_TTL_MS) {
|
|
389
|
+
throw new Error(`OTTO_EXECUTION_ARTIFACT_TTL_MS must not exceed ${MAX_ARTIFACT_TTL_MS} ms, the artifact freshness ceiling`);
|
|
390
|
+
}
|
|
391
|
+
// Same discipline as the LI.FI fee: a value override without a version bump would silently change
|
|
392
|
+
// what `fee_attribution.config_version` means, so it is a config-time throw.
|
|
393
|
+
const builderCodeOverride = env.OTTO_PM_BUILDER_CODE?.trim();
|
|
394
|
+
const makerBpsOverride = env.OTTO_PM_BUILDER_MAKER_FEE_BPS?.trim();
|
|
395
|
+
const takerBpsOverride = env.OTTO_PM_BUILDER_TAKER_FEE_BPS?.trim();
|
|
396
|
+
if ((builderCodeOverride || makerBpsOverride || takerBpsOverride) &&
|
|
397
|
+
!env.OTTO_PM_BUILDER_FEE_CONFIG_VERSION?.trim()) {
|
|
398
|
+
throw new Error('OTTO_PM_BUILDER_FEE_CONFIG_VERSION is required when overriding the Polymarket builder code or ruled rates');
|
|
399
|
+
}
|
|
400
|
+
const polymarketBuilderFeeConfigVersion = env.OTTO_PM_BUILDER_FEE_CONFIG_VERSION?.trim() || DEFAULT_PM_BUILDER_FEE_CONFIG_VERSION;
|
|
401
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(polymarketBuilderFeeConfigVersion)) {
|
|
402
|
+
throw new Error('OTTO_PM_BUILDER_FEE_CONFIG_VERSION is invalid');
|
|
403
|
+
}
|
|
404
|
+
// The Hyperliquid builder value is a Founder ruling that has not landed (spec §7.2). Reading it
|
|
405
|
+
// from the environment would let an unruled address or fee rate enter a digest the user signs, so
|
|
406
|
+
// the presence of either variable is a config-time throw rather than a value this code adopts.
|
|
407
|
+
for (const name of HL_BUILDER_VALUE_ENV_NAMES) {
|
|
408
|
+
if (env[name]?.trim()) {
|
|
409
|
+
throw new Error(`${name} must not be set: the Hyperliquid builder fee is spec'd and valueless until the Step-0 approveBuilderFee reversal lands`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const hyperliquidBuilderFeeConfigVersion = env.OTTO_HL_BUILDER_FEE_CONFIG_VERSION?.trim() || DEFAULT_HL_BUILDER_FEE_CONFIG_VERSION;
|
|
413
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(hyperliquidBuilderFeeConfigVersion)) {
|
|
414
|
+
throw new Error('OTTO_HL_BUILDER_FEE_CONFIG_VERSION is invalid');
|
|
415
|
+
}
|
|
416
|
+
return Object.freeze({
|
|
417
|
+
artifactTtlMs,
|
|
418
|
+
lifiApiKey: env.LIFI_API_KEY?.trim() || undefined,
|
|
419
|
+
lifiBaseUrl: checkedBaseUrl(env.LIFI_BASE_URL?.trim() || 'https://li.quest/v1'),
|
|
420
|
+
lifiAggregateFeeCap,
|
|
421
|
+
lifiAllowedFeeRecipients: Object.freeze([DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT, feeRecipient]),
|
|
422
|
+
lifiFee,
|
|
423
|
+
lifiFeeConfigVersion: checkedConfigVersion(env.OTTO_LIFI_FEE_CONFIG_VERSION),
|
|
424
|
+
lifiFeeRecipient: feeRecipient,
|
|
425
|
+
lifiTimeoutMs: positiveInteger(env.LIFI_TIMEOUT_MS, 30_000, 'LIFI_TIMEOUT_MS'),
|
|
426
|
+
polymarketClobBaseUrl: checkedClobBaseUrl(env.POLYMARKET_CLOB_URL?.trim() || 'https://clob.polymarket.com'),
|
|
427
|
+
polymarketBuilderCode: checkedBuilderCode(builderCodeOverride),
|
|
428
|
+
polymarketBuilderFeeConfigVersion,
|
|
429
|
+
polymarketRuledBuilderFeeBps: Object.freeze({
|
|
430
|
+
maker: checkedFeeBps(makerBpsOverride, PM_RULED_BUILDER_FEE_BPS.maker, 'OTTO_PM_BUILDER_MAKER_FEE_BPS'),
|
|
431
|
+
taker: checkedFeeBps(takerBpsOverride, PM_RULED_BUILDER_FEE_BPS.taker, 'OTTO_PM_BUILDER_TAKER_FEE_BPS'),
|
|
432
|
+
}),
|
|
433
|
+
polymarketTimeoutMs: positiveInteger(env.POLYMARKET_TIMEOUT_MS, 30_000, 'POLYMARKET_TIMEOUT_MS'),
|
|
434
|
+
hyperliquidApiBaseUrl: checkedHyperliquidBaseUrl(env.HYPERLIQUID_API_URL?.trim() || 'https://api.hyperliquid.xyz'),
|
|
435
|
+
hyperliquidBuilderFeeConfigVersion,
|
|
436
|
+
hyperliquidBuilderAttributionStatus: HL_BUILDER_ATTRIBUTION_STATUS,
|
|
437
|
+
hyperliquidTimeoutMs: positiveInteger(env.HYPERLIQUID_TIMEOUT_MS, 30_000, 'HYPERLIQUID_TIMEOUT_MS'),
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
export function attributedFeeAmount(amount, rate) {
|
|
441
|
+
return (amount * rate.numerator) / rate.denominator;
|
|
442
|
+
}
|
|
443
|
+
//# sourceMappingURL=execution-config.js.map
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-delegated-definition.ts — the ONE execution tool that submits: `otto_submit_under_delegation`.
|
|
3
|
+
* Kept apart from the prepare-only `EXECUTION_TOOL_DEFINITIONS` on purpose: it registers only when the
|
|
4
|
+
* hosted service holds Otto's CDP developer credentials, it is not read-only, and its caller is
|
|
5
|
+
* authenticated per request by a header the arguments never carry.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export declare const DELEGATED_EXECUTION_TOOL_NAME: "otto_submit_under_delegation";
|
|
9
|
+
/** Per-request caller authentication rides THIS header — never a tool argument (arguments are logged). */
|
|
10
|
+
export declare const DELEGATION_AUTH_HEADER: "x-otto-delegation-auth";
|
|
11
|
+
export declare const SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA: z.ZodObject<{
|
|
12
|
+
userId: z.ZodString;
|
|
13
|
+
envelope: z.ZodObject<{
|
|
14
|
+
schema_version: z.ZodLiteral<"1">;
|
|
15
|
+
artifact_id: z.ZodString;
|
|
16
|
+
rail: z.ZodLiteral<"lifi">;
|
|
17
|
+
operation: z.ZodLiteral<"swap">;
|
|
18
|
+
intent_digest: z.ZodString;
|
|
19
|
+
account_binding: z.ZodObject<{
|
|
20
|
+
kind: z.ZodEnum<{
|
|
21
|
+
safe: "safe";
|
|
22
|
+
eoa: "eoa";
|
|
23
|
+
}>;
|
|
24
|
+
address: z.ZodString;
|
|
25
|
+
chainId: z.ZodNumber;
|
|
26
|
+
protocol_roles: z.ZodObject<{
|
|
27
|
+
lifi: z.ZodObject<{
|
|
28
|
+
sender: z.ZodString;
|
|
29
|
+
receiver: z.ZodString;
|
|
30
|
+
}, z.core.$strict>;
|
|
31
|
+
}, z.core.$strict>;
|
|
32
|
+
}, z.core.$strict>;
|
|
33
|
+
required_capabilities: z.ZodArray<z.ZodEnum<{
|
|
34
|
+
send_evm_transaction: "send_evm_transaction";
|
|
35
|
+
submit_evm_batch: "submit_evm_batch";
|
|
36
|
+
}>>;
|
|
37
|
+
payload: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
38
|
+
kind: z.ZodLiteral<"evm_execution_plan">;
|
|
39
|
+
chain_id: z.ZodNumber;
|
|
40
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
41
|
+
to: z.ZodString;
|
|
42
|
+
value: z.ZodString;
|
|
43
|
+
data: z.ZodString;
|
|
44
|
+
kind: z.ZodEnum<{
|
|
45
|
+
call: "call";
|
|
46
|
+
approve_reset: "approve_reset";
|
|
47
|
+
approve: "approve";
|
|
48
|
+
}>;
|
|
49
|
+
}, z.core.$strict>>;
|
|
50
|
+
}, z.core.$strict>], "kind">;
|
|
51
|
+
fee_attribution: z.ZodObject<{
|
|
52
|
+
mechanism: z.ZodLiteral<"lifi_integrator_fee">;
|
|
53
|
+
config_version: z.ZodString;
|
|
54
|
+
}, z.core.$strict>;
|
|
55
|
+
replay_disclosure: z.ZodObject<{
|
|
56
|
+
artifact_role: z.ZodLiteral<"construction_aid">;
|
|
57
|
+
freshness_boundary: z.ZodLiteral<"valid_until">;
|
|
58
|
+
onchain_replay_boundary: z.ZodLiteral<"signer_account_nonce">;
|
|
59
|
+
tool_guarantees_single_execution: z.ZodLiteral<false>;
|
|
60
|
+
}, z.core.$strict>;
|
|
61
|
+
assertions: z.ZodArray<z.ZodObject<{
|
|
62
|
+
id: z.ZodString;
|
|
63
|
+
result: z.ZodLiteral<"passed">;
|
|
64
|
+
}, z.core.$strict>>;
|
|
65
|
+
valid_until: z.ZodString;
|
|
66
|
+
submission: z.ZodObject<{
|
|
67
|
+
method: z.ZodEnum<{
|
|
68
|
+
send_evm_transaction: "send_evm_transaction";
|
|
69
|
+
submit_evm_batch: "submit_evm_batch";
|
|
70
|
+
}>;
|
|
71
|
+
chain_id: z.ZodNumber;
|
|
72
|
+
artifact_id: z.ZodString;
|
|
73
|
+
payload_digest: z.ZodString;
|
|
74
|
+
artifact_digest: z.ZodString;
|
|
75
|
+
adapter_owned_fields: z.ZodTuple<[z.ZodLiteral<"nonce">, z.ZodLiteral<"gas">, z.ZodLiteral<"serialization">], null>;
|
|
76
|
+
client_ref: z.ZodOptional<z.ZodString>;
|
|
77
|
+
}, z.core.$strict>;
|
|
78
|
+
}, z.core.$strict>;
|
|
79
|
+
prepareInput: z.ZodObject<{
|
|
80
|
+
accountProfile: z.ZodObject<{
|
|
81
|
+
kind: z.ZodEnum<{
|
|
82
|
+
safe: "safe";
|
|
83
|
+
eoa: "eoa";
|
|
84
|
+
}>;
|
|
85
|
+
address: z.ZodString;
|
|
86
|
+
chainId: z.ZodNumber;
|
|
87
|
+
}, z.core.$strict>;
|
|
88
|
+
chainId: z.ZodNumber;
|
|
89
|
+
fromToken: z.ZodString;
|
|
90
|
+
toToken: z.ZodString;
|
|
91
|
+
fromAmount: z.ZodString;
|
|
92
|
+
slippage: z.ZodObject<{
|
|
93
|
+
maxBps: z.ZodNumber;
|
|
94
|
+
minAmountOut: z.ZodOptional<z.ZodString>;
|
|
95
|
+
}, z.core.$strict>;
|
|
96
|
+
client_ref: z.ZodOptional<z.ZodString>;
|
|
97
|
+
}, z.core.$strict>;
|
|
98
|
+
}, z.core.$strict>;
|
|
99
|
+
export type SubmitUnderDelegationInput = z.infer<typeof SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA>;
|
|
100
|
+
/**
|
|
101
|
+
* What the tool REGISTERS as its input schema: permissive on purpose. The MCP SDK validates the registered
|
|
102
|
+
* schema BEFORE the handler runs, which would answer an unauthenticated caller with the exact validation
|
|
103
|
+
* failure — a schema oracle in front of the caller check. Registering a passthrough object moves ALL
|
|
104
|
+
* validation behind the header check: the handler parses `SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA` itself,
|
|
105
|
+
* strictly, after the caller is accepted. The strict schema stays exported for consumers.
|
|
106
|
+
*/
|
|
107
|
+
export declare const DELEGATED_TOOL_TRANSPORT_SCHEMA: z.ZodObject<{}, z.core.$loose>;
|
|
108
|
+
export declare const DELEGATED_SEND_RESULT_SCHEMA: z.ZodObject<{
|
|
109
|
+
status: z.ZodLiteral<"sent">;
|
|
110
|
+
user_id: z.ZodString;
|
|
111
|
+
address: z.ZodString;
|
|
112
|
+
chain_id: z.ZodLiteral<8453>;
|
|
113
|
+
artifact_id: z.ZodString;
|
|
114
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
115
|
+
index: z.ZodNumber;
|
|
116
|
+
kind: z.ZodEnum<{
|
|
117
|
+
call: "call";
|
|
118
|
+
approve_reset: "approve_reset";
|
|
119
|
+
approve: "approve";
|
|
120
|
+
}>;
|
|
121
|
+
to: z.ZodString;
|
|
122
|
+
transaction_hash: z.ZodString;
|
|
123
|
+
status: z.ZodLiteral<"success">;
|
|
124
|
+
block_number: z.ZodString;
|
|
125
|
+
gas_used: z.ZodString;
|
|
126
|
+
}, z.core.$strict>>;
|
|
127
|
+
fence: z.ZodObject<{
|
|
128
|
+
policy_id: z.ZodString;
|
|
129
|
+
policy_name: z.ZodString;
|
|
130
|
+
rules_digest: z.ZodString;
|
|
131
|
+
per_swap_cap_usd: z.ZodLiteral<5000>;
|
|
132
|
+
}, z.core.$strict>;
|
|
133
|
+
delegation: z.ZodObject<{
|
|
134
|
+
expires_at: z.ZodString;
|
|
135
|
+
}, z.core.$strict>;
|
|
136
|
+
replayed: z.ZodOptional<z.ZodBoolean>;
|
|
137
|
+
verification: z.ZodObject<{
|
|
138
|
+
checks: z.ZodArray<z.ZodString>;
|
|
139
|
+
gross: z.ZodString;
|
|
140
|
+
min_amount_out: z.ZodString;
|
|
141
|
+
otto_fee_atomic: z.ZodString;
|
|
142
|
+
otto_fee_recipient: z.ZodString;
|
|
143
|
+
fee_config_version: z.ZodString;
|
|
144
|
+
receiver: z.ZodString;
|
|
145
|
+
call_data_digest: z.ZodOptional<z.ZodString>;
|
|
146
|
+
}, z.core.$strict>;
|
|
147
|
+
}, z.core.$strict>;
|
|
148
|
+
export type DelegatedSendResult = z.infer<typeof DELEGATED_SEND_RESULT_SCHEMA>;
|
|
149
|
+
/** Not read-only, not idempotent: every accepted call moves the end user's funds through the delegation. */
|
|
150
|
+
export declare const DELEGATED_TOOL_ANNOTATIONS: Readonly<{
|
|
151
|
+
readOnlyHint: false;
|
|
152
|
+
destructiveHint: true;
|
|
153
|
+
idempotentHint: false;
|
|
154
|
+
openWorldHint: true;
|
|
155
|
+
}>;
|
|
156
|
+
export interface DelegatedExecutionToolDefinition {
|
|
157
|
+
readonly name: typeof DELEGATED_EXECUTION_TOOL_NAME;
|
|
158
|
+
readonly title: string;
|
|
159
|
+
readonly description: string;
|
|
160
|
+
/** The registered (transport) schema — permissive; the strict one is parsed after authentication. */
|
|
161
|
+
readonly inputSchema: typeof DELEGATED_TOOL_TRANSPORT_SCHEMA;
|
|
162
|
+
readonly strictInputSchema: typeof SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA;
|
|
163
|
+
readonly outputSchema: typeof DELEGATED_SEND_RESULT_SCHEMA;
|
|
164
|
+
}
|
|
165
|
+
export declare const DELEGATED_EXECUTION_TOOL_DEFINITION: DelegatedExecutionToolDefinition;
|