@oracle-agent/oracle 0.14.1 → 0.15.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 (447) 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 +59 -0
  7. package/dist/bin/oracle-data-mcp.mjs +8 -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 +61 -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/swap.mjs +36 -0
  44. package/dist/cli/commands/upgrade.mjs +2 -0
  45. package/dist/cli/commands/vault.mjs +1 -0
  46. package/dist/cli/commands/version.mjs +2 -0
  47. package/dist/data/desk-data.mjs +55 -0
  48. package/dist/data/names.mjs +2 -0
  49. package/dist/data/providers/farming.mjs +2 -0
  50. package/dist/equities/index.mjs +1 -0
  51. package/dist/index.mjs +60 -0
  52. package/dist/nft-gas-war-guard.mjs +1 -0
  53. package/dist/onboarding/harness-configs.mjs +4 -0
  54. package/dist/portfolio-risk.mjs +1 -0
  55. package/dist/prepare-envelope.mjs +1 -0
  56. package/dist/public-api/agent-grants.mjs +3 -0
  57. package/dist/public-control/policy-schema.mjs +1 -0
  58. package/dist/router/index.mjs +2 -0
  59. package/dist/scanner/index.mjs +4 -0
  60. package/dist/signals/index.mjs +1 -0
  61. package/dist/watch-preferences.mjs +1 -0
  62. package/package.json +32 -78
  63. package/public/oracle-splash/_variants/ice.html +5 -18
  64. package/public/oracle-splash/_variants/ivory.html +5 -18
  65. package/public/oracle-splash/index.html +114 -34
  66. package/CONTRIBUTING.md +0 -99
  67. package/bin/desk-server.mjs +0 -473
  68. package/bin/oracle-data-mcp.mjs +0 -1252
  69. package/bin/oracle-equities.mjs +0 -168
  70. package/bin/oracle-init.mjs +0 -525
  71. package/bin/oracle-public-server.mjs +0 -36
  72. package/bin/oracle-route.mjs +0 -254
  73. package/bin/oracle-scan.mjs +0 -192
  74. package/bin/oracle-upgrade.mjs +0 -42
  75. package/bin/oracle.mjs +0 -34
  76. package/docs/adding-a-chain.md +0 -229
  77. package/docs/architecture.md +0 -139
  78. package/docs/buzz-integration.md +0 -115
  79. package/docs/cli.md +0 -143
  80. package/docs/connectors.md +0 -13
  81. package/docs/hyperliquid-builder-code.md +0 -50
  82. package/docs/locals-only-fee-waiver.md +0 -34
  83. package/docs/oracle-pack-standard.md +0 -29
  84. package/docs/profiles.md +0 -151
  85. package/docs/public-surface.md +0 -206
  86. package/examples/add-a-chain.mjs +0 -65
  87. package/examples/oracle-pack-template.mjs +0 -38
  88. package/examples/research-a-token.mjs +0 -70
  89. package/plugins/oracle-owner-gate/__init__.py +0 -288
  90. package/plugins/oracle-owner-gate/plugin.yaml +0 -9
  91. package/protocols/templates/safe-erc20/SECURITY.md +0 -16
  92. package/protocols/templates/safe-erc20/foundry.toml +0 -12
  93. package/protocols/templates/safe-erc20/remappings.txt +0 -2
  94. package/protocols/templates/safe-erc20/src/SafeERC20.sol +0 -53
  95. package/protocols/templates/safe-erc20/test/SafeERC20.t.sol +0 -72
  96. package/scripts/adversarial-bench.mjs +0 -114
  97. package/scripts/build-dist.mjs +0 -119
  98. package/scripts/build-inscription.py +0 -230
  99. package/scripts/check-doc-drift.mjs +0 -123
  100. package/scripts/check-test-count.mjs +0 -105
  101. package/scripts/e2e-hl-markets.mjs +0 -21
  102. package/scripts/e2e-hl-perps.mjs +0 -48
  103. package/scripts/e2e-hypercore-staking.mjs +0 -128
  104. package/scripts/e2e-solana-bitcoin.mjs +0 -183
  105. package/scripts/guard-publish.mjs +0 -22
  106. package/scripts/protocol-template-gate.mjs +0 -15
  107. package/scripts/public-api-scan.mjs +0 -23
  108. package/scripts/publish-dist.mjs +0 -67
  109. package/scripts/record-hero-cli.sh +0 -80
  110. package/scripts/secret-scan.mjs +0 -169
  111. package/scripts/smoke-dist-bins.mjs +0 -89
  112. package/scripts/verify-dist.mjs +0 -29
  113. package/scripts/verify-v3-venues.mjs +0 -233
  114. package/skins/oracle.yaml +0 -56
  115. package/src/action-receipts.mjs +0 -265
  116. package/src/action-semantics.mjs +0 -62
  117. package/src/address-book.mjs +0 -208
  118. package/src/agent-auth.mjs +0 -206
  119. package/src/approval-guard.mjs +0 -282
  120. package/src/attestation-secret.mjs +0 -94
  121. package/src/audit-log.mjs +0 -196
  122. package/src/auth/oauth.mjs +0 -672
  123. package/src/auto-slippage.mjs +0 -378
  124. package/src/capability-posture.mjs +0 -125
  125. package/src/cards.mjs +0 -422
  126. package/src/chains.mjs +0 -62
  127. package/src/cli/chain-catalog.mjs +0 -215
  128. package/src/cli/chain-state.mjs +0 -74
  129. package/src/cli/commands/auth.mjs +0 -193
  130. package/src/cli/commands/bootstrap.mjs +0 -87
  131. package/src/cli/commands/chain.mjs +0 -143
  132. package/src/cli/commands/chat.mjs +0 -465
  133. package/src/cli/commands/credential.mjs +0 -9
  134. package/src/cli/commands/data-mcp.mjs +0 -18
  135. package/src/cli/commands/data.mjs +0 -75
  136. package/src/cli/commands/doctor.mjs +0 -136
  137. package/src/cli/commands/equities.mjs +0 -41
  138. package/src/cli/commands/farm.mjs +0 -129
  139. package/src/cli/commands/fees.mjs +0 -109
  140. package/src/cli/commands/follow.mjs +0 -130
  141. package/src/cli/commands/harness.mjs +0 -63
  142. package/src/cli/commands/help.mjs +0 -7
  143. package/src/cli/commands/init.mjs +0 -27
  144. package/src/cli/commands/mcp.mjs +0 -154
  145. package/src/cli/commands/model.mjs +0 -106
  146. package/src/cli/commands/plugins.mjs +0 -275
  147. package/src/cli/commands/prepare.mjs +0 -12
  148. package/src/cli/commands/public.mjs +0 -20
  149. package/src/cli/commands/resolve.mjs +0 -64
  150. package/src/cli/commands/route.mjs +0 -12
  151. package/src/cli/commands/runner.mjs +0 -9
  152. package/src/cli/commands/scan.mjs +0 -12
  153. package/src/cli/commands/setup.mjs +0 -294
  154. package/src/cli/commands/sign.mjs +0 -21
  155. package/src/cli/commands/signer.mjs +0 -9
  156. package/src/cli/commands/swap.mjs +0 -102
  157. package/src/cli/commands/upgrade.mjs +0 -12
  158. package/src/cli/commands/vault.mjs +0 -9
  159. package/src/cli/commands/version.mjs +0 -22
  160. package/src/cli/first-run.mjs +0 -23
  161. package/src/cli/kernel.mjs +0 -262
  162. package/src/cli/mcp-targets/chatgpt.mjs +0 -51
  163. package/src/cli/mcp-targets/claude-code.mjs +0 -39
  164. package/src/cli/mcp-targets/claude-desktop.mjs +0 -24
  165. package/src/cli/mcp-targets/codex.mjs +0 -41
  166. package/src/cli/mcp-targets/cursor.mjs +0 -32
  167. package/src/cli/mcp-targets/shared.mjs +0 -71
  168. package/src/cli/mcp-targets/vscode.mjs +0 -39
  169. package/src/cli/messaging-platforms.mjs +0 -217
  170. package/src/cli/model-config.mjs +0 -78
  171. package/src/cli/operator-dispatch.mjs +0 -194
  172. package/src/cli/oracle-harness.py +0 -415
  173. package/src/cli/paths.mjs +0 -107
  174. package/src/cli/runtime.mjs +0 -351
  175. package/src/cli/setup-state.mjs +0 -168
  176. package/src/cli/spawn-child.mjs +0 -43
  177. package/src/data/catalog.mjs +0 -858
  178. package/src/data/desk-data.mjs +0 -877
  179. package/src/data/http.mjs +0 -253
  180. package/src/data/names.mjs +0 -188
  181. package/src/data/provider-endpoint.mjs +0 -94
  182. package/src/data/providers/aave-v3.mjs +0 -139
  183. package/src/data/providers/across-bridge.mjs +0 -41
  184. package/src/data/providers/aerodrome.mjs +0 -245
  185. package/src/data/providers/aevo.mjs +0 -33
  186. package/src/data/providers/approval-indexer.mjs +0 -133
  187. package/src/data/providers/approvals.mjs +0 -594
  188. package/src/data/providers/babylon-staking.mjs +0 -149
  189. package/src/data/providers/balancer.mjs +0 -209
  190. package/src/data/providers/beefy-yields.mjs +0 -45
  191. package/src/data/providers/birdeye-tokens.mjs +0 -33
  192. package/src/data/providers/bisq-markets.mjs +0 -145
  193. package/src/data/providers/bitcoin-esplora.mjs +0 -216
  194. package/src/data/providers/bitcoin-meta.mjs +0 -378
  195. package/src/data/providers/blockscout.mjs +0 -14
  196. package/src/data/providers/bob-rootstock.mjs +0 -198
  197. package/src/data/providers/braiins-insights.mjs +0 -103
  198. package/src/data/providers/bridge-aggregator.mjs +0 -259
  199. package/src/data/providers/bridges.mjs +0 -242
  200. package/src/data/providers/bungee.mjs +0 -29
  201. package/src/data/providers/celestia-da.mjs +0 -38
  202. package/src/data/providers/changenow.mjs +0 -135
  203. package/src/data/providers/compound.mjs +0 -32
  204. package/src/data/providers/connext.mjs +0 -28
  205. package/src/data/providers/convex.mjs +0 -30
  206. package/src/data/providers/cow-protocol.mjs +0 -110
  207. package/src/data/providers/cowswap.mjs +0 -415
  208. package/src/data/providers/curve.mjs +0 -201
  209. package/src/data/providers/das-assets.mjs +0 -59
  210. package/src/data/providers/debridge.mjs +0 -109
  211. package/src/data/providers/defillama.mjs +0 -88
  212. package/src/data/providers/dexscreener.mjs +0 -43
  213. package/src/data/providers/drift.mjs +0 -32
  214. package/src/data/providers/dydx.mjs +0 -41
  215. package/src/data/providers/evm-rpc.mjs +0 -225
  216. package/src/data/providers/farming.mjs +0 -291
  217. package/src/data/providers/geckoterminal.mjs +0 -34
  218. package/src/data/providers/gmx.mjs +0 -496
  219. package/src/data/providers/hiro-stacks.mjs +0 -150
  220. package/src/data/providers/hl-assets.mjs +0 -165
  221. package/src/data/providers/hl-info.mjs +0 -108
  222. package/src/data/providers/hl-markets.mjs +0 -210
  223. package/src/data/providers/hl-outcome.mjs +0 -61
  224. package/src/data/providers/hl-perps.mjs +0 -419
  225. package/src/data/providers/hl-staking.mjs +0 -353
  226. package/src/data/providers/hl-ws.mjs +0 -119
  227. package/src/data/providers/hyperevm-dex.mjs +0 -49
  228. package/src/data/providers/jito-mev.mjs +0 -117
  229. package/src/data/providers/jupiter-venues.mjs +0 -119
  230. package/src/data/providers/jupiter.mjs +0 -315
  231. package/src/data/providers/kamino-strategies.mjs +0 -143
  232. package/src/data/providers/l2beat-tvl.mjs +0 -42
  233. package/src/data/providers/lifi.mjs +0 -166
  234. package/src/data/providers/liquid-esplora.mjs +0 -152
  235. package/src/data/providers/magiceden-ordinals.mjs +0 -141
  236. package/src/data/providers/magiceden-sol.mjs +0 -406
  237. package/src/data/providers/mempool-lightning.mjs +0 -144
  238. package/src/data/providers/mempool-mining.mjs +0 -142
  239. package/src/data/providers/meteora-dlmm.mjs +0 -168
  240. package/src/data/providers/morpho.mjs +0 -177
  241. package/src/data/providers/nft-gallery.mjs +0 -163
  242. package/src/data/providers/nft-portfolio.mjs +0 -494
  243. package/src/data/providers/odos.mjs +0 -156
  244. package/src/data/providers/ondo.mjs +0 -31
  245. package/src/data/providers/oneinch.mjs +0 -174
  246. package/src/data/providers/opensea-multichain.mjs +0 -136
  247. package/src/data/providers/opensea-nft.mjs +0 -371
  248. package/src/data/providers/orca.mjs +0 -29
  249. package/src/data/providers/ordinals-runes.mjs +0 -180
  250. package/src/data/providers/pancakeswap.mjs +0 -28
  251. package/src/data/providers/paraswap.mjs +0 -125
  252. package/src/data/providers/pendle.mjs +0 -188
  253. package/src/data/providers/poly-clob.mjs +0 -283
  254. package/src/data/providers/poly-public.mjs +0 -96
  255. package/src/data/providers/poly-ws.mjs +0 -103
  256. package/src/data/providers/polygon-staking.mjs +0 -227
  257. package/src/data/providers/portfolio-history.mjs +0 -394
  258. package/src/data/providers/portfolio.mjs +0 -600
  259. package/src/data/providers/pyth-price-feeds.mjs +0 -172
  260. package/src/data/providers/raydium.mjs +0 -35
  261. package/src/data/providers/relayscan-mev.mjs +0 -72
  262. package/src/data/providers/rfq.mjs +0 -15
  263. package/src/data/providers/rh-agent.mjs +0 -59
  264. package/src/data/providers/sanctum-lst.mjs +0 -180
  265. package/src/data/providers/satflow.mjs +0 -341
  266. package/src/data/providers/signed-material-guard.mjs +0 -108
  267. package/src/data/providers/solana-rpc.mjs +0 -197
  268. package/src/data/providers/solend-lending.mjs +0 -166
  269. package/src/data/providers/spark-lend.mjs +0 -42
  270. package/src/data/providers/squid-router.mjs +0 -29
  271. package/src/data/providers/stargate.mjs +0 -29
  272. package/src/data/providers/sushiswap.mjs +0 -30
  273. package/src/data/providers/symbiotic-restaking.mjs +0 -41
  274. package/src/data/providers/thorchain.mjs +0 -36
  275. package/src/data/providers/traderjoe.mjs +0 -28
  276. package/src/data/providers/twap.mjs +0 -152
  277. package/src/data/providers/uniswap-v3.mjs +0 -457
  278. package/src/data/providers/uniswap-v4.mjs +0 -414
  279. package/src/data/providers/vertex.mjs +0 -41
  280. package/src/data/providers/wallet-watch.mjs +0 -264
  281. package/src/data/providers/wormhole.mjs +0 -28
  282. package/src/data/providers/yearn.mjs +0 -31
  283. package/src/data/providers/zerox.mjs +0 -167
  284. package/src/data/public-api-scan.mjs +0 -61
  285. package/src/data/quote-placeholder.mjs +0 -31
  286. package/src/desk/swap-prepare.mjs +0 -194
  287. package/src/equities/bridge.mjs +0 -388
  288. package/src/equities/fixtures/bridge_routes.json +0 -159
  289. package/src/equities/fixtures/dst_transitions.json +0 -29
  290. package/src/equities/fixtures/equity_dex_pools.json +0 -154
  291. package/src/equities/fixtures/hip3_l2books.json +0 -2711
  292. package/src/equities/fixtures/nyse_holidays.json +0 -1
  293. package/src/equities/fixtures/oracles.json +0 -100
  294. package/src/equities/fixtures/rh_pool_state.json +0 -244
  295. package/src/equities/fixtures/solana_xstocks.json +0 -704
  296. package/src/equities/fixtures/ton_xstocks.json +0 -35
  297. package/src/equities/fixtures/universe.json +0 -411
  298. package/src/equities/fixtures/venues.json +0 -3542
  299. package/src/equities/fixtures.mjs +0 -405
  300. package/src/equities/funding.mjs +0 -142
  301. package/src/equities/index.mjs +0 -166
  302. package/src/equities/liveness.mjs +0 -148
  303. package/src/equities/marketHours.mjs +0 -249
  304. package/src/equities/normalize.mjs +0 -295
  305. package/src/equities/num.mjs +0 -146
  306. package/src/equities/prepare.mjs +0 -95
  307. package/src/equities/rank.mjs +0 -260
  308. package/src/equities/sourceNum.mjs +0 -69
  309. package/src/equities/types.mjs +0 -220
  310. package/src/equities/venues/arcus-perp.mjs +0 -84
  311. package/src/equities/venues/arcus-spot.mjs +0 -105
  312. package/src/equities/venues/hyperliquid-hip3.mjs +0 -224
  313. package/src/equities/venues/rh-uniswap.mjs +0 -216
  314. package/src/equities/venues/solana-xstocks.mjs +0 -139
  315. package/src/equities/venues/ton-stonfi.mjs +0 -139
  316. package/src/exact-integer.mjs +0 -72
  317. package/src/exec-policy.mjs +0 -419
  318. package/src/flags.mjs +0 -15
  319. package/src/fresh-window.mjs +0 -76
  320. package/src/gmx-attestation.mjs +0 -176
  321. package/src/index.mjs +0 -107
  322. package/src/licensing/locals-only.mjs +0 -25
  323. package/src/nft-gas-war-guard.mjs +0 -139
  324. package/src/onboarding/agent-keys.mjs +0 -161
  325. package/src/onboarding/harness-configs.mjs +0 -102
  326. package/src/onboarding/index.mjs +0 -18
  327. package/src/onboarding/tiers.mjs +0 -139
  328. package/src/oracle-env.mjs +0 -55
  329. package/src/package-manifest.mjs +0 -42
  330. package/src/plugins/agent-plugin-loader.mjs +0 -225
  331. package/src/portfolio-risk.mjs +0 -170
  332. package/src/prepare-envelope.mjs +0 -204
  333. package/src/profile-upgrade.mjs +0 -277
  334. package/src/protocol-execution.mjs +0 -84
  335. package/src/protocol-templates/gate.mjs +0 -179
  336. package/src/protocol-templates/prepare-deploy.mjs +0 -81
  337. package/src/public-api/agent-grants.mjs +0 -57
  338. package/src/public-api/buzz-integration.mjs +0 -267
  339. package/src/public-api/connect-agent.mjs +0 -424
  340. package/src/public-api/grants.mjs +0 -142
  341. package/src/public-api/http.mjs +0 -707
  342. package/src/public-api/metering.mjs +0 -111
  343. package/src/public-control/aa-adapter.mjs +0 -402
  344. package/src/public-control/build-registry.mjs +0 -227
  345. package/src/public-control/bundler-client.mjs +0 -357
  346. package/src/public-control/grant-indexer.mjs +0 -296
  347. package/src/public-control/policy-render.mjs +0 -69
  348. package/src/public-control/policy-schema.mjs +0 -322
  349. package/src/public-control/runtime-config.mjs +0 -275
  350. package/src/public-control/session-key-model.mjs +0 -379
  351. package/src/public-control/session-orchestrator.mjs +0 -418
  352. package/src/rfq/intent.mjs +0 -180
  353. package/src/rfq/sources.mjs +0 -181
  354. package/src/route-attestation.mjs +0 -132
  355. package/src/router/best-execution.mjs +0 -326
  356. package/src/router/index.mjs +0 -250
  357. package/src/router/integrator-fee.mjs +0 -135
  358. package/src/router/prepare-bridge.mjs +0 -293
  359. package/src/router/prepare-route.mjs +0 -398
  360. package/src/router/proposal.mjs +0 -311
  361. package/src/router/risk-classifier.mjs +0 -119
  362. package/src/router/route-sources.mjs +0 -364
  363. package/src/scanner/chains.config.mjs +0 -543
  364. package/src/scanner/contract.mjs +0 -270
  365. package/src/scanner/evm-scanner.mjs +0 -394
  366. package/src/scanner/index.mjs +0 -9
  367. package/src/scanner/v2-venue.mjs +0 -351
  368. package/src/scanner/v3-venue.mjs +0 -308
  369. package/src/scopes.mjs +0 -44
  370. package/src/sell-simulation.mjs +0 -167
  371. package/src/signals/engine.mjs +0 -146
  372. package/src/signals/index.mjs +0 -1
  373. package/src/token-transfer-guard.mjs +0 -188
  374. package/src/tui/app.mjs +0 -438
  375. package/src/tui/backend.mjs +0 -215
  376. package/src/tui/format.mjs +0 -272
  377. package/src/tui/gateway-client.mjs +0 -177
  378. package/src/tui/input.mjs +0 -501
  379. package/src/tui/memory.mjs +0 -105
  380. package/src/tui/meta-learn.mjs +0 -85
  381. package/src/tui/renderer.mjs +0 -443
  382. package/src/tui/scheduler.mjs +0 -205
  383. package/src/tui/skills-loader.mjs +0 -140
  384. package/src/tui/standalone-client.mjs +0 -752
  385. package/src/tui/theme.mjs +0 -81
  386. package/src/vault-attestation.mjs +0 -146
  387. package/src/venues.mjs +0 -206
  388. package/src/watch-preferences.mjs +0 -85
  389. /package/{profiles → dist/assets/profiles}/_template/SOUL.md +0 -0
  390. /package/{profiles → dist/assets/profiles}/_template/profile.json +0 -0
  391. /package/{profiles → dist/assets/profiles}/bitcoin-agent/SOUL.md +0 -0
  392. /package/{profiles → dist/assets/profiles}/bitcoin-agent/profile.json +0 -0
  393. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/SOUL.md +0 -0
  394. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/profile.json +0 -0
  395. /package/{profiles → dist/assets/profiles}/oracle/SOUL.md +0 -0
  396. /package/{profiles → dist/assets/profiles}/oracle/profile.json +0 -0
  397. /package/{profiles → dist/assets/profiles}/polymarket-agent/SOUL.md +0 -0
  398. /package/{profiles → dist/assets/profiles}/polymarket-agent/profile.json +0 -0
  399. /package/{profiles → dist/assets/profiles}/profile.schema.json +0 -0
  400. /package/{profiles → dist/assets/profiles}/protocol-builder/SOUL.md +0 -0
  401. /package/{profiles → dist/assets/profiles}/protocol-builder/profile.json +0 -0
  402. /package/{profiles → dist/assets/profiles}/robinhood-agent/SOUL.md +0 -0
  403. /package/{profiles → dist/assets/profiles}/robinhood-agent/profile.json +0 -0
  404. /package/{profiles → dist/assets/profiles}/solana-agent/SOUL.md +0 -0
  405. /package/{profiles → dist/assets/profiles}/solana-agent/profile.json +0 -0
  406. /package/{profiles → dist/assets/profiles}/stable-agent/SOUL.md +0 -0
  407. /package/{profiles → dist/assets/profiles}/stable-agent/profile.json +0 -0
  408. /package/{skills → dist/assets/skills}/balance/SKILL.md +0 -0
  409. /package/{skills → dist/assets/skills}/bitcoin-l1.md +0 -0
  410. /package/{skills → dist/assets/skills}/chain-defi-ecosystem-profiling.md +0 -0
  411. /package/{skills → dist/assets/skills}/evm-contract-research.md +0 -0
  412. /package/{skills → dist/assets/skills}/hyperliquid.md +0 -0
  413. /package/{skills → dist/assets/skills}/multichain-exec-desk.md +0 -0
  414. /package/{skills → dist/assets/skills}/oracle-action-semantics/SKILL.md +0 -0
  415. /package/{skills → dist/assets/skills}/oracle-best-execution/SKILL.md +0 -0
  416. /package/{skills → dist/assets/skills}/oracle-best-execution.md +0 -0
  417. /package/{skills → dist/assets/skills}/oracle-bitcoin/SKILL.md +0 -0
  418. /package/{skills → dist/assets/skills}/oracle-chain-graphs-telegram-cards/SKILL.md +0 -0
  419. /package/{skills → dist/assets/skills}/oracle-chat/SKILL.md +0 -0
  420. /package/{skills → dist/assets/skills}/oracle-chat/chain.SKILL.md +0 -0
  421. /package/{skills → dist/assets/skills}/oracle-chat/setup.SKILL.md +0 -0
  422. /package/{skills → dist/assets/skills}/oracle-circuit-breaker/SKILL.md +0 -0
  423. /package/{skills → dist/assets/skills}/oracle-contract-research/SKILL.md +0 -0
  424. /package/{skills → dist/assets/skills}/oracle-desk/SKILL.md +0 -0
  425. /package/{skills → dist/assets/skills}/oracle-desk.md +0 -0
  426. /package/{skills → dist/assets/skills}/oracle-dex-launch/SKILL.md +0 -0
  427. /package/{skills → dist/assets/skills}/oracle-equities/SKILL.md +0 -0
  428. /package/{skills → dist/assets/skills}/oracle-grants/SKILL.md +0 -0
  429. /package/{skills → dist/assets/skills}/oracle-hypercore-staking/SKILL.md +0 -0
  430. /package/{skills → dist/assets/skills}/oracle-hyperliquid/SKILL.md +0 -0
  431. /package/{skills → dist/assets/skills}/oracle-meme-token-sniper/SKILL.md +0 -0
  432. /package/{skills → dist/assets/skills}/oracle-multichain-nft-launch/SKILL.md +0 -0
  433. /package/{skills → dist/assets/skills}/oracle-multichain-token-launch/SKILL.md +0 -0
  434. /package/{skills → dist/assets/skills}/oracle-nft-gacha-launch/SKILL.md +0 -0
  435. /package/{skills → dist/assets/skills}/oracle-nft-mint-gas-war/SKILL.md +0 -0
  436. /package/{skills → dist/assets/skills}/oracle-polymarket/SKILL.md +0 -0
  437. /package/{skills → dist/assets/skills}/oracle-protocol-builder/SKILL.md +0 -0
  438. /package/{skills → dist/assets/skills}/oracle-protocol-security/SKILL.md +0 -0
  439. /package/{skills → dist/assets/skills}/oracle-public-product/SKILL.md +0 -0
  440. /package/{skills → dist/assets/skills}/oracle-receipts/SKILL.md +0 -0
  441. /package/{skills → dist/assets/skills}/oracle-receipts.md +0 -0
  442. /package/{skills → dist/assets/skills}/oracle-rfq-tokenized-assets/SKILL.md +0 -0
  443. /package/{skills → dist/assets/skills}/oracle-smart-wallet-scanner/SKILL.md +0 -0
  444. /package/{skills → dist/assets/skills}/oracle-solana/SKILL.md +0 -0
  445. /package/{skills → dist/assets/skills}/oracle-solana-nft/SKILL.md +0 -0
  446. /package/{skills → dist/assets/skills}/oracle-token-research/SKILL.md +0 -0
  447. /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
- }