otto-intel-mcp 0.1.2 β†’ 0.1.3

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.
Files changed (51) hide show
  1. package/README.md +4 -2
  2. package/dist/adapter/cdp-signer.d.ts +44 -5
  3. package/dist/adapter/cdp-signer.js +168 -18
  4. package/dist/adapter/evm-call-failure.d.ts +29 -0
  5. package/dist/adapter/evm-call-failure.js +109 -0
  6. package/dist/adapter/refusal.d.ts +1 -1
  7. package/dist/adapter/refusal.js +13 -0
  8. package/dist/adapter/route-simulator.d.ts +91 -0
  9. package/dist/adapter/route-simulator.js +143 -0
  10. package/dist/executable-equities.d.ts +55 -0
  11. package/dist/executable-equities.js +116 -0
  12. package/dist/executable-yield-markets.d.ts +261 -0
  13. package/dist/executable-yield-markets.js +154 -0
  14. package/dist/execution-delegated-definition.d.ts +1 -0
  15. package/dist/execution-delegated-definition.js +13 -2
  16. package/dist/execution-delegation-admin-definition.d.ts +126 -5
  17. package/dist/execution-delegation-admin-definition.js +154 -5
  18. package/dist/execution-delegation-admin.d.ts +48 -2
  19. package/dist/execution-delegation-admin.js +314 -12
  20. package/dist/execution-delegation-cap-store.d.ts +369 -0
  21. package/dist/execution-delegation-cap-store.js +1083 -0
  22. package/dist/execution-delegation-policy.d.ts +153 -6
  23. package/dist/execution-delegation-policy.js +260 -16
  24. package/dist/execution-delegation.d.ts +35 -0
  25. package/dist/execution-delegation.js +224 -9
  26. package/dist/execution-errors.d.ts +1 -1
  27. package/dist/execution-errors.js +98 -0
  28. package/dist/execution-index.d.ts +3 -2
  29. package/dist/execution-index.js +3 -2
  30. package/dist/execution-registration.d.ts +51 -2
  31. package/dist/execution-registration.js +45 -2
  32. package/dist/execution-tools.d.ts +44 -1
  33. package/dist/execution-tools.js +157 -13
  34. package/dist/lifi-execution-client.d.ts +6 -0
  35. package/dist/lifi-execution-client.js +3 -0
  36. package/dist/stock-buy-definition.d.ts +338 -0
  37. package/dist/stock-buy-definition.js +127 -0
  38. package/dist/stock-buy-oracle.d.ts +8 -0
  39. package/dist/stock-buy-oracle.js +22 -0
  40. package/dist/stock-buy.d.ts +17 -0
  41. package/dist/stock-buy.js +164 -0
  42. package/dist/tool-definitions.js +1 -1
  43. package/dist/yield-deposit-definition.d.ts +323 -0
  44. package/dist/yield-deposit-definition.js +121 -0
  45. package/dist/yield-deposit.d.ts +16 -0
  46. package/dist/yield-deposit.js +141 -0
  47. package/dist/yield-withdraw-definition.d.ts +306 -0
  48. package/dist/yield-withdraw-definition.js +88 -0
  49. package/dist/yield-withdraw.d.ts +21 -0
  50. package/dist/yield-withdraw.js +120 -0
  51. package/package.json +28 -4
@@ -0,0 +1,91 @@
1
+ /**
2
+ * route-simulator.ts β€” proves a quoted swap's calldata EXECUTES before it is ever prepared for a signer.
3
+ *
4
+ * πŸ”΄ WHY (2026-09-03, the first live delegated swap): LI.FI routed a $1 USDCβ†’WETH quote through KyberSwap
5
+ * and the diamond call reverted deterministically ("Call failed") β€” at any gas, on a fresh quote too β€”
6
+ * while the same request routed through SushiSwap executed. The constructor had passed the calldata
7
+ * verbatim and pinned no tool. A static allowlist rots; a simulation from the executing account does not.
8
+ *
9
+ * HOW: `eth_call` of the swap step from the account that will sign it, at `latest`, with the input token's
10
+ * balance and its allowance to the spender OVERRIDDEN to the amount the plan approves β€” because at prepare
11
+ * time neither exists yet (the approve is the plan's own step 1). The override needs the token's storage
12
+ * layout; the table below holds every token a v1 constructor may hand in as the swap's input: USDC on Base
13
+ * (FiatTokenV2: `balances` at slot 9, `allowed` at slot 10) and the two Yield Copilot share tokens the
14
+ * delegated WITHDRAW sells (MetaMorpho vaults on plain OpenZeppelin ERC20: `_balances` at 0, `_allowances`
15
+ * at 1). πŸ”΄ A LAYOUT IS ADDED ONLY ONCE IT HAS BEEN PROVEN LIVE with this module's own probe read-back on
16
+ * the production RPC (merge gate 2026-09-04: the withdraw's share token had no layout, so a hosted withdraw
17
+ * refused as `unavailable`); a token whose layout is not in the table β€” or does not read back β€” is refused
18
+ * by name, never simulated by guesswork and never passed unsimulated.
19
+ *
20
+ * πŸ”΄ THE OVERRIDE IS PROVEN BEFORE THE STEP IS JUDGED, AND PROVEN ON BOTH SLOTS WITH PROBE VALUES NO REAL
21
+ * ACCOUNT HOLDS (gate r7, finding 4): a node that silently ignores overrides, or a token whose slots differ,
22
+ * is caught even when the account's real balance happens to equal the plan amount. A layout that does not
23
+ * read back is `unavailable`, never a pass.
24
+ *
25
+ * πŸ”΄ A STEP THE NODE COULD NOT RUN IS NOT A REVERT (gate r7, finding 3): a timeout, a rate limit or a
26
+ * dropped connection during the step call is `unavailable` β€” the route is not blamed and not denied β€”
27
+ * while only a PROVEN revert (`adapter/evm-call-failure.ts`) is `reverted`.
28
+ *
29
+ * Verified on the production RPC 2026-09-03 (overridden balance read back exactly; the Sushi calldata
30
+ * simulated OK; Kyber's reverted). Nothing here prints the RPC URL.
31
+ */
32
+ import type { Address, Hex, PublicClient } from 'viem';
33
+ export type RouteSimulation = {
34
+ readonly outcome: 'ok';
35
+ } | {
36
+ readonly outcome: 'reverted';
37
+ readonly detail: string;
38
+ } | {
39
+ readonly outcome: 'unavailable';
40
+ readonly detail: string;
41
+ };
42
+ export interface RouteSimulationRequest {
43
+ /** The account that will sign and send the plan. */
44
+ readonly from: Address;
45
+ /** The swap step, exactly as it will be sent. */
46
+ readonly to: Address;
47
+ readonly data: Hex;
48
+ readonly value: bigint;
49
+ /** The ERC20 the plan spends, the spender it approves, and the amount β€” the state the step assumes. */
50
+ readonly token: Address;
51
+ readonly spender: Address;
52
+ readonly amount: bigint;
53
+ }
54
+ export interface RouteSimulator {
55
+ simulate(request: RouteSimulationRequest): Promise<RouteSimulation>;
56
+ }
57
+ /** Storage layout of an ERC20 whose `balances` and `allowed` mappings this simulator can override. */
58
+ export interface Erc20StorageLayout {
59
+ readonly balancesSlot: bigint;
60
+ readonly allowancesSlot: bigint;
61
+ }
62
+ /** Circle's FiatTokenV2 (USDC): `mapping(address => uint256) balances` at 9, `allowed` at 10. */
63
+ export declare const FIAT_TOKEN_V2_LAYOUT: Erc20StorageLayout;
64
+ /** Plain (non-upgradeable) OpenZeppelin ERC20: `_balances` at slot 0, `_allowances` at slot 1. */
65
+ export declare const OPENZEPPELIN_ERC20_LAYOUT: Erc20StorageLayout;
66
+ /**
67
+ * Tokens whose layout is known, by lowercase address β€” every input token a v1 swap-shaped constructor can
68
+ * produce. Each entry was proven on the production Base RPC on the date given, by writing the probe values
69
+ * into the candidate slots with `eth_call` state overrides and reading them back through `balanceOf` /
70
+ * `allowance` (exactly `createRouteSimulator`'s proof); of the candidate layouts tried (plain OZ 0/1, OZ v5
71
+ * ERC-7201 namespace, OZ v4 upgradeable 51/52, FiatTokenV2 9/10, Aave aToken 52/53) exactly one read back
72
+ * for each token. `test/route-simulator.test.ts` pins that every fence-permitted withdraw share token is here.
73
+ */
74
+ export declare const KNOWN_ERC20_LAYOUTS: ReadonlyMap<string, Erc20StorageLayout>;
75
+ /**
76
+ * The two probe values the proof writes: far above any real ERC20 balance (2^200), distinct from each
77
+ * other, and distinct from the plan amount β€” so a node that ignores the override cannot pass the proof by
78
+ * coincidence, whatever the account really holds.
79
+ */
80
+ export declare const PROBE_BALANCE: bigint;
81
+ export declare const PROBE_ALLOWANCE: bigint;
82
+ /** The two storage words that make the step's preconditions true for this account. */
83
+ export declare function erc20OverrideSlots(layout: Erc20StorageLayout, owner: Address, spender: Address): {
84
+ readonly balance: Hex;
85
+ readonly allowance: Hex;
86
+ };
87
+ /**
88
+ * The production simulator over a viem public client. `layouts` defaults to the known table; a token
89
+ * without a layout is `unavailable` (the constructor then refuses rather than guessing).
90
+ */
91
+ export declare function createRouteSimulator(pub: Pick<PublicClient, 'call'>, layouts?: ReadonlyMap<string, Erc20StorageLayout>): RouteSimulator;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * route-simulator.ts β€” proves a quoted swap's calldata EXECUTES before it is ever prepared for a signer.
3
+ *
4
+ * πŸ”΄ WHY (2026-09-03, the first live delegated swap): LI.FI routed a $1 USDCβ†’WETH quote through KyberSwap
5
+ * and the diamond call reverted deterministically ("Call failed") β€” at any gas, on a fresh quote too β€”
6
+ * while the same request routed through SushiSwap executed. The constructor had passed the calldata
7
+ * verbatim and pinned no tool. A static allowlist rots; a simulation from the executing account does not.
8
+ *
9
+ * HOW: `eth_call` of the swap step from the account that will sign it, at `latest`, with the input token's
10
+ * balance and its allowance to the spender OVERRIDDEN to the amount the plan approves β€” because at prepare
11
+ * time neither exists yet (the approve is the plan's own step 1). The override needs the token's storage
12
+ * layout; the table below holds every token a v1 constructor may hand in as the swap's input: USDC on Base
13
+ * (FiatTokenV2: `balances` at slot 9, `allowed` at slot 10) and the two Yield Copilot share tokens the
14
+ * delegated WITHDRAW sells (MetaMorpho vaults on plain OpenZeppelin ERC20: `_balances` at 0, `_allowances`
15
+ * at 1). πŸ”΄ A LAYOUT IS ADDED ONLY ONCE IT HAS BEEN PROVEN LIVE with this module's own probe read-back on
16
+ * the production RPC (merge gate 2026-09-04: the withdraw's share token had no layout, so a hosted withdraw
17
+ * refused as `unavailable`); a token whose layout is not in the table β€” or does not read back β€” is refused
18
+ * by name, never simulated by guesswork and never passed unsimulated.
19
+ *
20
+ * πŸ”΄ THE OVERRIDE IS PROVEN BEFORE THE STEP IS JUDGED, AND PROVEN ON BOTH SLOTS WITH PROBE VALUES NO REAL
21
+ * ACCOUNT HOLDS (gate r7, finding 4): a node that silently ignores overrides, or a token whose slots differ,
22
+ * is caught even when the account's real balance happens to equal the plan amount. A layout that does not
23
+ * read back is `unavailable`, never a pass.
24
+ *
25
+ * πŸ”΄ A STEP THE NODE COULD NOT RUN IS NOT A REVERT (gate r7, finding 3): a timeout, a rate limit or a
26
+ * dropped connection during the step call is `unavailable` β€” the route is not blamed and not denied β€”
27
+ * while only a PROVEN revert (`adapter/evm-call-failure.ts`) is `reverted`.
28
+ *
29
+ * Verified on the production RPC 2026-09-03 (overridden balance read back exactly; the Sushi calldata
30
+ * simulated OK; Kyber's reverted). Nothing here prints the RPC URL.
31
+ */
32
+ import { encodeAbiParameters, encodeFunctionData, keccak256, pad, parseAbi, toHex } from 'viem';
33
+ import { classifyEvmCallFailure, evmCallFailureDetail } from './evm-call-failure.js';
34
+ /** Circle's FiatTokenV2 (USDC): `mapping(address => uint256) balances` at 9, `allowed` at 10. */
35
+ export const FIAT_TOKEN_V2_LAYOUT = Object.freeze({ balancesSlot: 9n, allowancesSlot: 10n });
36
+ /** Plain (non-upgradeable) OpenZeppelin ERC20: `_balances` at slot 0, `_allowances` at slot 1. */
37
+ export const OPENZEPPELIN_ERC20_LAYOUT = Object.freeze({ balancesSlot: 0n, allowancesSlot: 1n });
38
+ /**
39
+ * Tokens whose layout is known, by lowercase address β€” every input token a v1 swap-shaped constructor can
40
+ * produce. Each entry was proven on the production Base RPC on the date given, by writing the probe values
41
+ * into the candidate slots with `eth_call` state overrides and reading them back through `balanceOf` /
42
+ * `allowance` (exactly `createRouteSimulator`'s proof); of the candidate layouts tried (plain OZ 0/1, OZ v5
43
+ * ERC-7201 namespace, OZ v4 upgradeable 51/52, FiatTokenV2 9/10, Aave aToken 52/53) exactly one read back
44
+ * for each token. `test/route-simulator.test.ts` pins that every fence-permitted withdraw share token is here.
45
+ */
46
+ export const KNOWN_ERC20_LAYOUTS = new Map([
47
+ // USDC on Base (FiatTokenV2) β€” proven 2026-09-03.
48
+ ['0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', FIAT_TOKEN_V2_LAYOUT],
49
+ // gtUSDCp β€” Gauntlet USDC Prime, MetaMorpho vault share on Base β€” proven 2026-09-04.
50
+ ['0xee8f4ec5672f09119b96ab6fb59c27e1b7e44b61', OPENZEPPELIN_ERC20_LAYOUT],
51
+ // steakUSDC β€” Steakhouse USDC, MetaMorpho vault share on Base β€” proven 2026-09-04.
52
+ ['0xbeefe94c8ad530842bfe7d8b397938ffc1cb83b2', OPENZEPPELIN_ERC20_LAYOUT],
53
+ ]);
54
+ /**
55
+ * The two probe values the proof writes: far above any real ERC20 balance (2^200), distinct from each
56
+ * other, and distinct from the plan amount β€” so a node that ignores the override cannot pass the proof by
57
+ * coincidence, whatever the account really holds.
58
+ */
59
+ export const PROBE_BALANCE = (1n << 200n) + 7n;
60
+ export const PROBE_ALLOWANCE = (1n << 200n) + 11n;
61
+ const ERC20_VIEW_ABI = parseAbi([
62
+ 'function balanceOf(address owner) view returns (uint256)',
63
+ 'function allowance(address owner, address spender) view returns (uint256)',
64
+ ]);
65
+ function mappingSlot(key, slot) {
66
+ return keccak256(encodeAbiParameters([{ type: 'bytes32' }, { type: 'bytes32' }], [key, slot]));
67
+ }
68
+ /** The two storage words that make the step's preconditions true for this account. */
69
+ export function erc20OverrideSlots(layout, owner, spender) {
70
+ const ownerKey = pad(owner, { size: 32 });
71
+ const balance = mappingSlot(ownerKey, pad(toHex(layout.balancesSlot), { size: 32 }));
72
+ const inner = mappingSlot(ownerKey, pad(toHex(layout.allowancesSlot), { size: 32 }));
73
+ const allowance = mappingSlot(pad(spender, { size: 32 }), inner);
74
+ return { balance, allowance };
75
+ }
76
+ function overrideFor(token, slots, balance, allowance) {
77
+ return [
78
+ {
79
+ address: token,
80
+ stateDiff: [
81
+ { slot: slots.balance, value: pad(toHex(balance), { size: 32 }) },
82
+ { slot: slots.allowance, value: pad(toHex(allowance), { size: 32 }) },
83
+ ],
84
+ },
85
+ ];
86
+ }
87
+ /**
88
+ * The production simulator over a viem public client. `layouts` defaults to the known table; a token
89
+ * without a layout is `unavailable` (the constructor then refuses rather than guessing).
90
+ */
91
+ export function createRouteSimulator(pub, layouts = KNOWN_ERC20_LAYOUTS) {
92
+ return Object.freeze({
93
+ async simulate(request) {
94
+ const layout = layouts.get(request.token.toLowerCase());
95
+ if (!layout)
96
+ return { outcome: 'unavailable', detail: `no storage layout is known for token ${request.token}` };
97
+ const slots = erc20OverrideSlots(layout, request.from, request.spender);
98
+ // (1) PROVE the override on BOTH slots, with probe values no real account holds.
99
+ const probe = overrideFor(request.token, slots, PROBE_BALANCE, PROBE_ALLOWANCE);
100
+ try {
101
+ const balance = await pub.call({
102
+ to: request.token,
103
+ data: encodeFunctionData({ abi: ERC20_VIEW_ABI, functionName: 'balanceOf', args: [request.from] }),
104
+ stateOverride: probe,
105
+ });
106
+ const allowance = await pub.call({
107
+ to: request.token,
108
+ data: encodeFunctionData({ abi: ERC20_VIEW_ABI, functionName: 'allowance', args: [request.from, request.spender] }),
109
+ stateOverride: probe,
110
+ });
111
+ const seenBalance = balance.data ? BigInt(balance.data) : -1n;
112
+ const seenAllowance = allowance.data ? BigInt(allowance.data) : -1n;
113
+ if (seenBalance !== PROBE_BALANCE || seenAllowance !== PROBE_ALLOWANCE) {
114
+ return {
115
+ outcome: 'unavailable',
116
+ detail: `the state override did not take: balanceOf read ${seenBalance}, allowance read ${seenAllowance}`,
117
+ };
118
+ }
119
+ }
120
+ catch (error) {
121
+ return { outcome: 'unavailable', detail: `the state-override proof failed: ${evmCallFailureDetail(error)}` };
122
+ }
123
+ // (2) The step itself, from the signer's account, under the plan's own preconditions.
124
+ try {
125
+ await pub.call({
126
+ account: request.from,
127
+ to: request.to,
128
+ data: request.data,
129
+ value: request.value,
130
+ stateOverride: overrideFor(request.token, slots, request.amount, request.amount),
131
+ });
132
+ return { outcome: 'ok' };
133
+ }
134
+ catch (error) {
135
+ // Only a PROVEN revert blames the route; a node that could not run the call proves nothing.
136
+ return classifyEvmCallFailure(error) === 'revert'
137
+ ? { outcome: 'reverted', detail: evmCallFailureDetail(error) }
138
+ : { outcome: 'unavailable', detail: `the step could not be simulated: ${evmCallFailureDetail(error)}` };
139
+ }
140
+ },
141
+ });
142
+ }
143
+ //# sourceMappingURL=route-simulator.js.map
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Server-owned executable tokenized-equity registry β€” the Coinbase B20 venue on Base ONLY
3
+ * (Founder word `delegate` beyond swaps, 2026-09-03).
4
+ *
5
+ * LIVENESS (gate G4, live-derived 2026-09-04): only the B20 names whose pools actually QUOTE are
6
+ * executable β€” a $25 li.quest probe of all 13 B20 rows returned routes for exactly these EIGHT
7
+ * (AAPL AMZN GOOGL META MSTR NVDA SPCX TSLA); COIN/CRCL/INTC/MSFT/SNDK had no available quotes and
8
+ * are NOT rows here β€” an executable name that can never prepare is a lie of the registry, not a
9
+ * refusal. Re-derive when pools appear: re-run the $25 probe over the dApp registry's b20 rows and
10
+ * re-add the names that quote (the parity test pins every row byte-identical to the dApp registry).
11
+ *
12
+ * WHY B20/BASE ONLY: a delegated stock BUY must stay inside the fence as it stands β€”
13
+ * USDC (allow-listed input) β†’ an ERC-20 through the diamond's two fenced GenericSwapV3
14
+ * entry points, output to the signing account, on Base where the delegation lives. Every
15
+ * other venue is another chain (Robinhood Chain / BNB / Solana) or would need new fence
16
+ * rules; those are sized in docs/0903_delegation_empire_scope.md, not shipped here.
17
+ *
18
+ * The rows are a byte-identical copy of the dApp's generated registry
19
+ * (src/lib/stocks-terminal/registry.generated.ts, b20 venue: address / decimals / feed
20
+ * read ON-CHAIN at generation) β€” a parity test asserts the copy against that file, the
21
+ * same discipline as the executable-yield registry's TEA copy. Do NOT hand-edit
22
+ * addresses; regenerate the dApp registry and re-copy.
23
+ *
24
+ * The commitment mechanism is the yield registry's, with its own domain tag.
25
+ */
26
+ export interface ExecutableEquity {
27
+ readonly id: string;
28
+ readonly venue: 'COINBASE_B20';
29
+ readonly chainId: 8453;
30
+ readonly chainSlug: 'base';
31
+ readonly underlying: string;
32
+ readonly symbol: string;
33
+ /** The B20 wrapped-equity ERC-20 on Base β€” the swap's OUTPUT token, never a caller input. */
34
+ readonly tokenAddress: `0x${string}`;
35
+ readonly tokenDecimals: 8;
36
+ /** Chainlink total-return feed on Base (8 decimals) β€” the price-parity reference. */
37
+ readonly feed: `0x${string}`;
38
+ readonly asset: {
39
+ readonly symbol: 'USDC';
40
+ readonly address: `0x${string}`;
41
+ readonly decimals: 6;
42
+ };
43
+ }
44
+ export declare const EXECUTABLE_EQUITIES: readonly [ExecutableEquity, ExecutableEquity, ExecutableEquity, ExecutableEquity, ExecutableEquity, ExecutableEquity, ExecutableEquity, ExecutableEquity];
45
+ /** All 13 known B20 token addresses (executable + not-yet-executable), lowercased. */
46
+ export declare const KNOWN_B20_TOKENS: readonly string[];
47
+ export type ExecutableEquityCommitment = `sha256:${string}`;
48
+ /** SHA-256 commitment over every enumerable field in the complete, canonical registry record. */
49
+ export declare function getExecutableEquityCommitment(equity: ExecutableEquity): ExecutableEquityCommitment;
50
+ export declare function getExecutableEquity(equityId: string): ExecutableEquity | null;
51
+ /**
52
+ * The generic-swap refusal set: EVERY known B20 token, not just the executable ones (gate r2 β€”
53
+ * see KNOWN_B20_TOKENS for why the deny plane is wider than the prepare plane).
54
+ */
55
+ export declare const EXECUTABLE_EQUITY_TOKEN_SET: ReadonlySet<string>;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Server-owned executable tokenized-equity registry β€” the Coinbase B20 venue on Base ONLY
3
+ * (Founder word `delegate` beyond swaps, 2026-09-03).
4
+ *
5
+ * LIVENESS (gate G4, live-derived 2026-09-04): only the B20 names whose pools actually QUOTE are
6
+ * executable β€” a $25 li.quest probe of all 13 B20 rows returned routes for exactly these EIGHT
7
+ * (AAPL AMZN GOOGL META MSTR NVDA SPCX TSLA); COIN/CRCL/INTC/MSFT/SNDK had no available quotes and
8
+ * are NOT rows here β€” an executable name that can never prepare is a lie of the registry, not a
9
+ * refusal. Re-derive when pools appear: re-run the $25 probe over the dApp registry's b20 rows and
10
+ * re-add the names that quote (the parity test pins every row byte-identical to the dApp registry).
11
+ *
12
+ * WHY B20/BASE ONLY: a delegated stock BUY must stay inside the fence as it stands β€”
13
+ * USDC (allow-listed input) β†’ an ERC-20 through the diamond's two fenced GenericSwapV3
14
+ * entry points, output to the signing account, on Base where the delegation lives. Every
15
+ * other venue is another chain (Robinhood Chain / BNB / Solana) or would need new fence
16
+ * rules; those are sized in docs/0903_delegation_empire_scope.md, not shipped here.
17
+ *
18
+ * The rows are a byte-identical copy of the dApp's generated registry
19
+ * (src/lib/stocks-terminal/registry.generated.ts, b20 venue: address / decimals / feed
20
+ * read ON-CHAIN at generation) β€” a parity test asserts the copy against that file, the
21
+ * same discipline as the executable-yield registry's TEA copy. Do NOT hand-edit
22
+ * addresses; regenerate the dApp registry and re-copy.
23
+ *
24
+ * The commitment mechanism is the yield registry's, with its own domain tag.
25
+ */
26
+ import { createHash } from 'node:crypto';
27
+ const USDC_BASE = {
28
+ symbol: 'USDC',
29
+ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
30
+ decimals: 6,
31
+ };
32
+ function deepFreeze(value) {
33
+ if (value && typeof value === 'object' && !Object.isFrozen(value)) {
34
+ for (const nested of Object.values(value))
35
+ deepFreeze(nested);
36
+ Object.freeze(value);
37
+ }
38
+ return value;
39
+ }
40
+ const row = (underlying, symbol, tokenAddress, feed) => ({
41
+ id: `b20-${underlying.toLowerCase()}`,
42
+ venue: 'COINBASE_B20',
43
+ chainId: 8453,
44
+ chainSlug: 'base',
45
+ underlying,
46
+ symbol,
47
+ tokenAddress,
48
+ tokenDecimals: 8,
49
+ feed,
50
+ asset: USDC_BASE,
51
+ });
52
+ export const EXECUTABLE_EQUITIES = deepFreeze([
53
+ row('AAPL', 'AAPLc', '0xb200000000000000000000C2e324d24d7eEcd1fb', '0x787f13dEa48Db0897CbCDD985de77809D837F988'),
54
+ row('AMZN', 'AMZNc', '0xb200000000000000000000d9192b6B456483C2E8', '0x06A8E4b3aBB3B7543d8396FB2B763d22820cB295'),
55
+ row('GOOGL', 'GOOGLc', '0xb2000000000000000000002D0BA3164cc74f58B7', '0x5bF49E0ffA937CE2FfF033c739aD7C634c4D34F2'),
56
+ row('META', 'METAc', '0xb2000000000000000000008bC8786B856E61707C', '0x6526aE6797A76123638b863AeE4dD27Ba4E4b27D'),
57
+ row('MSTR', 'MSTRc', '0xb2000000000000000000004884b426556b92883d', '0xB3cE282CD188b35DA0E38D8Bc7d58e33173D202a'),
58
+ row('NVDA', 'NVDAc', '0xb20000000000000000000078ee7ce2fE4908108C', '0x04689a41629776563E6822F76f2e57D148d28513'),
59
+ row('SPCX', 'SPCXc', '0xb2000000000000000000007b9fcbd005511aCBd5', '0x6A634B235903C4ad6376892180d6fF8612e3Fa68'),
60
+ row('TSLA', 'TSLAc', '0xb2000000000000000000001e800a7f5189430cD0', '0xFaf869185383a24F8cb00e27BdA6b63B9905DCb4'),
61
+ ]);
62
+ /**
63
+ * πŸ”΄ EVERY KNOWN B20 TOKEN ON BASE β€” the DENY plane, which is NOT the executable plane (gate r2).
64
+ *
65
+ * The executable list above is the live-quoting subset; this is all 13 rows the dApp registry
66
+ * carries. The generic `otto_prepare_swap` must refuse the WHOLE class: if one of the five that
67
+ * cannot quote today gains (or intermittently exposes) a pool before anyone re-derives the
68
+ * executable set, a generic USDC→B20 swap would otherwise prepare and submit inside the fence with
69
+ * NONE of this constructor's guards β€” no registry commitment, no oracle freshness, no price parity.
70
+ * Deny is derived from the known universe; prepare is derived from what actually quotes.
71
+ * The parity test pins every address here against the dApp's generated registry.
72
+ */
73
+ const NON_EXECUTABLE_B20_TOKENS = [
74
+ '0xb200000000000000000000c85a31389D71F3ecfb', // COINc β€” no route 2026-09-04
75
+ '0xB20000000000000000000019f6E7C675b73C2e4D', // CRCLc β€” no route 2026-09-04
76
+ '0xB2000000000000000000004AFF16039bA04bdFBc', // INTCc β€” no route 2026-09-04
77
+ '0xB200000000000000000000Ab99cFa739E253872B', // MSFTc β€” no route 2026-09-04
78
+ '0xb200000000000000000000397293Cb8cda9a10c5', // SNDKc β€” no route 2026-09-04
79
+ ];
80
+ /** All 13 known B20 token addresses (executable + not-yet-executable), lowercased. */
81
+ export const KNOWN_B20_TOKENS = Object.freeze([
82
+ ...EXECUTABLE_EQUITIES.map((equity) => equity.tokenAddress.toLowerCase()),
83
+ ...NON_EXECUTABLE_B20_TOKENS.map((address) => address.toLowerCase()),
84
+ ]);
85
+ function canonicalJson(value) {
86
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') {
87
+ return JSON.stringify(value);
88
+ }
89
+ if (typeof value === 'number') {
90
+ if (!Number.isFinite(value))
91
+ throw new Error('executable equity registry contains a non-finite number');
92
+ return JSON.stringify(value);
93
+ }
94
+ if (Array.isArray(value))
95
+ return `[${value.map(canonicalJson).join(',')}]`;
96
+ if (typeof value === 'object') {
97
+ const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
98
+ return `{${entries.map(([key, nested]) => `${JSON.stringify(key)}:${canonicalJson(nested)}`).join(',')}}`;
99
+ }
100
+ throw new Error('executable equity registry contains a non-canonical value');
101
+ }
102
+ /** SHA-256 commitment over every enumerable field in the complete, canonical registry record. */
103
+ export function getExecutableEquityCommitment(equity) {
104
+ const canonical = canonicalJson(equity);
105
+ const digest = createHash('sha256').update(`otto:executable-equity:v1:${canonical}`, 'utf8').digest('hex');
106
+ return `sha256:${digest}`;
107
+ }
108
+ export function getExecutableEquity(equityId) {
109
+ return EXECUTABLE_EQUITIES.find((equity) => equity.id === equityId) ?? null;
110
+ }
111
+ /**
112
+ * The generic-swap refusal set: EVERY known B20 token, not just the executable ones (gate r2 β€”
113
+ * see KNOWN_B20_TOKENS for why the deny plane is wider than the prepare plane).
114
+ */
115
+ export const EXECUTABLE_EQUITY_TOKEN_SET = new Set(KNOWN_B20_TOKENS);
116
+ //# sourceMappingURL=executable-equities.js.map
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Server-owned yield execution registry.
3
+ *
4
+ * The dApp's server route and TEA import the exact same records. Discovery can
5
+ * attach APY data to a record, but it cannot create or mutate execution
6
+ * eligibility. The canonical commitment is server-only (both consumers run in
7
+ * Node) and covers the complete registry record, including nested fields.
8
+ */
9
+ export type ExecutableYieldAction = 'deposit' | 'withdraw';
10
+ export type ExecutableYieldCall = 'approve' | 'supply' | 'deposit' | 'withdraw' | 'redeem';
11
+ interface ExecutableYieldMarketBase {
12
+ readonly id: string;
13
+ readonly venue: string;
14
+ readonly chainId: 8453;
15
+ readonly chainSlug: 'base';
16
+ readonly asset: {
17
+ readonly symbol: 'USDC';
18
+ readonly group: 'USDC';
19
+ readonly address: `0x${string}`;
20
+ readonly decimals: 6;
21
+ };
22
+ /** Exact protocol call target (Aave Pool proxy or per-vault Morpho ERC-4626). */
23
+ readonly protocolTarget: `0x${string}`;
24
+ readonly vaultAddress: `0x${string}`;
25
+ readonly nativeValue: '0';
26
+ readonly allowedCallSequences: {
27
+ readonly deposit: readonly (readonly ExecutableYieldCall[])[];
28
+ readonly withdraw: readonly (readonly ExecutableYieldCall[])[];
29
+ };
30
+ readonly safeBinding: {
31
+ readonly executionOwner: 'SAFE';
32
+ readonly depositReceiver: 'SAFE';
33
+ readonly withdrawReceiver: 'SAFE';
34
+ };
35
+ /** Exact Yield Copilot discovery identity that may receive this execution ID. */
36
+ readonly discoveryMarketId: string;
37
+ }
38
+ export type ExecutableYieldMarket = (ExecutableYieldMarketBase & {
39
+ readonly protocol: 'AAVE';
40
+ readonly builder: 'aave-native';
41
+ readonly selectors: {
42
+ readonly approve: `0x${string}`;
43
+ readonly supply: `0x${string}`;
44
+ readonly withdraw: `0x${string}`;
45
+ };
46
+ }) | (ExecutableYieldMarketBase & {
47
+ readonly protocol: 'MORPHO';
48
+ readonly builder: 'morpho-native';
49
+ /** Pinned ERC-4626 share-token decimals for exact `redeem(max)` accounting. */
50
+ readonly vaultShareDecimals: 18;
51
+ readonly selectors: {
52
+ readonly approve: `0x${string}`;
53
+ readonly deposit: `0x${string}`;
54
+ readonly withdraw: `0x${string}`;
55
+ readonly redeem: `0x${string}`;
56
+ };
57
+ });
58
+ export declare const BASE_USDC_AAVE_V3_MARKET: {
59
+ readonly id: "base-usdc-aave-v3";
60
+ readonly venue: "AAVE V3";
61
+ readonly protocol: "AAVE";
62
+ readonly chainId: 8453;
63
+ readonly chainSlug: "base";
64
+ readonly asset: {
65
+ readonly symbol: "USDC";
66
+ readonly group: "USDC";
67
+ readonly address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
68
+ readonly decimals: 6;
69
+ };
70
+ readonly protocolTarget: "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5";
71
+ readonly vaultAddress: "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5";
72
+ readonly nativeValue: "0";
73
+ readonly builder: "aave-native";
74
+ readonly selectors: {
75
+ readonly approve: "0x095ea7b3";
76
+ readonly supply: "0x617ba037";
77
+ readonly withdraw: "0x69328dec";
78
+ };
79
+ readonly allowedCallSequences: {
80
+ readonly deposit: readonly [readonly ["approve", "supply"], readonly ["supply"]];
81
+ readonly withdraw: readonly [readonly ["withdraw"]];
82
+ };
83
+ readonly safeBinding: {
84
+ readonly executionOwner: "SAFE";
85
+ readonly depositReceiver: "SAFE";
86
+ readonly withdrawReceiver: "SAFE";
87
+ };
88
+ readonly discoveryMarketId: "aave:8453:0xa238dd80c259a72e81d7e4664a9801593f98d1c5:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
89
+ };
90
+ export declare const BASE_USDC_MORPHO_GAUNTLET_PRIME_MARKET: {
91
+ readonly id: "base-usdc-morpho-gauntlet-prime";
92
+ readonly venue: "MORPHO Β· GAUNTLET USDC PRIME";
93
+ readonly protocol: "MORPHO";
94
+ readonly chainId: 8453;
95
+ readonly chainSlug: "base";
96
+ readonly asset: {
97
+ readonly symbol: "USDC";
98
+ readonly group: "USDC";
99
+ readonly address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
100
+ readonly decimals: 6;
101
+ };
102
+ readonly protocolTarget: "0xeE8F4eC5672F09119b96Ab6fB59C27E1b7e44b61";
103
+ readonly vaultAddress: "0xeE8F4eC5672F09119b96Ab6fB59C27E1b7e44b61";
104
+ readonly vaultShareDecimals: 18;
105
+ readonly nativeValue: "0";
106
+ readonly builder: "morpho-native";
107
+ readonly selectors: {
108
+ readonly approve: "0x095ea7b3";
109
+ readonly deposit: "0x6e553f65";
110
+ readonly withdraw: "0xb460af94";
111
+ readonly redeem: "0xba087652";
112
+ };
113
+ readonly allowedCallSequences: {
114
+ readonly deposit: readonly [readonly ["approve", "deposit"], readonly ["deposit"]];
115
+ readonly withdraw: readonly [readonly ["withdraw"], readonly ["redeem"]];
116
+ };
117
+ readonly safeBinding: {
118
+ readonly executionOwner: "SAFE";
119
+ readonly depositReceiver: "SAFE";
120
+ readonly withdrawReceiver: "SAFE";
121
+ };
122
+ readonly discoveryMarketId: "morpho:8453:0xee8f4ec5672f09119b96ab6fb59c27e1b7e44b61:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
123
+ };
124
+ export declare const BASE_USDC_MORPHO_STEAKHOUSE_PRIME_MARKET: {
125
+ readonly id: "base-usdc-morpho-steakhouse-prime";
126
+ readonly venue: "MORPHO Β· STEAKHOUSE PRIME USDC";
127
+ readonly protocol: "MORPHO";
128
+ readonly chainId: 8453;
129
+ readonly chainSlug: "base";
130
+ readonly asset: {
131
+ readonly symbol: "USDC";
132
+ readonly group: "USDC";
133
+ readonly address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
134
+ readonly decimals: 6;
135
+ };
136
+ readonly protocolTarget: "0xBEEFE94c8aD530842bfE7d8B397938fFc1cb83b2";
137
+ readonly vaultAddress: "0xBEEFE94c8aD530842bfE7d8B397938fFc1cb83b2";
138
+ readonly vaultShareDecimals: 18;
139
+ readonly nativeValue: "0";
140
+ readonly builder: "morpho-native";
141
+ readonly selectors: {
142
+ readonly approve: "0x095ea7b3";
143
+ readonly deposit: "0x6e553f65";
144
+ readonly withdraw: "0xb460af94";
145
+ readonly redeem: "0xba087652";
146
+ };
147
+ readonly allowedCallSequences: {
148
+ readonly deposit: readonly [readonly ["approve", "deposit"], readonly ["deposit"]];
149
+ readonly withdraw: readonly [readonly ["withdraw"], readonly ["redeem"]];
150
+ };
151
+ readonly safeBinding: {
152
+ readonly executionOwner: "SAFE";
153
+ readonly depositReceiver: "SAFE";
154
+ readonly withdrawReceiver: "SAFE";
155
+ };
156
+ readonly discoveryMarketId: "morpho:8453:0xbeefe94c8ad530842bfe7d8b397938ffc1cb83b2:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
157
+ };
158
+ export declare const EXECUTABLE_YIELD_MARKETS: readonly [{
159
+ readonly id: "base-usdc-aave-v3";
160
+ readonly venue: "AAVE V3";
161
+ readonly protocol: "AAVE";
162
+ readonly chainId: 8453;
163
+ readonly chainSlug: "base";
164
+ readonly asset: {
165
+ readonly symbol: "USDC";
166
+ readonly group: "USDC";
167
+ readonly address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
168
+ readonly decimals: 6;
169
+ };
170
+ readonly protocolTarget: "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5";
171
+ readonly vaultAddress: "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5";
172
+ readonly nativeValue: "0";
173
+ readonly builder: "aave-native";
174
+ readonly selectors: {
175
+ readonly approve: "0x095ea7b3";
176
+ readonly supply: "0x617ba037";
177
+ readonly withdraw: "0x69328dec";
178
+ };
179
+ readonly allowedCallSequences: {
180
+ readonly deposit: readonly [readonly ["approve", "supply"], readonly ["supply"]];
181
+ readonly withdraw: readonly [readonly ["withdraw"]];
182
+ };
183
+ readonly safeBinding: {
184
+ readonly executionOwner: "SAFE";
185
+ readonly depositReceiver: "SAFE";
186
+ readonly withdrawReceiver: "SAFE";
187
+ };
188
+ readonly discoveryMarketId: "aave:8453:0xa238dd80c259a72e81d7e4664a9801593f98d1c5:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
189
+ }, {
190
+ readonly id: "base-usdc-morpho-gauntlet-prime";
191
+ readonly venue: "MORPHO Β· GAUNTLET USDC PRIME";
192
+ readonly protocol: "MORPHO";
193
+ readonly chainId: 8453;
194
+ readonly chainSlug: "base";
195
+ readonly asset: {
196
+ readonly symbol: "USDC";
197
+ readonly group: "USDC";
198
+ readonly address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
199
+ readonly decimals: 6;
200
+ };
201
+ readonly protocolTarget: "0xeE8F4eC5672F09119b96Ab6fB59C27E1b7e44b61";
202
+ readonly vaultAddress: "0xeE8F4eC5672F09119b96Ab6fB59C27E1b7e44b61";
203
+ readonly vaultShareDecimals: 18;
204
+ readonly nativeValue: "0";
205
+ readonly builder: "morpho-native";
206
+ readonly selectors: {
207
+ readonly approve: "0x095ea7b3";
208
+ readonly deposit: "0x6e553f65";
209
+ readonly withdraw: "0xb460af94";
210
+ readonly redeem: "0xba087652";
211
+ };
212
+ readonly allowedCallSequences: {
213
+ readonly deposit: readonly [readonly ["approve", "deposit"], readonly ["deposit"]];
214
+ readonly withdraw: readonly [readonly ["withdraw"], readonly ["redeem"]];
215
+ };
216
+ readonly safeBinding: {
217
+ readonly executionOwner: "SAFE";
218
+ readonly depositReceiver: "SAFE";
219
+ readonly withdrawReceiver: "SAFE";
220
+ };
221
+ readonly discoveryMarketId: "morpho:8453:0xee8f4ec5672f09119b96ab6fb59c27e1b7e44b61:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
222
+ }, {
223
+ readonly id: "base-usdc-morpho-steakhouse-prime";
224
+ readonly venue: "MORPHO Β· STEAKHOUSE PRIME USDC";
225
+ readonly protocol: "MORPHO";
226
+ readonly chainId: 8453;
227
+ readonly chainSlug: "base";
228
+ readonly asset: {
229
+ readonly symbol: "USDC";
230
+ readonly group: "USDC";
231
+ readonly address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
232
+ readonly decimals: 6;
233
+ };
234
+ readonly protocolTarget: "0xBEEFE94c8aD530842bfE7d8B397938fFc1cb83b2";
235
+ readonly vaultAddress: "0xBEEFE94c8aD530842bfE7d8B397938fFc1cb83b2";
236
+ readonly vaultShareDecimals: 18;
237
+ readonly nativeValue: "0";
238
+ readonly builder: "morpho-native";
239
+ readonly selectors: {
240
+ readonly approve: "0x095ea7b3";
241
+ readonly deposit: "0x6e553f65";
242
+ readonly withdraw: "0xb460af94";
243
+ readonly redeem: "0xba087652";
244
+ };
245
+ readonly allowedCallSequences: {
246
+ readonly deposit: readonly [readonly ["approve", "deposit"], readonly ["deposit"]];
247
+ readonly withdraw: readonly [readonly ["withdraw"], readonly ["redeem"]];
248
+ };
249
+ readonly safeBinding: {
250
+ readonly executionOwner: "SAFE";
251
+ readonly depositReceiver: "SAFE";
252
+ readonly withdrawReceiver: "SAFE";
253
+ };
254
+ readonly discoveryMarketId: "morpho:8453:0xbeefe94c8ad530842bfe7d8b397938ffc1cb83b2:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
255
+ }];
256
+ export type ExecutableYieldMarketId = (typeof EXECUTABLE_YIELD_MARKETS)[number]['id'];
257
+ export type ExecutableYieldMarketCommitment = `sha256:${string}`;
258
+ /** SHA-256 commitment over every enumerable field in the complete, canonical registry record. */
259
+ export declare function getExecutableYieldMarketCommitment(market: ExecutableYieldMarket): ExecutableYieldMarketCommitment;
260
+ export declare function getExecutableYieldMarket(marketId: string): ExecutableYieldMarket | null;
261
+ export {};