@oracle-agent/oracle 0.3.1 → 0.3.3
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 +44 -12
- package/SETUP.md +49 -1
- package/artifacts/specialist-packs/oracle-full-crypto.json +31 -9
- package/bin/oracle-data-mcp.mjs +247 -4
- package/bin/oracle-init.mjs +100 -27
- package/docs/profiles.md +27 -7
- package/package.json +1 -1
- package/profiles/_template/SOUL.md +8 -1
- package/profiles/oracle/SOUL.md +8 -1
- package/profiles/oracle/profile.json +5 -2
- package/profiles/protocol-builder/SOUL.md +13 -6
- package/profiles/protocol-builder/profile.json +3 -1
- package/public/index.html +12 -0
- package/skills/balance/SKILL.md +176 -0
- package/skills/oracle-multichain-nft-launch/SKILL.md +338 -0
- package/skills/oracle-multichain-token-launch/SKILL.md +300 -0
- package/src/agent-auth.mjs +15 -0
- package/src/data/catalog.mjs +27 -3
- package/src/data/desk-data.mjs +34 -4
- package/src/data/providers/magiceden-sol.mjs +21 -2
- package/src/data/providers/nft-gallery.mjs +163 -0
- package/src/data/providers/nft-portfolio.mjs +494 -0
- package/src/data/providers/opensea-nft.mjs +272 -0
- package/src/data/providers/portfolio-history.mjs +394 -0
- package/src/data/providers/portfolio.mjs +594 -0
- package/src/data/providers/satflow.mjs +1 -0
- package/src/exec-policy.mjs +5 -0
- package/src/gmx-attestation.mjs +1 -0
- package/src/index.mjs +1 -0
- package/src/prepare-envelope.mjs +66 -10
- package/src/vault-attestation.mjs +1 -0
package/src/prepare-envelope.mjs
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
// Canonical prepare envelope shared by Oracle prepare helpers and the local operator.
|
|
2
|
-
// The versioned checksum binds payload and freshness metadata. It detects mutation
|
|
3
|
-
//
|
|
2
|
+
// The versioned checksum binds payload and freshness metadata. It detects mutation.
|
|
3
|
+
// Optional HMAC (ORACLE_STAMP_HMAC_SECRET) adds keyed integrity when configured.
|
|
4
|
+
// Signer-owned policy, not this public checksum alone, authorizes execution.
|
|
4
5
|
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
6
7
|
|
|
7
8
|
export const PREPARE_VERSION = 2;
|
|
8
9
|
/** Default max age for a prepared envelope at sign time (ms). */
|
|
9
10
|
export const PREPARE_MAX_AGE_MS = 5 * 60 * 1000;
|
|
10
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Hard ceiling for maxAgeMs. Caller/agent cannot widen past this.
|
|
13
|
+
* Override upward only via signer-process env ORACLE_PREPARE_HARD_MAX_AGE_MS.
|
|
14
|
+
*/
|
|
11
15
|
export const PREPARE_HARD_MAX_AGE_MS = (() => {
|
|
12
16
|
const n = Number(process.env.ORACLE_PREPARE_HARD_MAX_AGE_MS);
|
|
13
|
-
return Number.isFinite(n) && n > 0 ? n : 30 * 60 * 1000;
|
|
17
|
+
return Number.isFinite(n) && n > 0 ? n : 30 * 60 * 1000; // 30 minutes
|
|
14
18
|
})();
|
|
15
19
|
|
|
20
|
+
/** Resolve max age: default 5m, never above hard max (even if caller passes MAX_SAFE_INTEGER). */
|
|
16
21
|
export function resolvePrepareMaxAgeMs(requested) {
|
|
17
|
-
const base =
|
|
22
|
+
const base =
|
|
23
|
+
requested == null || requested === ""
|
|
24
|
+
? PREPARE_MAX_AGE_MS
|
|
25
|
+
: Number(requested);
|
|
18
26
|
if (!Number.isFinite(base) || base < 0) return PREPARE_MAX_AGE_MS;
|
|
19
27
|
return Math.min(base, PREPARE_HARD_MAX_AGE_MS);
|
|
20
28
|
}
|
|
@@ -26,11 +34,21 @@ function stableStringify(value) {
|
|
|
26
34
|
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
|
|
27
35
|
}
|
|
28
36
|
|
|
37
|
+
function stampSecret(env = process.env) {
|
|
38
|
+
const s = String(env.ORACLE_STAMP_HMAC_SECRET || "").trim();
|
|
39
|
+
return s.length >= 16 ? s : "";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function requireMac(env = process.env) {
|
|
43
|
+
return String(env.ORACLE_STAMP_REQUIRE_MAC || "").trim() === "1";
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
/** Fields that bind the envelope (must not include the stamp fields themselves). */
|
|
30
47
|
export function prepareBodyForHash(prepared) {
|
|
31
48
|
const {
|
|
32
49
|
oraclePrepared,
|
|
33
50
|
prepareHash,
|
|
51
|
+
prepareMac,
|
|
34
52
|
note,
|
|
35
53
|
...body
|
|
36
54
|
} = prepared || {};
|
|
@@ -42,11 +60,27 @@ export function computePrepareHash(prepared) {
|
|
|
42
60
|
return createHash("sha256").update(stableStringify(body)).digest("hex");
|
|
43
61
|
}
|
|
44
62
|
|
|
63
|
+
export function computePrepareMac(prepareHash, secret = stampSecret()) {
|
|
64
|
+
if (!secret) return "";
|
|
65
|
+
return createHmac("sha256", secret).update(String(prepareHash)).digest("hex");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function safeEqualHex(a, b) {
|
|
69
|
+
try {
|
|
70
|
+
const ba = Buffer.from(String(a), "hex");
|
|
71
|
+
const bb = Buffer.from(String(b), "hex");
|
|
72
|
+
if (ba.length === 0 || ba.length !== bb.length) return false;
|
|
73
|
+
return timingSafeEqual(ba, bb);
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
45
79
|
/**
|
|
46
80
|
* Stamp a prepare result. Call at the end of every prepare helper.
|
|
47
81
|
* @param {object} payload provider-specific prepare fields (action/tx/psbt/...)
|
|
48
82
|
*/
|
|
49
|
-
export function stampPrepared(payload, { provider, kind } = {}) {
|
|
83
|
+
export function stampPrepared(payload, { provider, kind, env = process.env } = {}) {
|
|
50
84
|
if (!payload || typeof payload !== "object") throw new Error("stampPrepared: payload object required");
|
|
51
85
|
const base = {
|
|
52
86
|
...payload,
|
|
@@ -64,11 +98,15 @@ export function stampPrepared(payload, { provider, kind } = {}) {
|
|
|
64
98
|
const prepareVersion = PREPARE_VERSION;
|
|
65
99
|
const withMeta = { ...base, preparedAt, expiresAt, prepareVersion };
|
|
66
100
|
const prepareHash = computePrepareHash(withMeta);
|
|
67
|
-
|
|
101
|
+
const secret = stampSecret(env);
|
|
102
|
+
const prepareMac = secret ? computePrepareMac(prepareHash, secret) : undefined;
|
|
103
|
+
const out = {
|
|
68
104
|
...withMeta,
|
|
69
105
|
oraclePrepared: true,
|
|
70
106
|
prepareHash,
|
|
71
107
|
};
|
|
108
|
+
if (prepareMac) out.prepareMac = prepareMac;
|
|
109
|
+
return out;
|
|
72
110
|
}
|
|
73
111
|
|
|
74
112
|
/**
|
|
@@ -81,13 +119,14 @@ export function assertPreparedEnvelope(prepared, {
|
|
|
81
119
|
providers = null,
|
|
82
120
|
kinds = null,
|
|
83
121
|
label = "operator",
|
|
122
|
+
env = process.env,
|
|
84
123
|
} = {}) {
|
|
85
124
|
if (!prepared || typeof prepared !== "object") {
|
|
86
125
|
throw new Error(`${label}: prepared envelope object required`);
|
|
87
126
|
}
|
|
88
127
|
if (prepared.oraclePrepared !== true) {
|
|
89
128
|
throw new Error(
|
|
90
|
-
`${label}: prepared integrity envelope missing — use an @oracle-agent/oracle prepare result
|
|
129
|
+
`${label}: prepared integrity envelope missing — use an @oracle-agent/oracle prepare result`,
|
|
91
130
|
);
|
|
92
131
|
}
|
|
93
132
|
if (Number(prepared.prepareVersion) !== PREPARE_VERSION) {
|
|
@@ -97,6 +136,21 @@ export function assertPreparedEnvelope(prepared, {
|
|
|
97
136
|
if (String(prepared.prepareHash || "") !== expected) {
|
|
98
137
|
throw new Error(`${label}: prepareHash mismatch — payload was altered after prepare`);
|
|
99
138
|
}
|
|
139
|
+
|
|
140
|
+
const secret = stampSecret(env);
|
|
141
|
+
const mac = String(prepared.prepareMac || "");
|
|
142
|
+
if (requireMac(env) || mac) {
|
|
143
|
+
if (!secret) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`${label}: prepareMac present or ORACLE_STAMP_REQUIRE_MAC=1 but ORACLE_STAMP_HMAC_SECRET missing in signer`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
const expectedMac = computePrepareMac(expected, secret);
|
|
149
|
+
if (!mac || !safeEqualHex(mac, expectedMac)) {
|
|
150
|
+
throw new Error(`${label}: prepareMac mismatch — stamp not from trusted preparer`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
100
154
|
const preparedAt = Number(prepared.preparedAt);
|
|
101
155
|
const expiresAt = Number(prepared.expiresAt);
|
|
102
156
|
const effectiveMax = resolvePrepareMaxAgeMs(maxAgeMs);
|
|
@@ -112,7 +166,9 @@ export function assertPreparedEnvelope(prepared, {
|
|
|
112
166
|
age > effectiveMax ||
|
|
113
167
|
nowMs > expiresAt
|
|
114
168
|
) {
|
|
115
|
-
throw new Error(
|
|
169
|
+
throw new Error(
|
|
170
|
+
`${label}: prepare envelope expired or clock-skewed (ageMs=${age}, max=${effectiveMax})`,
|
|
171
|
+
);
|
|
116
172
|
}
|
|
117
173
|
if (providers) {
|
|
118
174
|
const allow = new Set(providers);
|
|
@@ -129,6 +129,7 @@ export function assertVaultAttestation(attestation, tx = {}, { chainId, nowMs =
|
|
|
129
129
|
export function assertVaultApprovalAttestation(attestation, tx = {}, guard = {}, { chainId, nowMs = Date.now(), secret } = {}) {
|
|
130
130
|
if (!attestation || attestation.mode !== "vault-attestation") throw new Error("vault attestation required");
|
|
131
131
|
if (String(attestation.action) !== "deposit") throw new Error("vault approval requires deposit attestation");
|
|
132
|
+
assertFreshWindow(attestation, nowMs, "vault approval attestation");
|
|
132
133
|
if (Number(attestation.expiresAtMs) <= Number(nowMs)) throw new Error("vault attestation expired");
|
|
133
134
|
const expected = hmac(attestationSecret(secret), canonicalJson(unsigned(attestation)));
|
|
134
135
|
if (!sameSignature(attestation.signature, expected)) throw new Error("vault attestation signature mismatch");
|