@oracle-agent/oracle 0.14.1 → 0.16.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 (450) hide show
  1. package/README.md +2 -2
  2. package/SETUP.md +4 -4
  3. package/dist/action-receipts.mjs +1 -0
  4. package/dist/action-semantics.mjs +1 -0
  5. package/dist/address-book.mjs +2 -0
  6. package/dist/bin/desk-server.mjs +60 -0
  7. package/dist/bin/oracle-data-mcp.mjs +9 -0
  8. package/dist/bin/oracle-equities.mjs +20 -0
  9. package/dist/bin/oracle-init.mjs +24 -0
  10. package/dist/bin/oracle-public-server.mjs +5 -0
  11. package/dist/bin/oracle-route.mjs +22 -0
  12. package/dist/bin/oracle-scan.mjs +18 -0
  13. package/dist/bin/oracle-upgrade.mjs +21 -0
  14. package/dist/bin/oracle.mjs +63 -0
  15. package/dist/chains.mjs +1 -0
  16. package/dist/cli/commands/auth.mjs +16 -0
  17. package/dist/cli/commands/bootstrap.mjs +18 -0
  18. package/dist/cli/commands/chain.mjs +21 -0
  19. package/dist/cli/commands/chat.mjs +45 -0
  20. package/dist/cli/commands/credential.mjs +1 -0
  21. package/dist/cli/commands/data-mcp.mjs +2 -0
  22. package/dist/cli/commands/data.mjs +12 -0
  23. package/dist/cli/commands/doctor.mjs +5 -0
  24. package/dist/cli/commands/equities.mjs +15 -0
  25. package/dist/cli/commands/farm.mjs +59 -0
  26. package/dist/cli/commands/fees.mjs +10 -0
  27. package/dist/cli/commands/follow.mjs +28 -0
  28. package/dist/cli/commands/harness.mjs +15 -0
  29. package/dist/cli/commands/help.mjs +1 -0
  30. package/dist/cli/commands/init.mjs +3 -0
  31. package/dist/cli/commands/mcp.mjs +36 -0
  32. package/dist/cli/commands/model.mjs +54 -0
  33. package/dist/cli/commands/plugins.mjs +43 -0
  34. package/dist/cli/commands/prepare.mjs +2 -0
  35. package/dist/cli/commands/public.mjs +3 -0
  36. package/dist/cli/commands/resolve.mjs +23 -0
  37. package/dist/cli/commands/route.mjs +2 -0
  38. package/dist/cli/commands/runner.mjs +1 -0
  39. package/dist/cli/commands/scan.mjs +2 -0
  40. package/dist/cli/commands/setup.mjs +34 -0
  41. package/dist/cli/commands/sign.mjs +4 -0
  42. package/dist/cli/commands/signer.mjs +1 -0
  43. package/dist/cli/commands/strategy.mjs +18 -0
  44. package/dist/cli/commands/swap.mjs +36 -0
  45. package/dist/cli/commands/upgrade.mjs +2 -0
  46. package/dist/cli/commands/vault.mjs +1 -0
  47. package/dist/cli/commands/version.mjs +2 -0
  48. package/dist/data/desk-data.mjs +55 -0
  49. package/dist/data/names.mjs +2 -0
  50. package/dist/data/providers/farming.mjs +2 -0
  51. package/dist/equities/index.mjs +1 -0
  52. package/dist/index.mjs +60 -0
  53. package/dist/nft-gas-war-guard.mjs +1 -0
  54. package/dist/onboarding/harness-configs.mjs +4 -0
  55. package/dist/portfolio-risk.mjs +1 -0
  56. package/dist/prepare-envelope.mjs +1 -0
  57. package/dist/public-api/agent-grants.mjs +3 -0
  58. package/dist/public-control/policy-schema.mjs +1 -0
  59. package/dist/router/index.mjs +2 -0
  60. package/dist/scanner/index.mjs +4 -0
  61. package/dist/signals/index.mjs +1 -0
  62. package/dist/strategy/index.mjs +3 -0
  63. package/dist/watch-preferences.mjs +1 -0
  64. package/package.json +33 -78
  65. package/public/oracle-splash/_variants/ice.html +5 -18
  66. package/public/oracle-splash/_variants/ivory.html +5 -18
  67. package/public/oracle-splash/downloads/index.html +4 -4
  68. package/public/oracle-splash/index.html +113 -34
  69. package/CONTRIBUTING.md +0 -99
  70. package/bin/desk-server.mjs +0 -473
  71. package/bin/oracle-data-mcp.mjs +0 -1252
  72. package/bin/oracle-equities.mjs +0 -168
  73. package/bin/oracle-init.mjs +0 -525
  74. package/bin/oracle-public-server.mjs +0 -36
  75. package/bin/oracle-route.mjs +0 -254
  76. package/bin/oracle-scan.mjs +0 -192
  77. package/bin/oracle-upgrade.mjs +0 -42
  78. package/bin/oracle.mjs +0 -34
  79. package/docs/adding-a-chain.md +0 -229
  80. package/docs/architecture.md +0 -139
  81. package/docs/buzz-integration.md +0 -115
  82. package/docs/cli.md +0 -143
  83. package/docs/connectors.md +0 -13
  84. package/docs/hyperliquid-builder-code.md +0 -50
  85. package/docs/locals-only-fee-waiver.md +0 -34
  86. package/docs/oracle-pack-standard.md +0 -29
  87. package/docs/profiles.md +0 -151
  88. package/docs/public-surface.md +0 -206
  89. package/examples/add-a-chain.mjs +0 -65
  90. package/examples/oracle-pack-template.mjs +0 -38
  91. package/examples/research-a-token.mjs +0 -70
  92. package/plugins/oracle-owner-gate/__init__.py +0 -288
  93. package/plugins/oracle-owner-gate/plugin.yaml +0 -9
  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-dist.mjs +0 -119
  101. package/scripts/build-inscription.py +0 -230
  102. package/scripts/check-doc-drift.mjs +0 -123
  103. package/scripts/check-test-count.mjs +0 -105
  104. package/scripts/e2e-hl-markets.mjs +0 -21
  105. package/scripts/e2e-hl-perps.mjs +0 -48
  106. package/scripts/e2e-hypercore-staking.mjs +0 -128
  107. package/scripts/e2e-solana-bitcoin.mjs +0 -183
  108. package/scripts/guard-publish.mjs +0 -22
  109. package/scripts/protocol-template-gate.mjs +0 -15
  110. package/scripts/public-api-scan.mjs +0 -23
  111. package/scripts/publish-dist.mjs +0 -67
  112. package/scripts/record-hero-cli.sh +0 -80
  113. package/scripts/secret-scan.mjs +0 -169
  114. package/scripts/smoke-dist-bins.mjs +0 -89
  115. package/scripts/verify-dist.mjs +0 -29
  116. package/scripts/verify-v3-venues.mjs +0 -233
  117. package/skins/oracle.yaml +0 -56
  118. package/src/action-receipts.mjs +0 -265
  119. package/src/action-semantics.mjs +0 -62
  120. package/src/address-book.mjs +0 -208
  121. package/src/agent-auth.mjs +0 -206
  122. package/src/approval-guard.mjs +0 -282
  123. package/src/attestation-secret.mjs +0 -94
  124. package/src/audit-log.mjs +0 -196
  125. package/src/auth/oauth.mjs +0 -672
  126. package/src/auto-slippage.mjs +0 -378
  127. package/src/capability-posture.mjs +0 -125
  128. package/src/cards.mjs +0 -422
  129. package/src/chains.mjs +0 -62
  130. package/src/cli/chain-catalog.mjs +0 -215
  131. package/src/cli/chain-state.mjs +0 -74
  132. package/src/cli/commands/auth.mjs +0 -193
  133. package/src/cli/commands/bootstrap.mjs +0 -87
  134. package/src/cli/commands/chain.mjs +0 -143
  135. package/src/cli/commands/chat.mjs +0 -465
  136. package/src/cli/commands/credential.mjs +0 -9
  137. package/src/cli/commands/data-mcp.mjs +0 -18
  138. package/src/cli/commands/data.mjs +0 -75
  139. package/src/cli/commands/doctor.mjs +0 -136
  140. package/src/cli/commands/equities.mjs +0 -41
  141. package/src/cli/commands/farm.mjs +0 -129
  142. package/src/cli/commands/fees.mjs +0 -109
  143. package/src/cli/commands/follow.mjs +0 -130
  144. package/src/cli/commands/harness.mjs +0 -63
  145. package/src/cli/commands/help.mjs +0 -7
  146. package/src/cli/commands/init.mjs +0 -27
  147. package/src/cli/commands/mcp.mjs +0 -154
  148. package/src/cli/commands/model.mjs +0 -106
  149. package/src/cli/commands/plugins.mjs +0 -275
  150. package/src/cli/commands/prepare.mjs +0 -12
  151. package/src/cli/commands/public.mjs +0 -20
  152. package/src/cli/commands/resolve.mjs +0 -64
  153. package/src/cli/commands/route.mjs +0 -12
  154. package/src/cli/commands/runner.mjs +0 -9
  155. package/src/cli/commands/scan.mjs +0 -12
  156. package/src/cli/commands/setup.mjs +0 -294
  157. package/src/cli/commands/sign.mjs +0 -21
  158. package/src/cli/commands/signer.mjs +0 -9
  159. package/src/cli/commands/swap.mjs +0 -102
  160. package/src/cli/commands/upgrade.mjs +0 -12
  161. package/src/cli/commands/vault.mjs +0 -9
  162. package/src/cli/commands/version.mjs +0 -22
  163. package/src/cli/first-run.mjs +0 -23
  164. package/src/cli/kernel.mjs +0 -262
  165. package/src/cli/mcp-targets/chatgpt.mjs +0 -51
  166. package/src/cli/mcp-targets/claude-code.mjs +0 -39
  167. package/src/cli/mcp-targets/claude-desktop.mjs +0 -24
  168. package/src/cli/mcp-targets/codex.mjs +0 -41
  169. package/src/cli/mcp-targets/cursor.mjs +0 -32
  170. package/src/cli/mcp-targets/shared.mjs +0 -71
  171. package/src/cli/mcp-targets/vscode.mjs +0 -39
  172. package/src/cli/messaging-platforms.mjs +0 -217
  173. package/src/cli/model-config.mjs +0 -78
  174. package/src/cli/operator-dispatch.mjs +0 -194
  175. package/src/cli/oracle-harness.py +0 -415
  176. package/src/cli/paths.mjs +0 -107
  177. package/src/cli/runtime.mjs +0 -351
  178. package/src/cli/setup-state.mjs +0 -168
  179. package/src/cli/spawn-child.mjs +0 -43
  180. package/src/data/catalog.mjs +0 -858
  181. package/src/data/desk-data.mjs +0 -877
  182. package/src/data/http.mjs +0 -253
  183. package/src/data/names.mjs +0 -188
  184. package/src/data/provider-endpoint.mjs +0 -94
  185. package/src/data/providers/aave-v3.mjs +0 -139
  186. package/src/data/providers/across-bridge.mjs +0 -41
  187. package/src/data/providers/aerodrome.mjs +0 -245
  188. package/src/data/providers/aevo.mjs +0 -33
  189. package/src/data/providers/approval-indexer.mjs +0 -133
  190. package/src/data/providers/approvals.mjs +0 -594
  191. package/src/data/providers/babylon-staking.mjs +0 -149
  192. package/src/data/providers/balancer.mjs +0 -209
  193. package/src/data/providers/beefy-yields.mjs +0 -45
  194. package/src/data/providers/birdeye-tokens.mjs +0 -33
  195. package/src/data/providers/bisq-markets.mjs +0 -145
  196. package/src/data/providers/bitcoin-esplora.mjs +0 -216
  197. package/src/data/providers/bitcoin-meta.mjs +0 -378
  198. package/src/data/providers/blockscout.mjs +0 -14
  199. package/src/data/providers/bob-rootstock.mjs +0 -198
  200. package/src/data/providers/braiins-insights.mjs +0 -103
  201. package/src/data/providers/bridge-aggregator.mjs +0 -259
  202. package/src/data/providers/bridges.mjs +0 -242
  203. package/src/data/providers/bungee.mjs +0 -29
  204. package/src/data/providers/celestia-da.mjs +0 -38
  205. package/src/data/providers/changenow.mjs +0 -135
  206. package/src/data/providers/compound.mjs +0 -32
  207. package/src/data/providers/connext.mjs +0 -28
  208. package/src/data/providers/convex.mjs +0 -30
  209. package/src/data/providers/cow-protocol.mjs +0 -110
  210. package/src/data/providers/cowswap.mjs +0 -415
  211. package/src/data/providers/curve.mjs +0 -201
  212. package/src/data/providers/das-assets.mjs +0 -59
  213. package/src/data/providers/debridge.mjs +0 -109
  214. package/src/data/providers/defillama.mjs +0 -88
  215. package/src/data/providers/dexscreener.mjs +0 -43
  216. package/src/data/providers/drift.mjs +0 -32
  217. package/src/data/providers/dydx.mjs +0 -41
  218. package/src/data/providers/evm-rpc.mjs +0 -225
  219. package/src/data/providers/farming.mjs +0 -291
  220. package/src/data/providers/geckoterminal.mjs +0 -34
  221. package/src/data/providers/gmx.mjs +0 -496
  222. package/src/data/providers/hiro-stacks.mjs +0 -150
  223. package/src/data/providers/hl-assets.mjs +0 -165
  224. package/src/data/providers/hl-info.mjs +0 -108
  225. package/src/data/providers/hl-markets.mjs +0 -210
  226. package/src/data/providers/hl-outcome.mjs +0 -61
  227. package/src/data/providers/hl-perps.mjs +0 -419
  228. package/src/data/providers/hl-staking.mjs +0 -353
  229. package/src/data/providers/hl-ws.mjs +0 -119
  230. package/src/data/providers/hyperevm-dex.mjs +0 -49
  231. package/src/data/providers/jito-mev.mjs +0 -117
  232. package/src/data/providers/jupiter-venues.mjs +0 -119
  233. package/src/data/providers/jupiter.mjs +0 -315
  234. package/src/data/providers/kamino-strategies.mjs +0 -143
  235. package/src/data/providers/l2beat-tvl.mjs +0 -42
  236. package/src/data/providers/lifi.mjs +0 -166
  237. package/src/data/providers/liquid-esplora.mjs +0 -152
  238. package/src/data/providers/magiceden-ordinals.mjs +0 -141
  239. package/src/data/providers/magiceden-sol.mjs +0 -406
  240. package/src/data/providers/mempool-lightning.mjs +0 -144
  241. package/src/data/providers/mempool-mining.mjs +0 -142
  242. package/src/data/providers/meteora-dlmm.mjs +0 -168
  243. package/src/data/providers/morpho.mjs +0 -177
  244. package/src/data/providers/nft-gallery.mjs +0 -163
  245. package/src/data/providers/nft-portfolio.mjs +0 -494
  246. package/src/data/providers/odos.mjs +0 -156
  247. package/src/data/providers/ondo.mjs +0 -31
  248. package/src/data/providers/oneinch.mjs +0 -174
  249. package/src/data/providers/opensea-multichain.mjs +0 -136
  250. package/src/data/providers/opensea-nft.mjs +0 -371
  251. package/src/data/providers/orca.mjs +0 -29
  252. package/src/data/providers/ordinals-runes.mjs +0 -180
  253. package/src/data/providers/pancakeswap.mjs +0 -28
  254. package/src/data/providers/paraswap.mjs +0 -125
  255. package/src/data/providers/pendle.mjs +0 -188
  256. package/src/data/providers/poly-clob.mjs +0 -283
  257. package/src/data/providers/poly-public.mjs +0 -96
  258. package/src/data/providers/poly-ws.mjs +0 -103
  259. package/src/data/providers/polygon-staking.mjs +0 -227
  260. package/src/data/providers/portfolio-history.mjs +0 -394
  261. package/src/data/providers/portfolio.mjs +0 -600
  262. package/src/data/providers/pyth-price-feeds.mjs +0 -172
  263. package/src/data/providers/raydium.mjs +0 -35
  264. package/src/data/providers/relayscan-mev.mjs +0 -72
  265. package/src/data/providers/rfq.mjs +0 -15
  266. package/src/data/providers/rh-agent.mjs +0 -59
  267. package/src/data/providers/sanctum-lst.mjs +0 -180
  268. package/src/data/providers/satflow.mjs +0 -341
  269. package/src/data/providers/signed-material-guard.mjs +0 -108
  270. package/src/data/providers/solana-rpc.mjs +0 -197
  271. package/src/data/providers/solend-lending.mjs +0 -166
  272. package/src/data/providers/spark-lend.mjs +0 -42
  273. package/src/data/providers/squid-router.mjs +0 -29
  274. package/src/data/providers/stargate.mjs +0 -29
  275. package/src/data/providers/sushiswap.mjs +0 -30
  276. package/src/data/providers/symbiotic-restaking.mjs +0 -41
  277. package/src/data/providers/thorchain.mjs +0 -36
  278. package/src/data/providers/traderjoe.mjs +0 -28
  279. package/src/data/providers/twap.mjs +0 -152
  280. package/src/data/providers/uniswap-v3.mjs +0 -457
  281. package/src/data/providers/uniswap-v4.mjs +0 -414
  282. package/src/data/providers/vertex.mjs +0 -41
  283. package/src/data/providers/wallet-watch.mjs +0 -264
  284. package/src/data/providers/wormhole.mjs +0 -28
  285. package/src/data/providers/yearn.mjs +0 -31
  286. package/src/data/providers/zerox.mjs +0 -167
  287. package/src/data/public-api-scan.mjs +0 -61
  288. package/src/data/quote-placeholder.mjs +0 -31
  289. package/src/desk/swap-prepare.mjs +0 -194
  290. package/src/equities/bridge.mjs +0 -388
  291. package/src/equities/fixtures/bridge_routes.json +0 -159
  292. package/src/equities/fixtures/dst_transitions.json +0 -29
  293. package/src/equities/fixtures/equity_dex_pools.json +0 -154
  294. package/src/equities/fixtures/hip3_l2books.json +0 -2711
  295. package/src/equities/fixtures/nyse_holidays.json +0 -1
  296. package/src/equities/fixtures/oracles.json +0 -100
  297. package/src/equities/fixtures/rh_pool_state.json +0 -244
  298. package/src/equities/fixtures/solana_xstocks.json +0 -704
  299. package/src/equities/fixtures/ton_xstocks.json +0 -35
  300. package/src/equities/fixtures/universe.json +0 -411
  301. package/src/equities/fixtures/venues.json +0 -3542
  302. package/src/equities/fixtures.mjs +0 -405
  303. package/src/equities/funding.mjs +0 -142
  304. package/src/equities/index.mjs +0 -166
  305. package/src/equities/liveness.mjs +0 -148
  306. package/src/equities/marketHours.mjs +0 -249
  307. package/src/equities/normalize.mjs +0 -295
  308. package/src/equities/num.mjs +0 -146
  309. package/src/equities/prepare.mjs +0 -95
  310. package/src/equities/rank.mjs +0 -260
  311. package/src/equities/sourceNum.mjs +0 -69
  312. package/src/equities/types.mjs +0 -220
  313. package/src/equities/venues/arcus-perp.mjs +0 -84
  314. package/src/equities/venues/arcus-spot.mjs +0 -105
  315. package/src/equities/venues/hyperliquid-hip3.mjs +0 -224
  316. package/src/equities/venues/rh-uniswap.mjs +0 -216
  317. package/src/equities/venues/solana-xstocks.mjs +0 -139
  318. package/src/equities/venues/ton-stonfi.mjs +0 -139
  319. package/src/exact-integer.mjs +0 -72
  320. package/src/exec-policy.mjs +0 -419
  321. package/src/flags.mjs +0 -15
  322. package/src/fresh-window.mjs +0 -76
  323. package/src/gmx-attestation.mjs +0 -176
  324. package/src/index.mjs +0 -107
  325. package/src/licensing/locals-only.mjs +0 -25
  326. package/src/nft-gas-war-guard.mjs +0 -139
  327. package/src/onboarding/agent-keys.mjs +0 -161
  328. package/src/onboarding/harness-configs.mjs +0 -102
  329. package/src/onboarding/index.mjs +0 -18
  330. package/src/onboarding/tiers.mjs +0 -139
  331. package/src/oracle-env.mjs +0 -55
  332. package/src/package-manifest.mjs +0 -42
  333. package/src/plugins/agent-plugin-loader.mjs +0 -225
  334. package/src/portfolio-risk.mjs +0 -170
  335. package/src/prepare-envelope.mjs +0 -204
  336. package/src/profile-upgrade.mjs +0 -277
  337. package/src/protocol-execution.mjs +0 -84
  338. package/src/protocol-templates/gate.mjs +0 -179
  339. package/src/protocol-templates/prepare-deploy.mjs +0 -81
  340. package/src/public-api/agent-grants.mjs +0 -57
  341. package/src/public-api/buzz-integration.mjs +0 -267
  342. package/src/public-api/connect-agent.mjs +0 -424
  343. package/src/public-api/grants.mjs +0 -142
  344. package/src/public-api/http.mjs +0 -707
  345. package/src/public-api/metering.mjs +0 -111
  346. package/src/public-control/aa-adapter.mjs +0 -402
  347. package/src/public-control/build-registry.mjs +0 -227
  348. package/src/public-control/bundler-client.mjs +0 -357
  349. package/src/public-control/grant-indexer.mjs +0 -296
  350. package/src/public-control/policy-render.mjs +0 -69
  351. package/src/public-control/policy-schema.mjs +0 -322
  352. package/src/public-control/runtime-config.mjs +0 -275
  353. package/src/public-control/session-key-model.mjs +0 -379
  354. package/src/public-control/session-orchestrator.mjs +0 -418
  355. package/src/rfq/intent.mjs +0 -180
  356. package/src/rfq/sources.mjs +0 -181
  357. package/src/route-attestation.mjs +0 -132
  358. package/src/router/best-execution.mjs +0 -326
  359. package/src/router/index.mjs +0 -250
  360. package/src/router/integrator-fee.mjs +0 -135
  361. package/src/router/prepare-bridge.mjs +0 -293
  362. package/src/router/prepare-route.mjs +0 -398
  363. package/src/router/proposal.mjs +0 -311
  364. package/src/router/risk-classifier.mjs +0 -119
  365. package/src/router/route-sources.mjs +0 -364
  366. package/src/scanner/chains.config.mjs +0 -543
  367. package/src/scanner/contract.mjs +0 -270
  368. package/src/scanner/evm-scanner.mjs +0 -394
  369. package/src/scanner/index.mjs +0 -9
  370. package/src/scanner/v2-venue.mjs +0 -351
  371. package/src/scanner/v3-venue.mjs +0 -308
  372. package/src/scopes.mjs +0 -44
  373. package/src/sell-simulation.mjs +0 -167
  374. package/src/signals/engine.mjs +0 -146
  375. package/src/signals/index.mjs +0 -1
  376. package/src/token-transfer-guard.mjs +0 -188
  377. package/src/tui/app.mjs +0 -438
  378. package/src/tui/backend.mjs +0 -215
  379. package/src/tui/format.mjs +0 -272
  380. package/src/tui/gateway-client.mjs +0 -177
  381. package/src/tui/input.mjs +0 -501
  382. package/src/tui/memory.mjs +0 -105
  383. package/src/tui/meta-learn.mjs +0 -85
  384. package/src/tui/renderer.mjs +0 -443
  385. package/src/tui/scheduler.mjs +0 -205
  386. package/src/tui/skills-loader.mjs +0 -140
  387. package/src/tui/standalone-client.mjs +0 -752
  388. package/src/tui/theme.mjs +0 -81
  389. package/src/vault-attestation.mjs +0 -146
  390. package/src/venues.mjs +0 -206
  391. package/src/watch-preferences.mjs +0 -85
  392. /package/{profiles → dist/assets/profiles}/_template/SOUL.md +0 -0
  393. /package/{profiles → dist/assets/profiles}/_template/profile.json +0 -0
  394. /package/{profiles → dist/assets/profiles}/bitcoin-agent/SOUL.md +0 -0
  395. /package/{profiles → dist/assets/profiles}/bitcoin-agent/profile.json +0 -0
  396. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/SOUL.md +0 -0
  397. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/profile.json +0 -0
  398. /package/{profiles → dist/assets/profiles}/oracle/SOUL.md +0 -0
  399. /package/{profiles → dist/assets/profiles}/oracle/profile.json +0 -0
  400. /package/{profiles → dist/assets/profiles}/polymarket-agent/SOUL.md +0 -0
  401. /package/{profiles → dist/assets/profiles}/polymarket-agent/profile.json +0 -0
  402. /package/{profiles → dist/assets/profiles}/profile.schema.json +0 -0
  403. /package/{profiles → dist/assets/profiles}/protocol-builder/SOUL.md +0 -0
  404. /package/{profiles → dist/assets/profiles}/protocol-builder/profile.json +0 -0
  405. /package/{profiles → dist/assets/profiles}/robinhood-agent/SOUL.md +0 -0
  406. /package/{profiles → dist/assets/profiles}/robinhood-agent/profile.json +0 -0
  407. /package/{profiles → dist/assets/profiles}/solana-agent/SOUL.md +0 -0
  408. /package/{profiles → dist/assets/profiles}/solana-agent/profile.json +0 -0
  409. /package/{profiles → dist/assets/profiles}/stable-agent/SOUL.md +0 -0
  410. /package/{profiles → dist/assets/profiles}/stable-agent/profile.json +0 -0
  411. /package/{skills → dist/assets/skills}/balance/SKILL.md +0 -0
  412. /package/{skills → dist/assets/skills}/bitcoin-l1.md +0 -0
  413. /package/{skills → dist/assets/skills}/chain-defi-ecosystem-profiling.md +0 -0
  414. /package/{skills → dist/assets/skills}/evm-contract-research.md +0 -0
  415. /package/{skills → dist/assets/skills}/hyperliquid.md +0 -0
  416. /package/{skills → dist/assets/skills}/multichain-exec-desk.md +0 -0
  417. /package/{skills → dist/assets/skills}/oracle-action-semantics/SKILL.md +0 -0
  418. /package/{skills → dist/assets/skills}/oracle-best-execution/SKILL.md +0 -0
  419. /package/{skills → dist/assets/skills}/oracle-best-execution.md +0 -0
  420. /package/{skills → dist/assets/skills}/oracle-bitcoin/SKILL.md +0 -0
  421. /package/{skills → dist/assets/skills}/oracle-chain-graphs-telegram-cards/SKILL.md +0 -0
  422. /package/{skills → dist/assets/skills}/oracle-chat/SKILL.md +0 -0
  423. /package/{skills → dist/assets/skills}/oracle-chat/chain.SKILL.md +0 -0
  424. /package/{skills → dist/assets/skills}/oracle-chat/setup.SKILL.md +0 -0
  425. /package/{skills → dist/assets/skills}/oracle-circuit-breaker/SKILL.md +0 -0
  426. /package/{skills → dist/assets/skills}/oracle-contract-research/SKILL.md +0 -0
  427. /package/{skills → dist/assets/skills}/oracle-desk/SKILL.md +0 -0
  428. /package/{skills → dist/assets/skills}/oracle-desk.md +0 -0
  429. /package/{skills → dist/assets/skills}/oracle-dex-launch/SKILL.md +0 -0
  430. /package/{skills → dist/assets/skills}/oracle-equities/SKILL.md +0 -0
  431. /package/{skills → dist/assets/skills}/oracle-grants/SKILL.md +0 -0
  432. /package/{skills → dist/assets/skills}/oracle-hypercore-staking/SKILL.md +0 -0
  433. /package/{skills → dist/assets/skills}/oracle-hyperliquid/SKILL.md +0 -0
  434. /package/{skills → dist/assets/skills}/oracle-meme-token-sniper/SKILL.md +0 -0
  435. /package/{skills → dist/assets/skills}/oracle-multichain-nft-launch/SKILL.md +0 -0
  436. /package/{skills → dist/assets/skills}/oracle-multichain-token-launch/SKILL.md +0 -0
  437. /package/{skills → dist/assets/skills}/oracle-nft-gacha-launch/SKILL.md +0 -0
  438. /package/{skills → dist/assets/skills}/oracle-nft-mint-gas-war/SKILL.md +0 -0
  439. /package/{skills → dist/assets/skills}/oracle-polymarket/SKILL.md +0 -0
  440. /package/{skills → dist/assets/skills}/oracle-protocol-builder/SKILL.md +0 -0
  441. /package/{skills → dist/assets/skills}/oracle-protocol-security/SKILL.md +0 -0
  442. /package/{skills → dist/assets/skills}/oracle-public-product/SKILL.md +0 -0
  443. /package/{skills → dist/assets/skills}/oracle-receipts/SKILL.md +0 -0
  444. /package/{skills → dist/assets/skills}/oracle-receipts.md +0 -0
  445. /package/{skills → dist/assets/skills}/oracle-rfq-tokenized-assets/SKILL.md +0 -0
  446. /package/{skills → dist/assets/skills}/oracle-smart-wallet-scanner/SKILL.md +0 -0
  447. /package/{skills → dist/assets/skills}/oracle-solana/SKILL.md +0 -0
  448. /package/{skills → dist/assets/skills}/oracle-solana-nft/SKILL.md +0 -0
  449. /package/{skills → dist/assets/skills}/oracle-token-research/SKILL.md +0 -0
  450. /package/{skills → dist/assets/skills}/solana.md +0 -0
@@ -1,250 +0,0 @@
1
- // Public entry point: find the best swap or bridge route.
2
- //
3
- // Returns a ranked comparison, not just a winner. Showing the runners-up and the
4
- // spread is what lets a caller sanity-check the result instead of trusting it -- and
5
- // a one-source "best route" is not a comparison at all, so that case is labelled
6
- // rather than presented as if it beat something.
7
-
8
- import {
9
- gatherRoutes, rankRoutes, QUALITY, measurePriceImpact, applyImpactCeiling,
10
- } from "./best-execution.mjs";
11
- import { swapCandidates, bridgeCandidates, nativeUsd } from "./route-sources.mjs";
12
- import { llamaPrices } from "../data/providers/defillama.mjs";
13
-
14
- /** DefiLlama key for an ERC-20 on a given chain. */
15
- const LLAMA_CHAIN = {
16
- 1: "ethereum",
17
- 10: "optimism",
18
- 56: "bsc",
19
- 137: "polygon",
20
- 8453: "base",
21
- 42161: "arbitrum",
22
- 43114: "avax",
23
- };
24
-
25
- async function destPrice(chainId, token, opts) {
26
- const chain = LLAMA_CHAIN[Number(chainId)];
27
- if (!chain) return { priceUsd: null, decimals: null };
28
- const key = `${chain}:${token}`;
29
- try {
30
- const res = await llamaPrices([key], opts);
31
- const hit = res?.coins?.[key];
32
- const p = Number(hit?.price);
33
- const d = Number(hit?.decimals);
34
- return {
35
- priceUsd: Number.isFinite(p) ? p : null,
36
- decimals: Number.isFinite(d) ? d : null,
37
- };
38
- } catch {
39
- return { priceUsd: null, decimals: null };
40
- }
41
- }
42
-
43
- /**
44
- * Best same-chain swap route.
45
- *
46
- * @param {object} p
47
- * @param {number} p.chainId
48
- * @param {string} p.tokenIn
49
- * @param {string} p.tokenOut
50
- * @param {string|bigint} p.amountIn raw units of tokenIn
51
- * @param {string} [p.taker] address used for quoting only
52
- * @param {number} [p.decimalsIn] enables ParaSwap
53
- * @param {number} [p.decimalsOut] enables ParaSwap + net ranking
54
- */
55
- export async function bestSwapRoute(p, opts = {}) {
56
- const { chainId, tokenIn, tokenOut, amountIn } = p;
57
- if (chainId == null || !tokenIn || !tokenOut || amountIn == null) {
58
- throw new Error("bestSwapRoute requires chainId, tokenIn, tokenOut, amountIn");
59
- }
60
-
61
- // Prices and quotes run CONCURRENTLY, not one after the other.
62
- //
63
- // The obvious shape -- await the prices, then fan out the quotes -- adds the price
64
- // latency to every single comparison. Measured on Base: ~890ms of DefiLlama in
65
- // front of ~3.3s of quotes, for 4.7s total when the true floor is 3.3s.
66
- //
67
- // Neither price is needed to ASK for a quote:
68
- // * destPrice is only used at RANK time, after every quote has returned
69
- // * nativePriceUsd is only used by the 0x adapter to convert its native-wei gas
70
- // figure, so it is passed as a PROMISE and awaited inside that one adapter
71
- //
72
- // So the whole price fetch hides behind the slowest quote instead of preceding it.
73
- const nativePricePromise = nativeUsd(chainId, opts);
74
- const destPromise = destPrice(chainId, tokenOut, opts);
75
-
76
- const candidates = swapCandidates({
77
- ...p,
78
- nativePriceUsd: nativePricePromise,
79
- decimalsOut: p.decimalsOut,
80
- destDecimalsPromise: destPromise,
81
- opts,
82
- });
83
-
84
- const [routes, dest] = await Promise.all([
85
- gatherRoutes(candidates, { timeoutMs: opts.timeoutMs ?? 12_000 }),
86
- destPromise,
87
- ]);
88
-
89
- // Swallow an unused rejection so a failed price lookup cannot surface as an
90
- // unhandled rejection when no adapter happened to await it.
91
- nativePricePromise.catch(() => {});
92
-
93
- const ranked = rankRoutes(routes, {
94
- destPriceUsd: dest.priceUsd,
95
- destDecimals: p.decimalsOut ?? dest.decimals,
96
- });
97
-
98
- // Liquidity check. Ranking alone will happily crown a quote taken against a
99
- // drained pool, because a thin venue returns a well-formed number rather than
100
- // an error. Re-quote the winner at 1/1000th size and compare per-unit price:
101
- // real slippage decays smoothly, a dead pool falls off a cliff.
102
- //
103
- // Opt out with `maxImpactPct: null` for callers that genuinely want the raw
104
- // ranking (analytics, spread display) rather than an executable route.
105
- const maxImpactPct = opts.maxImpactPct === undefined ? 25 : opts.maxImpactPct;
106
- if (maxImpactPct != null && ranked.best) {
107
- const winner = ranked.best.source;
108
- const probe = async (amt) => {
109
- const sub = swapCandidates({
110
- ...p,
111
- amountIn: amt.toString(),
112
- nativePriceUsd: nativePricePromise,
113
- decimalsOut: p.decimalsOut,
114
- destDecimalsPromise: destPromise,
115
- opts,
116
- }).filter((c) => c.source === winner);
117
- if (!sub.length) return null;
118
- const got = await gatherRoutes(sub, { timeoutMs: opts.timeoutMs ?? 12_000 });
119
- return got?.[0]?.amountOut ?? null;
120
- };
121
-
122
- const impact = await measurePriceImpact(probe, amountIn);
123
- if (impact) {
124
- const guarded = applyImpactCeiling(ranked, { [winner]: impact }, { maxImpactPct });
125
- return {
126
- kind: "swap",
127
- chainId,
128
- tokenIn,
129
- tokenOut,
130
- amountIn: String(amountIn),
131
- ...guarded,
132
- // Provenance travels with the guarded result too. Omitting it here made
133
- // the CLI print "sources: undefined/undefined answered" and a bare
134
- // "undefined" note on exactly the swaps that DID get impact-measured.
135
- sourcesTried: candidates.length,
136
- sourcesAnswered: routes.filter((r) => r.quality !== QUALITY.FAILED).length,
137
- note: describe(guarded, candidates.length),
138
- priceImpact: { [winner]: Number(impact.impactPct.toFixed(2)) },
139
- };
140
- }
141
- }
142
-
143
- return {
144
- kind: "swap",
145
- chainId,
146
- tokenIn,
147
- tokenOut,
148
- amountIn: String(amountIn),
149
- ...ranked,
150
- sourcesTried: candidates.length,
151
- sourcesAnswered: routes.filter((r) => r.quality !== QUALITY.FAILED).length,
152
- note: describe(ranked, candidates.length),
153
- };
154
- }
155
-
156
- /**
157
- * Best cross-chain bridge route.
158
- *
159
- * Duration is reported, never folded into the score. A route that saves $2 and takes
160
- * 30 minutes is not strictly better than one costing $2 more that lands in 20
161
- * seconds -- that trade-off belongs to the caller, and burying it in a single number
162
- * would hide the only thing distinguishing two similar quotes.
163
- */
164
- export async function bestBridgeRoute(p, opts = {}) {
165
- const { fromChainId, toChainId, tokenIn, tokenOut, amountIn } = p;
166
- if (fromChainId == null || toChainId == null || !tokenIn || !tokenOut || amountIn == null) {
167
- throw new Error(
168
- "bestBridgeRoute requires fromChainId, toChainId, tokenIn, tokenOut, amountIn",
169
- );
170
- }
171
- if (Number(fromChainId) === Number(toChainId)) {
172
- throw new Error("bestBridgeRoute is for cross-chain; use bestSwapRoute for same-chain");
173
- }
174
-
175
- // Same concurrency rule as swaps: the destination price is only needed at RANK
176
- // time, so fetching it before the quotes would add its latency to every bridge
177
- // comparison for no reason.
178
- const destPromise = destPrice(toChainId, tokenOut, opts);
179
- const candidates = bridgeCandidates({ ...p, opts });
180
- const [routes, dest] = await Promise.all([
181
- gatherRoutes(candidates, { timeoutMs: opts.timeoutMs ?? 15_000 }),
182
- destPromise,
183
- ]);
184
- const ranked = rankRoutes(routes, {
185
- destPriceUsd: dest.priceUsd,
186
- destDecimals: p.decimalsOut ?? dest.decimals,
187
- });
188
-
189
- const durations = ranked.routes
190
- .map((r) => ({ source: r.source, seconds: r.meta?.durationSeconds ?? null }))
191
- .filter((d) => d.seconds != null);
192
-
193
- return {
194
- kind: "bridge",
195
- fromChainId,
196
- toChainId,
197
- tokenIn,
198
- tokenOut,
199
- amountIn: String(amountIn),
200
- ...ranked,
201
- durations,
202
- sourcesTried: candidates.length,
203
- sourcesAnswered: routes.filter((r) => r.quality !== QUALITY.FAILED).length,
204
- note: describe(ranked, candidates.length),
205
- };
206
- }
207
-
208
- function describe(ranked, tried) {
209
- if (!ranked.best) return "no source returned a usable route";
210
- const answered = ranked.routes.length;
211
- if (answered === 1) {
212
- return (
213
- `only 1 of ${tried} sources answered, so this is the ONLY route, not a proven ` +
214
- "best one. Treat it as unbenchmarked."
215
- );
216
- }
217
- if (ranked.improvementBps != null) {
218
- const bps = ranked.improvementBps;
219
- const pct = bps / 100;
220
- // Below 0.01bps the winner is a coin flip against quote noise, so claiming
221
- // an edge in ANY unit is a false precision claim. Say they are tied.
222
- if (bps < 0.01) {
223
- return (
224
- `best of ${answered} routes, ranked on ${ranked.rankedOn}: ` +
225
- `effectively tied with the runner-up (${ranked.spreadBasis}), ` +
226
- "inside quote noise. Re-quote before you size up."
227
- );
228
- }
229
- // A sub-bp edge is still an edge and on size it is real money, so report it
230
- // in bps rather than letting the percentage round it away to 0.00%.
231
- const margin = pct >= 0.01 ? `${pct.toFixed(2)}%` : `${bps.toFixed(2)}bps`;
232
- return (
233
- `best of ${answered} routes, ranked on ${ranked.rankedOn}: ` +
234
- `${margin} better than the runner-up ` +
235
- `(${ranked.spreadBasis})`
236
- );
237
- }
238
- // No spread claim when the top two are not measured the same way. Saying "X% better"
239
- // off a mismatched pair is worse than saying nothing.
240
- return (
241
- `best of ${answered} routes, ranked on ${ranked.rankedOn}. No spread quoted: the ` +
242
- "top routes report cost differently, so the margin is not comparable."
243
- );
244
- }
245
-
246
- export { QUALITY };
247
-
248
- // Exported for tests: the note text is a user-facing claim about execution
249
- // quality, so it needs deterministic coverage independent of live quotes.
250
- export { describe as formatComparisonNote };
@@ -1,135 +0,0 @@
1
- /**
2
- * Integrator fee policy.
3
- *
4
- * One place decides whether a route carries a fee, so the answer cannot drift
5
- * between providers. Three rules, in order:
6
- *
7
- * 1. OFF unless explicitly configured. No silent default skim.
8
- * 2. Locals Only holders pay ZERO Oracle integrator fees. Ownership changes
9
- * only this rate and never changes product access.
10
- * 3. Whatever is charged must be disclosable. `describeFee()` returns the
11
- * exact bps and recipient so the caller can render it BEFORE the user
12
- * signs. Oracle's pitch is "decoded before you sign" — an undisclosed fee
13
- * would cost more credibility than the basis points are worth.
14
- *
15
- * Note on the status quo this replaces: LI.FI already applies its own 25 bps
16
- * to every route Oracle sends and keeps all of it, because Oracle never
17
- * identified itself as an integrator. Verified live 2026-08-06 — an
18
- * unregistered quote returns feeCosts [{ name: "LIFI Fixed Fee", percentage:
19
- * "0.0025" }]. So this is not a new cost to the user on that path; it is
20
- * Oracle claiming a share of a fee that is already being charged.
21
- */
22
-
23
- const MAX_BPS = 100; // 1%. A hard ceiling so a typo cannot ship a 50% fee.
24
-
25
- /**
26
- * 10 bps. Deliberately at the low end: LI.FI's own default is 25 bps and
27
- * Matcha runs 15, so this stays invisible to anyone comparison-shopping.
28
- *
29
- * The fee's job is not revenue maximisation — Locals Only holders pay zero, so
30
- * a small non-holder fee makes the NFT obviously worth holding. A large one
31
- * would just teach people to route around Oracle.
32
- */
33
- export const DEFAULT_FEE_BPS = 10;
34
-
35
- /**
36
- * Per-action pricing. A same-chain swap is the most price-shopped action in
37
- * crypto — every aggregator quotes it and users compare to the basis point —
38
- * so it stays cheapest. The others are not comparison-shopped the same way and
39
- * deliver more work per transaction.
40
- *
41
- * swap 10 bps below LI.FI 25 and Matcha 15
42
- * bridge 15 bps multi-chain routing, longer settlement, more failure modes
43
- * perps 5 bps venues already charge taker fees; stacking is punitive
44
- * nft 0 marketplace fees are already heavy
45
- *
46
- * A holder pays zero on every one of these.
47
- */
48
- export const FEE_TIERS = Object.freeze({
49
- swap: 10,
50
- bridge: 15,
51
- perps: 5,
52
- nft: 0,
53
- });
54
-
55
- export function tierBps(action) {
56
- const key = String(action || "swap").toLowerCase();
57
- return Object.prototype.hasOwnProperty.call(FEE_TIERS, key) ? FEE_TIERS[key] : DEFAULT_FEE_BPS;
58
- }
59
-
60
- export const FEE_ENV = "ORACLE_INTEGRATOR_FEE_BPS";
61
- export const FEE_RECIPIENT_ENV = "ORACLE_INTEGRATOR_FEE_RECIPIENT";
62
- export const INTEGRATOR_ENV = "ORACLE_INTEGRATOR_ID";
63
-
64
- export const DEFAULT_INTEGRATOR_ID = "oracle";
65
-
66
- function parseBps(raw, { fallback = 0 } = {}) {
67
- // Absent means "use the house default". An explicitly EMPTY string means the
68
- // operator set it to nothing on purpose, which is an instruction to charge
69
- // nothing — not an invitation to fall back.
70
- if (raw === undefined || raw === null) return fallback;
71
- if (String(raw).trim() === "") return 0;
72
- const n = Number(raw);
73
- if (!Number.isFinite(n) || n <= 0) return 0;
74
- return Math.min(Math.floor(n), MAX_BPS);
75
- }
76
-
77
- /**
78
- * Resolve the fee that applies to one route.
79
- *
80
- * `isHolder` is passed in rather than looked up here: this module must stay
81
- * synchronous and side-effect free so it can be called from inside quote
82
- * builders without turning them into async chain-readers.
83
- */
84
- export function resolveFee({ env = process.env, isHolder = false, action = "swap" } = {}) {
85
- const recipient = String(env[FEE_RECIPIENT_ENV] || "").trim();
86
- // Setting a recipient is the opt-in. Once that exists, bps defaults to 10
87
- // rather than forcing every deployment to restate the house number.
88
- const bps = parseBps(env[FEE_ENV], { fallback: recipient ? tierBps(action) : 0 });
89
- const integrator = String(env[INTEGRATOR_ENV] || DEFAULT_INTEGRATOR_ID).trim();
90
-
91
- // Holder check comes first. A zero-bps tier (nft) would otherwise return
92
- // "not-configured" for a holder, which is a true zero with a misleading
93
- // reason — the UI would say "No Oracle fee" instead of crediting the NFT.
94
- if (isHolder) {
95
- return { bps: 0, recipient, integrator, action, applies: false, reason: "locals-only-holder" };
96
- }
97
-
98
- if (!bps) {
99
- return { bps: 0, recipient: "", integrator, action, applies: false, reason: "not-configured" };
100
- }
101
- if (!/^0x[a-fA-F0-9]{40}$/.test(recipient)) {
102
- // Fail closed. A fee with nowhere to go is a fee that silently vanishes
103
- // into the aggregator, which is the exact situation this module exists to
104
- // end.
105
- return { bps: 0, recipient: "", integrator, action, applies: false, reason: "no-recipient" };
106
- }
107
- return { bps, recipient, integrator, action, applies: true, reason: "configured" };
108
- }
109
-
110
- /** Human-readable disclosure. Render this next to the quote, before signing. */
111
- export function describeFee(fee) {
112
- if (!fee?.applies) {
113
- if (fee?.reason === "locals-only-holder") return "No Oracle fee (Locals Only holder).";
114
- return "No Oracle fee.";
115
- }
116
- return `Oracle routing fee ${fee.bps} bps (${(fee.bps / 100).toFixed(2)}%) to ${fee.recipient}.`;
117
- }
118
-
119
- /** LI.FI wants a decimal fraction plus a registered integrator id. */
120
- export function lifiFeeParams(fee) {
121
- if (!fee?.applies) return {};
122
- return { integrator: fee.integrator, fee: String(fee.bps / 10_000) };
123
- }
124
-
125
- /** ParaSwap wants integer bps and a partner string. */
126
- export function paraswapFeeParams(fee) {
127
- if (!fee?.applies) return {};
128
- return {
129
- partner: fee.integrator,
130
- partnerFeeBps: String(fee.bps),
131
- partnerAddress: fee.recipient,
132
- };
133
- }
134
-
135
- export const FEE_MAX_BPS = MAX_BPS;
@@ -1,293 +0,0 @@
1
- // Prepare a cross-chain bridge route.
2
- //
3
- // Bridging differs from swapping in ways that matter for a signable artifact:
4
- //
5
- // 1. IT IS OFTEN MULTIPLE TRANSACTIONS. Relay can return an approval and a deposit
6
- // as separate items. A caller handed a bare "transaction" would sign the first
7
- // and believe they were done, leaving funds approved but not bridged. So the
8
- // artifact is always a LIST, and its length is stated.
9
- //
10
- // 2. THE DESTINATION CHAIN IS PART OF THE TRUST DECISION. Every transaction here
11
- // executes on the ORIGIN chain, but the value lands elsewhere. Signing an
12
- // origin-chain tx that credits the wrong destination is unrecoverable, so
13
- // toChainId is echoed alongside the destination address.
14
- //
15
- // 3. TIME IS A REAL COST. A bridge that saves $2 and takes 30 minutes is not
16
- // obviously better than one costing $2 more that lands in 20 seconds. Duration
17
- // is surfaced, never folded into a score.
18
- //
19
- // 4. FINALITY IS NOT ATOMIC. The origin transaction succeeding does NOT mean funds
20
- // arrived. That gap is where users panic, so it is stated explicitly rather than
21
- // left implied.
22
- //
23
- // Custody is unchanged: artifacts only. This module cannot sign or broadcast.
24
-
25
- import { bestBridgeRoute } from "./index.mjs";
26
- import { stampPrepared } from "../prepare-envelope.mjs";
27
-
28
- const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
29
-
30
- /**
31
- * Placeholder addresses: fine for quoting, never for preparing.
32
- * Same reasoning as the swap path -- some venues catch this and some do not.
33
- */
34
- const NON_TAKER_ADDRESSES = new Set(
35
- [
36
- "0x0000000000000000000000000000000000000000",
37
- "0x000000000000000000000000000000000000dead",
38
- "0x0000000000000000000000000000000000000001",
39
- "0x00000000000000000000000000000000000a1ce5",
40
- ].map((a) => a.toLowerCase()),
41
- );
42
-
43
- const DEFAULT_DRIFT_TOLERANCE_BPS = 100;
44
-
45
- const PREPARERS = {
46
- relay: async ({ fromChainId, toChainId, tokenIn, tokenOut, amountIn, taker }, opts) => {
47
- const { relayPrepare } = await import("../data/providers/bridges.mjs");
48
- const p = await relayPrepare(
49
- {
50
- user: taker,
51
- originChainId: fromChainId,
52
- destinationChainId: toChainId,
53
- originCurrency: tokenIn,
54
- destinationCurrency: tokenOut,
55
- amount: String(amountIn),
56
- },
57
- opts,
58
- );
59
- return {
60
- transactions: p.transactions || [],
61
- amountOut: p.quote?.details?.currencyOut?.amount ?? null,
62
- minOut: p.quote?.details?.currencyOut?.minimumAmount ?? null,
63
- durationSeconds: p.quote?.details?.timeEstimate ?? null,
64
- };
65
- },
66
-
67
- lifi: async ({ fromChainId, toChainId, tokenIn, tokenOut, amountIn, taker, isHolder }, opts) => {
68
- const { lifiPrepare } = await import("../data/providers/lifi.mjs");
69
- const p = await lifiPrepare(
70
- {
71
- fromChain: fromChainId,
72
- toChain: toChainId,
73
- fromToken: tokenIn,
74
- toToken: tokenOut,
75
- fromAmount: amountIn,
76
- fromAddress: taker,
77
- // A bridge is not a same-chain swap: more routing, longer settlement,
78
- // more failure modes. Priced on its own tier.
79
- isHolder,
80
- feeAction: "bridge",
81
- },
82
- opts,
83
- );
84
- // LI.FI returns a single transactionRequest. Normalized into a list so callers
85
- // handle one shape regardless of source -- a single-item list is honest, a bare
86
- // object next to Relay's list is a trap.
87
- return {
88
- transactions: p.transaction ? [p.transaction] : [],
89
- requiresApproval: p.requiresApproval ?? null,
90
- amountOut: p.quote?.estimate?.toAmount ?? null,
91
- minOut: p.quote?.estimate?.toAmountMin ?? null,
92
- durationSeconds: p.quote?.estimate?.executionDuration ?? null,
93
- };
94
- },
95
- };
96
-
97
- export function supportedBridgePreparers() {
98
- return Object.keys(PREPARERS);
99
- }
100
-
101
- /**
102
- * Compare bridge routes, then prepare the winner.
103
- *
104
- * @param {object} p same shape as bestBridgeRoute, plus:
105
- * @param {string} p.taker the real wallet that will sign
106
- * @param {string} [p.source] force a specific source
107
- * @param {number} [p.driftToleranceBps]
108
- */
109
- export async function prepareBestBridgeRoute(p, opts = {}) {
110
- const { taker, fromChainId, toChainId } = p;
111
-
112
- if (!taker) {
113
- throw new Error(
114
- "prepareBestBridgeRoute requires taker: bridge transactions are built FOR an " +
115
- "address, and the quote-only placeholder must never end up in one",
116
- );
117
- }
118
- if (!ADDRESS_RE.test(taker)) {
119
- throw new Error(`taker must be a valid 20-byte address, got "${taker}"`);
120
- }
121
- if (NON_TAKER_ADDRESSES.has(taker.toLowerCase())) {
122
- throw new Error(
123
- `taker "${taker}" is a placeholder/burn address. Bridging to it would send ` +
124
- "funds to an address nobody controls, on a chain where you cannot undo it.",
125
- );
126
- }
127
-
128
- const comparison = await bestBridgeRoute(p, opts);
129
- if (!comparison.best) {
130
- return { ok: false, reason: "no source returned a usable bridge route", comparison };
131
- }
132
-
133
- const chosenSource = p.source || comparison.best.source;
134
- const chosen = comparison.routes.find((r) => r.source === chosenSource);
135
- if (!chosen) {
136
- return { ok: false, reason: `source "${chosenSource}" returned no usable route`, comparison };
137
- }
138
-
139
- const prepare = PREPARERS[chosenSource];
140
- if (!prepare) {
141
- const alt = comparison.routes.find((r) => PREPARERS[r.source] && r.source !== chosenSource);
142
- return {
143
- ok: false,
144
- reason:
145
- `${chosenSource} won the comparison but has no bridge prepare path in this ` +
146
- `build. ` +
147
- (alt
148
- ? `Next preparable route is ${alt.source}; pass source:"${alt.source}".`
149
- : "No route in this comparison can be prepared."),
150
- // Machine-readable siblings of the message above. A caller should not have to
151
- // regex prose to find the fallback -- and the CLI/agent path reads these.
152
- failureKind: "no-prepare-path",
153
- ...(alt ? { alternative: alt.source } : {}),
154
- winner: chosenSource,
155
- comparison,
156
- };
157
- }
158
-
159
- let prepared;
160
- try {
161
- prepared = await prepare(p, opts);
162
- } catch (err) {
163
- const detail =
164
- typeof err?.body === "string" ? err.body : err?.body ? JSON.stringify(err.body) : "";
165
- const msg = [String(err?.message || err), detail].filter(Boolean).join(" :: ");
166
-
167
- const allowanceIssue = /allowance/i.test(msg);
168
- const balanceIssue = !allowanceIssue && /not enough|insufficient|balance/i.test(msg);
169
- const alt = comparison.routes.find((r) => PREPARERS[r.source] && r.source !== chosenSource);
170
-
171
- return {
172
- ok: false,
173
- reason: allowanceIssue
174
- ? `${chosenSource} will not build calldata until the token approval exists ` +
175
- `on-chain. Approve first, then prepare again. Underlying: ${msg}`
176
- : balanceIssue
177
- ? `${chosenSource} refused to build: ${msg}. Quoting works for any address; ` +
178
- `preparing needs the real funded wallet on chain ${fromChainId}.`
179
- : `${chosenSource} bridge prepare failed: ${msg}`,
180
- failureKind: allowanceIssue
181
- ? "approval-required-first"
182
- : balanceIssue
183
- ? "taker-not-funded"
184
- : "provider-error",
185
- ...(alt ? { alternative: alt.source } : {}),
186
- comparison,
187
- };
188
- }
189
-
190
- const txs = prepared.transactions || [];
191
- if (!txs.length) {
192
- return {
193
- ok: false,
194
- reason: `${chosenSource} returned no executable transaction for this route`,
195
- failureKind: "provider-error",
196
- comparison,
197
- };
198
- }
199
-
200
- // Every transaction must execute on the ORIGIN chain. A wrong chainId here means a
201
- // wallet could be prompted on the wrong network, and the user would be signing
202
- // something they cannot reason about.
203
- const wrongChain = txs.filter(
204
- (t) => t.chainId != null && Number(t.chainId) !== Number(fromChainId),
205
- );
206
- if (wrongChain.length) {
207
- return {
208
- ok: false,
209
- reason:
210
- `${chosenSource} returned a transaction for chain ${wrongChain[0].chainId} but ` +
211
- `the origin chain is ${fromChainId}. Refusing to hand over a mismatched ` +
212
- `artifact.`,
213
- failureKind: "chain-mismatch",
214
- comparison,
215
- };
216
- }
217
-
218
- // Drift against the comparison. Same reasoning as swaps: the comparison is stale.
219
- const tolerance = p.driftToleranceBps ?? DEFAULT_DRIFT_TOLERANCE_BPS;
220
- let driftBps = null;
221
- let driftWarning = null;
222
- if (prepared.amountOut != null && chosen.grossOut != null) {
223
- try {
224
- const then = BigInt(chosen.grossOut);
225
- const now = BigInt(prepared.amountOut);
226
- if (then > 0n) {
227
- driftBps = Number(((now - then) * 10_000n) / then);
228
- if (driftBps < -tolerance) {
229
- driftWarning =
230
- `route moved ${(driftBps / 100).toFixed(2)}% against you since the ` +
231
- `comparison (tolerance ${(tolerance / 100).toFixed(2)}%). Re-compare ` +
232
- `before signing.`;
233
- }
234
- }
235
- } catch {
236
- /* no fabricated drift */
237
- }
238
- }
239
-
240
- const warnings = [
241
- ...(driftWarning ? [driftWarning] : []),
242
- ...(comparison.warnings || []),
243
- // The single most common bridging surprise.
244
- `Bridging is NOT atomic: the origin transaction succeeding does not mean funds ` +
245
- `arrived on chain ${toChainId}. Expect ~${
246
- prepared.durationSeconds != null ? `${prepared.durationSeconds}s` : "minutes"
247
- } before the destination credits, and do not re-send if it is pending.`,
248
- ];
249
-
250
- if (txs.length > 1) {
251
- warnings.push(
252
- `This route needs ${txs.length} transactions signed IN ORDER. Signing only the ` +
253
- `first leaves funds approved but not bridged.`,
254
- );
255
- }
256
-
257
- return stampPrepared({
258
- ok: true,
259
- chosen: {
260
- source: chosenSource,
261
- wasWinner: chosenSource === comparison.best.source,
262
- netOut: chosen.netOut,
263
- grossOut: chosen.grossOut,
264
- gasUsd: chosen.gasUsd,
265
- },
266
- artifactKind: "unsigned-transaction-sequence",
267
- fromChainId: Number(fromChainId),
268
- toChainId: Number(toChainId),
269
- transactionCount: txs.length,
270
- transactions: txs,
271
- requiresApproval: prepared.requiresApproval ?? null,
272
- minOut: prepared.minOut ?? null,
273
- durationSeconds: prepared.durationSeconds ?? null,
274
- // Where value goes on the origin chain. The first destination is the bridge entry.
275
- destination: txs[0]?.to ?? null,
276
- unsigned: true,
277
- signedBy: "user-wallet",
278
- driftBps,
279
- warnings,
280
- runnersUp: comparison.routes
281
- .filter((r) => r.source !== chosenSource)
282
- .map((r) => ({ source: r.source, netOut: r.netOut, gasUsd: r.gasUsd })),
283
- comparison: {
284
- rankedOn: comparison.rankedOn,
285
- sourcesAnswered: comparison.sourcesAnswered,
286
- sourcesTried: comparison.sourcesTried,
287
- improvementBps: comparison.improvementBps,
288
- },
289
- note:
290
- `UNSIGNED. ${txs.length} transaction(s) on chain ${fromChainId}, crediting ` +
291
- `chain ${toChainId}. Verify every destination address before signing.`,
292
- }, { provider: "router", kind: "best-bridge" });
293
- }