@oracle-agent/oracle 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/LICENSE +115 -201
  2. package/README.md +66 -84
  3. package/SECURITY.md +37 -12
  4. package/SETUP.md +207 -272
  5. package/dist/action-receipts.mjs +1 -0
  6. package/dist/action-semantics.mjs +1 -0
  7. package/dist/address-book.mjs +2 -0
  8. package/dist/bin/desk-server.mjs +14 -0
  9. package/dist/bin/oracle-data-mcp.mjs +8 -0
  10. package/dist/bin/oracle-init.mjs +24 -0
  11. package/dist/bin/oracle-public-server.mjs +5 -0
  12. package/dist/bin/oracle-route.mjs +22 -0
  13. package/dist/bin/oracle-scan.mjs +18 -0
  14. package/dist/bin/oracle-upgrade.mjs +21 -0
  15. package/dist/bin/oracle.mjs +68 -0
  16. package/dist/chains.mjs +1 -0
  17. package/dist/cli/commands/auth.mjs +16 -0
  18. package/dist/cli/commands/bootstrap.mjs +18 -0
  19. package/dist/cli/commands/chain.mjs +21 -0
  20. package/dist/cli/commands/chat.mjs +35 -0
  21. package/dist/cli/commands/credential.mjs +1 -0
  22. package/dist/cli/commands/data-mcp.mjs +2 -0
  23. package/dist/cli/commands/data.mjs +12 -0
  24. package/dist/cli/commands/doctor.mjs +5 -0
  25. package/dist/cli/commands/gate.mjs +13 -0
  26. package/dist/cli/commands/help.mjs +1 -0
  27. package/dist/cli/commands/init.mjs +3 -0
  28. package/dist/cli/commands/mcp.mjs +33 -0
  29. package/dist/cli/commands/model.mjs +44 -0
  30. package/dist/cli/commands/prepare.mjs +2 -0
  31. package/dist/cli/commands/public.mjs +3 -0
  32. package/dist/cli/commands/route.mjs +2 -0
  33. package/dist/cli/commands/runner.mjs +1 -0
  34. package/dist/cli/commands/scan.mjs +2 -0
  35. package/dist/cli/commands/setup.mjs +34 -0
  36. package/dist/cli/commands/sign.mjs +4 -0
  37. package/dist/cli/commands/signer.mjs +1 -0
  38. package/dist/cli/commands/upgrade.mjs +2 -0
  39. package/dist/cli/commands/vault.mjs +1 -0
  40. package/dist/cli/commands/version.mjs +2 -0
  41. package/dist/data/desk-data.mjs +12 -0
  42. package/dist/index.mjs +17 -0
  43. package/dist/nft-gas-war-guard.mjs +1 -0
  44. package/dist/onboarding/harness-configs.mjs +4 -0
  45. package/dist/portfolio-risk.mjs +1 -0
  46. package/dist/prepare-envelope.mjs +1 -0
  47. package/dist/public-control/policy-schema.mjs +1 -0
  48. package/dist/router/index.mjs +2 -0
  49. package/dist/scanner/index.mjs +4 -0
  50. package/dist/signals/index.mjs +1 -0
  51. package/dist/watch-preferences.mjs +1 -0
  52. package/package.json +27 -65
  53. package/public/oracle-splash/index.html +11 -1
  54. package/CONTRIBUTING.md +0 -98
  55. package/bin/desk-server.mjs +0 -440
  56. package/bin/oracle-data-mcp.mjs +0 -1019
  57. package/bin/oracle-init.mjs +0 -496
  58. package/bin/oracle-public-server.mjs +0 -36
  59. package/bin/oracle-route.mjs +0 -254
  60. package/bin/oracle-scan.mjs +0 -192
  61. package/bin/oracle-upgrade.mjs +0 -42
  62. package/bin/oracle.mjs +0 -33
  63. package/docs/adding-a-chain.md +0 -229
  64. package/docs/architecture.md +0 -138
  65. package/docs/cli.md +0 -47
  66. package/docs/connectors.md +0 -13
  67. package/docs/oracle-pack-standard.md +0 -29
  68. package/docs/profiles.md +0 -151
  69. package/docs/public-surface.md +0 -133
  70. package/examples/add-a-chain.mjs +0 -65
  71. package/examples/oracle-pack-template.mjs +0 -38
  72. package/examples/research-a-token.mjs +0 -70
  73. package/plugins/oracle-owner-gate/__init__.py +0 -227
  74. package/plugins/oracle-owner-gate/plugin.yaml +0 -9
  75. package/profiles/_template/SOUL.md +0 -54
  76. package/profiles/_template/profile.json +0 -22
  77. package/profiles/bitcoin-agent/SOUL.md +0 -31
  78. package/profiles/bitcoin-agent/profile.json +0 -32
  79. package/profiles/hyperliquid-agent/SOUL.md +0 -34
  80. package/profiles/hyperliquid-agent/profile.json +0 -37
  81. package/profiles/oracle/SOUL.md +0 -107
  82. package/profiles/oracle/profile.json +0 -39
  83. package/profiles/polymarket-agent/SOUL.md +0 -35
  84. package/profiles/polymarket-agent/profile.json +0 -34
  85. package/profiles/profile.schema.json +0 -90
  86. package/profiles/protocol-builder/SOUL.md +0 -57
  87. package/profiles/protocol-builder/profile.json +0 -39
  88. package/profiles/robinhood-agent/SOUL.md +0 -53
  89. package/profiles/robinhood-agent/profile.json +0 -40
  90. package/profiles/solana-agent/SOUL.md +0 -37
  91. package/profiles/solana-agent/profile.json +0 -37
  92. package/profiles/stable-agent/SOUL.md +0 -43
  93. package/profiles/stable-agent/profile.json +0 -37
  94. package/protocols/templates/safe-erc20/SECURITY.md +0 -16
  95. package/protocols/templates/safe-erc20/foundry.toml +0 -12
  96. package/protocols/templates/safe-erc20/remappings.txt +0 -2
  97. package/protocols/templates/safe-erc20/src/SafeERC20.sol +0 -53
  98. package/protocols/templates/safe-erc20/test/SafeERC20.t.sol +0 -72
  99. package/scripts/adversarial-bench.mjs +0 -114
  100. package/scripts/build-inscription.py +0 -230
  101. package/scripts/check-doc-drift.mjs +0 -123
  102. package/scripts/check-test-count.mjs +0 -105
  103. package/scripts/e2e-hl-markets.mjs +0 -21
  104. package/scripts/e2e-hl-perps.mjs +0 -48
  105. package/scripts/e2e-hypercore-staking.mjs +0 -128
  106. package/scripts/e2e-solana-bitcoin.mjs +0 -183
  107. package/scripts/protocol-template-gate.mjs +0 -15
  108. package/scripts/public-api-scan.mjs +0 -23
  109. package/scripts/secret-scan.mjs +0 -181
  110. package/scripts/verify-v3-venues.mjs +0 -192
  111. package/skills/balance/SKILL.md +0 -176
  112. package/skills/oracle-action-semantics/SKILL.md +0 -40
  113. package/skills/oracle-best-execution/SKILL.md +0 -127
  114. package/skills/oracle-bitcoin/SKILL.md +0 -53
  115. package/skills/oracle-chain-graphs-telegram-cards/SKILL.md +0 -59
  116. package/skills/oracle-chat/SKILL.md +0 -61
  117. package/skills/oracle-chat/chain.SKILL.md +0 -31
  118. package/skills/oracle-chat/setup.SKILL.md +0 -37
  119. package/skills/oracle-circuit-breaker/SKILL.md +0 -51
  120. package/skills/oracle-contract-research/SKILL.md +0 -55
  121. package/skills/oracle-desk/SKILL.md +0 -58
  122. package/skills/oracle-dex-launch/SKILL.md +0 -38
  123. package/skills/oracle-grants/SKILL.md +0 -69
  124. package/skills/oracle-hypercore-staking/SKILL.md +0 -57
  125. package/skills/oracle-hyperliquid/SKILL.md +0 -56
  126. package/skills/oracle-meme-token-sniper/SKILL.md +0 -73
  127. package/skills/oracle-multichain-nft-launch/SKILL.md +0 -338
  128. package/skills/oracle-multichain-token-launch/SKILL.md +0 -300
  129. package/skills/oracle-nft-gacha-launch/SKILL.md +0 -48
  130. package/skills/oracle-nft-mint-gas-war/SKILL.md +0 -63
  131. package/skills/oracle-polymarket/SKILL.md +0 -60
  132. package/skills/oracle-protocol-builder/SKILL.md +0 -59
  133. package/skills/oracle-protocol-security/SKILL.md +0 -60
  134. package/skills/oracle-public-product/SKILL.md +0 -44
  135. package/skills/oracle-receipts/SKILL.md +0 -52
  136. package/skills/oracle-rfq-tokenized-assets/SKILL.md +0 -69
  137. package/skills/oracle-smart-wallet-scanner/SKILL.md +0 -49
  138. package/skills/oracle-solana/SKILL.md +0 -65
  139. package/skills/oracle-solana-nft/SKILL.md +0 -54
  140. package/skills/oracle-token-research/SKILL.md +0 -67
  141. package/skins/oracle.yaml +0 -56
  142. package/src/action-receipts.mjs +0 -265
  143. package/src/action-semantics.mjs +0 -62
  144. package/src/address-book.mjs +0 -208
  145. package/src/agent-auth.mjs +0 -206
  146. package/src/approval-guard.mjs +0 -282
  147. package/src/attestation-secret.mjs +0 -88
  148. package/src/audit-log.mjs +0 -196
  149. package/src/auth/oauth.mjs +0 -672
  150. package/src/auto-slippage.mjs +0 -378
  151. package/src/capability-posture.mjs +0 -125
  152. package/src/chains.mjs +0 -62
  153. package/src/cli/chain-catalog.mjs +0 -215
  154. package/src/cli/chain-state.mjs +0 -74
  155. package/src/cli/commands/auth.mjs +0 -193
  156. package/src/cli/commands/bootstrap.mjs +0 -87
  157. package/src/cli/commands/chain.mjs +0 -143
  158. package/src/cli/commands/chat.mjs +0 -434
  159. package/src/cli/commands/credential.mjs +0 -9
  160. package/src/cli/commands/data-mcp.mjs +0 -18
  161. package/src/cli/commands/data.mjs +0 -75
  162. package/src/cli/commands/doctor.mjs +0 -136
  163. package/src/cli/commands/help.mjs +0 -7
  164. package/src/cli/commands/init.mjs +0 -27
  165. package/src/cli/commands/mcp.mjs +0 -142
  166. package/src/cli/commands/model.mjs +0 -99
  167. package/src/cli/commands/prepare.mjs +0 -12
  168. package/src/cli/commands/public.mjs +0 -20
  169. package/src/cli/commands/route.mjs +0 -12
  170. package/src/cli/commands/runner.mjs +0 -9
  171. package/src/cli/commands/scan.mjs +0 -12
  172. package/src/cli/commands/setup.mjs +0 -275
  173. package/src/cli/commands/sign.mjs +0 -21
  174. package/src/cli/commands/signer.mjs +0 -9
  175. package/src/cli/commands/upgrade.mjs +0 -12
  176. package/src/cli/commands/vault.mjs +0 -9
  177. package/src/cli/commands/version.mjs +0 -22
  178. package/src/cli/first-run.mjs +0 -23
  179. package/src/cli/kernel.mjs +0 -239
  180. package/src/cli/mcp-targets/chatgpt.mjs +0 -51
  181. package/src/cli/mcp-targets/claude-code.mjs +0 -39
  182. package/src/cli/mcp-targets/claude-desktop.mjs +0 -24
  183. package/src/cli/mcp-targets/codex.mjs +0 -41
  184. package/src/cli/mcp-targets/shared.mjs +0 -61
  185. package/src/cli/messaging-platforms.mjs +0 -209
  186. package/src/cli/model-config.mjs +0 -70
  187. package/src/cli/operator-dispatch.mjs +0 -258
  188. package/src/cli/oracle-harness.py +0 -415
  189. package/src/cli/paths.mjs +0 -83
  190. package/src/cli/runtime.mjs +0 -351
  191. package/src/cli/setup-state.mjs +0 -168
  192. package/src/cli/spawn-child.mjs +0 -43
  193. package/src/data/catalog.mjs +0 -585
  194. package/src/data/desk-data.mjs +0 -695
  195. package/src/data/http.mjs +0 -253
  196. package/src/data/provider-endpoint.mjs +0 -94
  197. package/src/data/providers/aerodrome.mjs +0 -245
  198. package/src/data/providers/balancer.mjs +0 -209
  199. package/src/data/providers/bitcoin-esplora.mjs +0 -216
  200. package/src/data/providers/bitcoin-meta.mjs +0 -378
  201. package/src/data/providers/blockscout.mjs +0 -14
  202. package/src/data/providers/bridges.mjs +0 -242
  203. package/src/data/providers/cowswap.mjs +0 -415
  204. package/src/data/providers/curve.mjs +0 -201
  205. package/src/data/providers/defillama.mjs +0 -88
  206. package/src/data/providers/dexscreener.mjs +0 -43
  207. package/src/data/providers/evm-rpc.mjs +0 -225
  208. package/src/data/providers/geckoterminal.mjs +0 -34
  209. package/src/data/providers/gmx.mjs +0 -496
  210. package/src/data/providers/hl-assets.mjs +0 -165
  211. package/src/data/providers/hl-info.mjs +0 -108
  212. package/src/data/providers/hl-markets.mjs +0 -210
  213. package/src/data/providers/hl-outcome.mjs +0 -61
  214. package/src/data/providers/hl-perps.mjs +0 -387
  215. package/src/data/providers/hl-staking.mjs +0 -353
  216. package/src/data/providers/hl-ws.mjs +0 -119
  217. package/src/data/providers/hyperevm-dex.mjs +0 -49
  218. package/src/data/providers/jupiter-venues.mjs +0 -119
  219. package/src/data/providers/jupiter.mjs +0 -315
  220. package/src/data/providers/lifi.mjs +0 -151
  221. package/src/data/providers/magiceden-sol.mjs +0 -406
  222. package/src/data/providers/morpho.mjs +0 -177
  223. package/src/data/providers/nft-gallery.mjs +0 -163
  224. package/src/data/providers/nft-portfolio.mjs +0 -494
  225. package/src/data/providers/odos.mjs +0 -156
  226. package/src/data/providers/oneinch.mjs +0 -174
  227. package/src/data/providers/opensea-multichain.mjs +0 -136
  228. package/src/data/providers/opensea-nft.mjs +0 -371
  229. package/src/data/providers/paraswap.mjs +0 -118
  230. package/src/data/providers/pendle.mjs +0 -188
  231. package/src/data/providers/poly-clob.mjs +0 -283
  232. package/src/data/providers/poly-public.mjs +0 -96
  233. package/src/data/providers/poly-ws.mjs +0 -103
  234. package/src/data/providers/portfolio-history.mjs +0 -394
  235. package/src/data/providers/portfolio.mjs +0 -594
  236. package/src/data/providers/rfq.mjs +0 -15
  237. package/src/data/providers/rh-agent.mjs +0 -59
  238. package/src/data/providers/satflow.mjs +0 -341
  239. package/src/data/providers/signed-material-guard.mjs +0 -108
  240. package/src/data/providers/solana-rpc.mjs +0 -197
  241. package/src/data/providers/uniswap-v3.mjs +0 -349
  242. package/src/data/providers/uniswap-v4.mjs +0 -414
  243. package/src/data/providers/zerox.mjs +0 -167
  244. package/src/data/public-api-scan.mjs +0 -61
  245. package/src/data/quote-placeholder.mjs +0 -31
  246. package/src/exact-integer.mjs +0 -72
  247. package/src/exec-policy.mjs +0 -455
  248. package/src/flags.mjs +0 -15
  249. package/src/fresh-window.mjs +0 -76
  250. package/src/gmx-attestation.mjs +0 -176
  251. package/src/index.mjs +0 -100
  252. package/src/nft-gas-war-guard.mjs +0 -139
  253. package/src/onboarding/agent-keys.mjs +0 -161
  254. package/src/onboarding/harness-configs.mjs +0 -102
  255. package/src/onboarding/index.mjs +0 -18
  256. package/src/onboarding/tiers.mjs +0 -139
  257. package/src/oracle-env.mjs +0 -47
  258. package/src/portfolio-risk.mjs +0 -170
  259. package/src/prepare-envelope.mjs +0 -204
  260. package/src/profile-upgrade.mjs +0 -277
  261. package/src/protocol-execution.mjs +0 -84
  262. package/src/protocol-templates/gate.mjs +0 -179
  263. package/src/protocol-templates/prepare-deploy.mjs +0 -81
  264. package/src/public-api/buzz-integration.mjs +0 -256
  265. package/src/public-api/connect-agent.mjs +0 -416
  266. package/src/public-api/grants.mjs +0 -142
  267. package/src/public-api/http.mjs +0 -389
  268. package/src/public-control/aa-adapter.mjs +0 -402
  269. package/src/public-control/build-registry.mjs +0 -227
  270. package/src/public-control/bundler-client.mjs +0 -357
  271. package/src/public-control/grant-indexer.mjs +0 -296
  272. package/src/public-control/policy-render.mjs +0 -69
  273. package/src/public-control/policy-schema.mjs +0 -318
  274. package/src/public-control/runtime-config.mjs +0 -275
  275. package/src/public-control/session-key-model.mjs +0 -374
  276. package/src/public-control/session-orchestrator.mjs +0 -412
  277. package/src/rfq/intent.mjs +0 -180
  278. package/src/rfq/sources.mjs +0 -181
  279. package/src/route-attestation.mjs +0 -132
  280. package/src/router/best-execution.mjs +0 -326
  281. package/src/router/index.mjs +0 -226
  282. package/src/router/prepare-bridge.mjs +0 -289
  283. package/src/router/prepare-route.mjs +0 -398
  284. package/src/router/proposal.mjs +0 -311
  285. package/src/router/risk-classifier.mjs +0 -119
  286. package/src/router/route-sources.mjs +0 -364
  287. package/src/scanner/chains.config.mjs +0 -430
  288. package/src/scanner/contract.mjs +0 -270
  289. package/src/scanner/evm-scanner.mjs +0 -394
  290. package/src/scanner/index.mjs +0 -9
  291. package/src/scanner/v2-venue.mjs +0 -335
  292. package/src/scanner/v3-venue.mjs +0 -290
  293. package/src/scopes.mjs +0 -44
  294. package/src/sell-simulation.mjs +0 -167
  295. package/src/signals/engine.mjs +0 -146
  296. package/src/signals/index.mjs +0 -1
  297. package/src/token-transfer-guard.mjs +0 -188
  298. package/src/tui/app.mjs +0 -438
  299. package/src/tui/backend.mjs +0 -145
  300. package/src/tui/format.mjs +0 -272
  301. package/src/tui/gateway-client.mjs +0 -177
  302. package/src/tui/input.mjs +0 -501
  303. package/src/tui/renderer.mjs +0 -443
  304. package/src/tui/standalone-client.mjs +0 -666
  305. package/src/tui/theme.mjs +0 -81
  306. package/src/vault-attestation.mjs +0 -146
  307. package/src/venues.mjs +0 -206
  308. package/src/watch-preferences.mjs +0 -85
@@ -1,594 +0,0 @@
1
- // Read-only multichain portfolio aggregation. No keys, signing, or broadcast.
2
-
3
- import { getAddress } from "ethers";
4
- import { CHAIN_CONFIGS } from "../../scanner/chains.config.mjs";
5
- import { mapLimit, timed } from "../http.mjs";
6
- import { rpcGetBalance } from "./evm-rpc.mjs";
7
- import {
8
- SPL_TOKEN_PROGRAM_ID,
9
- solanaGetBalance,
10
- solanaTokenAccounts,
11
- solanaPubkey,
12
- } from "./solana-rpc.mjs";
13
- import { btcAddress, btcAddressInfo } from "./bitcoin-esplora.mjs";
14
- import { btcInscriptions, btcRuneBalances } from "./bitcoin-meta.mjs";
15
- import { hlAllMids, hlClearinghouse, hlUserState } from "./hl-info.mjs";
16
- import { llamaPrices } from "./defillama.mjs";
17
-
18
- export const SPL_TOKEN_2022_PROGRAM_ID = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
19
-
20
- const PRICE_KEY_BY_SYMBOL = Object.freeze({
21
- ETH: "coingecko:ethereum",
22
- POL: "coingecko:polygon-ecosystem-token",
23
- BNB: "coingecko:binancecoin",
24
- AVAX: "coingecko:avalanche-2",
25
- HYPE: "coingecko:hyperliquid",
26
- USDT0: "coingecko:tether",
27
- SOL: "coingecko:solana",
28
- BTC: "coingecko:bitcoin",
29
- });
30
-
31
- function sharedOpts(opts = {}, key) {
32
- return {
33
- fetchImpl: opts.fetchImpl,
34
- timeoutMs: opts.timeoutMs,
35
- ...(opts[key] || {}),
36
- };
37
- }
38
-
39
- function envAddress(env, ...names) {
40
- for (const name of names) {
41
- const value = String(env?.[name] || "").trim();
42
- if (value) return value;
43
- }
44
- return null;
45
- }
46
-
47
- function normalizeEvmAddress(value) {
48
- if (!value) return null;
49
- try {
50
- return getAddress(String(value).trim().toLowerCase());
51
- } catch {
52
- throw new Error("portfolio: EVM address must be a valid 20-byte address");
53
- }
54
- }
55
-
56
- export function resolvePortfolioAddresses(args = {}, env = process.env) {
57
- const input = args.addresses || {};
58
- const evm = normalizeEvmAddress(
59
- input.evm ||
60
- args.evmAddress ||
61
- envAddress(env, "ORACLE_EVM_ADDRESS", "ORACLE_DEFAULT_ADDRESS", "MAD_OPERATOR_ADDRESS"),
62
- );
63
- const solanaRaw =
64
- input.solana || args.solanaAddress || envAddress(env, "ORACLE_SOLANA_ADDRESS");
65
- const bitcoinRaw =
66
- input.bitcoin || args.bitcoinAddress || envAddress(env, "ORACLE_BITCOIN_ADDRESS");
67
- const hyperliquid = normalizeEvmAddress(
68
- input.hyperliquid ||
69
- args.hyperliquidAddress ||
70
- envAddress(env, "ORACLE_HYPERLIQUID_ADDRESS") ||
71
- evm,
72
- );
73
- return {
74
- evm,
75
- solana: solanaRaw ? solanaPubkey(solanaRaw, "portfolio Solana address") : null,
76
- bitcoin: bitcoinRaw ? btcAddress(bitcoinRaw, "portfolio Bitcoin address") : null,
77
- hyperliquid,
78
- };
79
- }
80
-
81
- export function formatUnits(raw, decimals) {
82
- const value = BigInt(String(raw || "0"));
83
- const places = Number(decimals);
84
- if (!Number.isInteger(places) || places < 0 || places > 36) {
85
- throw new Error("portfolio: decimals must be an integer from 0 to 36");
86
- }
87
- if (places === 0) return value.toString();
88
- const negative = value < 0n;
89
- const abs = negative ? -value : value;
90
- const base = 10n ** BigInt(places);
91
- const whole = abs / base;
92
- const fraction = String(abs % base).padStart(places, "0").replace(/0+$/, "");
93
- return `${negative ? "-" : ""}${whole}${fraction ? `.${fraction}` : ""}`;
94
- }
95
-
96
- function decimalNumber(value) {
97
- const number = Number(value);
98
- return Number.isFinite(number) ? number : null;
99
- }
100
-
101
- function roundedUsd(value) {
102
- return Number.isFinite(value) ? Number(value.toFixed(2)) : null;
103
- }
104
-
105
- function nonZero(value) {
106
- const number = Number(value);
107
- return Number.isFinite(number) && number !== 0;
108
- }
109
-
110
- function priceEntry(priceData, symbol) {
111
- const key = PRICE_KEY_BY_SYMBOL[symbol];
112
- const row = key ? priceData?.coins?.[key] : null;
113
- const priceUsd = Number(row?.price);
114
- return {
115
- key: key || null,
116
- priceUsd: Number.isFinite(priceUsd) ? priceUsd : null,
117
- timestamp: row?.timestamp ?? null,
118
- confidence: row?.confidence ?? null,
119
- };
120
- }
121
-
122
- function aggregateSolanaAccounts(results) {
123
- const byMint = new Map();
124
- for (const result of results) {
125
- if (!result?.ok) continue;
126
- for (const account of result.data?.accounts || []) {
127
- const raw = BigInt(String(account.amount || "0"));
128
- if (raw === 0n || !account.mint) continue;
129
- const key = `${account.mint}:${account.decimals ?? "unknown"}`;
130
- const current = byMint.get(key) || {
131
- mint: account.mint,
132
- amountRaw: 0n,
133
- decimals: account.decimals ?? null,
134
- programs: new Set(),
135
- accounts: 0,
136
- };
137
- current.amountRaw += raw;
138
- current.accounts += 1;
139
- if (result.program) current.programs.add(result.program);
140
- byMint.set(key, current);
141
- }
142
- }
143
- return [...byMint.values()].map((item) => {
144
- const amountRaw = item.amountRaw.toString();
145
- const amount = item.decimals == null ? null : formatUnits(amountRaw, item.decimals);
146
- const collectibleCandidate = item.decimals === 0 && item.amountRaw === 1n;
147
- return {
148
- mint: item.mint,
149
- amountRaw,
150
- amount,
151
- decimals: item.decimals,
152
- tokenPrograms: [...item.programs],
153
- accountCount: item.accounts,
154
- verification: "unverified",
155
- classification: collectibleCandidate ? "collectible-candidate" : "fungible-or-unclassified",
156
- priceUsd: null,
157
- usdValue: null,
158
- };
159
- });
160
- }
161
-
162
- function unavailableFamily(family, reason) {
163
- return { family, status: "unsupported", reason };
164
- }
165
-
166
- function decorateNative(native, priceData) {
167
- if (!native || native.status !== "ok") return native;
168
- const price = priceEntry(priceData, native.symbol);
169
- const amountNumber = decimalNumber(native.amount);
170
- const usdValue =
171
- amountNumber != null && price.priceUsd != null
172
- ? roundedUsd(amountNumber * price.priceUsd)
173
- : null;
174
- return { ...native, price, usdValue };
175
- }
176
-
177
- export async function portfolioHealth(opts = {}) {
178
- const addresses = resolvePortfolioAddresses({}, opts.env || process.env);
179
- return {
180
- ok: true,
181
- provider: "portfolio",
182
- readOnly: true,
183
- configuredFamilies: Object.entries(addresses)
184
- .filter(([, value]) => Boolean(value))
185
- .map(([family]) => family),
186
- };
187
- }
188
-
189
- export async function portfolioBalance(args = {}, opts = {}) {
190
- const queriedAt = new Date().toISOString();
191
- const addresses = resolvePortfolioAddresses(args, opts.env || process.env);
192
- const includePrices = args.includePrices !== false;
193
- const includeTokens = args.includeTokens !== false;
194
- const includeCollectibles = args.includeCollectibles !== false;
195
- const configuredChains = new Map(CHAIN_CONFIGS.map((chain) => [chain.chainId, chain]));
196
- const requestedIds = Array.isArray(args.evmChainIds) && args.evmChainIds.length
197
- ? [...new Set(args.evmChainIds.map(Number))]
198
- : [...configuredChains.keys()];
199
-
200
- const priceKeys = [...new Set(Object.values(PRICE_KEY_BY_SYMBOL))];
201
- const priceTask = includePrices
202
- ? timed(() => llamaPrices(priceKeys, sharedOpts(opts, "prices")))
203
- : Promise.resolve({ ok: false, ms: 0, error: "price lookup disabled" });
204
-
205
- const evmTask = mapLimit(requestedIds, args.concurrency || 5, async (chainId) => {
206
- const chain = configuredChains.get(chainId);
207
- if (!chain) {
208
- return {
209
- family: "evm",
210
- chainId,
211
- name: null,
212
- address: addresses.evm,
213
- status: "unsupported",
214
- error: "chainId is not in Oracle's configured EVM registry",
215
- };
216
- }
217
- if (!addresses.evm) {
218
- return {
219
- family: "evm",
220
- chainId,
221
- name: chain.name,
222
- address: null,
223
- status: "not-configured",
224
- error: "set ORACLE_EVM_ADDRESS or pass addresses.evm",
225
- };
226
- }
227
- const rpcOpts = {
228
- ...sharedOpts(opts, "evm"),
229
- rpcUrl: args.rpcUrls?.[chainId] || opts.evm?.rpcUrls?.[chainId],
230
- };
231
- const result = await timed(() => rpcGetBalance(chainId, addresses.evm, rpcOpts));
232
- if (!result.ok) {
233
- return {
234
- family: "evm",
235
- chainId,
236
- name: chain.name,
237
- address: addresses.evm,
238
- status: "unavailable",
239
- latencyMs: result.ms,
240
- error: result.error,
241
- };
242
- }
243
- const decimals = chain.nativeCurrency?.decimals ?? 18;
244
- const symbol = chain.nativeCurrency?.symbol || "NATIVE";
245
- const amountRaw = result.data.balanceWei;
246
- return {
247
- family: "evm",
248
- chainId,
249
- name: chain.name,
250
- address: addresses.evm,
251
- status: "ok",
252
- latencyMs: result.ms,
253
- native: {
254
- status: "ok",
255
- symbol,
256
- amountRaw,
257
- decimals,
258
- amount: formatUnits(amountRaw, decimals),
259
- },
260
- fungibleTokens: {
261
- status: includeTokens ? "unavailable" : "not-requested",
262
- assets: [],
263
- reason: includeTokens
264
- ? "EVM JSON-RPC cannot enumerate wallet tokens without an address indexer"
265
- : "token discovery disabled",
266
- },
267
- collectibles: {
268
- status: includeCollectibles ? "unavailable" : "not-requested",
269
- assets: [],
270
- reason: includeCollectibles
271
- ? "EVM JSON-RPC cannot enumerate wallet NFTs without an address indexer"
272
- : "collectible discovery disabled",
273
- },
274
- source: "evm-rpc",
275
- };
276
- });
277
-
278
- const solanaTask = (async () => {
279
- if (!addresses.solana) {
280
- return {
281
- family: "solana",
282
- name: "Solana Mainnet",
283
- address: null,
284
- status: "not-configured",
285
- error: "set ORACLE_SOLANA_ADDRESS or pass addresses.solana",
286
- };
287
- }
288
- const solOpts = sharedOpts(opts, "solana");
289
- const native = await timed(() => solanaGetBalance({ address: addresses.solana }, solOpts));
290
- const tokenReads = includeTokens || includeCollectibles
291
- ? await Promise.all([
292
- timed(() =>
293
- solanaTokenAccounts(
294
- { owner: addresses.solana, programId: SPL_TOKEN_PROGRAM_ID },
295
- solOpts,
296
- ),
297
- ).then((result) => ({ ...result, program: "spl-token" })),
298
- timed(() =>
299
- solanaTokenAccounts(
300
- { owner: addresses.solana, programId: SPL_TOKEN_2022_PROGRAM_ID },
301
- solOpts,
302
- ),
303
- ).then((result) => ({ ...result, program: "token-2022" })),
304
- ])
305
- : [];
306
- const assets = aggregateSolanaAccounts(tokenReads);
307
- const candidates = assets.filter((asset) => asset.classification === "collectible-candidate");
308
- const fungibles = assets.filter((asset) => asset.classification !== "collectible-candidate");
309
- const tokenErrors = tokenReads.filter((result) => !result.ok).map((result) => result.error);
310
- return {
311
- family: "solana",
312
- name: "Solana Mainnet",
313
- address: addresses.solana,
314
- status: native.ok || tokenReads.some((result) => result.ok) ? "ok" : "unavailable",
315
- native: native.ok
316
- ? {
317
- status: "ok",
318
- symbol: "SOL",
319
- amountRaw: native.data.lamports,
320
- decimals: 9,
321
- amount: formatUnits(native.data.lamports, 9),
322
- slot: native.data.slot,
323
- }
324
- : { status: "unavailable", symbol: "SOL", error: native.error },
325
- fungibleTokens: {
326
- status: !includeTokens
327
- ? "not-requested"
328
- : tokenErrors.length === tokenReads.length
329
- ? "unavailable"
330
- : tokenErrors.length
331
- ? "partial"
332
- : "ok",
333
- assets: includeTokens ? fungibles : [],
334
- errors: tokenErrors,
335
- note: "SPL accounts are unverified until a metadata/indexer source confirms symbol and spam status",
336
- },
337
- collectibles: {
338
- status: !includeCollectibles
339
- ? "not-requested"
340
- : tokenErrors.length === tokenReads.length
341
- ? "unavailable"
342
- : "partial",
343
- assets: includeCollectibles ? candidates : [],
344
- errors: tokenErrors,
345
- note: "decimals=0 and amount=1 is only a collectible candidate, not verified NFT metadata",
346
- },
347
- source: "solana-rpc",
348
- };
349
- })();
350
-
351
- const bitcoinTask = (async () => {
352
- if (!addresses.bitcoin) {
353
- return {
354
- family: "bitcoin",
355
- name: "Bitcoin Mainnet",
356
- address: null,
357
- status: "not-configured",
358
- error: "set ORACLE_BITCOIN_ADDRESS or pass addresses.bitcoin",
359
- };
360
- }
361
- const btcOpts = sharedOpts(opts, "bitcoin");
362
- const metaOpts = sharedOpts(opts, "bitcoinMeta");
363
- const [native, runes, inscriptions] = await Promise.all([
364
- timed(() => btcAddressInfo({ address: addresses.bitcoin }, btcOpts)),
365
- includeTokens
366
- ? timed(() => btcRuneBalances({ address: addresses.bitcoin }, metaOpts))
367
- : Promise.resolve({ ok: false, ms: 0, error: "token discovery disabled" }),
368
- includeCollectibles
369
- ? timed(() => btcInscriptions({ address: addresses.bitcoin }, metaOpts))
370
- : Promise.resolve({ ok: false, ms: 0, error: "collectible discovery disabled" }),
371
- ]);
372
- const runeData = runes.ok ? runes.data : null;
373
- const inscriptionData = inscriptions.ok ? inscriptions.data : null;
374
- return {
375
- family: "bitcoin",
376
- name: "Bitcoin Mainnet",
377
- address: addresses.bitcoin,
378
- status: native.ok || runeData?.ok || inscriptionData?.ok ? "ok" : "unavailable",
379
- native: native.ok
380
- ? {
381
- status: "ok",
382
- symbol: "BTC",
383
- amountRaw: native.data.balanceSats,
384
- decimals: 8,
385
- amount: formatUnits(native.data.balanceSats, 8),
386
- }
387
- : { status: "unavailable", symbol: "BTC", error: native.error },
388
- fungibleTokens: {
389
- status: !includeTokens
390
- ? "not-requested"
391
- : runeData?.ok
392
- ? "ok"
393
- : "unavailable",
394
- assets: runeData?.balances || [],
395
- error: runeData?.ok ? null : runeData?.error || runes.error,
396
- verification: "indexer-reported",
397
- },
398
- collectibles: {
399
- status: !includeCollectibles
400
- ? "not-requested"
401
- : inscriptionData?.ok
402
- ? "ok"
403
- : "unavailable",
404
- assets: inscriptionData?.inscriptions || [],
405
- error: inscriptionData?.ok ? null : inscriptionData?.error || inscriptions.error,
406
- unknownNotEmpty: Boolean(inscriptionData?.unknownNotEmpty),
407
- verification: "indexer-reported",
408
- },
409
- source: "bitcoin-esplora+bitcoin-meta",
410
- };
411
- })();
412
-
413
- const hyperliquidTask = (async () => {
414
- if (!addresses.hyperliquid) {
415
- return {
416
- family: "hyperliquid",
417
- name: "Hyperliquid HyperCore",
418
- address: null,
419
- status: "not-configured",
420
- error: "set ORACLE_HYPERLIQUID_ADDRESS or pass addresses.hyperliquid",
421
- };
422
- }
423
- const hlOpts = sharedOpts(opts, "hyperliquid");
424
- const [spot, perps, mids] = await Promise.all([
425
- timed(() => hlClearinghouse(addresses.hyperliquid, hlOpts)),
426
- timed(() => hlUserState(addresses.hyperliquid, hlOpts)),
427
- timed(() => hlAllMids(hlOpts)),
428
- ]);
429
- const spotBalances = (spot.ok && Array.isArray(spot.data?.balances) ? spot.data.balances : [])
430
- .filter((item) => nonZero(item.total) || nonZero(item.hold))
431
- .map((item) => ({
432
- coin: item.coin || null,
433
- total: String(item.total ?? "0"),
434
- hold: String(item.hold ?? "0"),
435
- priceUsd: null,
436
- usdValue: null,
437
- }));
438
- const positions = (perps.ok && Array.isArray(perps.data?.assetPositions)
439
- ? perps.data.assetPositions
440
- : [])
441
- .map((item) => item.position || item)
442
- .filter((item) => nonZero(item.szi))
443
- .map((item) => ({
444
- coin: item.coin || null,
445
- size: String(item.szi ?? "0"),
446
- entryPrice: item.entryPx ?? null,
447
- positionValueUsd: item.positionValue ?? null,
448
- unrealizedPnlUsd: item.unrealizedPnl ?? null,
449
- liquidationPrice: item.liquidationPx ?? null,
450
- }));
451
- return {
452
- family: "hyperliquid",
453
- name: "Hyperliquid HyperCore",
454
- address: addresses.hyperliquid,
455
- status: spot.ok || perps.ok ? "ok" : "unavailable",
456
- spot: {
457
- status: spot.ok ? "ok" : "unavailable",
458
- balances: spotBalances,
459
- error: spot.ok ? null : spot.error,
460
- },
461
- perps: {
462
- status: perps.ok ? "ok" : "unavailable",
463
- accountValueUsd: perps.data?.marginSummary?.accountValue ?? null,
464
- withdrawableUsd: perps.data?.withdrawable ?? null,
465
- positions,
466
- error: perps.ok ? null : perps.error,
467
- },
468
- mids: mids.ok ? mids.data : {},
469
- source: "hyperliquid-info",
470
- };
471
- })();
472
-
473
- const [evm, solana, bitcoin, hyperliquid, priceResult] = await Promise.all([
474
- evmTask,
475
- solanaTask,
476
- bitcoinTask,
477
- hyperliquidTask,
478
- priceTask,
479
- ]);
480
- const priceData = priceResult.ok ? priceResult.data : null;
481
-
482
- const decoratedEvm = evm.map((chain) =>
483
- chain.native ? { ...chain, native: decorateNative(chain.native, priceData) } : chain,
484
- );
485
- if (solana.native) solana.native = decorateNative(solana.native, priceData);
486
- if (bitcoin.native) bitcoin.native = decorateNative(bitcoin.native, priceData);
487
-
488
- let knownUsd = 0;
489
- let pricedItems = 0;
490
- let unpricedNonzeroItems = 0;
491
- for (const chain of [...decoratedEvm, solana, bitcoin]) {
492
- if (chain.native?.status !== "ok" || !nonZero(chain.native.amount)) continue;
493
- if (chain.native.usdValue != null) {
494
- knownUsd += chain.native.usdValue;
495
- pricedItems += 1;
496
- } else {
497
- unpricedNonzeroItems += 1;
498
- }
499
- }
500
-
501
- const hypePrice = priceEntry(priceData, "HYPE");
502
- for (const item of hyperliquid.spot?.balances || []) {
503
- const symbol = String(item.coin || "").toUpperCase();
504
- const priceUsd = symbol === "USDC" || symbol === "USDT" || symbol === "USDT0"
505
- ? 1
506
- : symbol === "HYPE"
507
- ? hypePrice.priceUsd || decimalNumber(hyperliquid.mids?.HYPE)
508
- : decimalNumber(hyperliquid.mids?.[item.coin]);
509
- const amount = decimalNumber(item.total);
510
- item.priceUsd = priceUsd;
511
- item.usdValue = amount != null && priceUsd != null ? roundedUsd(amount * priceUsd) : null;
512
- if (!nonZero(item.total)) continue;
513
- if (item.usdValue != null) {
514
- knownUsd += item.usdValue;
515
- pricedItems += 1;
516
- } else {
517
- unpricedNonzeroItems += 1;
518
- }
519
- }
520
- const perpsValue = decimalNumber(hyperliquid.perps?.accountValueUsd);
521
- if (perpsValue != null && perpsValue !== 0) {
522
- knownUsd += perpsValue;
523
- pricedItems += 1;
524
- }
525
-
526
- const solanaUnpriced = [
527
- ...(solana.fungibleTokens?.assets || []),
528
- ...(solana.collectibles?.assets || []),
529
- ].filter((asset) => nonZero(asset.amount)).length;
530
- const bitcoinUnpriced =
531
- (bitcoin.fungibleTokens?.assets || []).length +
532
- (bitcoin.collectibles?.assets || []).length;
533
- unpricedNonzeroItems += solanaUnpriced + bitcoinUnpriced;
534
-
535
- const allSurfaces = [...decoratedEvm, solana, bitcoin, hyperliquid];
536
- const count = (status) => allSurfaces.filter((surface) => surface.status === status).length;
537
- const warnings = [];
538
- if (decoratedEvm.some((chain) => chain.fungibleTokens?.status === "unavailable")) {
539
- warnings.push("EVM token and NFT discovery is unavailable without a configured address indexer; native balances are still live.");
540
- }
541
- if (solana.fungibleTokens?.assets?.length || solana.collectibles?.assets?.length) {
542
- warnings.push("Solana token accounts are unverified and may include spam; collectible candidates are not confirmed NFTs.");
543
- }
544
- if (bitcoin.collectibles?.unknownNotEmpty) {
545
- warnings.push("Bitcoin inscription holdings are unknown, not zero, because no address indexer is configured.");
546
- }
547
- if (!priceResult.ok) warnings.push(`Price provider unavailable: ${priceResult.error}`);
548
-
549
- const unsupportedFamilies = [
550
- unavailableFamily("cosmos", "requires a chain-specific bech32 address and LCD/RPC adapter"),
551
- unavailableFamily("sui", "Sui balance adapter is not installed"),
552
- unavailableFamily("aptos", "Aptos balance adapter is not installed"),
553
- ];
554
-
555
- const discoveryIncomplete = allSurfaces.some((surface) =>
556
- ["unavailable", "partial", "not-configured"].includes(surface.fungibleTokens?.status) ||
557
- ["unavailable", "partial", "not-configured"].includes(surface.collectibles?.status),
558
- );
559
-
560
- return {
561
- provider: "portfolio",
562
- operation: "balances",
563
- readOnly: true,
564
- queriedAt,
565
- addresses,
566
- coverage: {
567
- requestedSurfaces: allSurfaces.length,
568
- ok: count("ok"),
569
- unavailable: count("unavailable"),
570
- notConfigured: count("not-configured"),
571
- unsupported: count("unsupported") + unsupportedFamilies.length,
572
- evmChainsRequested: requestedIds.length,
573
- evmChainsOk: decoratedEvm.filter((chain) => chain.status === "ok").length,
574
- },
575
- valuation: {
576
- knownUsd: roundedUsd(knownUsd),
577
- label: "known priced value, not a complete portfolio total",
578
- pricedItems,
579
- unpricedNonzeroItems,
580
- complete:
581
- unpricedNonzeroItems === 0 &&
582
- !discoveryIncomplete &&
583
- count("unavailable") === 0 &&
584
- count("not-configured") === 0 &&
585
- unsupportedFamilies.length === 0,
586
- priceSource: includePrices ? "DefiLlama" : null,
587
- priceStatus: priceResult.ok ? "ok" : "unavailable",
588
- priceQueriedAt: queriedAt,
589
- },
590
- chains: [...decoratedEvm, solana, bitcoin, hyperliquid],
591
- unsupportedFamilies,
592
- warnings,
593
- };
594
- }
@@ -1,15 +0,0 @@
1
- import { normalizeRfqIntent } from "../../rfq/intent.mjs";
2
- import { requestRfqQuotes } from "../../rfq/sources.mjs";
3
-
4
- export function rfqIntent(args = {}, opts = {}) {
5
- return normalizeRfqIntent(args, opts);
6
- }
7
-
8
- export async function rfqQuote(args = {}, opts = {}) {
9
- const intent = args.intent?.kind === "rfq-intent" ? args.intent : normalizeRfqIntent(args, opts);
10
- return requestRfqQuotes(intent, opts);
11
- }
12
-
13
- export async function rfqHealth() {
14
- return { ok: true, provider: "rfq", mode: "intent-rfq", execution: "prepare" };
15
- }
@@ -1,59 +0,0 @@
1
- // RH agent HTTP read client — talks to the live RH product over HTTP.
2
- // Unauthenticated D0/D1 ops only. Does NOT import rhbot.
3
-
4
- import { httpJson } from "../http.mjs";
5
-
6
- export const RH_AGENT_DEFAULT_BASE = "http://127.0.0.1:8792";
7
-
8
- function baseUrl(opts = {}) {
9
- return (
10
- opts.baseUrl ||
11
- process.env.RH_AGENT_BASE_URL ||
12
- process.env.RH_AGENT_URL ||
13
- RH_AGENT_DEFAULT_BASE
14
- ).replace(/\/$/, "");
15
- }
16
-
17
- async function get(path, opts = {}) {
18
- return httpJson(`${baseUrl(opts)}${path}`, {
19
- fetchImpl: opts.fetchImpl,
20
- timeoutMs: opts.timeoutMs,
21
- });
22
- }
23
-
24
- export async function rhHealth(opts = {}) {
25
- return get("/api/health", opts);
26
- }
27
-
28
- export async function rhPolicy(opts = {}) {
29
- return get("/api/agent/policy", opts);
30
- }
31
-
32
- // D1 — public RH routes (no requireUser)
33
- export async function rhChainConfig(opts = {}) {
34
- return get("/api/chain/config", opts);
35
- }
36
-
37
- export async function rhChainGas(opts = {}) {
38
- return get("/api/chain/gas", opts);
39
- }
40
-
41
- export async function rhSwapsConfig(opts = {}) {
42
- return get("/api/swaps/config", opts);
43
- }
44
-
45
- export async function rhSwapsPresets(opts = {}) {
46
- return get("/api/swaps/presets", opts);
47
- }
48
-
49
- export async function rhTradingStatus(opts = {}) {
50
- return get("/api/trading/status", opts);
51
- }
52
-
53
- export async function rhNftDrops(opts = {}) {
54
- return get("/api/nft-drops", opts);
55
- }
56
-
57
- export async function rhFeeEstimate(opts = {}) {
58
- return get("/api/fee/estimate", opts);
59
- }