@oracle-agent/oracle 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oracle-agent/oracle",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Oracle: prepare-only multichain agent control plane. Policy-bounded intents for a user-signed wallet. Self-custody by default — the public package never takes your key. Built for Hermes; no model key required.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,6 +29,22 @@ export const UNI_V3_CHAINS = {
|
|
|
29
29
|
weth: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
30
30
|
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
31
31
|
},
|
|
32
|
+
// Robinhood Chain. Addresses mirror rhbot server/swap.js SWAP_CONFIG (the vetted
|
|
33
|
+
// source) and were re-proved on the live chain before being added here:
|
|
34
|
+
// eth_chainId == 4663, eth_getCode non-empty for every address below, and a real
|
|
35
|
+
// quoteExactInputSingle returned 1 WETH -> 1866.58 USDG at fee 100 (2026-07-31).
|
|
36
|
+
//
|
|
37
|
+
// Why this entry matters: LI.FI / ParaSwap / CoW do not index 4663, so the router's
|
|
38
|
+
// aggregator-only candidate list returned "no usable route" for every RH swap. The
|
|
39
|
+
// on-chain QuoterV2 is the only source that can price this chain. `usdc` is USDG
|
|
40
|
+
// (Global Dollar, 6-dec) — the stable quote RH pools actually use.
|
|
41
|
+
4663: {
|
|
42
|
+
name: "robinhood",
|
|
43
|
+
quoter: "0x33e885ed0ec9bf04ecfb19341582aadcb4c8a9e7",
|
|
44
|
+
router: "0xcaf681a66d020601342297493863e78c959e5cb2",
|
|
45
|
+
weth: "0x0bd7d308f8e1639fab988df18a8011f41eacad73",
|
|
46
|
+
usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
47
|
+
},
|
|
32
48
|
};
|
|
33
49
|
|
|
34
50
|
export const UNI_V3_FEE_TIERS = [100, 500, 3000, 10000];
|
|
@@ -274,7 +290,7 @@ export async function uniV3PrepareExactIn(q = {}, opts = {}) {
|
|
|
274
290
|
value: nativeInput ? quote.amountIn : "0",
|
|
275
291
|
slippageGuard: bindAutoSlippageGuardToCall(quote.autoSlippage, { chainId, venue: meta.router, data }),
|
|
276
292
|
},
|
|
277
|
-
});
|
|
293
|
+
}, { provider: "uniswap-v3", kind: "univ3-swap-tx" });
|
|
278
294
|
}
|
|
279
295
|
|
|
280
296
|
export async function uniV3Health(opts = {}) {
|
|
@@ -45,6 +45,29 @@ export const ARTIFACT = Object.freeze({
|
|
|
45
45
|
const DEFAULT_DRIFT_TOLERANCE_BPS = 100;
|
|
46
46
|
|
|
47
47
|
const PREPARERS = {
|
|
48
|
+
// On-chain Uniswap V3. Unlike the aggregators below, this builds calldata from the
|
|
49
|
+
// chain's own router rather than an API's transaction payload — which is what makes
|
|
50
|
+
// chains no aggregator indexes (e.g. Robinhood 4663) preparable at all.
|
|
51
|
+
//
|
|
52
|
+
// The provider stamps requiresUserSignature:true / signingReady:false /
|
|
53
|
+
// broadcastReady:false. Preparing is NOT permission to execute: the wallet is still
|
|
54
|
+
// the only thing that can authorize this, and the unattended daemon surface list
|
|
55
|
+
// (hl, poly) is deliberately unchanged by adding this.
|
|
56
|
+
"uniswap-v3": async ({ chainId, tokenIn, tokenOut, amountIn, taker }, opts) => {
|
|
57
|
+
const { uniV3PrepareExactIn } = await import("../data/providers/uniswap-v3.mjs");
|
|
58
|
+
const p = await uniV3PrepareExactIn(
|
|
59
|
+
{ chainId, tokenIn, tokenOut, amountIn, recipient: taker },
|
|
60
|
+
opts,
|
|
61
|
+
);
|
|
62
|
+
return {
|
|
63
|
+
artifactKind: ARTIFACT.TRANSACTION,
|
|
64
|
+
transaction: p.transaction,
|
|
65
|
+
requiresApproval: p.requiresApproval ?? null,
|
|
66
|
+
amountOut: p.quote?.amountOut ?? null,
|
|
67
|
+
minOut: p.quote?.amountOutMinimum ?? null,
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
|
|
48
71
|
lifi: async ({ chainId, tokenIn, tokenOut, amountIn, taker }, opts) => {
|
|
49
72
|
const { lifiPrepare } = await import("../data/providers/lifi.mjs");
|
|
50
73
|
const p = await lifiPrepare(
|
|
@@ -20,6 +20,7 @@ import { paraswapPrice } from "../data/providers/paraswap.mjs";
|
|
|
20
20
|
import { zeroxQuote } from "../data/providers/zerox.mjs";
|
|
21
21
|
import { oneinchQuote } from "../data/providers/oneinch.mjs";
|
|
22
22
|
import { cowQuote } from "../data/providers/cowswap.mjs";
|
|
23
|
+
import { uniV3QuoteExactIn, UNI_V3_CHAINS } from "../data/providers/uniswap-v3.mjs";
|
|
23
24
|
import { llamaPrices } from "../data/providers/defillama.mjs";
|
|
24
25
|
import { NATIVE_PRICE_KEY } from "./best-execution.mjs";
|
|
25
26
|
|
|
@@ -188,6 +189,42 @@ export function swapCandidates({
|
|
|
188
189
|
},
|
|
189
190
|
});
|
|
190
191
|
|
|
192
|
+
// On-chain Uniswap V3 QuoterV2. UNLIKE every other source in this list, this is
|
|
193
|
+
// not a third-party API — it is an eth_call against the chain itself.
|
|
194
|
+
//
|
|
195
|
+
// Why it exists here: aggregators only index chains they chose to support. On
|
|
196
|
+
// Robinhood Chain (4663) LI.FI/ParaSwap/CoW all return nothing, so the candidate
|
|
197
|
+
// list came back empty and the router reported "no usable route" — which reads
|
|
198
|
+
// like a permission wall but is really zero coverage. A chain with a live V3
|
|
199
|
+
// deployment can always be priced directly, with no API key and no indexer.
|
|
200
|
+
//
|
|
201
|
+
// Kept LAST so it acts as a floor: when aggregators do cover a chain they usually
|
|
202
|
+
// win on route quality, and best-execution still compares net output. This only
|
|
203
|
+
// decides the trade when it is the sole source that answered.
|
|
204
|
+
//
|
|
205
|
+
// Read-only. quoteExactInputSingle is a view call; this cannot sign or broadcast.
|
|
206
|
+
if (UNI_V3_CHAINS[Number(chainId)]) {
|
|
207
|
+
c.push({
|
|
208
|
+
source: "uniswap-v3",
|
|
209
|
+
run: async () => {
|
|
210
|
+
const q = await uniV3QuoteExactIn(
|
|
211
|
+
{ chainId, tokenIn, tokenOut, amountIn },
|
|
212
|
+
opts,
|
|
213
|
+
);
|
|
214
|
+
// Gas is deliberately null, not 0: the quoter returns a gasEstimate in UNITS,
|
|
215
|
+
// and without a gas price that is not a USD cost. Reporting 0 would make this
|
|
216
|
+
// source look free and beat correctly-priced aggregators. See CoW above for
|
|
217
|
+
// the one case where 0 is a measured fact rather than a missing number.
|
|
218
|
+
return {
|
|
219
|
+
amountOut: q?.amountOut,
|
|
220
|
+
minOut: q?.minOut ?? q?.amountOutMinimum ?? null,
|
|
221
|
+
gasUsd: null,
|
|
222
|
+
meta: { onchain: true, fee: q?.fee, gasUnits: q?.gasEstimate, quoter: q?.quoter },
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
191
228
|
return c;
|
|
192
229
|
}
|
|
193
230
|
|