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.
Files changed (239) hide show
  1. package/README.md +76 -18
  2. package/dist/account-userop-policy-definition.d.ts +14 -0
  3. package/dist/account-userop-policy-definition.js +12 -0
  4. package/dist/account-userop-policy-relation.d.ts +371 -0
  5. package/dist/account-userop-policy-relation.js +87 -0
  6. package/dist/adapter/cdp-signer.d.ts +76 -9
  7. package/dist/adapter/cdp-signer.js +274 -33
  8. package/dist/adapter/evm-call-failure.d.ts +29 -0
  9. package/dist/adapter/evm-call-failure.js +109 -0
  10. package/dist/adapter/refusal.d.ts +1 -1
  11. package/dist/adapter/refusal.js +13 -0
  12. package/dist/adapter/route-simulator.d.ts +91 -0
  13. package/dist/adapter/route-simulator.js +143 -0
  14. package/dist/adapter-cdp-index.d.ts +1 -1
  15. package/dist/executable-equities.d.ts +55 -0
  16. package/dist/executable-equities.js +116 -0
  17. package/dist/executable-yield-markets.d.ts +261 -0
  18. package/dist/executable-yield-markets.js +154 -0
  19. package/dist/execution-account-gas-quote.d.ts +141 -0
  20. package/dist/execution-account-gas-quote.js +443 -0
  21. package/dist/execution-account-gas-review.d.ts +123 -0
  22. package/dist/execution-account-gas-review.js +104 -0
  23. package/dist/execution-account-gas-runtime.d.ts +33 -0
  24. package/dist/execution-account-gas-runtime.js +131 -0
  25. package/dist/execution-account-gas-view.d.ts +936 -0
  26. package/dist/execution-account-gas-view.js +56 -0
  27. package/dist/execution-config.d.ts +3 -0
  28. package/dist/execution-config.js +4 -0
  29. package/dist/execution-delegated-definition.d.ts +7 -6
  30. package/dist/execution-delegated-definition.js +13 -2
  31. package/dist/execution-delegation-admin-definition.d.ts +127 -5
  32. package/dist/execution-delegation-admin-definition.js +155 -5
  33. package/dist/execution-delegation-admin.d.ts +48 -2
  34. package/dist/execution-delegation-admin.js +422 -12
  35. package/dist/execution-delegation-cap-store.d.ts +386 -0
  36. package/dist/execution-delegation-cap-store.js +1150 -0
  37. package/dist/execution-delegation-exit-definition.d.ts +309 -0
  38. package/dist/execution-delegation-exit-definition.js +204 -0
  39. package/dist/execution-delegation-exit-store.d.ts +114 -0
  40. package/dist/execution-delegation-exit-store.js +671 -0
  41. package/dist/execution-delegation-exit.d.ts +4 -0
  42. package/dist/execution-delegation-exit.js +614 -0
  43. package/dist/execution-delegation-finality.d.ts +22 -0
  44. package/dist/execution-delegation-finality.js +90 -0
  45. package/dist/execution-delegation-operation-store.d.ts +129 -0
  46. package/dist/execution-delegation-operation-store.js +1107 -0
  47. package/dist/execution-delegation-operations-definition.d.ts +282 -0
  48. package/dist/execution-delegation-operations-definition.js +117 -0
  49. package/dist/execution-delegation-operations.d.ts +6 -0
  50. package/dist/execution-delegation-operations.js +282 -0
  51. package/dist/execution-delegation-policy.d.ts +154 -8
  52. package/dist/execution-delegation-policy.js +273 -40
  53. package/dist/execution-delegation.d.ts +51 -0
  54. package/dist/execution-delegation.js +441 -31
  55. package/dist/execution-errors.d.ts +1 -1
  56. package/dist/execution-errors.js +122 -0
  57. package/dist/execution-index.d.ts +7 -2
  58. package/dist/execution-index.js +7 -2
  59. package/dist/execution-journal-schema.d.ts +96 -0
  60. package/dist/execution-journal-schema.js +368 -0
  61. package/dist/execution-journal-tables.d.ts +14 -0
  62. package/dist/execution-journal-tables.js +227 -0
  63. package/dist/execution-policy-compatibility.d.ts +34 -0
  64. package/dist/execution-policy-compatibility.js +76 -0
  65. package/dist/execution-registration.d.ts +54 -2
  66. package/dist/execution-registration.js +151 -5
  67. package/dist/execution-sponsored-account-profile.d.ts +32 -0
  68. package/dist/execution-sponsored-account-profile.js +59 -0
  69. package/dist/execution-sponsored-account.d.ts +1519 -0
  70. package/dist/execution-sponsored-account.js +222 -0
  71. package/dist/execution-sponsored-definition.d.ts +1713 -0
  72. package/dist/execution-sponsored-definition.js +185 -0
  73. package/dist/execution-sponsored-gas-policy.d.ts +27 -0
  74. package/dist/execution-sponsored-gas-policy.js +110 -0
  75. package/dist/execution-sponsored-hosted.d.ts +3 -0
  76. package/dist/execution-sponsored-hosted.js +71 -0
  77. package/dist/execution-sponsored-index.d.ts +1322 -0
  78. package/dist/execution-sponsored-index.js +102 -0
  79. package/dist/execution-sponsored-mcp-definition.d.ts +1662 -0
  80. package/dist/execution-sponsored-mcp-definition.js +94 -0
  81. package/dist/execution-sponsored-receipt.d.ts +47 -0
  82. package/dist/execution-sponsored-receipt.js +74 -0
  83. package/dist/execution-sponsored-registration.d.ts +14 -0
  84. package/dist/execution-sponsored-registration.js +77 -0
  85. package/dist/execution-sponsored-runtime.d.ts +84 -0
  86. package/dist/execution-sponsored-runtime.js +149 -0
  87. package/dist/execution-sponsored-settlement.d.ts +15 -0
  88. package/dist/execution-sponsored-settlement.js +88 -0
  89. package/dist/execution-sponsored-store.d.ts +67 -0
  90. package/dist/execution-sponsored-store.js +517 -0
  91. package/dist/execution-sponsored-transport.d.ts +90 -0
  92. package/dist/execution-sponsored-transport.js +180 -0
  93. package/dist/execution-tool-definitions.d.ts +21 -21
  94. package/dist/execution-tool-definitions.js +0 -7
  95. package/dist/execution-tools.d.ts +53 -1
  96. package/dist/execution-tools.js +171 -13
  97. package/dist/execution-worker-guard.d.ts +28 -0
  98. package/dist/execution-worker-guard.js +124 -0
  99. package/dist/hybrid-yield-index.d.ts +10 -0
  100. package/dist/hybrid-yield-index.js +11 -0
  101. package/dist/hybrid-yield-markets.d.ts +96 -0
  102. package/dist/hybrid-yield-markets.js +124 -0
  103. package/dist/hybrid-yield.d.ts +411 -0
  104. package/dist/hybrid-yield.js +155 -0
  105. package/dist/hyperliquid-order-builder.js +4 -0
  106. package/dist/lifi-execution-client.d.ts +6 -0
  107. package/dist/lifi-execution-client.js +3 -0
  108. package/dist/lp-userop-account.d.ts +461 -0
  109. package/dist/lp-userop-account.js +417 -0
  110. package/dist/lp-userop-authority.d.ts +49 -0
  111. package/dist/lp-userop-authority.js +211 -0
  112. package/dist/lp-userop-definition.d.ts +233 -0
  113. package/dist/lp-userop-definition.js +85 -0
  114. package/dist/lp-userop-hosted.d.ts +3 -0
  115. package/dist/lp-userop-hosted.js +73 -0
  116. package/dist/lp-userop-index.d.ts +1569 -0
  117. package/dist/lp-userop-index.js +112 -0
  118. package/dist/lp-userop-mcp-definition.d.ts +1220 -0
  119. package/dist/lp-userop-mcp-definition.js +46 -0
  120. package/dist/lp-userop-operation.d.ts +1230 -0
  121. package/dist/lp-userop-operation.js +140 -0
  122. package/dist/lp-userop-registration.d.ts +14 -0
  123. package/dist/lp-userop-registration.js +70 -0
  124. package/dist/lp-userop-settlement.d.ts +89 -0
  125. package/dist/lp-userop-settlement.js +184 -0
  126. package/dist/lp-userop.d.ts +643 -0
  127. package/dist/lp-userop.js +343 -0
  128. package/dist/native-yield-account-definition.d.ts +1192 -0
  129. package/dist/native-yield-account-definition.js +232 -0
  130. package/dist/native-yield-account-gas.d.ts +58 -0
  131. package/dist/native-yield-account-gas.js +228 -0
  132. package/dist/native-yield-account-index.d.ts +23 -0
  133. package/dist/native-yield-account-index.js +28 -0
  134. package/dist/native-yield-account-policy.d.ts +168 -0
  135. package/dist/native-yield-account-policy.js +525 -0
  136. package/dist/native-yield-account-rpc.d.ts +8 -0
  137. package/dist/native-yield-account-rpc.js +128 -0
  138. package/dist/native-yield-account-runtime.d.ts +71 -0
  139. package/dist/native-yield-account-runtime.js +125 -0
  140. package/dist/native-yield-account-store.d.ts +40 -0
  141. package/dist/native-yield-account-store.js +307 -0
  142. package/dist/native-yield-account.d.ts +662 -0
  143. package/dist/native-yield-account.js +371 -0
  144. package/dist/native-yield-consent-store.d.ts +20 -0
  145. package/dist/native-yield-consent-store.js +317 -0
  146. package/dist/native-yield-definition.d.ts +288 -0
  147. package/dist/native-yield-definition.js +105 -0
  148. package/dist/native-yield-reader.d.ts +4 -0
  149. package/dist/native-yield-reader.js +137 -0
  150. package/dist/native-yield-settlement.d.ts +48 -0
  151. package/dist/native-yield-settlement.js +167 -0
  152. package/dist/native-yield-simulation.d.ts +18 -0
  153. package/dist/native-yield-simulation.js +45 -0
  154. package/dist/native-yield-userop-account.d.ts +675 -0
  155. package/dist/native-yield-userop-account.js +357 -0
  156. package/dist/native-yield-userop-authority.d.ts +214 -0
  157. package/dist/native-yield-userop-authority.js +296 -0
  158. package/dist/native-yield-userop-definition.d.ts +331 -0
  159. package/dist/native-yield-userop-definition.js +105 -0
  160. package/dist/native-yield-userop-hosted.d.ts +3 -0
  161. package/dist/native-yield-userop-hosted.js +71 -0
  162. package/dist/native-yield-userop-index.d.ts +1522 -0
  163. package/dist/native-yield-userop-index.js +142 -0
  164. package/dist/native-yield-userop-mcp-definition.d.ts +1356 -0
  165. package/dist/native-yield-userop-mcp-definition.js +50 -0
  166. package/dist/native-yield-userop-operation.d.ts +845 -0
  167. package/dist/native-yield-userop-operation.js +127 -0
  168. package/dist/native-yield-userop-policy.d.ts +23 -0
  169. package/dist/native-yield-userop-policy.js +25 -0
  170. package/dist/native-yield-userop-registration.d.ts +17 -0
  171. package/dist/native-yield-userop-registration.js +76 -0
  172. package/dist/native-yield-userop-settlement.d.ts +8 -0
  173. package/dist/native-yield-userop-settlement.js +79 -0
  174. package/dist/native-yield-userop-simulation.d.ts +18 -0
  175. package/dist/native-yield-userop-simulation.js +34 -0
  176. package/dist/native-yield-userop.d.ts +35 -0
  177. package/dist/native-yield-userop.js +156 -0
  178. package/dist/native-yield-verify.d.ts +30 -0
  179. package/dist/native-yield-verify.js +141 -0
  180. package/dist/native-yield.d.ts +17 -0
  181. package/dist/native-yield.js +119 -0
  182. package/dist/stdio-main.js +0 -0
  183. package/dist/stock-buy-definition.d.ts +338 -0
  184. package/dist/stock-buy-definition.js +127 -0
  185. package/dist/stock-buy-oracle.d.ts +8 -0
  186. package/dist/stock-buy-oracle.js +22 -0
  187. package/dist/stock-buy.d.ts +17 -0
  188. package/dist/stock-buy.js +164 -0
  189. package/dist/tool-definitions.d.ts +3 -1
  190. package/dist/tool-definitions.js +55 -2
  191. package/dist/vendor/lp-v3/lp-v3-atomic-contract.d.ts +445 -0
  192. package/dist/vendor/lp-v3/lp-v3-atomic-contract.js +90 -0
  193. package/dist/vendor/lp-v3/lp-v3-atomic-evidence.d.ts +14 -0
  194. package/dist/vendor/lp-v3/lp-v3-atomic-evidence.js +82 -0
  195. package/dist/vendor/lp-v3/lp-v3-atomic-index.d.ts +80 -0
  196. package/dist/vendor/lp-v3/lp-v3-atomic-index.js +19 -0
  197. package/dist/vendor/lp-v3/lp-v3-atomic-plan.d.ts +43 -0
  198. package/dist/vendor/lp-v3/lp-v3-atomic-plan.js +112 -0
  199. package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.d.ts +312 -0
  200. package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.js +41 -0
  201. package/dist/vendor/lp-v3/lp-v3-atomic-read-index.d.ts +3 -0
  202. package/dist/vendor/lp-v3/lp-v3-atomic-read-index.js +3 -0
  203. package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.d.ts +49 -0
  204. package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.js +88 -0
  205. package/dist/vendor/lp-v3/lp-v3-atomic-read.d.ts +17 -0
  206. package/dist/vendor/lp-v3/lp-v3-atomic-read.js +232 -0
  207. package/dist/vendor/lp-v3/lp-v3-atomic-settlement.d.ts +105 -0
  208. package/dist/vendor/lp-v3/lp-v3-atomic-settlement.js +162 -0
  209. package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.d.ts +606 -0
  210. package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.js +59 -0
  211. package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.d.ts +168 -0
  212. package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.js +23 -0
  213. package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.d.ts +32 -0
  214. package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.js +228 -0
  215. package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.d.ts +11 -0
  216. package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.js +91 -0
  217. package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.d.ts +9 -0
  218. package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.js +124 -0
  219. package/dist/vendor/lp-v3/lp-v3-contract.d.ts +538 -0
  220. package/dist/vendor/lp-v3/lp-v3-contract.js +137 -0
  221. package/dist/vendor/lp-v3/lp-v3-evidence.d.ts +25 -0
  222. package/dist/vendor/lp-v3/lp-v3-evidence.js +168 -0
  223. package/dist/vendor/lp-v3/lp-v3-plan.d.ts +22 -0
  224. package/dist/vendor/lp-v3/lp-v3-plan.js +135 -0
  225. package/dist/vendor/lp-v3/types.d.ts +131 -0
  226. package/dist/vendor/lp-v3/types.js +6 -0
  227. package/dist/vendor/lp-v3/v4-math.d.ts +18 -0
  228. package/dist/vendor/lp-v3/v4-math.js +90 -0
  229. package/dist/yield-deposit-definition.d.ts +374 -0
  230. package/dist/yield-deposit-definition.js +123 -0
  231. package/dist/yield-deposit.d.ts +16 -0
  232. package/dist/yield-deposit.js +149 -0
  233. package/dist/yield-route-semantics.d.ts +27 -0
  234. package/dist/yield-route-semantics.js +71 -0
  235. package/dist/yield-withdraw-definition.d.ts +357 -0
  236. package/dist/yield-withdraw-definition.js +90 -0
  237. package/dist/yield-withdraw.d.ts +11 -0
  238. package/dist/yield-withdraw.js +122 -0
  239. package/package.json +239 -7
@@ -1,9 +1,13 @@
1
+ import { createHostedLpUserOpBinding } from './lp-userop-hosted.js';
2
+ import { createHostedNativeYieldUserOpBinding } from './native-yield-userop-hosted.js';
3
+ import { createHostedSponsoredAccountBinding } from './execution-sponsored-hosted.js';
1
4
  import { randomBytes } from 'node:crypto';
2
5
  import { encodeFunctionData, getAddress, parseAbi } from 'viem';
3
6
  import { attributedFeeAmount, HL_BUILDER_ATTRIBUTION_STATUS, HYPERLIQUID_L1_SIGNATURE_CHAIN_ID, isEnumeratedCctpBridgeRoute, reviewedLiFiFeeForwarders, loadExecutionServiceConfig, POLYMARKET_CHAIN_ID, } from './execution-config.js';
4
7
  import { refuse } from './execution-errors.js';
5
8
  import { createDelegationServicesFromEnv, } from './execution-delegation.js';
6
9
  import { recordPreparedArtifact } from './prepared-artifacts.js';
10
+ import { createRouteSimulator } from './adapter/route-simulator.js';
7
11
  import { swapIntentDigestInput } from './execution-intent.js';
8
12
  import { BRIDGE_EXECUTION_ENVELOPE_SCHEMA, EXECUTION_ENVELOPE_SCHEMA, HYPERLIQUID_EXECUTION_ENVELOPE_SCHEMA, POLYMARKET_EXECUTION_ENVELOPE_SCHEMA, PREPARE_BRIDGE_INPUT_SCHEMA, PREPARE_PERP_ORDER_INPUT_SCHEMA, PREPARE_POLYMARKET_ORDER_INPUT_SCHEMA, PREPARE_SWAP_INPUT_SCHEMA, } from './execution-tool-definitions.js';
9
13
  import { accountBinding, artifactCommitmentDigest, bridgeAccountBinding, createArtifactId, executionDigest, hyperliquidAccountBinding, polymarketAccountBinding, } from './execution-types.js';
@@ -11,8 +15,12 @@ import { RestHyperliquidInfoClient, } from './hyperliquid-info-client.js';
11
15
  import { assertHyperliquidBracketCoherent, assertHyperliquidPriceOnGrid, assertHyperliquidSizeOnGrid, decodeAndAssertHyperliquidAction, hyperliquidSigningDeclaration, } from './hyperliquid-order-assertions.js';
12
16
  import { buildUnsignedHyperliquidOrderAction, } from './hyperliquid-order-builder.js';
13
17
  import { decodeAndAssertLiFiBridge } from './lifi-bridge-assertions.js';
14
- import { RestLiFiQuoteClient } from './lifi-execution-client.js';
18
+ import { RestLiFiQuoteClient, } from './lifi-execution-client.js';
15
19
  import { decodeAndAssertLiFiSwap } from './lifi-swap-assertions.js';
20
+ import { modelBWithdrawToken } from './execution-delegation-policy.js';
21
+ import { viemYieldVaultReader } from './yield-withdraw.js';
22
+ import { viemEquityOracleReader } from './stock-buy-oracle.js';
23
+ import { EXECUTABLE_EQUITY_TOKEN_SET } from './executable-equities.js';
16
24
  import { RestPolymarketClobClient } from './polymarket-clob-client.js';
17
25
  import { assertPolymarketBuilderAttributionRuled, assertPolymarketPriceOnTick, decodeAndAssertPolymarketOrder, polymarketAtomicUnits, } from './polymarket-order-assertions.js';
18
26
  import { buildUnsignedPolymarketOrder, } from './polymarket-order-builder.js';
@@ -31,6 +39,12 @@ export function createExecutionToolRuntime(config, overrides = {}) {
31
39
  entropy: overrides.entropy ?? (() => randomBytes(10)),
32
40
  clientOrderIdEntropy: overrides.clientOrderIdEntropy ?? (() => randomBytes(16)),
33
41
  ...(overrides.delegation ? { delegation: overrides.delegation } : {}),
42
+ ...(overrides.sponsoredAccount ? { sponsoredAccount: overrides.sponsoredAccount } : {}),
43
+ ...(overrides.nativeYieldUserOp ? { nativeYieldUserOp: overrides.nativeYieldUserOp } : {}),
44
+ ...(overrides.lpUserOp ? { lpUserOp: overrides.lpUserOp } : {}),
45
+ ...(overrides.routeSimulator ? { routeSimulator: overrides.routeSimulator } : {}),
46
+ ...(overrides.yieldVaultReader ? { yieldVaultReader: overrides.yieldVaultReader } : {}),
47
+ ...(overrides.equityOracleReader ? { equityOracleReader: overrides.equityOracleReader } : {}),
34
48
  });
35
49
  }
36
50
  export function createExecutionToolRuntimeFromEnv(env = process.env) {
@@ -52,8 +66,24 @@ export async function createExecutionToolRuntimeWithDelegationFromEnv(env = proc
52
66
  }
53
67
  return undefined;
54
68
  }
55
- return createExecutionToolRuntime(config, delegation ? { delegation } : {});
69
+ // The delegated rail's Base client doubles as the route simulator's view of the chain (#2125) and as
70
+ // the yield constructor's vault read and the stock constructor's Chainlink read (#2129).
71
+ return createExecutionToolRuntime(config, {
72
+ ...(delegation
73
+ ? {
74
+ delegation,
75
+ sponsoredAccount: await createHostedSponsoredAccountBinding(env, delegation),
76
+ nativeYieldUserOp: await createHostedNativeYieldUserOpBinding(env, delegation),
77
+ lpUserOp: await createHostedLpUserOpBinding(env, delegation),
78
+ yieldVaultReader: viemYieldVaultReader(delegation.rig.rig.pub),
79
+ equityOracleReader: viemEquityOracleReader(delegation.rig.rig.pub),
80
+ }
81
+ : {}),
82
+ ...(delegation?.pub ? { routeSimulator: createRouteSimulator(delegation.pub) } : {}),
83
+ });
56
84
  }
85
+ /** How many LI.FI tools the constructor will try before it refuses: the first quote plus two re-quotes. */
86
+ export const MAX_ROUTE_ATTEMPTS = 3;
57
87
  function normalizedAddress(value) {
58
88
  return getAddress(value.toLowerCase());
59
89
  }
@@ -135,6 +165,29 @@ function capabilityFor(profile) {
135
165
  }
136
166
  export async function prepareSwap(runtime, rawInput, signal) {
137
167
  const input = PREPARE_SWAP_INPUT_SCHEMA.parse(rawInput);
168
+ /**
169
+ * 🔴 REGISTRY VAULT SHARES ARE EXCLUSIVE TO THE YIELD CONSTRUCTORS (codex gate r1 HIGH 2). A generic swap
170
+ * whose input or output is a Yield Copilot vault share would reach the delegated gate as an
171
+ * indistinguishable `operation: 'swap'` artifact and be admitted as a deposit/withdraw WITHOUT the registry
172
+ * commitment and the vault-parity guard the yield constructors run. So this public constructor refuses that
173
+ * token class by name; `otto_prepare_yield_deposit` / `otto_prepare_yield_withdraw` are the only paths.
174
+ */
175
+ for (const token of [input.fromToken, input.toToken]) {
176
+ if (modelBWithdrawToken(normalizedAddress(token))) {
177
+ return refuse('UNSUPPORTED_LIFI_ROUTE', `${token} is a Yield Copilot vault share token; use otto_prepare_yield_deposit / otto_prepare_yield_withdraw, which pin the registry row and check vault parity`);
178
+ }
179
+ // Same discipline for the executable equities (2026-09-03): a generic swap into a B20 token would
180
+ // reach the delegated gate as an indistinguishable swap artifact WITHOUT the registry commitment,
181
+ // the Chainlink price-parity guard and the freshness gate. The stock constructor is the only path.
182
+ if (EXECUTABLE_EQUITY_TOKEN_SET.has(token.toLowerCase())) {
183
+ return refuse('UNSUPPORTED_LIFI_ROUTE', `${token} is an executable tokenized equity; use otto_prepare_stock_buy, which pins the registry row and checks price parity against the equity's own Chainlink mark`);
184
+ }
185
+ }
186
+ const quote = await quoteSwap(runtime, input, signal);
187
+ return prepareSwapFromQuote(runtime, input, quote);
188
+ }
189
+ /** The swap's account profile, normalized; refuses a profile whose chain is not the swap's chain. */
190
+ function swapProfile(input) {
138
191
  const profile = {
139
192
  kind: input.accountProfile.kind,
140
193
  address: normalizedAddress(input.accountProfile.address),
@@ -143,24 +196,114 @@ export async function prepareSwap(runtime, rawInput, signal) {
143
196
  if (profile.chainId !== input.chainId) {
144
197
  return refuse('ACCOUNT_BINDING_MISMATCH', 'account profile chain does not match swap chain');
145
198
  }
199
+ return profile;
200
+ }
201
+ /**
202
+ * The route-set and fee pre-checks, then ONE LI.FI quote for the parsed swap input. Split from
203
+ * `prepareSwapFromQuote` so a constructor that must read the quote itself (the Yield Copilot deposit:
204
+ * expected shares, the router's floor, the fee lines) quotes exactly once and builds the envelope from
205
+ * that same quote — a second quote could route through a different tool and carry a different floor.
206
+ */
207
+ export async function quoteSwap(runtime, input, signal) {
208
+ const profile = swapProfile(input);
146
209
  if (!reviewedLiFiFeeForwarders(input.chainId)) {
147
210
  return refuse('UNSUPPORTED_LIFI_ROUTE', 'chain is outside the reviewed LI.FI v1 route set');
148
211
  }
149
- const fromToken = normalizedAddress(input.fromToken);
150
- const toToken = normalizedAddress(input.toToken);
151
212
  const fromAmount = BigInt(input.fromAmount);
152
213
  if (attributedFeeAmount(fromAmount, runtime.config.lifiFee) <= 0n) {
153
214
  return refuse('UNSUPPORTED_LIFI_ROUTE', 'input amount is too small for the configured Otto fee');
154
215
  }
155
- const quote = await runtime.quoteClient.getSameChainSwapQuote({
156
- chainId: input.chainId,
157
- fromToken,
158
- toToken,
159
- fromAmount: input.fromAmount,
160
- sender: profile.address,
161
- receiver: profile.address,
162
- maxSlippageBps: input.slippage.maxBps,
163
- }, signal);
216
+ /**
217
+ * ══════════════════════════════════════════════════════════════════════════════════════════════
218
+ * 🔴 A ROUTE IS QUOTED ONLY ONCE IT HAS BEEN SEEN TO EXECUTE (2026-09-03, the first live delegated swap)
219
+ * ══════════════════════════════════════════════════════════════════════════════════════════════
220
+ * LI.FI picks the exchange tool per quote. That day it picked KyberSwap for a $1 USDC→WETH swap, and
221
+ * the diamond call reverted deterministically — at any gas, on a fresh quote too — while the same
222
+ * request through SushiSwap executed. Every assertion the constructor makes is about SHAPE (selector,
223
+ * receiver, fee, minimum output); none of them can see that a tool's calldata fails inside the diamond.
224
+ *
225
+ * So, on a server that has a chain view (the delegated rail's Base client), the quoted swap step is
226
+ * SIMULATED from the executing account with its preconditions overridden (balance and allowance to the
227
+ * spender — neither exists at prepare time). A route that reverts is re-quoted with that tool DENIED,
228
+ * bounded to `MAX_ROUTE_ATTEMPTS` tools; if none executes the quote refuses and names them. A
229
+ * simulation that cannot be run (no layout for the token, a node that ignores overrides) refuses too —
230
+ * an unproven route is not prepared on a server that was supposed to prove it.
231
+ *
232
+ * It lives HERE, in the one quote every swap-shaped constructor takes — the public swap and the Yield
233
+ * Copilot deposit/withdraw alike — so the quote a caller reads (expected shares, the floor, the fee
234
+ * lines) IS the quote that was proven, and `prepareSwapFromQuote` builds the envelope from exactly it.
235
+ * The proven tool and whether it was simulated ride on the returned quote (`route`), are recorded in
236
+ * the envelope's assertions, and the delegated receipt repeats them, so the receipt says which venue
237
+ * executed. A server without a chain view (the prepare-only x402 constructor) quotes as before and
238
+ * records `route_simulated:false`.
239
+ */
240
+ const fromToken = normalizedAddress(input.fromToken);
241
+ const toToken = normalizedAddress(input.toToken);
242
+ const denied = [];
243
+ for (let attempt = 1; attempt <= MAX_ROUTE_ATTEMPTS; attempt += 1) {
244
+ const quote = await runtime.quoteClient.getSameChainSwapQuote({
245
+ chainId: input.chainId,
246
+ fromToken,
247
+ toToken,
248
+ fromAmount: input.fromAmount,
249
+ sender: profile.address,
250
+ receiver: profile.address,
251
+ maxSlippageBps: input.slippage.maxBps,
252
+ // A COPY: the quote client must never see this list grow after it answered.
253
+ ...(denied.length > 0 ? { denyExchanges: Object.freeze([...denied]) } : {}),
254
+ }, signal);
255
+ const routeTool = typeof quote.tool === 'string' && quote.tool.trim() ? quote.tool.trim().slice(0, 48) : 'unknown';
256
+ if (!runtime.routeSimulator) {
257
+ return Object.freeze({ ...quote, route: Object.freeze({ tool: routeTool, simulated: false }) });
258
+ }
259
+ /**
260
+ * 🔴 SHAPE BEFORE SIMULATION (merge gate 2026-09-04). The quote is decoded and asserted HERE, on the
261
+ * quote-stage input, before the node sees it: a quote whose spender or target is not the diamond, whose
262
+ * selector, receiver, fee or floor is wrong, is refused by the assertion — it is never simulated, and a
263
+ * simulation that failed only because the quote's own `approvalAddress` was wrong can never deny a
264
+ * healthy tool. What the node simulates is the ASSERTED transaction and spender, the same bytes
265
+ * `prepareSwapFromQuote` asserts again (with the caller's floor) when it builds the envelope.
266
+ */
267
+ const asserted = decodeAndAssertLiFiSwap({
268
+ quote,
269
+ profile,
270
+ chainId: input.chainId,
271
+ fromToken,
272
+ toToken,
273
+ fromAmount,
274
+ maxSlippageBps: input.slippage.maxBps,
275
+ minAmountOut: input.slippage.minAmountOut ? BigInt(input.slippage.minAmountOut) : undefined,
276
+ config: runtime.config,
277
+ });
278
+ const simulation = await runtime.routeSimulator.simulate({
279
+ from: profile.address,
280
+ to: asserted.transaction.to,
281
+ data: asserted.transaction.data,
282
+ value: BigInt(asserted.transaction.value),
283
+ token: fromToken,
284
+ spender: asserted.approvalSpender,
285
+ amount: fromAmount,
286
+ });
287
+ if (simulation.outcome === 'ok') {
288
+ return Object.freeze({ ...quote, route: Object.freeze({ tool: routeTool, simulated: true }) });
289
+ }
290
+ if (simulation.outcome === 'unavailable') {
291
+ return refuse('UNSUPPORTED_LIFI_ROUTE', `[ROUTE_SIMULATION_UNAVAILABLE] the quoted route could not be simulated from ${profile.address}, so it is not prepared: ${simulation.detail}`);
292
+ }
293
+ denied.push(routeTool);
294
+ // A tool LI.FI did not name cannot be denied on the next quote; re-asking would only repeat it.
295
+ if (attempt === MAX_ROUTE_ATTEMPTS || routeTool === 'unknown') {
296
+ return refuse('UNSUPPORTED_LIFI_ROUTE', `[ROUTE_SIMULATION_REVERTED] no LI.FI route executes from ${profile.address} for this swap: tried ${denied.join(', ')} (last: ${simulation.detail})`);
297
+ }
298
+ }
299
+ return refuse('UNSUPPORTED_LIFI_ROUTE', 'no LI.FI quote was obtained');
300
+ }
301
+ /** Decode, assert and commit the envelope for a parsed swap input over the quote `quoteSwap` returned for it. */
302
+ export function prepareSwapFromQuote(runtime, input, quote) {
303
+ const profile = swapProfile(input);
304
+ const fromToken = normalizedAddress(input.fromToken);
305
+ const toToken = normalizedAddress(input.toToken);
306
+ const fromAmount = BigInt(input.fromAmount);
164
307
  const asserted = decodeAndAssertLiFiSwap({
165
308
  quote,
166
309
  profile,
@@ -172,6 +315,13 @@ export async function prepareSwap(runtime, rawInput, signal) {
172
315
  minAmountOut: input.slippage.minAmountOut ? BigInt(input.slippage.minAmountOut) : undefined,
173
316
  config: runtime.config,
174
317
  });
318
+ // The route facts `quoteSwap` proved; a bare quote (a caller that quoted elsewhere) was not simulated.
319
+ const route = quote.route ?? {
320
+ tool: typeof quote.tool === 'string' && quote.tool.trim() ? quote.tool.trim().slice(0, 48) : 'unknown',
321
+ simulated: false,
322
+ };
323
+ const routeTool = route.tool;
324
+ const routeSimulated = route.simulated;
175
325
  const capability = capabilityFor(profile);
176
326
  const approvalCalldata = (amount) => encodeFunctionData({
177
327
  abi: ERC20_APPROVE_ABI,
@@ -230,6 +380,9 @@ export async function prepareSwap(runtime, rawInput, signal) {
230
380
  },
231
381
  assertions: [
232
382
  ...asserted.assertions,
383
+ // The route that was prepared, and whether it was proven to execute — repeated on the delegated receipt.
384
+ { id: `route_tool:${routeTool}`, result: 'passed' },
385
+ { id: `route_simulated:${routeSimulated}`, result: 'passed' },
233
386
  { id: 'payload_digest_committed', result: 'passed' },
234
387
  { id: 'erc20_allowance_reset_ordered', result: 'passed' },
235
388
  { id: 'artifact_expiry_committed', result: 'passed' },
@@ -528,6 +681,11 @@ export async function preparePolymarketOrder(runtime, rawInput, signal) {
528
681
  */
529
682
  export async function preparePerpOrder(runtime, rawInput, signal) {
530
683
  const input = PREPARE_PERP_ORDER_INPUT_SCHEMA.parse(rawInput);
684
+ // Kept for existing account recovery only, and deliberately absent from public discovery.
685
+ // Refuse before market reads, entropy, or an injected builder can construct new exposure.
686
+ if (input.reduceOnly !== true) {
687
+ return refuse('HYPERLIQUID_RETIRED', 'New Hyperliquid trading is retired; only explicit reduce-only recovery is supported');
688
+ }
531
689
  const profile = {
532
690
  kind: input.accountProfile.kind,
533
691
  address: normalizedAddress(input.accountProfile.address),
@@ -0,0 +1,28 @@
1
+ declare const KINDS: readonly ["delegated", "exit", "native", "sponsored"];
2
+ export type ExecutionWorkerKind = (typeof KINDS)[number];
3
+ export interface ExecutionWorkerGuard {
4
+ readonly kind: ExecutionWorkerKind;
5
+ readonly id: string;
6
+ release(): void;
7
+ }
8
+ export interface ExecutionWorkerGuardStore {
9
+ readonly available: boolean;
10
+ readonly unavailableReason?: string;
11
+ /** Create/validate the operation's permanent guard file and hold its writer lock until release. */
12
+ acquire(kind: ExecutionWorkerKind, id: string, create?: boolean): Promise<ExecutionWorkerGuard>;
13
+ }
14
+ export declare class ExecutionWorkerBusyError extends Error {
15
+ constructor();
16
+ }
17
+ export declare class ExecutionWorkerGuardInvalidError extends Error {
18
+ constructor();
19
+ }
20
+ export declare function unavailableExecutionWorkerGuardStore(reason: string): ExecutionWorkerGuardStore;
21
+ /**
22
+ * The lock lives in a different SQLite file from operations.db. Holding BEGIN IMMEDIATE here therefore
23
+ * proves the prior handler is gone without blocking that same handler's durable journal transactions.
24
+ * The file and its exact metadata are permanent. Missing/corrupt metadata is a refusal, never evidence
25
+ * that an old owner died.
26
+ */
27
+ export declare function openExecutionWorkerGuardStore(root: string): Promise<ExecutionWorkerGuardStore>;
28
+ export {};
@@ -0,0 +1,124 @@
1
+ import { access, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ const GUARD_ID = /^[a-zA-Z0-9_-]{8,128}$/;
4
+ const KINDS = ['delegated', 'exit', 'native', 'sponsored'];
5
+ export class ExecutionWorkerBusyError extends Error {
6
+ constructor() {
7
+ super('another worker still owns this operation');
8
+ this.name = 'ExecutionWorkerBusyError';
9
+ }
10
+ }
11
+ export class ExecutionWorkerGuardInvalidError extends Error {
12
+ constructor() {
13
+ super('the operation worker guard is missing, corrupt, or belongs to another operation');
14
+ this.name = 'ExecutionWorkerGuardInvalidError';
15
+ }
16
+ }
17
+ export function unavailableExecutionWorkerGuardStore(reason) {
18
+ return Object.freeze({
19
+ available: false,
20
+ unavailableReason: reason,
21
+ acquire: () => Promise.reject(new ExecutionWorkerGuardInvalidError()),
22
+ });
23
+ }
24
+ /**
25
+ * The lock lives in a different SQLite file from operations.db. Holding BEGIN IMMEDIATE here therefore
26
+ * proves the prior handler is gone without blocking that same handler's durable journal transactions.
27
+ * The file and its exact metadata are permanent. Missing/corrupt metadata is a refusal, never evidence
28
+ * that an old owner died.
29
+ */
30
+ export async function openExecutionWorkerGuardStore(root) {
31
+ let DatabaseSync;
32
+ try {
33
+ const specifier = 'node:sqlite';
34
+ ({ DatabaseSync } = (await import(specifier)));
35
+ }
36
+ catch {
37
+ return unavailableExecutionWorkerGuardStore('this runtime has no node:sqlite worker locks');
38
+ }
39
+ const guardRoot = join(root, 'operation-guards');
40
+ try {
41
+ await mkdir(guardRoot, { recursive: true });
42
+ }
43
+ catch {
44
+ return unavailableExecutionWorkerGuardStore('the operation worker guard directory is unavailable');
45
+ }
46
+ return Object.freeze({
47
+ available: true,
48
+ async acquire(kind, id, create = false) {
49
+ if (!KINDS.includes(kind) || !GUARD_ID.test(id))
50
+ throw new ExecutionWorkerGuardInvalidError();
51
+ const path = join(guardRoot, `${kind}-${id}.db`);
52
+ let db;
53
+ try {
54
+ if (!create) {
55
+ try {
56
+ await access(path);
57
+ }
58
+ catch {
59
+ throw new ExecutionWorkerGuardInvalidError();
60
+ }
61
+ }
62
+ db = new DatabaseSync(path);
63
+ db.exec('PRAGMA journal_mode = WAL');
64
+ db.exec('PRAGMA synchronous = FULL');
65
+ db.exec('PRAGMA busy_timeout = 0');
66
+ if (create) {
67
+ db.exec(`CREATE TABLE IF NOT EXISTS guard_meta (
68
+ singleton INTEGER NOT NULL PRIMARY KEY CHECK (singleton = 1),
69
+ kind TEXT NOT NULL CHECK (kind IN ('delegated','exit','native','sponsored')),
70
+ guard_id TEXT NOT NULL
71
+ )`);
72
+ db.prepare('INSERT OR IGNORE INTO guard_meta (singleton, kind, guard_id) VALUES (1, ?, ?)')
73
+ .run(kind, id);
74
+ }
75
+ const meta = db.prepare('SELECT kind, guard_id FROM guard_meta WHERE singleton = 1').get();
76
+ if (meta?.kind !== kind || meta?.guard_id !== id)
77
+ throw new ExecutionWorkerGuardInvalidError();
78
+ try {
79
+ db.exec('BEGIN IMMEDIATE');
80
+ }
81
+ catch {
82
+ throw new ExecutionWorkerBusyError();
83
+ }
84
+ // Re-read under the held lock. Replacing the file or metadata can never manufacture liveness.
85
+ const held = db.prepare('SELECT kind, guard_id FROM guard_meta WHERE singleton = 1').get();
86
+ if (held?.kind !== kind || held?.guard_id !== id)
87
+ throw new ExecutionWorkerGuardInvalidError();
88
+ let released = false;
89
+ return Object.freeze({
90
+ kind,
91
+ id,
92
+ release() {
93
+ if (released)
94
+ return;
95
+ released = true;
96
+ try {
97
+ db.exec('ROLLBACK');
98
+ }
99
+ catch { /* close still releases the OS lock */ }
100
+ try {
101
+ db.close();
102
+ }
103
+ catch { /* best effort */ }
104
+ },
105
+ });
106
+ }
107
+ catch (error) {
108
+ try {
109
+ db?.close();
110
+ }
111
+ catch { /* the original refusal stands */ }
112
+ if (error instanceof ExecutionWorkerBusyError || error instanceof ExecutionWorkerGuardInvalidError)
113
+ throw error;
114
+ // node:sqlite currently reports both SQLITE_BUSY and SQLITE_LOCKED through ERR_SQLITE_ERROR;
115
+ // the stable distinction available here is its fixed engine message. Nothing upstream is relayed.
116
+ const detail = error instanceof Error ? error.message.toLowerCase() : '';
117
+ if (detail.includes('database is locked') || detail.includes('database is busy'))
118
+ throw new ExecutionWorkerBusyError();
119
+ throw new ExecutionWorkerGuardInvalidError();
120
+ }
121
+ },
122
+ });
123
+ }
124
+ //# sourceMappingURL=execution-worker-guard.js.map
@@ -0,0 +1,10 @@
1
+ /** Gated hybrid-yield library. Importing this module registers no MCP tool or signer authority. */
2
+ export * from './hybrid-yield-markets.js';
3
+ export * from './hybrid-yield.js';
4
+ export * from './native-yield-definition.js';
5
+ export * from './native-yield.js';
6
+ export * from './native-yield-reader.js';
7
+ export * from './native-yield-verify.js';
8
+ export * from './native-yield-settlement.js';
9
+ export * from './yield-route-semantics.js';
10
+ export * from './native-yield-simulation.js';
@@ -0,0 +1,11 @@
1
+ /** Gated hybrid-yield library. Importing this module registers no MCP tool or signer authority. */
2
+ export * from './hybrid-yield-markets.js';
3
+ export * from './hybrid-yield.js';
4
+ export * from './native-yield-definition.js';
5
+ export * from './native-yield.js';
6
+ export * from './native-yield-reader.js';
7
+ export * from './native-yield-verify.js';
8
+ export * from './native-yield-settlement.js';
9
+ export * from './yield-route-semantics.js';
10
+ export * from './native-yield-simulation.js';
11
+ //# sourceMappingURL=hybrid-yield-index.js.map
@@ -0,0 +1,96 @@
1
+ import { type Address } from 'viem';
2
+ export type HybridYieldChainId = 8453 | 42161;
3
+ export type HybridYieldAction = 'deposit' | 'withdraw';
4
+ export interface HybridYieldMarket {
5
+ readonly id: string;
6
+ readonly discoveryMarketId: string;
7
+ readonly chainId: HybridYieldChainId;
8
+ readonly chainSlug: 'base' | 'arbitrum';
9
+ readonly protocol: 'AAVE' | 'MORPHO';
10
+ readonly venue: string;
11
+ readonly asset: {
12
+ readonly address: Address;
13
+ readonly symbol: 'USDC' | 'WETH';
14
+ readonly decimals: 6 | 18;
15
+ readonly group: 'USDC' | 'ETH';
16
+ readonly assetClass: 'stable' | 'eth';
17
+ };
18
+ readonly position: {
19
+ readonly tokenAddress: Address;
20
+ readonly symbol: string;
21
+ readonly decimals: 6 | 18;
22
+ readonly kind: 'erc4626-shares' | 'rebasing-atoken';
23
+ };
24
+ readonly protocolTarget: Address;
25
+ readonly curation: {
26
+ readonly status: 'reviewed-candidate' | 'legacy-exit-only' | 'existing-supported';
27
+ readonly reviewedAt: '2026-09-10';
28
+ readonly rationale: string;
29
+ readonly risks: readonly string[];
30
+ readonly sources: readonly string[];
31
+ };
32
+ readonly capabilities: {
33
+ readonly native: {
34
+ readonly deposit: 'gated' | 'unavailable';
35
+ readonly withdraw: 'gated';
36
+ readonly reason: string;
37
+ };
38
+ readonly lifi: {
39
+ readonly deposit: 'existing-route' | 'unavailable';
40
+ readonly withdraw: 'existing-route' | 'unavailable';
41
+ readonly crossChain: 'gated';
42
+ };
43
+ };
44
+ /** Old pair used ONLY for the already-reviewed LI.FI constructor. */
45
+ readonly legacyRegistryCommitment: `sha256:${string}` | null;
46
+ }
47
+ export declare function freezeHybridValue<T>(value: T): T;
48
+ export declare const HYBRID_YIELD_MARKETS: readonly HybridYieldMarket[];
49
+ export declare function hybridYieldCommitment(value: unknown): `sha256:${string}`;
50
+ export declare function getHybridYieldMarket(id: string): HybridYieldMarket | null;
51
+ export declare function getHybridYieldCapabilities(): {
52
+ schemaVersion: "1";
53
+ markets: {
54
+ registryCommitment: `sha256:${string}`;
55
+ id: string;
56
+ discoveryMarketId: string;
57
+ chainId: HybridYieldChainId;
58
+ chainSlug: "base" | "arbitrum";
59
+ protocol: "AAVE" | "MORPHO";
60
+ venue: string;
61
+ asset: {
62
+ readonly address: Address;
63
+ readonly symbol: "USDC" | "WETH";
64
+ readonly decimals: 6 | 18;
65
+ readonly group: "USDC" | "ETH";
66
+ readonly assetClass: "stable" | "eth";
67
+ };
68
+ position: {
69
+ readonly tokenAddress: Address;
70
+ readonly symbol: string;
71
+ readonly decimals: 6 | 18;
72
+ readonly kind: "erc4626-shares" | "rebasing-atoken";
73
+ };
74
+ protocolTarget: Address;
75
+ curation: {
76
+ readonly status: "reviewed-candidate" | "legacy-exit-only" | "existing-supported";
77
+ readonly reviewedAt: "2026-09-10";
78
+ readonly rationale: string;
79
+ readonly risks: readonly string[];
80
+ readonly sources: readonly string[];
81
+ };
82
+ capabilities: {
83
+ readonly native: {
84
+ readonly deposit: "gated" | "unavailable";
85
+ readonly withdraw: "gated";
86
+ readonly reason: string;
87
+ };
88
+ readonly lifi: {
89
+ readonly deposit: "existing-route" | "unavailable";
90
+ readonly withdraw: "existing-route" | "unavailable";
91
+ readonly crossChain: "gated";
92
+ };
93
+ };
94
+ legacyRegistryCommitment: `sha256:${string}` | null;
95
+ }[];
96
+ };
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Chain-general capability registry for hybrid Yield. These records DO NOT extend
3
+ * the legacy TEA/Safe or CDP allowlist in executable-yield-markets.ts.
4
+ * Curation evidence: docs/0910_hybrid_yield_curation.md (10 September 2026).
5
+ */
6
+ import { createHash } from 'node:crypto';
7
+ import { getAddress } from 'viem';
8
+ import { BASE_USDC_MORPHO_GAUNTLET_PRIME_MARKET, BASE_USDC_MORPHO_STEAKHOUSE_PRIME_MARKET, getExecutableYieldMarketCommitment, } from './executable-yield-markets.js';
9
+ export function freezeHybridValue(value) {
10
+ if (value && typeof value === 'object' && !Object.isFrozen(value)) {
11
+ for (const child of Object.values(value))
12
+ freezeHybridValue(child);
13
+ Object.freeze(value);
14
+ }
15
+ return value;
16
+ }
17
+ const NATIVE_GATE = 'Requires reviewed native executor deployment, chain/account policy, limits and funded acceptance; registry membership grants no signing authority.';
18
+ const AAVE_RISKS = [
19
+ 'Aave Pool and reserve configuration can be upgraded by governance.',
20
+ 'Supply yield and available withdrawal liquidity vary with utilization; instant liquidity is not guaranteed.',
21
+ 'Underlying token, oracle, bad-debt and chain risks remain; a protocol review is not a safety guarantee.',
22
+ ];
23
+ const MORPHO_RISKS = [
24
+ 'The vault curator chooses collateral markets, oracles and caps; review the exact vault, not only Morpho.',
25
+ 'Borrower liquidation, bad debt, curator changes and depleted liquidity can impair value or delay exits.',
26
+ 'USDC issuer/peg and Base network risks remain; APY is variable and not a guarantee.',
27
+ ];
28
+ function aave(chainId, symbol, assetAddress, pool, receiptAddress, receiptSymbol) {
29
+ const chainSlug = chainId === 8453 ? 'base' : 'arbitrum';
30
+ return {
31
+ id: `${chainSlug}-${symbol.toLowerCase()}-aave-v3`,
32
+ discoveryMarketId: `aave:${chainId}:${pool.toLowerCase()}:${assetAddress.toLowerCase()}`,
33
+ chainId, chainSlug, protocol: 'AAVE', venue: `Aave V3 · ${symbol}`,
34
+ asset: {
35
+ address: getAddress(assetAddress), symbol, decimals: symbol === 'USDC' ? 6 : 18,
36
+ group: symbol === 'USDC' ? 'USDC' : 'ETH', assetClass: symbol === 'USDC' ? 'stable' : 'eth',
37
+ },
38
+ position: {
39
+ tokenAddress: getAddress(receiptAddress), symbol: receiptSymbol,
40
+ decimals: symbol === 'USDC' ? 6 : 18, kind: 'rebasing-atoken',
41
+ },
42
+ protocolTarget: getAddress(pool),
43
+ curation: {
44
+ status: 'reviewed-candidate', reviewedAt: '2026-09-10',
45
+ rationale: symbol === 'USDC'
46
+ ? 'Established already-integrated lending protocol; exact native USDC reserve and receipt token verified. Candidate by integration/exit fit, not highest APY.'
47
+ : 'Separate ETH-denominated lending exposure using plain WETH. This is lending, not staking, and accepts neither native ETH nor a liquid-staking token.',
48
+ risks: AAVE_RISKS,
49
+ sources: [
50
+ `https://github.com/aave-dao/aave-address-book/blob/main/src/AaveV3${chainId === 8453 ? 'Base' : 'Arbitrum'}.sol`,
51
+ 'https://aave.com/docs/aave-v3/smart-contracts/pool',
52
+ ],
53
+ },
54
+ capabilities: {
55
+ native: { deposit: 'gated', withdraw: 'gated', reason: NATIVE_GATE },
56
+ lifi: { deposit: 'unavailable', withdraw: 'unavailable', crossChain: 'gated' },
57
+ },
58
+ legacyRegistryCommitment: null,
59
+ };
60
+ }
61
+ function morpho(old, legacy) {
62
+ return {
63
+ id: old.id, discoveryMarketId: old.discoveryMarketId, chainId: 8453, chainSlug: 'base',
64
+ protocol: 'MORPHO', venue: old.venue,
65
+ asset: { ...old.asset, address: getAddress(old.asset.address), assetClass: 'stable' },
66
+ position: {
67
+ tokenAddress: getAddress(old.vaultAddress), symbol: legacy ? 'steakUSDC' : 'gtUSDCp',
68
+ decimals: old.vaultShareDecimals, kind: 'erc4626-shares',
69
+ },
70
+ protocolTarget: getAddress(old.protocolTarget),
71
+ curation: {
72
+ status: legacy ? 'legacy-exit-only' : 'existing-supported', reviewedAt: '2026-09-10',
73
+ rationale: legacy
74
+ ? 'Steakhouse lists this exact vault as operational but deprioritized/superseded. Retain the position and exit; do not promote new allocations.'
75
+ : 'Retained exact previously supported vault identity. A technical execution pin is not a best-yield ranking or blanket curator approval.',
76
+ risks: MORPHO_RISKS,
77
+ sources: [
78
+ `https://app.morpho.org/base/vault/${old.vaultAddress}`,
79
+ ...(legacy ? ['https://www.steakhouse.financial/docs/products/vault-products/legacy-vaults'] : []),
80
+ ],
81
+ },
82
+ capabilities: {
83
+ native: { deposit: legacy ? 'unavailable' : 'gated', withdraw: 'gated', reason: NATIVE_GATE },
84
+ // Legacy positions remain routable for exit; new preparation is disabled.
85
+ lifi: { deposit: legacy ? 'unavailable' : 'existing-route', withdraw: 'existing-route', crossChain: 'gated' },
86
+ },
87
+ legacyRegistryCommitment: getExecutableYieldMarketCommitment(old),
88
+ };
89
+ }
90
+ export const HYBRID_YIELD_MARKETS = freezeHybridValue([
91
+ aave(8453, 'USDC', '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5', '0x4e65fE4DbA92790696d040ac24Aa414708F5c0AB', 'aBasUSDC'),
92
+ aave(8453, 'WETH', '0x4200000000000000000000000000000000000006', '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5', '0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7', 'aBasWETH'),
93
+ aave(42161, 'USDC', '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', '0x794a61358D6845594F94dc1DB02A252b5b4814aD', '0x724dc807b04555b71ed48a6896b6F41593b8C637', 'aArbUSDCn'),
94
+ aave(42161, 'WETH', '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', '0x794a61358D6845594F94dc1DB02A252b5b4814aD', '0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8', 'aArbWETH'),
95
+ morpho(BASE_USDC_MORPHO_GAUNTLET_PRIME_MARKET, false),
96
+ morpho(BASE_USDC_MORPHO_STEAKHOUSE_PRIME_MARKET, true),
97
+ ]);
98
+ function canonical(value) {
99
+ if (value === null || ['string', 'boolean'].includes(typeof value))
100
+ return JSON.stringify(value);
101
+ if (typeof value === 'number' && Number.isFinite(value))
102
+ return JSON.stringify(value);
103
+ if (Array.isArray(value))
104
+ return `[${value.map(canonical).join(',')}]`;
105
+ if (typeof value === 'object')
106
+ return `{${Object.entries(value)
107
+ .sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(',')}}`;
108
+ throw new Error('Non-canonical hybrid yield value');
109
+ }
110
+ export function hybridYieldCommitment(value) {
111
+ return `sha256:${createHash('sha256').update('otto:hybrid-yield:v1:' + canonical(value)).digest('hex')}`;
112
+ }
113
+ export function getHybridYieldMarket(id) {
114
+ return HYBRID_YIELD_MARKETS.find((m) => m.id === id) ?? null;
115
+ }
116
+ export function getHybridYieldCapabilities() {
117
+ return freezeHybridValue({
118
+ schemaVersion: '1',
119
+ markets: HYBRID_YIELD_MARKETS.map((market) => ({
120
+ ...market, registryCommitment: hybridYieldCommitment(market),
121
+ })),
122
+ });
123
+ }
124
+ //# sourceMappingURL=hybrid-yield-markets.js.map