@oracle-agent/oracle 0.10.0 → 0.11.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.
- package/README.md +21 -12
- package/bin/oracle-data-mcp.mjs +9 -0
- package/docs/architecture.md +3 -1
- package/docs/profiles.md +3 -6
- package/docs/public-surface.md +133 -0
- package/package.json +1 -1
- package/public/oracle-splash/assets/cli-chain-hyperliquid.jpg +0 -0
- package/public/oracle-splash/brand/aave.webp +0 -0
- package/public/oracle-splash/brand/aavev2.webp +0 -0
- package/public/oracle-splash/brand/aborean.webp +0 -0
- package/public/oracle-splash/brand/across.webp +0 -0
- package/public/oracle-splash/brand/aerodrome.webp +0 -0
- package/public/oracle-splash/brand/afi.webp +0 -0
- package/public/oracle-splash/brand/arcus.webp +0 -0
- package/public/oracle-splash/brand/aster.webp +0 -0
- package/public/oracle-splash/brand/avant.webp +0 -0
- package/public/oracle-splash/brand/balancer.webp +0 -0
- package/public/oracle-splash/brand/beefy.webp +0 -0
- package/public/oracle-splash/brand/benqi.webp +0 -0
- package/public/oracle-splash/brand/bestinslot.ico +0 -0
- package/public/oracle-splash/brand/bitfi.webp +0 -0
- package/public/oracle-splash/brand/bonkswap.webp +0 -0
- package/public/oracle-splash/brand/bouncebit.webp +0 -0
- package/public/oracle-splash/brand/camelot.webp +0 -0
- package/public/oracle-splash/brand/compound.webp +0 -0
- package/public/oracle-splash/brand/concrete.webp +0 -0
- package/public/oracle-splash/brand/cow.webp +0 -0
- package/public/oracle-splash/brand/curve.webp +0 -0
- package/public/oracle-splash/brand/derive.webp +0 -0
- package/public/oracle-splash/brand/dolomite.webp +0 -0
- package/public/oracle-splash/brand/eigenlayer.webp +0 -0
- package/public/oracle-splash/brand/ekubo.webp +0 -0
- package/public/oracle-splash/brand/ethena.webp +0 -0
- package/public/oracle-splash/brand/etherfi.webp +0 -0
- package/public/oracle-splash/brand/etherfiborrow.webp +0 -0
- package/public/oracle-splash/brand/felix.webp +0 -0
- package/public/oracle-splash/brand/fluid.webp +0 -0
- package/public/oracle-splash/brand/fluxbeam.webp +0 -0
- package/public/oracle-splash/brand/folks.webp +0 -0
- package/public/oracle-splash/brand/fortyacres.webp +0 -0
- package/public/oracle-splash/brand/gainsnetwork.webp +0 -0
- package/public/oracle-splash/brand/gassss.webp +0 -0
- package/public/oracle-splash/brand/gmx.webp +0 -0
- package/public/oracle-splash/brand/harmonix.webp +0 -0
- package/public/oracle-splash/brand/hlorderbook.webp +0 -0
- package/public/oracle-splash/brand/hlp.webp +0 -0
- package/public/oracle-splash/brand/hop.webp +0 -0
- package/public/oracle-splash/brand/hyperbeat.webp +0 -0
- package/public/oracle-splash/brand/hyperlend.webp +0 -0
- package/public/oracle-splash/brand/hyperliquid.webp +0 -0
- package/public/oracle-splash/brand/hyperliquidhpl.webp +0 -0
- package/public/oracle-splash/brand/hyperliquidspot.webp +0 -0
- package/public/oracle-splash/brand/hyperswap.webp +0 -0
- package/public/oracle-splash/brand/hypha.webp +0 -0
- package/public/oracle-splash/brand/joedex.webp +0 -0
- package/public/oracle-splash/brand/jupiter.webp +0 -0
- package/public/oracle-splash/brand/kelp.webp +0 -0
- package/public/oracle-splash/brand/kinetiq.webp +0 -0
- package/public/oracle-splash/brand/kmhype.webp +0 -0
- package/public/oracle-splash/brand/kona.webp +0 -0
- package/public/oracle-splash/brand/konalend.webp +0 -0
- package/public/oracle-splash/brand/lfj.webp +0 -0
- package/public/oracle-splash/brand/lido.webp +0 -0
- package/public/oracle-splash/brand/lifi.webp +0 -0
- package/public/oracle-splash/brand/lighter.webp +0 -0
- package/public/oracle-splash/brand/lista.webp +0 -0
- package/public/oracle-splash/brand/loopedhype.webp +0 -0
- package/public/oracle-splash/brand/lorenzo.webp +0 -0
- package/public/oracle-splash/brand/magic-eden.webp +0 -0
- package/public/oracle-splash/brand/magiceden.webp +0 -0
- package/public/oracle-splash/brand/mempool.png +0 -0
- package/public/oracle-splash/brand/meteora.webp +0 -0
- package/public/oracle-splash/brand/morpho.webp +0 -0
- package/public/oracle-splash/brand/myriad.webp +0 -0
- package/public/oracle-splash/brand/noxa.webp +0 -0
- package/public/oracle-splash/brand/odos.webp +0 -0
- package/public/oracle-splash/brand/oneinch.webp +0 -0
- package/public/oracle-splash/brand/openbook.webp +0 -0
- package/public/oracle-splash/brand/opensea.webp +0 -0
- package/public/oracle-splash/brand/orca.webp +0 -0
- package/public/oracle-splash/brand/ordinals.png +0 -0
- package/public/oracle-splash/brand/ostium.webp +0 -0
- package/public/oracle-splash/brand/pancakeswap.webp +0 -0
- package/public/oracle-splash/brand/pendle.webp +0 -0
- package/public/oracle-splash/brand/perena.webp +0 -0
- package/public/oracle-splash/brand/pharaoh.webp +0 -0
- package/public/oracle-splash/brand/phoenix.webp +0 -0
- package/public/oracle-splash/brand/polymarket.webp +0 -0
- package/public/oracle-splash/brand/polynomial.webp +0 -0
- package/public/oracle-splash/brand/projectx.webp +0 -0
- package/public/oracle-splash/brand/pumpfun.webp +0 -0
- package/public/oracle-splash/brand/quickswap.webp +0 -0
- package/public/oracle-splash/brand/rain.webp +0 -0
- package/public/oracle-splash/brand/raydium.webp +0 -0
- package/public/oracle-splash/brand/relay.webp +0 -0
- package/public/oracle-splash/brand/rocketpool.webp +0 -0
- package/public/oracle-splash/brand/saber.webp +0 -0
- package/public/oracle-splash/brand/sakuraswap.webp +0 -0
- package/public/oracle-splash/brand/sanctum.webp +0 -0
- package/public/oracle-splash/brand/satflow.png +0 -0
- package/public/oracle-splash/brand/sierra.webp +0 -0
- package/public/oracle-splash/brand/sky.webp +0 -0
- package/public/oracle-splash/brand/solv.webp +0 -0
- package/public/oracle-splash/brand/spark.webp +0 -0
- package/public/oracle-splash/brand/sparksavings.webp +0 -0
- package/public/oracle-splash/brand/sphere.webp +0 -0
- package/public/oracle-splash/brand/ssv.webp +0 -0
- package/public/oracle-splash/brand/stabble.webp +0 -0
- package/public/oracle-splash/brand/stableswap.webp +0 -0
- package/public/oracle-splash/brand/stakewise.webp +0 -0
- package/public/oracle-splash/brand/stargate.webp +0 -0
- package/public/oracle-splash/brand/sthype.webp +0 -0
- package/public/oracle-splash/brand/sushiswap.webp +0 -0
- package/public/oracle-splash/brand/unisat.png +0 -0
- package/public/oracle-splash/brand/uniswap.webp +0 -0
- package/public/oracle-splash/brand/uniswapv2.webp +0 -0
- package/public/oracle-splash/brand/uniswapv4.webp +0 -0
- package/public/oracle-splash/brand/unit.webp +0 -0
- package/public/oracle-splash/brand/velodrome.webp +0 -0
- package/public/oracle-splash/brand/velora.webp +0 -0
- package/public/oracle-splash/brand/venus.webp +0 -0
- package/public/oracle-splash/brand/wbeth.webp +0 -0
- package/public/oracle-splash/brand/xsy.webp +0 -0
- package/public/oracle-splash/brand/yieldyak.webp +0 -0
- package/public/oracle-splash/brand/zerobase.webp +0 -0
- package/public/oracle-splash/brand/zerox.webp +0 -0
- package/public/oracle-splash/index.html +236 -169
- package/scripts/check-doc-drift.mjs +11 -0
- package/scripts/e2e-solana-bitcoin.mjs +1 -1
- package/scripts/secret-scan.mjs +1 -1
- package/src/action-receipts.mjs +108 -11
- package/src/cli/commands/data.mjs +3 -1
- package/src/cli/commands/doctor.mjs +11 -1
- package/src/data/catalog.mjs +38 -7
- package/src/data/desk-data.mjs +12 -0
- package/src/data/providers/evm-rpc.mjs +19 -16
- package/src/data/providers/jupiter-venues.mjs +119 -0
- package/src/data/providers/jupiter.mjs +132 -0
- package/src/data/providers/magiceden-sol.mjs +13 -0
- package/src/data/providers/morpho.mjs +6 -2
- package/src/data/providers/pendle.mjs +3 -2
- package/src/data/providers/satflow.mjs +4 -1
- package/src/data/providers/signed-material-guard.mjs +108 -0
- package/src/data/providers/uniswap-v3.mjs +34 -5
- package/src/data/providers/uniswap-v4.mjs +414 -0
- package/src/prepare-envelope.mjs +22 -4
- package/src/router/prepare-route.mjs +17 -0
- package/src/router/route-sources.mjs +35 -0
|
@@ -9,6 +9,7 @@ import { httpJson } from "../http.mjs";
|
|
|
9
9
|
import { solanaPubkey } from "./solana-rpc.mjs";
|
|
10
10
|
import { resolveProviderEndpoint, credentialedHeaders } from "../provider-endpoint.mjs";
|
|
11
11
|
import { stampPrepared } from "../../prepare-envelope.mjs";
|
|
12
|
+
import { assertNoSignedMaterial } from "./signed-material-guard.mjs";
|
|
12
13
|
|
|
13
14
|
export const MAGICEDEN_SOL_API = "https://api-mainnet.magiceden.dev/v2";
|
|
14
15
|
export const LAMPORTS_PER_SOL = 1_000_000_000;
|
|
@@ -242,6 +243,10 @@ export async function magicEdenSolPrepareBuy(args = {}, opts = {}) {
|
|
|
242
243
|
fetchImpl: opts.fetchImpl,
|
|
243
244
|
timeoutMs: opts.timeoutMs ?? 15_000,
|
|
244
245
|
});
|
|
246
|
+
// Refuse pre-signed material by PRESENCE, before selecting unsigned bytes.
|
|
247
|
+
// A mixed payload (valid unsigned bytes plus a signed sibling) previously
|
|
248
|
+
// skipped this check entirely and bound the signed field into the envelope.
|
|
249
|
+
assertNoSignedMaterial("magiceden", raw);
|
|
245
250
|
const data = raw?.v0?.tx?.data ?? raw?.tx?.data ?? null;
|
|
246
251
|
// Accept ONLY the documented unsigned instruction field. A pre-signed payload
|
|
247
252
|
// (txSigned) from a compromised or impersonated API must never be laundered
|
|
@@ -299,6 +304,10 @@ export async function magicEdenSolPrepareList(args = {}, opts = {}) {
|
|
|
299
304
|
fetchImpl: opts.fetchImpl,
|
|
300
305
|
timeoutMs: opts.timeoutMs ?? 15_000,
|
|
301
306
|
});
|
|
307
|
+
// Refuse pre-signed material by PRESENCE, before selecting unsigned bytes.
|
|
308
|
+
// A mixed payload (valid unsigned bytes plus a signed sibling) previously
|
|
309
|
+
// skipped this check entirely and bound the signed field into the envelope.
|
|
310
|
+
assertNoSignedMaterial("magiceden", raw);
|
|
302
311
|
const data = raw?.v0?.tx?.data ?? raw?.tx?.data ?? null;
|
|
303
312
|
if (!Array.isArray(data)) {
|
|
304
313
|
if (raw?.txSigned) throw new Error("magiceden: refused a pre-signed transaction payload from the API");
|
|
@@ -365,6 +374,10 @@ export async function magicEdenSolPrepareMint(args = {}, opts = {}) {
|
|
|
365
374
|
);
|
|
366
375
|
}
|
|
367
376
|
|
|
377
|
+
// Refuse pre-signed material by PRESENCE, before selecting unsigned bytes.
|
|
378
|
+
// A mixed payload (valid unsigned bytes plus a signed sibling) previously
|
|
379
|
+
// skipped this check entirely and bound the signed field into the envelope.
|
|
380
|
+
assertNoSignedMaterial("magiceden", raw);
|
|
368
381
|
const data = raw?.v0?.tx?.data ?? raw?.tx?.data ?? null;
|
|
369
382
|
if (!Array.isArray(data)) {
|
|
370
383
|
if (raw?.txSigned) throw new Error("magiceden: refused a pre-signed transaction payload from the API");
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Interface, getAddress, isAddress, keccak256, toUtf8Bytes } from "ethers";
|
|
6
6
|
import { httpJson } from "../http.mjs";
|
|
7
7
|
import { rpcCall as defaultRpcCall } from "./evm-rpc.mjs";
|
|
8
|
+
import { stampPrepared } from "../../prepare-envelope.mjs";
|
|
8
9
|
|
|
9
10
|
export const MORPHO_API = "https://api.morpho.org/graphql";
|
|
10
11
|
|
|
@@ -146,11 +147,14 @@ export async function morphoPrepareVault(args = {}, opts = {}) {
|
|
|
146
147
|
};
|
|
147
148
|
vaultGuard.guardHash = keccak256(toUtf8Bytes(canonicalJson(vaultGuard)));
|
|
148
149
|
|
|
149
|
-
return {
|
|
150
|
+
return stampPrepared({
|
|
150
151
|
provider: "morpho",
|
|
151
152
|
protocol: "erc4626-vault",
|
|
152
153
|
vaultReady: true,
|
|
153
154
|
executionReady: false,
|
|
155
|
+
signingReady: false,
|
|
156
|
+
broadcastReady: false,
|
|
157
|
+
requiresUserSignature: true,
|
|
154
158
|
chainId,
|
|
155
159
|
action,
|
|
156
160
|
vault,
|
|
@@ -169,5 +173,5 @@ export async function morphoPrepareVault(args = {}, opts = {}) {
|
|
|
169
173
|
value: "0x0",
|
|
170
174
|
madVault: vaultGuard,
|
|
171
175
|
},
|
|
172
|
-
};
|
|
176
|
+
}, { provider: "morpho", kind: "erc4626-vault" });
|
|
173
177
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { getAddress } from "ethers";
|
|
3
3
|
import { httpJson } from "../http.mjs";
|
|
4
4
|
import { attachAutoSlippage, bindAutoSlippageGuardToCall, resolveAutoSlippage } from "../../auto-slippage.mjs";
|
|
5
|
+
import { stampPrepared } from "../../prepare-envelope.mjs";
|
|
5
6
|
|
|
6
7
|
export const PENDLE_API = "https://api-v2.pendle.finance/core";
|
|
7
8
|
|
|
@@ -154,7 +155,7 @@ export async function pendlePrepare(q = {}, opts = {}) {
|
|
|
154
155
|
priceChange5m: q.priceChange5m,
|
|
155
156
|
requestedCapBps: q.maxSlippageBps ?? q.slippageBps,
|
|
156
157
|
});
|
|
157
|
-
return {
|
|
158
|
+
return stampPrepared({
|
|
158
159
|
provider: "pendle",
|
|
159
160
|
calldataReady: true,
|
|
160
161
|
// NOT executable authority. The bytes are assembled but nothing is signed:
|
|
@@ -177,7 +178,7 @@ export async function pendlePrepare(q = {}, opts = {}) {
|
|
|
177
178
|
value: tx.value ?? "0x0",
|
|
178
179
|
slippageGuard: bindAutoSlippageGuardToCall(quote.autoSlippage, { chainId, venue: tx.to, data: tx.data }),
|
|
179
180
|
},
|
|
180
|
-
};
|
|
181
|
+
}, { provider: "pendle", kind: "pendle-swap" });
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
export async function pendleQuote(q = {}, opts = {}) {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { btcAddress } from "./bitcoin-esplora.mjs";
|
|
9
9
|
import { resolveProviderEndpoint, credentialedHeaders } from "../provider-endpoint.mjs";
|
|
10
10
|
import { stampPrepared } from "../../prepare-envelope.mjs";
|
|
11
|
+
import { assertNoSignedMaterial } from "./signed-material-guard.mjs";
|
|
11
12
|
|
|
12
13
|
export const SATFLOW_BASE = "https://api.satflow.com/v1";
|
|
13
14
|
|
|
@@ -295,6 +296,8 @@ export async function satflowPreparePurchase(args = {}, opts = {}) {
|
|
|
295
296
|
throw new Error(`satflow: purchase price ${settled} sats exceeds maxSats cap ${maxSats} — PSBT withheld`);
|
|
296
297
|
}
|
|
297
298
|
|
|
299
|
+
assertNoSignedMaterial("satflow", data);
|
|
300
|
+
|
|
298
301
|
return stampPrepared({
|
|
299
302
|
provider: "satflow",
|
|
300
303
|
kind: "purchase-intent",
|
|
@@ -312,7 +315,6 @@ export async function satflowPreparePurchase(args = {}, opts = {}) {
|
|
|
312
315
|
data?.unsignedPSBTBase64 ||
|
|
313
316
|
data?.unsignedPsbtBase64 ||
|
|
314
317
|
data?.unsigned_psbt ||
|
|
315
|
-
data?.psbt ||
|
|
316
318
|
null,
|
|
317
319
|
note: "Sign PSBT in a Bitcoin wallet. Broadcast only through the user's wallet or an explicitly armed Bitcoin execution plane.",
|
|
318
320
|
});
|
|
@@ -325,6 +327,7 @@ export async function satflowPrepareList(args = {}, opts = {}) {
|
|
|
325
327
|
throw new Error("satflow: list requires inscription_id or runes_output");
|
|
326
328
|
}
|
|
327
329
|
const data = await sfFetch("/intent/sell", { method: "POST", body, opts });
|
|
330
|
+
assertNoSignedMaterial("satflow", data);
|
|
328
331
|
return stampPrepared({
|
|
329
332
|
provider: "satflow",
|
|
330
333
|
kind: "list-intent",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Shared refusal for pre-signed material returned by an upstream marketplace or
|
|
2
|
+
// aggregator API. Every prepare path calls this before selecting unsigned bytes
|
|
3
|
+
// or binding the upstream response into a stamped envelope.
|
|
4
|
+
|
|
5
|
+
const MAX_DEPTH = 16;
|
|
6
|
+
const MAX_NODES = 2048;
|
|
7
|
+
|
|
8
|
+
const UNSIGNED_PREFIXES = ["unsigned", "notsigned", "tosign", "signable"];
|
|
9
|
+
|
|
10
|
+
const STATUS_KEYS = new Set([
|
|
11
|
+
"requiresusersignature",
|
|
12
|
+
"requiressignature",
|
|
13
|
+
"needssignature",
|
|
14
|
+
"signaturerequired",
|
|
15
|
+
"signaturesrequired",
|
|
16
|
+
"signingready",
|
|
17
|
+
"signaturecount",
|
|
18
|
+
"signaturethreshold",
|
|
19
|
+
"requiredsignatures",
|
|
20
|
+
"signatureneeded",
|
|
21
|
+
"hassignature",
|
|
22
|
+
"allowsignatures",
|
|
23
|
+
"requestsignature",
|
|
24
|
+
"multisig",
|
|
25
|
+
"cosig",
|
|
26
|
+
"msig",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const MATERIAL_KEYS = new Set([
|
|
30
|
+
"sig",
|
|
31
|
+
"sigs",
|
|
32
|
+
"scriptsig",
|
|
33
|
+
"witness",
|
|
34
|
+
"psbt",
|
|
35
|
+
"finalizedpsbt",
|
|
36
|
+
"completepsbt",
|
|
37
|
+
"finalpsbt",
|
|
38
|
+
"psbtfinalized",
|
|
39
|
+
"serializedtransaction",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
function normalizeKey(key) {
|
|
43
|
+
return String(key).toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isUnsignedField(norm) {
|
|
47
|
+
return UNSIGNED_PREFIXES.some((prefix) => norm.startsWith(prefix)) || STATUS_KEYS.has(norm);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isSignedMaterialKey(key) {
|
|
51
|
+
const raw = String(key);
|
|
52
|
+
if (!/^[\x20-\x7e]+$/.test(raw)) return true;
|
|
53
|
+
const norm = normalizeKey(raw);
|
|
54
|
+
if (isUnsignedField(norm)) return false;
|
|
55
|
+
if (MATERIAL_KEYS.has(norm)) return true;
|
|
56
|
+
return norm.includes("signed") || norm.includes("signature") || norm.endsWith("sig") || norm.endsWith("sigs");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isSignatureTuple(value) {
|
|
60
|
+
const v = Number(value?.v);
|
|
61
|
+
const r = String(value?.r || "");
|
|
62
|
+
const s = String(value?.s || "");
|
|
63
|
+
return [0, 1, 27, 28].includes(v) && /^0x[0-9a-f]{2,64}$/i.test(r) && /^0x[0-9a-f]{2,64}$/i.test(s);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Find signed-material field names anywhere in an upstream response. */
|
|
67
|
+
export function findSignedFields(value, depth = 0, seen = new Set(), state = { nodes: 0 }) {
|
|
68
|
+
if (value == null || typeof value !== "object") return [];
|
|
69
|
+
if (depth > MAX_DEPTH) return ["<max-depth>"];
|
|
70
|
+
if (seen.has(value)) return [];
|
|
71
|
+
state.nodes += 1;
|
|
72
|
+
if (state.nodes > MAX_NODES) return ["<max-nodes>"];
|
|
73
|
+
seen.add(value);
|
|
74
|
+
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
return value.flatMap((item) => findSignedFields(item, depth + 1, seen, state));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const hits = [];
|
|
80
|
+
for (const [key, child] of Object.entries(value)) {
|
|
81
|
+
if (isSignedMaterialKey(key)) {
|
|
82
|
+
hits.push(key);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
hits.push(...findSignedFields(child, depth + 1, seen, state));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const keys = Object.keys(value).map(normalizeKey);
|
|
89
|
+
const hasPsbt = keys.some((key) => key.endsWith("psbt") || key.endsWith("psbtbase64"));
|
|
90
|
+
const claimsComplete = Object.entries(value).some(
|
|
91
|
+
([key, child]) => /complete|finalized|isfinal/.test(normalizeKey(key)) && child === true,
|
|
92
|
+
);
|
|
93
|
+
if (hasPsbt && claimsComplete) hits.push("psbt(complete)");
|
|
94
|
+
if (isSignatureTuple(value)) hits.push("v/r/s");
|
|
95
|
+
|
|
96
|
+
return hits;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Refuse an upstream response that carries pre-signed material. */
|
|
100
|
+
export function assertNoSignedMaterial(provider, raw) {
|
|
101
|
+
const hits = findSignedFields(raw);
|
|
102
|
+
if (hits.length) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`${provider}: refused a pre-signed transaction payload from the API (fields: ${[...new Set(hits)].join(", ")}) — ` +
|
|
105
|
+
`@oracle-agent/oracle is prepare-only and will not bind signed material into a prepared envelope`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Uniswap V3 QuoterV2 — exact-input quotes via eth_call (no API key).
|
|
2
|
-
// Chains: Ethereum, Base, Arbitrum. Read-only; never signs or swaps.
|
|
2
|
+
// Chains: Ethereum, Optimism, BNB, Polygon, Base, Arbitrum, Avalanche, Robinhood. Read-only; never signs or swaps.
|
|
3
3
|
|
|
4
4
|
import { Interface, getAddress, isAddress } from "ethers";
|
|
5
5
|
import { rpcCall } from "./evm-rpc.mjs";
|
|
@@ -34,10 +34,8 @@ export const UNI_V3_CHAINS = {
|
|
|
34
34
|
// eth_chainId == 4663, eth_getCode non-empty for every address below, and a real
|
|
35
35
|
// quoteExactInputSingle returned 1 WETH -> 1866.58 USDG at fee 100 (2026-07-31).
|
|
36
36
|
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
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.
|
|
37
|
+
// The router-level price-impact guard is what blocks dead RH V3 shells from being
|
|
38
|
+
// treated as executable routes.
|
|
41
39
|
4663: {
|
|
42
40
|
name: "robinhood",
|
|
43
41
|
quoter: "0x33e885ed0ec9bf04ecfb19341582aadcb4c8a9e7",
|
|
@@ -45,6 +43,37 @@ export const UNI_V3_CHAINS = {
|
|
|
45
43
|
weth: "0x0bd7d308f8e1639fab988df18a8011f41eacad73",
|
|
46
44
|
usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
47
45
|
},
|
|
46
|
+
// Optimism / BNB / Polygon / Avalanche. Official Uniswap v3 deployments,
|
|
47
|
+
// each proved on-chain in the MAD PR #16 lane: eth_getCode non-empty for
|
|
48
|
+
// quoter, router, wrapped-native and stable, plus a decimals-correct quote.
|
|
49
|
+
10: {
|
|
50
|
+
name: "optimism",
|
|
51
|
+
quoter: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
|
|
52
|
+
router: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45",
|
|
53
|
+
weth: "0x4200000000000000000000000000000000000006",
|
|
54
|
+
usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
55
|
+
},
|
|
56
|
+
56: {
|
|
57
|
+
name: "bsc",
|
|
58
|
+
quoter: "0x78D78E420Da98ad378D7799bE8f4AF69033EB077",
|
|
59
|
+
router: "0xB971eF87ede563556b2ED4b1C0b0019111Dd85d2",
|
|
60
|
+
weth: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
|
|
61
|
+
usdc: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
|
|
62
|
+
},
|
|
63
|
+
137: {
|
|
64
|
+
name: "polygon",
|
|
65
|
+
quoter: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
|
|
66
|
+
router: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45",
|
|
67
|
+
weth: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
|
|
68
|
+
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
69
|
+
},
|
|
70
|
+
43114: {
|
|
71
|
+
name: "avalanche",
|
|
72
|
+
quoter: "0xbe0F5544EC67e9B3b2D979aaA43f18Fd87E6257F",
|
|
73
|
+
router: "0xbb00FF08d01D300023C629E8fFfFcb65A5a578cE",
|
|
74
|
+
weth: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
|
|
75
|
+
usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
76
|
+
},
|
|
48
77
|
};
|
|
49
78
|
|
|
50
79
|
export const UNI_V3_FEE_TIERS = [100, 500, 3000, 10000];
|