otto-intel-mcp 0.1.2 → 0.1.4
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 +76 -18
- package/dist/account-userop-policy-definition.d.ts +14 -0
- package/dist/account-userop-policy-definition.js +12 -0
- package/dist/account-userop-policy-relation.d.ts +371 -0
- package/dist/account-userop-policy-relation.js +87 -0
- package/dist/adapter/cdp-signer.d.ts +76 -9
- package/dist/adapter/cdp-signer.js +274 -33
- package/dist/adapter/evm-call-failure.d.ts +29 -0
- package/dist/adapter/evm-call-failure.js +109 -0
- package/dist/adapter/refusal.d.ts +1 -1
- package/dist/adapter/refusal.js +13 -0
- package/dist/adapter/route-simulator.d.ts +91 -0
- package/dist/adapter/route-simulator.js +143 -0
- package/dist/adapter-cdp-index.d.ts +1 -1
- package/dist/executable-equities.d.ts +55 -0
- package/dist/executable-equities.js +116 -0
- package/dist/executable-yield-markets.d.ts +261 -0
- package/dist/executable-yield-markets.js +154 -0
- package/dist/execution-account-gas-quote.d.ts +141 -0
- package/dist/execution-account-gas-quote.js +443 -0
- package/dist/execution-account-gas-review.d.ts +123 -0
- package/dist/execution-account-gas-review.js +104 -0
- package/dist/execution-account-gas-runtime.d.ts +33 -0
- package/dist/execution-account-gas-runtime.js +131 -0
- package/dist/execution-account-gas-view.d.ts +936 -0
- package/dist/execution-account-gas-view.js +56 -0
- package/dist/execution-config.d.ts +3 -0
- package/dist/execution-config.js +4 -0
- package/dist/execution-delegated-definition.d.ts +7 -6
- package/dist/execution-delegated-definition.js +13 -2
- package/dist/execution-delegation-admin-definition.d.ts +127 -5
- package/dist/execution-delegation-admin-definition.js +155 -5
- package/dist/execution-delegation-admin.d.ts +48 -2
- package/dist/execution-delegation-admin.js +422 -12
- package/dist/execution-delegation-cap-store.d.ts +386 -0
- package/dist/execution-delegation-cap-store.js +1150 -0
- package/dist/execution-delegation-exit-definition.d.ts +309 -0
- package/dist/execution-delegation-exit-definition.js +204 -0
- package/dist/execution-delegation-exit-store.d.ts +114 -0
- package/dist/execution-delegation-exit-store.js +671 -0
- package/dist/execution-delegation-exit.d.ts +4 -0
- package/dist/execution-delegation-exit.js +614 -0
- package/dist/execution-delegation-finality.d.ts +22 -0
- package/dist/execution-delegation-finality.js +90 -0
- package/dist/execution-delegation-operation-store.d.ts +129 -0
- package/dist/execution-delegation-operation-store.js +1107 -0
- package/dist/execution-delegation-operations-definition.d.ts +282 -0
- package/dist/execution-delegation-operations-definition.js +117 -0
- package/dist/execution-delegation-operations.d.ts +6 -0
- package/dist/execution-delegation-operations.js +282 -0
- package/dist/execution-delegation-policy.d.ts +154 -8
- package/dist/execution-delegation-policy.js +273 -40
- package/dist/execution-delegation.d.ts +51 -0
- package/dist/execution-delegation.js +441 -31
- package/dist/execution-errors.d.ts +1 -1
- package/dist/execution-errors.js +122 -0
- package/dist/execution-index.d.ts +7 -2
- package/dist/execution-index.js +7 -2
- package/dist/execution-journal-schema.d.ts +96 -0
- package/dist/execution-journal-schema.js +368 -0
- package/dist/execution-journal-tables.d.ts +14 -0
- package/dist/execution-journal-tables.js +227 -0
- package/dist/execution-policy-compatibility.d.ts +34 -0
- package/dist/execution-policy-compatibility.js +76 -0
- package/dist/execution-registration.d.ts +54 -2
- package/dist/execution-registration.js +151 -5
- package/dist/execution-sponsored-account-profile.d.ts +32 -0
- package/dist/execution-sponsored-account-profile.js +59 -0
- package/dist/execution-sponsored-account.d.ts +1519 -0
- package/dist/execution-sponsored-account.js +222 -0
- package/dist/execution-sponsored-definition.d.ts +1713 -0
- package/dist/execution-sponsored-definition.js +185 -0
- package/dist/execution-sponsored-gas-policy.d.ts +27 -0
- package/dist/execution-sponsored-gas-policy.js +110 -0
- package/dist/execution-sponsored-hosted.d.ts +3 -0
- package/dist/execution-sponsored-hosted.js +71 -0
- package/dist/execution-sponsored-index.d.ts +1322 -0
- package/dist/execution-sponsored-index.js +102 -0
- package/dist/execution-sponsored-mcp-definition.d.ts +1662 -0
- package/dist/execution-sponsored-mcp-definition.js +94 -0
- package/dist/execution-sponsored-receipt.d.ts +47 -0
- package/dist/execution-sponsored-receipt.js +74 -0
- package/dist/execution-sponsored-registration.d.ts +14 -0
- package/dist/execution-sponsored-registration.js +77 -0
- package/dist/execution-sponsored-runtime.d.ts +84 -0
- package/dist/execution-sponsored-runtime.js +149 -0
- package/dist/execution-sponsored-settlement.d.ts +15 -0
- package/dist/execution-sponsored-settlement.js +88 -0
- package/dist/execution-sponsored-store.d.ts +67 -0
- package/dist/execution-sponsored-store.js +517 -0
- package/dist/execution-sponsored-transport.d.ts +90 -0
- package/dist/execution-sponsored-transport.js +180 -0
- package/dist/execution-tool-definitions.d.ts +21 -21
- package/dist/execution-tool-definitions.js +0 -7
- package/dist/execution-tools.d.ts +53 -1
- package/dist/execution-tools.js +171 -13
- package/dist/execution-worker-guard.d.ts +28 -0
- package/dist/execution-worker-guard.js +124 -0
- package/dist/hybrid-yield-index.d.ts +10 -0
- package/dist/hybrid-yield-index.js +11 -0
- package/dist/hybrid-yield-markets.d.ts +96 -0
- package/dist/hybrid-yield-markets.js +124 -0
- package/dist/hybrid-yield.d.ts +411 -0
- package/dist/hybrid-yield.js +155 -0
- package/dist/hyperliquid-order-builder.js +4 -0
- package/dist/lifi-execution-client.d.ts +6 -0
- package/dist/lifi-execution-client.js +3 -0
- package/dist/lp-userop-account.d.ts +461 -0
- package/dist/lp-userop-account.js +417 -0
- package/dist/lp-userop-authority.d.ts +49 -0
- package/dist/lp-userop-authority.js +211 -0
- package/dist/lp-userop-definition.d.ts +233 -0
- package/dist/lp-userop-definition.js +85 -0
- package/dist/lp-userop-hosted.d.ts +3 -0
- package/dist/lp-userop-hosted.js +73 -0
- package/dist/lp-userop-index.d.ts +1569 -0
- package/dist/lp-userop-index.js +112 -0
- package/dist/lp-userop-mcp-definition.d.ts +1220 -0
- package/dist/lp-userop-mcp-definition.js +46 -0
- package/dist/lp-userop-operation.d.ts +1230 -0
- package/dist/lp-userop-operation.js +140 -0
- package/dist/lp-userop-registration.d.ts +14 -0
- package/dist/lp-userop-registration.js +70 -0
- package/dist/lp-userop-settlement.d.ts +89 -0
- package/dist/lp-userop-settlement.js +184 -0
- package/dist/lp-userop.d.ts +643 -0
- package/dist/lp-userop.js +343 -0
- package/dist/native-yield-account-definition.d.ts +1192 -0
- package/dist/native-yield-account-definition.js +232 -0
- package/dist/native-yield-account-gas.d.ts +58 -0
- package/dist/native-yield-account-gas.js +228 -0
- package/dist/native-yield-account-index.d.ts +23 -0
- package/dist/native-yield-account-index.js +28 -0
- package/dist/native-yield-account-policy.d.ts +168 -0
- package/dist/native-yield-account-policy.js +525 -0
- package/dist/native-yield-account-rpc.d.ts +8 -0
- package/dist/native-yield-account-rpc.js +128 -0
- package/dist/native-yield-account-runtime.d.ts +71 -0
- package/dist/native-yield-account-runtime.js +125 -0
- package/dist/native-yield-account-store.d.ts +40 -0
- package/dist/native-yield-account-store.js +307 -0
- package/dist/native-yield-account.d.ts +662 -0
- package/dist/native-yield-account.js +371 -0
- package/dist/native-yield-consent-store.d.ts +20 -0
- package/dist/native-yield-consent-store.js +317 -0
- package/dist/native-yield-definition.d.ts +288 -0
- package/dist/native-yield-definition.js +105 -0
- package/dist/native-yield-reader.d.ts +4 -0
- package/dist/native-yield-reader.js +137 -0
- package/dist/native-yield-settlement.d.ts +48 -0
- package/dist/native-yield-settlement.js +167 -0
- package/dist/native-yield-simulation.d.ts +18 -0
- package/dist/native-yield-simulation.js +45 -0
- package/dist/native-yield-userop-account.d.ts +675 -0
- package/dist/native-yield-userop-account.js +357 -0
- package/dist/native-yield-userop-authority.d.ts +214 -0
- package/dist/native-yield-userop-authority.js +296 -0
- package/dist/native-yield-userop-definition.d.ts +331 -0
- package/dist/native-yield-userop-definition.js +105 -0
- package/dist/native-yield-userop-hosted.d.ts +3 -0
- package/dist/native-yield-userop-hosted.js +71 -0
- package/dist/native-yield-userop-index.d.ts +1522 -0
- package/dist/native-yield-userop-index.js +142 -0
- package/dist/native-yield-userop-mcp-definition.d.ts +1356 -0
- package/dist/native-yield-userop-mcp-definition.js +50 -0
- package/dist/native-yield-userop-operation.d.ts +845 -0
- package/dist/native-yield-userop-operation.js +127 -0
- package/dist/native-yield-userop-policy.d.ts +23 -0
- package/dist/native-yield-userop-policy.js +25 -0
- package/dist/native-yield-userop-registration.d.ts +17 -0
- package/dist/native-yield-userop-registration.js +76 -0
- package/dist/native-yield-userop-settlement.d.ts +8 -0
- package/dist/native-yield-userop-settlement.js +79 -0
- package/dist/native-yield-userop-simulation.d.ts +18 -0
- package/dist/native-yield-userop-simulation.js +34 -0
- package/dist/native-yield-userop.d.ts +35 -0
- package/dist/native-yield-userop.js +156 -0
- package/dist/native-yield-verify.d.ts +30 -0
- package/dist/native-yield-verify.js +141 -0
- package/dist/native-yield.d.ts +17 -0
- package/dist/native-yield.js +119 -0
- package/dist/stdio-main.js +0 -0
- package/dist/stock-buy-definition.d.ts +338 -0
- package/dist/stock-buy-definition.js +127 -0
- package/dist/stock-buy-oracle.d.ts +8 -0
- package/dist/stock-buy-oracle.js +22 -0
- package/dist/stock-buy.d.ts +17 -0
- package/dist/stock-buy.js +164 -0
- package/dist/tool-definitions.d.ts +3 -1
- package/dist/tool-definitions.js +55 -2
- package/dist/vendor/lp-v3/lp-v3-atomic-contract.d.ts +445 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-contract.js +90 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-evidence.d.ts +14 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-evidence.js +82 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-index.d.ts +80 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-index.js +19 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-plan.d.ts +43 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-plan.js +112 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.d.ts +312 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.js +41 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-index.d.ts +3 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-index.js +3 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.d.ts +49 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.js +88 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read.d.ts +17 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read.js +232 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-settlement.d.ts +105 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-settlement.js +162 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.d.ts +606 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.js +59 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.d.ts +168 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.js +23 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.d.ts +32 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.js +228 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.d.ts +11 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.js +91 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.d.ts +9 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.js +124 -0
- package/dist/vendor/lp-v3/lp-v3-contract.d.ts +538 -0
- package/dist/vendor/lp-v3/lp-v3-contract.js +137 -0
- package/dist/vendor/lp-v3/lp-v3-evidence.d.ts +25 -0
- package/dist/vendor/lp-v3/lp-v3-evidence.js +168 -0
- package/dist/vendor/lp-v3/lp-v3-plan.d.ts +22 -0
- package/dist/vendor/lp-v3/lp-v3-plan.js +135 -0
- package/dist/vendor/lp-v3/types.d.ts +131 -0
- package/dist/vendor/lp-v3/types.js +6 -0
- package/dist/vendor/lp-v3/v4-math.d.ts +18 -0
- package/dist/vendor/lp-v3/v4-math.js +90 -0
- package/dist/yield-deposit-definition.d.ts +374 -0
- package/dist/yield-deposit-definition.js +123 -0
- package/dist/yield-deposit.d.ts +16 -0
- package/dist/yield-deposit.js +149 -0
- package/dist/yield-route-semantics.d.ts +27 -0
- package/dist/yield-route-semantics.js +71 -0
- package/dist/yield-withdraw-definition.d.ts +357 -0
- package/dist/yield-withdraw-definition.js +90 -0
- package/dist/yield-withdraw.d.ts +11 -0
- package/dist/yield-withdraw.js +122 -0
- package/package.json +239 -7
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
*
|
|
30
30
|
* Description obeys the SDK's regex (`/^[A-Za-z0-9 ,.]{1,50}$/`): no hyphens, no parentheses.
|
|
31
31
|
*/
|
|
32
|
-
import { createHash } from 'node:crypto';
|
|
33
32
|
import { getAddress } from 'viem';
|
|
34
33
|
import { refuse } from './execution-errors.js';
|
|
35
34
|
import { LIFI_DIAMOND_ADDRESS } from './execution-config.js';
|
|
35
|
+
import { EXECUTABLE_YIELD_MARKETS } from './executable-yield-markets.js';
|
|
36
|
+
import { policyRulesDigest, readBackPinnedLegacyCompatibleCompositePolicy, } from './execution-policy-compatibility.js';
|
|
37
|
+
export { policyRulesDigest } from './execution-policy-compatibility.js';
|
|
36
38
|
/**
|
|
37
39
|
* The LINEAGE: a project policy is Otto's own only when its description matches the ANCHORED pattern
|
|
38
40
|
* `^OTTO model b swap fence v<N>$` (`modelBLineageVersion` returns N). The fence tool updates a lineage policy
|
|
@@ -40,10 +42,30 @@ import { LIFI_DIAMOND_ADDRESS } from './execution-config.js';
|
|
|
40
42
|
* the prefix in the middle, a suffix, no description — is foreign and is never touched.
|
|
41
43
|
*/
|
|
42
44
|
export const MODEL_B_POLICY_LINEAGE_PREFIX = 'OTTO model b swap fence';
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
/**
|
|
46
|
+
* This binary's fence version. Bump it whenever the rules change shape or caps.
|
|
47
|
+
*
|
|
48
|
+
* 🔴 v2 (Founder word `fence v2`, pending — Yield Copilot withdraw, 2026-09-03). v2 ADDS to v1: an approve
|
|
49
|
+
* rule per registry Morpho share token (spender = the diamond only, bounded by
|
|
50
|
+
* `MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC`) so a delegated vault WITHDRAW can route shares → USDC through the
|
|
51
|
+
* same fenced GenericSwapV3 entry, and a sign-only accept rule for a USDC `transfer` (see
|
|
52
|
+
* `modelBProjectPolicyRules`). The router rule is UNCHANGED and still carries no receiver condition — that
|
|
53
|
+
* is the person's only way to move value out of an Otto/CDP wallet today (ws-7, 2026-09-03), and the
|
|
54
|
+
* receiver pin lives in Otto's delegated verifier.
|
|
55
|
+
*
|
|
56
|
+
* TRANSITION: a v2 binary ACCEPTS a v1 fence (swaps and deposits keep working; a withdraw under v1 is
|
|
57
|
+
* refused by name) and the fence tool upgrades v1 → v2 in place only when it is run — deploying this code
|
|
58
|
+
* writes nothing to the CDP project.
|
|
59
|
+
*/
|
|
60
|
+
export const MODEL_B_POLICY_VERSION = 2;
|
|
61
|
+
/** The v1 match key — the fence every permission before `fence v2` was minted under; still accepted for swaps and deposits. */
|
|
62
|
+
export const MODEL_B_POLICY_NAME_V1 = `${MODEL_B_POLICY_LINEAGE_PREFIX} v1`;
|
|
63
|
+
/** The v2 match key — what the fence tool writes; the only fence that admits a delegated Yield Copilot withdraw. */
|
|
64
|
+
export const MODEL_B_POLICY_NAME_V2 = `${MODEL_B_POLICY_LINEAGE_PREFIX} v${MODEL_B_POLICY_VERSION}`;
|
|
65
|
+
/** The CURRENT match key (v2). */
|
|
66
|
+
export const MODEL_B_POLICY_NAME = MODEL_B_POLICY_NAME_V2;
|
|
67
|
+
/** Every lineage version this binary accepts on read-back, oldest first. */
|
|
68
|
+
export const MODEL_B_POLICY_ACCEPTED_VERSIONS = Object.freeze([1, 2]);
|
|
47
69
|
const MODEL_B_LINEAGE_PATTERN = new RegExp(`^${MODEL_B_POLICY_LINEAGE_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')} v(\\d+)$`);
|
|
48
70
|
/** The lineage version N of a policy description, or undefined when the description is not of our lineage. */
|
|
49
71
|
export function modelBLineageVersion(description) {
|
|
@@ -62,24 +84,81 @@ export const MODEL_B_CHAIN_ID = 8453;
|
|
|
62
84
|
*/
|
|
63
85
|
export const MODEL_B_DELEGATION_EXPIRY_DEFAULT_DAYS = 90;
|
|
64
86
|
export const MODEL_B_DELEGATION_EXPIRY_MAX_DAYS = 180;
|
|
65
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* The floor `boundedDelegationExpiry` has always enforced, NAMED — it was a literal `1` inside the bound,
|
|
89
|
+
* so the reserve schema could not state the range it accepts without hand-copying the number, and a
|
|
90
|
+
* hand-copied bound is a bound that drifts. Mirrors `MODEL_B_DELEGATION_EXPIRY_MIN_DAYS` in the dApp.
|
|
91
|
+
*/
|
|
92
|
+
export const MODEL_B_DELEGATION_EXPIRY_MIN_DAYS = 1;
|
|
93
|
+
/**
|
|
94
|
+
* A requested expiry, bounded: a hostile, absent or over-long value never reaches the mint.
|
|
95
|
+
*
|
|
96
|
+
* 🔴 THE RESULT LANDS ON A WHOLE SECOND (round 3). CDP stores `expiresAt` at whole-second granularity and
|
|
97
|
+
* truncates milliseconds toward zero — verified live on the dev project 2026-09-02 (`…:46.137Z` came back
|
|
98
|
+
* as `…:46Z` from `createDelegation` AND from `getDelegationForEndUser`; `…:45.999Z` came back as
|
|
99
|
+
* `…:45Z`, so it truncates rather than rounds). Everything downstream — the reservation, the durable row
|
|
100
|
+
* and every enforcement read — keys on that second, so the bound produces it directly rather than leaving
|
|
101
|
+
* a millisecond remainder for one of them to drop and another to keep.
|
|
102
|
+
*/
|
|
66
103
|
export function boundedDelegationExpiry(requestedDays, nowMs) {
|
|
67
104
|
const days = requestedDays === undefined || requestedDays === null
|
|
68
105
|
? MODEL_B_DELEGATION_EXPIRY_DEFAULT_DAYS
|
|
69
106
|
: Number(requestedDays);
|
|
70
|
-
if (!Number.isInteger(days) ||
|
|
71
|
-
|
|
107
|
+
if (!Number.isInteger(days) ||
|
|
108
|
+
days < MODEL_B_DELEGATION_EXPIRY_MIN_DAYS ||
|
|
109
|
+
days > MODEL_B_DELEGATION_EXPIRY_MAX_DAYS) {
|
|
110
|
+
throw new Error(`delegation expiry must be a whole number of days between ${MODEL_B_DELEGATION_EXPIRY_MIN_DAYS} and ${MODEL_B_DELEGATION_EXPIRY_MAX_DAYS}`);
|
|
72
111
|
}
|
|
73
|
-
|
|
112
|
+
const wholeSecond = Math.floor((nowMs + days * 86_400_000) / 1000) * 1000;
|
|
113
|
+
return Object.freeze({ days, expiresAt: new Date(wholeSecond).toISOString() });
|
|
74
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* The mint-expiry helpers live with the store that owns the reserved second; re-exported here because
|
|
117
|
+
* this is the module every consumer of the Model-B numbers already imports.
|
|
118
|
+
*/
|
|
119
|
+
export { MINT_EXPIRY_FILLER_MS as MODEL_B_MINT_EXPIRY_FILLER_MS } from './execution-delegation-cap-store.js';
|
|
120
|
+
export { mintExpiryForSecond as mintExpiryFor } from './execution-delegation-cap-store.js';
|
|
121
|
+
export { MODEL_B_CAP_RULESET_VERSION } from './execution-delegation-cap-store.js';
|
|
75
122
|
/**
|
|
76
123
|
* The per-swap cap in USD — the Founder's number (word `fence cap 5000`, 2026-08-29). It bounds ONE swap:
|
|
77
124
|
* the engine carries no daily or total limit, and no copy may imply one. Changing it = a new fence version.
|
|
78
125
|
* Engine conditions are on raw calldata, so the cap is expressed per allowed input token: v1 allows USDC
|
|
79
126
|
* only, where the mapping is exact (5,000 USD = 5,000,000,000 atomic on the approve amount, which is what
|
|
80
127
|
* bounds the swap's `fromAmount` on-chain).
|
|
128
|
+
*
|
|
129
|
+
* 🔴 THIS IS THE CEILING, SHARED BY EVERY DELEGATED USER — NOT ANY ONE PERSON'S CAP (Founder word
|
|
130
|
+
* `delegate cap choice`, 2026-09-02). Coinbase's engine offers end-user accounts exactly one fence, the
|
|
131
|
+
* project-scope policy, so the number compiled into those rules is the same for everybody and CANNOT be
|
|
132
|
+
* narrowed per user by the engine. A person who wants a lower number — "try it with $10" — gets it from
|
|
133
|
+
* Otto's own server: their choice is recorded against their grant (`execution-delegation-cap-store.ts`)
|
|
134
|
+
* and enforced on every delegated send, on top of this ceiling and never above it.
|
|
81
135
|
*/
|
|
82
136
|
export const MODEL_B_PER_SWAP_CAP_USD = 5000;
|
|
137
|
+
/**
|
|
138
|
+
* The floor of the range a person may choose. One whole dollar: the smallest cap that can still pass a
|
|
139
|
+
* real swap, and low enough that "try it with $10" is nowhere near it.
|
|
140
|
+
*/
|
|
141
|
+
export const MODEL_B_PER_SWAP_CAP_MIN_USD = 1;
|
|
142
|
+
/**
|
|
143
|
+
* A requested per-swap cap, bounded — the same shape as `boundedDelegationExpiry`, and the ONLY place a
|
|
144
|
+
* requested number becomes a cap.
|
|
145
|
+
*
|
|
146
|
+
* 🔴 IT REFUSES; IT NEVER CLAMPS. A hostile 50,000 is not silently turned into 5,000: a caller that asked
|
|
147
|
+
* for a number outside the range is a caller whose intent this server does not know, and quietly
|
|
148
|
+
* substituting one would make "the cap you chose" a claim nobody could check. Absent means the ceiling —
|
|
149
|
+
* the terms every grant already carried before a choice existed.
|
|
150
|
+
*/
|
|
151
|
+
export function boundedPerSwapCap(requestedUsd) {
|
|
152
|
+
if (requestedUsd === undefined || requestedUsd === null)
|
|
153
|
+
return MODEL_B_PER_SWAP_CAP_USD;
|
|
154
|
+
const capUsd = Number(requestedUsd);
|
|
155
|
+
if (!Number.isInteger(capUsd) ||
|
|
156
|
+
capUsd < MODEL_B_PER_SWAP_CAP_MIN_USD ||
|
|
157
|
+
capUsd > MODEL_B_PER_SWAP_CAP_USD) {
|
|
158
|
+
throw new Error(`per-swap cap must be a whole number of USD between ${MODEL_B_PER_SWAP_CAP_MIN_USD} and ${MODEL_B_PER_SWAP_CAP_USD}`);
|
|
159
|
+
}
|
|
160
|
+
return capUsd;
|
|
161
|
+
}
|
|
83
162
|
export const BASE_USDC = getAddress('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913');
|
|
84
163
|
/**
|
|
85
164
|
* The input tokens a delegated swap may spend, v1: USDC only — the one token whose USD cap is exact.
|
|
@@ -92,6 +171,25 @@ export function modelBInputToken(token) {
|
|
|
92
171
|
const target = getAddress(token);
|
|
93
172
|
return MODEL_B_INPUT_TOKENS_V1.find((entry) => entry.address === target);
|
|
94
173
|
}
|
|
174
|
+
export const MODEL_B_WITHDRAW_TOKENS_V2 = Object.freeze(EXECUTABLE_YIELD_MARKETS.filter((market) => market.protocol === 'MORPHO').map((market) => Object.freeze({
|
|
175
|
+
symbol: `${market.venue} shares`,
|
|
176
|
+
address: getAddress(market.vaultAddress),
|
|
177
|
+
decimals: 18,
|
|
178
|
+
marketId: market.id,
|
|
179
|
+
})));
|
|
180
|
+
export function modelBWithdrawToken(token) {
|
|
181
|
+
const target = getAddress(token);
|
|
182
|
+
return MODEL_B_WITHDRAW_TOKENS_V2.find((entry) => entry.address === target);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The engine-side bound on ONE withdraw's approve, in share atomic units: 5,000 shares. A USDC vault's
|
|
186
|
+
* share is worth ≥ 1 USDC by construction at inception and accrues from there (gtUSDCp ≈ 1.107 USDC on
|
|
187
|
+
* 2026-09-03), so 5,000 shares ≈ the 5,000 USD per-swap ceiling at par and somewhat more as yield accrues;
|
|
188
|
+
* after a vault loss the bound only tightens. It is the residual bound for a re-pointed withdraw, the same
|
|
189
|
+
* class as the swap rule's; the person's own cap does NOT apply to a withdraw (see the send gate: a withdraw
|
|
190
|
+
* returns the person's own assets to the person and never increases exposure).
|
|
191
|
+
*/
|
|
192
|
+
export const MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC = 5000n * 10n ** 18n;
|
|
95
193
|
/** The cap in the token's atomic units. */
|
|
96
194
|
export function modelBPerSwapCapAtomic(token, capUsd = MODEL_B_PER_SWAP_CAP_USD) {
|
|
97
195
|
if (!Number.isSafeInteger(capUsd) || capUsd <= 0)
|
|
@@ -140,7 +238,8 @@ export const GENERIC_SWAP_V3_ABI = [
|
|
|
140
238
|
},
|
|
141
239
|
];
|
|
142
240
|
/** The static v1 rules: one approve rule per allow-listed input token, one router rule. */
|
|
143
|
-
|
|
241
|
+
/** The v1 rules, frozen as history: USDC approve to the diamond up to the cap + the router rule. */
|
|
242
|
+
export function modelBProjectPolicyRulesV1(capUsd = MODEL_B_PER_SWAP_CAP_USD) {
|
|
144
243
|
const approveRules = MODEL_B_INPUT_TOKENS_V1.map((token) => ({
|
|
145
244
|
action: 'accept',
|
|
146
245
|
operation: 'sendEndUserEvmTransaction',
|
|
@@ -186,38 +285,99 @@ export function modelBProjectPolicyRules(capUsd = MODEL_B_PER_SWAP_CAP_USD) {
|
|
|
186
285
|
};
|
|
187
286
|
return Object.freeze([...approveRules, routerRule]);
|
|
188
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* The v2 rules = v1 + (a) one approve rule per registry Morpho share token, spender = the diamond only,
|
|
290
|
+
* value ≤ `MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC`, ethValue 0 — the delegated withdraw's allowance leg; the
|
|
291
|
+
* router rule is UNCHANGED (no receiver condition; the person's exit path); (b) a SIGN-ONLY accept rule
|
|
292
|
+
* (`signEndUserEvmTransaction`) for a USDC `transfer(to, value)` with value ≤ the per-swap cap and NO
|
|
293
|
+
* recipient condition — the person's own USDC exit without a swap, signed in their browser and broadcast
|
|
294
|
+
* by the page. The CDP policy schema carries no initiator/session criterion, so the engine cannot tell a
|
|
295
|
+
* user-signed send from a delegated one on the SAME operation; what it can do is fence a DIFFERENT
|
|
296
|
+
* operation, and Otto's delegated signer only ever calls the send operation (`adapter/cdp-signer.ts`).
|
|
297
|
+
* Stated residual: the developer-side end-user client also exposes `signEvmTransaction`, so under a
|
|
298
|
+
* delegation a compromised Otto could sign a USDC transfer to any address up to the cap — the same
|
|
299
|
+
* class and bound as the router rule's missing receiver condition, not a new one.
|
|
300
|
+
*/
|
|
301
|
+
export function modelBProjectPolicyRules(capUsd = MODEL_B_PER_SWAP_CAP_USD) {
|
|
302
|
+
const v1 = modelBProjectPolicyRulesV1(capUsd);
|
|
303
|
+
const routerRule = v1[v1.length - 1];
|
|
304
|
+
const inputApproveRules = v1.slice(0, -1);
|
|
305
|
+
const shareApproveRules = MODEL_B_WITHDRAW_TOKENS_V2.map((token) => ({
|
|
306
|
+
action: 'accept',
|
|
307
|
+
operation: 'sendEndUserEvmTransaction',
|
|
308
|
+
criteria: [
|
|
309
|
+
{ type: 'evmNetwork', networks: [MODEL_B_POLICY_NETWORK], operator: 'in' },
|
|
310
|
+
{ type: 'evmAddress', addresses: [token.address], operator: 'in' },
|
|
311
|
+
{ type: 'ethValue', ethValue: '0', operator: '<=' },
|
|
312
|
+
{
|
|
313
|
+
type: 'evmData',
|
|
314
|
+
abi: 'erc20',
|
|
315
|
+
conditions: [
|
|
316
|
+
{
|
|
317
|
+
function: 'approve',
|
|
318
|
+
params: [
|
|
319
|
+
{ name: 'spender', operator: 'in', values: [LIFI_DIAMOND_ADDRESS] },
|
|
320
|
+
{ name: 'value', operator: '<=', value: MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC.toString() },
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
}));
|
|
327
|
+
/**
|
|
328
|
+
* 🔴 NO NETWORK CRITERION ON THE SIGN RULE — by the SDK's schema, not by choice (codex gate r1 HIGH 1):
|
|
329
|
+
* `signEndUserEvmTransaction` admits only `ethValue | evmAddress | netUSDChange | evmData`
|
|
330
|
+
* (`@coinbase/cdp-sdk/_types/policies/evmSchema.d.ts`); `evmNetwork` is a discriminator only the SEND
|
|
331
|
+
* operation accepts, and the SDK's create/update parse throws on it BEFORE any write. So this rule is
|
|
332
|
+
* pinned by the USDC contract ADDRESS (Base USDC lives at that address on Base alone) and by the amount,
|
|
333
|
+
* not by a network name. Stated residual: the same address on another network would be signable up to
|
|
334
|
+
* the cap — a broader statement than v1's, recorded in the PR for the Founder. A test parses the whole
|
|
335
|
+
* body through the SDK's own CreatePolicyBodySchema/UpdatePolicyBodySchema so this can never regress.
|
|
336
|
+
*/
|
|
337
|
+
const userSignedUsdcTransferRules = MODEL_B_INPUT_TOKENS_V1.map((token) => ({
|
|
338
|
+
action: 'accept',
|
|
339
|
+
operation: 'signEndUserEvmTransaction',
|
|
340
|
+
criteria: [
|
|
341
|
+
{ type: 'evmAddress', addresses: [token.address], operator: 'in' },
|
|
342
|
+
{ type: 'ethValue', ethValue: '0', operator: '<=' },
|
|
343
|
+
{
|
|
344
|
+
type: 'evmData',
|
|
345
|
+
abi: 'erc20',
|
|
346
|
+
conditions: [
|
|
347
|
+
{
|
|
348
|
+
function: 'transfer',
|
|
349
|
+
params: [
|
|
350
|
+
{ name: 'value', operator: '<=', value: modelBPerSwapCapAtomic(token, capUsd).toString() },
|
|
351
|
+
],
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
}));
|
|
357
|
+
return Object.freeze([
|
|
358
|
+
...inputApproveRules,
|
|
359
|
+
...shareApproveRules,
|
|
360
|
+
routerRule,
|
|
361
|
+
...userSignedUsdcTransferRules,
|
|
362
|
+
]);
|
|
363
|
+
}
|
|
189
364
|
/** The body PR-2's installer creates or updates: scope, versioned description, the v1 rules. */
|
|
190
365
|
export function modelBProjectPolicyBody() {
|
|
191
366
|
return Object.freeze({
|
|
192
367
|
scope: 'project',
|
|
193
|
-
description:
|
|
368
|
+
description: MODEL_B_POLICY_NAME,
|
|
194
369
|
rules: modelBProjectPolicyRules(),
|
|
195
370
|
});
|
|
196
371
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
out[key] = canonical(item);
|
|
207
|
-
}
|
|
208
|
-
return out;
|
|
209
|
-
}
|
|
210
|
-
if (typeof value === 'string' && /^0x[0-9a-fA-F]+$/.test(value))
|
|
211
|
-
return value.toLowerCase();
|
|
212
|
-
return value;
|
|
213
|
-
}
|
|
214
|
-
/** SHA-256 over the canonical JSON of a rule list — the fence's integrity check at read-back. */
|
|
215
|
-
export function policyRulesDigest(rules) {
|
|
216
|
-
return createHash('sha256')
|
|
217
|
-
.update(JSON.stringify(canonical(rules)))
|
|
218
|
-
.digest('hex');
|
|
219
|
-
}
|
|
220
|
-
export const MODEL_B_POLICY_DIGEST_V1 = policyRulesDigest(modelBProjectPolicyRules());
|
|
372
|
+
export const MODEL_B_POLICY_DIGEST_V1 = policyRulesDigest(modelBProjectPolicyRulesV1());
|
|
373
|
+
export const MODEL_B_POLICY_DIGEST_V2 = policyRulesDigest(modelBProjectPolicyRules());
|
|
374
|
+
/** The CURRENT digest (v2). */
|
|
375
|
+
export const MODEL_B_POLICY_DIGEST = MODEL_B_POLICY_DIGEST_V2;
|
|
376
|
+
/** name → {version, digest} for every accepted lineage version. */
|
|
377
|
+
export const MODEL_B_POLICY_DIGEST_BY_NAME = new Map([
|
|
378
|
+
[MODEL_B_POLICY_NAME_V1, { version: 1, digest: MODEL_B_POLICY_DIGEST_V1 }],
|
|
379
|
+
[MODEL_B_POLICY_NAME_V2, { version: 2, digest: MODEL_B_POLICY_DIGEST_V2 }],
|
|
380
|
+
]);
|
|
221
381
|
/**
|
|
222
382
|
* The fence check the delegated-send path runs BEFORE any send: EXACTLY ONE project-scope policy reads back
|
|
223
383
|
* (Coinbase allows one; more is an anomalous snapshot and never blessed on the strength of "one of them
|
|
@@ -240,22 +400,46 @@ export function readBackModelBFence(policies) {
|
|
|
240
400
|
};
|
|
241
401
|
}
|
|
242
402
|
const named = project[0];
|
|
243
|
-
|
|
403
|
+
const known = typeof named.description === 'string'
|
|
404
|
+
? MODEL_B_POLICY_DIGEST_BY_NAME.get(named.description)
|
|
405
|
+
: undefined;
|
|
406
|
+
if (!known) {
|
|
407
|
+
const compatible = readBackPinnedLegacyCompatibleCompositePolicy(named, MODEL_B_POLICY_DIGEST_V2, modelBProjectPolicyRules().length);
|
|
408
|
+
if (compatible) {
|
|
409
|
+
// Preserve the public legacy shape exactly. Composite identity is consumed only by the native rail.
|
|
410
|
+
return {
|
|
411
|
+
present: true,
|
|
412
|
+
id: compatible.policyId,
|
|
413
|
+
rulesDigest: MODEL_B_POLICY_DIGEST_V2,
|
|
414
|
+
version: 2,
|
|
415
|
+
name: MODEL_B_POLICY_NAME_V2,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
244
418
|
return {
|
|
245
419
|
present: false,
|
|
246
420
|
reason: 'name_mismatch',
|
|
247
|
-
detail: `the project policy is not "${MODEL_B_POLICY_NAME_V1}"`,
|
|
421
|
+
detail: `the project policy is not "${MODEL_B_POLICY_NAME_V1}" or "${MODEL_B_POLICY_NAME_V2}"`,
|
|
248
422
|
};
|
|
249
423
|
}
|
|
250
424
|
const rulesDigest = policyRulesDigest(named.rules);
|
|
251
|
-
if (rulesDigest !==
|
|
425
|
+
if (rulesDigest !== known.digest) {
|
|
252
426
|
return {
|
|
253
427
|
present: false,
|
|
254
428
|
reason: 'digest_mismatch',
|
|
255
|
-
detail: `the project policy "${
|
|
429
|
+
detail: `the project policy "${named.description}" carries rules that do not digest to v${known.version} (${rulesDigest.slice(0, 16)}… ≠ ${known.digest.slice(0, 16)}…)`,
|
|
256
430
|
};
|
|
257
431
|
}
|
|
258
|
-
return {
|
|
432
|
+
return {
|
|
433
|
+
present: true,
|
|
434
|
+
id: named.id,
|
|
435
|
+
rulesDigest,
|
|
436
|
+
version: known.version,
|
|
437
|
+
name: named.description,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
/** True when the read-back is THIS binary's fence version — what the fence tool leaves alone. */
|
|
441
|
+
export function fenceIsCurrent(readBack) {
|
|
442
|
+
return readBack.present && readBack.version === MODEL_B_POLICY_VERSION;
|
|
259
443
|
}
|
|
260
444
|
/**
|
|
261
445
|
* The same bound the engine enforces, checked server-side BEFORE the first step is sent: a plan the
|
|
@@ -276,4 +460,53 @@ export function assertWithinModelBFence(plan, capUsd = MODEL_B_PER_SWAP_CAP_USD,
|
|
|
276
460
|
}
|
|
277
461
|
return { token, capAtomic };
|
|
278
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* 🔴 v2 — the delegated WITHDRAW's fence: the approve token must be a registry share token, the spender the
|
|
465
|
+
* diamond, the approve amount within the share cap, and the OUTPUT token USDC (a withdraw is "shares → the
|
|
466
|
+
* person's USDC", never a swap into something else under the withdraw's cap-free rule). The receiver pin
|
|
467
|
+
* (== the signing account) is the verifier's, already applied before this runs.
|
|
468
|
+
*/
|
|
469
|
+
export function assertWithdrawWithinModelBFence(plan) {
|
|
470
|
+
const token = modelBWithdrawToken(plan.token);
|
|
471
|
+
if (!token) {
|
|
472
|
+
return refuse('DELEGATION_OUTSIDE_FENCE', `input token ${plan.token} is neither an allow-listed delegated input nor a registry vault share token`);
|
|
473
|
+
}
|
|
474
|
+
if (getAddress(plan.spender) !== LIFI_DIAMOND_ADDRESS) {
|
|
475
|
+
return refuse('DELEGATION_OUTSIDE_FENCE', `approval spender ${plan.spender} is not the reviewed LI.FI diamond`);
|
|
476
|
+
}
|
|
477
|
+
if (getAddress(plan.outputToken) !== BASE_USDC) {
|
|
478
|
+
return refuse('DELEGATION_OUTSIDE_FENCE', `a delegated withdraw must deliver USDC to the signing account; this route delivers ${plan.outputToken}`);
|
|
479
|
+
}
|
|
480
|
+
if (plan.gross > MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC) {
|
|
481
|
+
return refuse('DELEGATION_OUTSIDE_FENCE', `withdraw of ${plan.gross} share atomic units exceeds the engine bound ${MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC}`);
|
|
482
|
+
}
|
|
483
|
+
return { token };
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* THE PERSON'S OWN CAP, enforced after the engine's (Founder word `delegate cap choice`, 2026-09-02).
|
|
487
|
+
*
|
|
488
|
+
* 🔴 WHY THIS IS A SEPARATE CHECK AND A SEPARATE CODE. `assertWithinModelBFence` above answers "would
|
|
489
|
+
* Coinbase's engine have signed this?" — it is the shared ceiling and it is also enforced on-chain by the
|
|
490
|
+
* project policy, so a plan past it never reaches a signature whatever this server does. THIS check
|
|
491
|
+
* answers a different question: "is it inside the number THIS PERSON chose when they minted?" The engine
|
|
492
|
+
* cannot ask it (one project policy, shared by every delegated user), so Otto's server is the only place
|
|
493
|
+
* it can be asked, and a plan that fails it is one the engine WOULD have signed.
|
|
494
|
+
*
|
|
495
|
+
* Folding the two into one call with a smaller number would pass every shape-level test while making the
|
|
496
|
+
* two facts indistinguishable in the refusal, in the logs and in the tests. They are kept apart so a
|
|
497
|
+
* mutation that drops the user's cap reddens on its own named code rather than hiding behind the ceiling.
|
|
498
|
+
*/
|
|
499
|
+
export function assertWithinUserPerSwapCap(grossAtomic, capUsd, token) {
|
|
500
|
+
if (!Number.isInteger(capUsd) ||
|
|
501
|
+
capUsd < MODEL_B_PER_SWAP_CAP_MIN_USD ||
|
|
502
|
+
capUsd > MODEL_B_PER_SWAP_CAP_USD) {
|
|
503
|
+
// A recorded cap outside the range is a corrupted record, not a permission. Fail closed.
|
|
504
|
+
return refuse('DELEGATION_CAP_UNRECORDED', `the recorded per-swap cap (${String(capUsd)}) is not a whole number of USD between ${MODEL_B_PER_SWAP_CAP_MIN_USD} and ${MODEL_B_PER_SWAP_CAP_USD}; nothing is submitted`);
|
|
505
|
+
}
|
|
506
|
+
const capAtomic = modelBPerSwapCapAtomic(token, capUsd);
|
|
507
|
+
if (grossAtomic > capAtomic) {
|
|
508
|
+
return refuse('DELEGATION_ABOVE_USER_CAP', `gross ${grossAtomic} ${token.symbol} atomic exceeds the ${capUsd} USD per-swap cap recorded for this delegation (${capAtomic} atomic)`);
|
|
509
|
+
}
|
|
510
|
+
return { capUsd, capAtomic };
|
|
511
|
+
}
|
|
279
512
|
//# sourceMappingURL=execution-delegation-policy.js.map
|
|
@@ -31,12 +31,30 @@
|
|
|
31
31
|
*
|
|
32
32
|
* Nothing here logs the header, the secret, or a credential. Refusals are machine-readable codes; upstream
|
|
33
33
|
* error bodies are never relayed.
|
|
34
|
+
*
|
|
35
|
+
* ══════════════════════════════════════════════════════════════════════════════════════════════════
|
|
36
|
+
* 🔴 THE THREAT MODEL THE PER-SWAP CAP GATE (step 5b) ENFORCES (Founder ruling, 2026-09-03, verbatim)
|
|
37
|
+
* ══════════════════════════════════════════════════════════════════════════════════════════════════
|
|
38
|
+
* "The per-person delegation cap protects every delegated send that goes through Otto's page
|
|
39
|
+
* (/app/delegate) and Otto's CLI (otto-execute delegate). A permission a person mints or revokes
|
|
40
|
+
* directly against Coinbase with their own login, outside Otto's page and CLI, is OUT OF SCOPE: Otto
|
|
41
|
+
* cannot distinguish it (CDP returns no grant identity, only an expiry) and does not defend against it;
|
|
42
|
+
* the CDP project-scope $5,000 ceiling remains the hard backstop for every permission. The delegated
|
|
43
|
+
* agent never holds the person's login."
|
|
44
|
+
*
|
|
45
|
+
* Note the direction that holds even out of the model: a grant Otto has no CONFIRMED record for
|
|
46
|
+
* authorises NOTHING here — it is refused by name, never admitted at the shared ceiling. An
|
|
47
|
+
* out-of-model actor can therefore shorten what Otto may do for them, never widen it.
|
|
34
48
|
*/
|
|
35
49
|
import { type Address, type PublicClient } from 'viem';
|
|
36
50
|
import type { SealedWaitOptions } from './adapter/chain.js';
|
|
37
51
|
import { DelegatedPlanHaltedError, type CdpDeveloperCredentials, type CdpRig, type DelegatedHaltCause } from './adapter/cdp-signer.js';
|
|
38
52
|
import { type DelegatedSendResult } from './execution-delegated-definition.js';
|
|
39
53
|
import { type DelegationPolicyRecord, type ModelBInputToken } from './execution-delegation-policy.js';
|
|
54
|
+
import { type DelegationCapStore } from './execution-delegation-cap-store.js';
|
|
55
|
+
import { type DelegationOperationStore } from './execution-delegation-operation-store.js';
|
|
56
|
+
import { type DelegationExitStore } from './execution-delegation-exit-store.js';
|
|
57
|
+
import { type ExecutionWorkerGuardStore } from './execution-worker-guard.js';
|
|
40
58
|
import { ExecutionRefusal } from './execution-errors.js';
|
|
41
59
|
import type { ExecutionToolRuntime } from './execution-tools.js';
|
|
42
60
|
export declare const DELEGATION_ENV_NAMES: readonly ["CDP_API_KEY_ID", "CDP_API_KEY_SECRET", "CDP_WALLET_SECRET", "OTTO_DELEGATION_SERVER_SECRET", "BASE_RPC_URL"];
|
|
@@ -121,6 +139,7 @@ export interface DelegationCdpClient {
|
|
|
121
139
|
address: string;
|
|
122
140
|
transaction: `0x${string}`;
|
|
123
141
|
network: 'base';
|
|
142
|
+
idempotencyKey?: string;
|
|
124
143
|
}): Promise<{
|
|
125
144
|
readonly transactionHash: string;
|
|
126
145
|
}>;
|
|
@@ -156,6 +175,12 @@ export interface DelegationCdpClient {
|
|
|
156
175
|
export declare function createDelegationRig(cdp: DelegationCdpClient, pub: PublicClient): DelegationRig;
|
|
157
176
|
export interface DelegationServices {
|
|
158
177
|
readonly rig: DelegationRig;
|
|
178
|
+
/**
|
|
179
|
+
* The Base public client the rig was built on — exposed so the constructor can SIMULATE a quoted route
|
|
180
|
+
* from the executing account before preparing it (`adapter/route-simulator.ts`). Absent in tests that
|
|
181
|
+
* build a rig without one; the constructor then prepares without a simulation and says so.
|
|
182
|
+
*/
|
|
183
|
+
readonly pub?: PublicClient;
|
|
159
184
|
/** The server-to-server secret the caller must present in `x-otto-delegation-auth`. Never logged. */
|
|
160
185
|
readonly serverSecret: string;
|
|
161
186
|
/** Otto's PUBLIC CDP project id (a UUID) the fence-status tool publishes; undefined disables that tool. */
|
|
@@ -171,6 +196,32 @@ export interface DelegationServices {
|
|
|
171
196
|
readonly perSwapCapUsd?: number;
|
|
172
197
|
readonly inputTokens?: readonly ModelBInputToken[];
|
|
173
198
|
};
|
|
199
|
+
/**
|
|
200
|
+
* Where the per-swap cap each person CHOSE when they minted is recorded, keyed by their grant
|
|
201
|
+
* (Founder word `delegate cap choice`, 2026-09-02). Durable across redeploys — see
|
|
202
|
+
* `execution-delegation-cap-store.ts` for why a Map would not do. A send whose grant has no record
|
|
203
|
+
* here refuses; an UNAVAILABLE store refuses everything, by name.
|
|
204
|
+
*/
|
|
205
|
+
readonly capStore: DelegationCapStore;
|
|
206
|
+
/** Durable, cross-process send journal. An unavailable or not-ready journal refuses every new send. */
|
|
207
|
+
readonly operationStore: DelegationOperationStore;
|
|
208
|
+
/** Same operations.db: exit records plus the unified mint/delegated/exit per-user claim. */
|
|
209
|
+
readonly exitStore: DelegationExitStore;
|
|
210
|
+
/** Permanent per-operation SQLite files; their held writer lock is process liveness, never elapsed time. */
|
|
211
|
+
readonly workerGuards: ExecutionWorkerGuardStore;
|
|
212
|
+
/**
|
|
213
|
+
* 🔴 THE STAGED-DEPLOY GATE. False (the default in code) means no NEW permission can be reserved on
|
|
214
|
+
* this worker — see `DELEGATION_MINT_ENABLED` in `execution-delegation-cap-store.ts` for why the first
|
|
215
|
+
* rollout cannot be fenced by the ruleset handshake alone. It gates CREATION only: a send under a
|
|
216
|
+
* permission that is already confirmed still runs under its own cap, and REVOKE always works.
|
|
217
|
+
*/
|
|
218
|
+
readonly mintEnabled: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Model B trade-permission creation only. Production keeps this false permanently while
|
|
221
|
+
* mintEnabled retains its separate meaning for Muse enrolment and research permissions.
|
|
222
|
+
* Test worlds may set it true to exercise the preserved legacy grant and exit contracts.
|
|
223
|
+
*/
|
|
224
|
+
readonly tradeMintEnabled: boolean;
|
|
174
225
|
}
|
|
175
226
|
export interface DelegationCaller {
|
|
176
227
|
/** The header value as presented, or undefined when absent. Never logged. */
|