sealed-precision-oracle 1.0.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 +109 -0
- package/bin/cli.js +17 -0
- package/contracts/IERC7857.sol +86 -0
- package/contracts/OracleDataVerifier.sol +72 -0
- package/contracts/PrecisionOracleID.sol +328 -0
- package/dist/hardhat.config.d.ts +6 -0
- package/dist/hardhat.config.js +29 -0
- package/dist/hardhat.config.js.map +1 -0
- package/dist/scripts/deploy.d.ts +1 -0
- package/dist/scripts/deploy.js +47 -0
- package/dist/scripts/deploy.js.map +1 -0
- package/dist/scripts/registerTee.d.ts +1 -0
- package/dist/scripts/registerTee.js +16 -0
- package/dist/scripts/registerTee.js.map +1 -0
- package/dist/src/fetchMarketData.d.ts +15 -0
- package/dist/src/fetchMarketData.js +160 -0
- package/dist/src/fetchMarketData.js.map +1 -0
- package/dist/src/oracle.d.ts +1 -0
- package/dist/src/oracle.js +263 -0
- package/dist/src/oracle.js.map +1 -0
- package/dist/src/teeInference.d.ts +43 -0
- package/dist/src/teeInference.js +200 -0
- package/dist/src/teeInference.js.map +1 -0
- package/dist/src/uploadReceipt.d.ts +12 -0
- package/dist/src/uploadReceipt.js +111 -0
- package/dist/src/uploadReceipt.js.map +1 -0
- package/package.json +44 -0
- package/skills/sealed-precision-oracle/SKILL.md +71 -0
- package/src/fetchMarketData.ts +187 -0
- package/src/oracle.ts +308 -0
- package/src/teeInference.ts +272 -0
- package/src/uploadReceipt.ts +92 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Direct TEE-attested inference via the 0G Compute SDK.
|
|
4
|
+
*
|
|
5
|
+
* Bypasses the Router API entirely. Instead:
|
|
6
|
+
* 1. Discovers providers on-chain from the InferenceServing contract
|
|
7
|
+
* 2. Calls inference directly to the provider's broker URL
|
|
8
|
+
* 3. Fetches the per-inference TEE signature from the provider
|
|
9
|
+
* 4. Verifies the signature against the on-chain TEE signer address
|
|
10
|
+
*
|
|
11
|
+
* This provides REAL per-inference attestation, not just metadata.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.discoverProviders = discoverProviders;
|
|
15
|
+
exports.teeInference = teeInference;
|
|
16
|
+
const ethers_1 = require("ethers");
|
|
17
|
+
const _0g_compute_ts_sdk_1 = require("@0gfoundation/0g-compute-ts-sdk");
|
|
18
|
+
const openai_1 = require("openai");
|
|
19
|
+
require("dotenv/config");
|
|
20
|
+
const OG_RPC_URL = process.env.OG_RPC_URL || "https://evmrpc.0g.ai";
|
|
21
|
+
/**
|
|
22
|
+
* Discover TEE-attested providers for a given model from the on-chain registry.
|
|
23
|
+
*/
|
|
24
|
+
async function discoverProviders(modelQuery) {
|
|
25
|
+
const broker = await (0, _0g_compute_ts_sdk_1.createZGComputeNetworkReadOnlyBroker)(OG_RPC_URL);
|
|
26
|
+
const services = await broker.inference.listService();
|
|
27
|
+
return services
|
|
28
|
+
.filter((s) => {
|
|
29
|
+
const isTee = s.verifiability === "TeeML" || s.verifiability === "TeeTLS";
|
|
30
|
+
const modelMatch = modelQuery
|
|
31
|
+
? s.model?.toLowerCase().includes(modelQuery.toLowerCase())
|
|
32
|
+
: true;
|
|
33
|
+
return isTee && modelMatch;
|
|
34
|
+
})
|
|
35
|
+
.map((s) => ({
|
|
36
|
+
provider: s.provider,
|
|
37
|
+
url: s.url,
|
|
38
|
+
model: s.model,
|
|
39
|
+
verifiability: s.verifiability,
|
|
40
|
+
teeSignerAddress: s.teeSignerAddress,
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Run inference directly on a TEE-attested provider and verify the response signature.
|
|
45
|
+
*/
|
|
46
|
+
async function teeInference(systemPrompt, userPrompt, modelQuery) {
|
|
47
|
+
const privateKey = process.env.PRIVATE_KEY;
|
|
48
|
+
if (!privateKey)
|
|
49
|
+
throw new Error("PRIVATE_KEY not set in .env");
|
|
50
|
+
const provider = new ethers_1.ethers.JsonRpcProvider(OG_RPC_URL);
|
|
51
|
+
const wallet = new ethers_1.ethers.Wallet(privateKey, provider);
|
|
52
|
+
// 1. Create broker with wallet (needed for auth headers + fees)
|
|
53
|
+
console.log("[TEE] Creating compute broker...");
|
|
54
|
+
const broker = await (0, _0g_compute_ts_sdk_1.createZGComputeNetworkBroker)(wallet);
|
|
55
|
+
// 2. Discover providers on-chain
|
|
56
|
+
console.log("[TEE] Discovering providers from on-chain registry...");
|
|
57
|
+
const services = await broker.inference.listService();
|
|
58
|
+
const service = services.find((s) => {
|
|
59
|
+
const isTee = s.verifiability === "TeeML" || s.verifiability === "TeeTLS";
|
|
60
|
+
const modelMatch = modelQuery
|
|
61
|
+
? s.model?.toLowerCase().includes(modelQuery.toLowerCase())
|
|
62
|
+
: true;
|
|
63
|
+
return isTee && modelMatch;
|
|
64
|
+
});
|
|
65
|
+
if (!service) {
|
|
66
|
+
// Fallback: list what's available
|
|
67
|
+
const available = services.map((s) => `${s.model} (${s.verifiability})`);
|
|
68
|
+
throw new Error(`No TEE provider found for model query "${modelQuery}". ` +
|
|
69
|
+
`Available: ${available.join(", ") || "none"}`);
|
|
70
|
+
}
|
|
71
|
+
const providerAddress = service.provider;
|
|
72
|
+
const providerUrl = service.url;
|
|
73
|
+
const model = service.model;
|
|
74
|
+
const teeSigner = service.teeSignerAddress;
|
|
75
|
+
console.log(`[TEE] Found provider: ${providerAddress}`);
|
|
76
|
+
console.log(`[TEE] Model: ${model}`);
|
|
77
|
+
console.log(`[TEE] URL: ${providerUrl}`);
|
|
78
|
+
console.log(`[TEE] TEE signer: ${teeSigner}`);
|
|
79
|
+
console.log(`[TEE] Verifiability: ${service.verifiability}`);
|
|
80
|
+
// 3. Setup: deposit funds and acknowledge provider
|
|
81
|
+
try {
|
|
82
|
+
await broker.ledger.depositFund(0.001); // Small deposit for testnet
|
|
83
|
+
console.log("[TEE] Deposited funds to ledger");
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
// May fail if already deposited — that's OK
|
|
87
|
+
console.log("[TEE] Deposit skipped (may already have funds):", err.message?.slice(0, 80));
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await broker.inference.acknowledgeProviderSigner(providerAddress);
|
|
91
|
+
console.log("[TEE] Acknowledged provider TEE signer");
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
console.log("[TEE] Acknowledge skipped (may already be done):", err.message?.slice(0, 80));
|
|
95
|
+
}
|
|
96
|
+
// 4. Get endpoint and auth headers
|
|
97
|
+
const metadata = await broker.inference.getServiceMetadata(providerAddress);
|
|
98
|
+
const headers = await broker.inference.getRequestHeaders(providerAddress);
|
|
99
|
+
console.log("[TEE] Calling inference directly on provider...");
|
|
100
|
+
// 5. Direct inference call to provider (no Router)
|
|
101
|
+
const openai = new openai_1.OpenAI({ baseURL: metadata.endpoint, apiKey: "not-needed" });
|
|
102
|
+
const { data: completion, response: httpResponse } = await openai.chat.completions
|
|
103
|
+
.create({
|
|
104
|
+
model: metadata.model,
|
|
105
|
+
messages: [
|
|
106
|
+
{ role: "system", content: systemPrompt },
|
|
107
|
+
{ role: "user", content: userPrompt },
|
|
108
|
+
],
|
|
109
|
+
temperature: 0.1,
|
|
110
|
+
max_tokens: 1024,
|
|
111
|
+
}, { headers: { ...headers } })
|
|
112
|
+
.withResponse();
|
|
113
|
+
const content = completion.choices[0]?.message?.content;
|
|
114
|
+
if (!content)
|
|
115
|
+
throw new Error("No response from provider");
|
|
116
|
+
// Get chat ID from response header (for signature lookup)
|
|
117
|
+
const chatId = httpResponse.headers.get("ZG-Res-Key") || completion.id || "unknown";
|
|
118
|
+
console.log("[TEE] Got response, chat ID:", chatId);
|
|
119
|
+
// 6. Process response (fees + signature verification via SDK)
|
|
120
|
+
let sdkVerified = null;
|
|
121
|
+
try {
|
|
122
|
+
sdkVerified = await broker.inference.processResponse(providerAddress, chatId, JSON.stringify(completion.usage || {}));
|
|
123
|
+
console.log("[TEE] SDK processResponse result:", sdkVerified);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
console.log("[TEE] SDK processResponse failed:", err.message?.slice(0, 100));
|
|
127
|
+
}
|
|
128
|
+
// 7. Manual signature verification for full transparency
|
|
129
|
+
let attestation = null;
|
|
130
|
+
let attestationNote = "";
|
|
131
|
+
try {
|
|
132
|
+
const sigUrl = `${providerUrl}/v1/proxy/signature/${chatId}?model=${model}`;
|
|
133
|
+
console.log("[TEE] Fetching per-inference signature...");
|
|
134
|
+
const sigRes = await fetch(sigUrl, { signal: AbortSignal.timeout(10000) });
|
|
135
|
+
if (sigRes.ok) {
|
|
136
|
+
const { text: signedText, signature } = (await sigRes.json());
|
|
137
|
+
// Determine the expected signing address
|
|
138
|
+
let expectedSigner = teeSigner;
|
|
139
|
+
try {
|
|
140
|
+
const additionalInfo = JSON.parse(service.additionalInfo || "{}");
|
|
141
|
+
if (additionalInfo.TargetSeparated &&
|
|
142
|
+
additionalInfo.ProviderType !== "centralized" &&
|
|
143
|
+
additionalInfo.TargetTeeAddress) {
|
|
144
|
+
expectedSigner = additionalInfo.TargetTeeAddress;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch { }
|
|
148
|
+
// Verify ECDSA signature
|
|
149
|
+
const messageHash = ethers_1.ethers.hashMessage(signedText);
|
|
150
|
+
const recoveredAddress = ethers_1.ethers.recoverAddress(messageHash, signature);
|
|
151
|
+
const verified = recoveredAddress.toLowerCase() === expectedSigner.toLowerCase();
|
|
152
|
+
attestation = {
|
|
153
|
+
signedText,
|
|
154
|
+
signature,
|
|
155
|
+
teeSigner: expectedSigner,
|
|
156
|
+
recoveredAddress,
|
|
157
|
+
verified,
|
|
158
|
+
};
|
|
159
|
+
attestationNote = verified
|
|
160
|
+
? `PER-INFERENCE TEE attestation VERIFIED. ` +
|
|
161
|
+
`Response signed by ${recoveredAddress}, matches on-chain TEE signer ${expectedSigner}.`
|
|
162
|
+
: `PER-INFERENCE TEE signature recovered ${recoveredAddress} but expected ${expectedSigner}. MISMATCH.`;
|
|
163
|
+
console.log("[TEE]", attestationNote);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
attestationNote = `Signature endpoint returned ${sigRes.status}. Per-inference attestation unavailable.`;
|
|
167
|
+
console.log("[TEE]", attestationNote);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
attestationNote = `Signature fetch failed: ${err.message}. Per-inference attestation unavailable.`;
|
|
172
|
+
console.log("[TEE]", attestationNote);
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
content,
|
|
176
|
+
chatId,
|
|
177
|
+
model,
|
|
178
|
+
providerAddress,
|
|
179
|
+
providerUrl,
|
|
180
|
+
attestation,
|
|
181
|
+
attestationNote,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// Allow direct execution for testing
|
|
185
|
+
if (require.main === module) {
|
|
186
|
+
(async () => {
|
|
187
|
+
console.log("=== TEE Provider Discovery ===\n");
|
|
188
|
+
try {
|
|
189
|
+
const providers = await discoverProviders();
|
|
190
|
+
console.log(`Found ${providers.length} TEE providers:\n`);
|
|
191
|
+
for (const p of providers) {
|
|
192
|
+
console.log(` ${p.model} | ${p.verifiability} | ${p.provider.slice(0, 10)}...`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
console.error("Discovery failed:", err);
|
|
197
|
+
}
|
|
198
|
+
})();
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=teeInference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teeInference.js","sourceRoot":"","sources":["../../src/teeInference.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
export interface UploadResult {
|
|
3
|
+
rootHash: string;
|
|
4
|
+
txHash: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Upload an AI resolution receipt to 0G Storage.
|
|
8
|
+
*
|
|
9
|
+
* Writes the content to a temp file, computes its Merkle tree root,
|
|
10
|
+
* uploads via the 0G Indexer, and returns the root hash + tx hash.
|
|
11
|
+
*/
|
|
12
|
+
export declare function uploadReceipt(content: string): Promise<UploadResult>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.uploadReceipt = uploadReceipt;
|
|
37
|
+
const _0g_ts_sdk_1 = require("@0gfoundation/0g-ts-sdk");
|
|
38
|
+
const ethers_1 = require("ethers");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
require("dotenv/config");
|
|
43
|
+
const OG_RPC_URL = process.env.OG_RPC_URL || "https://evmrpc.0g.ai";
|
|
44
|
+
const OG_INDEXER_RPC = process.env.OG_INDEXER_RPC ||
|
|
45
|
+
"https://indexer-storage-mainnet-turbo.0g.ai";
|
|
46
|
+
/**
|
|
47
|
+
* Upload an AI resolution receipt to 0G Storage.
|
|
48
|
+
*
|
|
49
|
+
* Writes the content to a temp file, computes its Merkle tree root,
|
|
50
|
+
* uploads via the 0G Indexer, and returns the root hash + tx hash.
|
|
51
|
+
*/
|
|
52
|
+
async function uploadReceipt(content) {
|
|
53
|
+
const privateKey = process.env.PRIVATE_KEY;
|
|
54
|
+
if (!privateKey)
|
|
55
|
+
throw new Error("PRIVATE_KEY not set in .env");
|
|
56
|
+
// Write content to a temp file (ZgFile works with file paths)
|
|
57
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "oracle-"));
|
|
58
|
+
const tmpFile = path.join(tmpDir, "resolution.json");
|
|
59
|
+
fs.writeFileSync(tmpFile, content, { encoding: "utf-8", mode: 0o600 });
|
|
60
|
+
try {
|
|
61
|
+
// Create ZgFile and compute Merkle tree
|
|
62
|
+
const file = await _0g_ts_sdk_1.ZgFile.fromFilePath(tmpFile);
|
|
63
|
+
const [tree, treeErr] = await file.merkleTree();
|
|
64
|
+
if (treeErr !== null || tree === null) {
|
|
65
|
+
throw new Error(`Merkle tree error: ${treeErr}`);
|
|
66
|
+
}
|
|
67
|
+
const rootHash = tree.rootHash();
|
|
68
|
+
if (!rootHash)
|
|
69
|
+
throw new Error("Failed to compute Merkle root hash");
|
|
70
|
+
console.log("[0G Storage] Merkle root hash:", rootHash);
|
|
71
|
+
// Connect signer
|
|
72
|
+
const provider = new ethers_1.ethers.JsonRpcProvider(OG_RPC_URL);
|
|
73
|
+
const signer = new ethers_1.ethers.Wallet(privateKey, provider);
|
|
74
|
+
// Upload via Indexer
|
|
75
|
+
const indexer = new _0g_ts_sdk_1.Indexer(OG_INDEXER_RPC);
|
|
76
|
+
console.log("[0G Storage] Uploading receipt...");
|
|
77
|
+
const [tx, uploadErr] = await indexer.upload(file, OG_RPC_URL, signer);
|
|
78
|
+
await file.close();
|
|
79
|
+
if (uploadErr !== null) {
|
|
80
|
+
throw new Error(`Upload error: ${uploadErr}`);
|
|
81
|
+
}
|
|
82
|
+
const txHash = tx && "txHash" in tx ? tx.txHash : "unknown";
|
|
83
|
+
console.log("[0G Storage] Upload complete!");
|
|
84
|
+
console.log("[0G Storage] TX hash:", txHash);
|
|
85
|
+
return { rootHash, txHash };
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
// Clean up temp files
|
|
89
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Allow running directly: ts-node src/uploadReceipt.ts "your content here"
|
|
93
|
+
if (require.main === module) {
|
|
94
|
+
const content = process.argv[2] || JSON.stringify({
|
|
95
|
+
market: "ETH Gas > 50 gwei",
|
|
96
|
+
resolution: "NO",
|
|
97
|
+
reasoning: "Sample test resolution",
|
|
98
|
+
timestamp: new Date().toISOString(),
|
|
99
|
+
});
|
|
100
|
+
uploadReceipt(content)
|
|
101
|
+
.then((result) => {
|
|
102
|
+
console.log("\n--- Upload Result ---");
|
|
103
|
+
console.log("Root Hash:", result.rootHash);
|
|
104
|
+
console.log("TX Hash:", result.txHash);
|
|
105
|
+
})
|
|
106
|
+
.catch((err) => {
|
|
107
|
+
console.error("Upload failed:", err);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=uploadReceipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadReceipt.js","sourceRoot":"","sources":["../../src/uploadReceipt.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sealed-precision-oracle",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Tamper-proof AI Oracle for precision prediction markets on 0G",
|
|
5
|
+
"main": "dist/src/oracle.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sealed-precision-oracle": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"bin",
|
|
12
|
+
"skills",
|
|
13
|
+
"contracts",
|
|
14
|
+
"src"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"compile": "hardhat compile",
|
|
20
|
+
"deploy": "hardhat run scripts/deploy.ts --network og_mainnet",
|
|
21
|
+
"deploy:testnet": "hardhat run scripts/deploy.ts --network og_galileo",
|
|
22
|
+
"oracle": "ts-node src/oracle.ts",
|
|
23
|
+
"upload": "ts-node src/uploadReceipt.ts",
|
|
24
|
+
"test": "hardhat test"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@0gfoundation/0g-compute-ts-sdk": "^0.8.3",
|
|
28
|
+
"@0gfoundation/0g-ts-sdk": "^1.2.8",
|
|
29
|
+
"@openzeppelin/contracts": "^5.1.0",
|
|
30
|
+
"dotenv": "^16.4.5",
|
|
31
|
+
"ethers": "6.13.1",
|
|
32
|
+
"openai": "^4.73.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@nomicfoundation/hardhat-chai-matchers": "^2.1.2",
|
|
36
|
+
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
|
37
|
+
"@types/chai": "^5.2.3",
|
|
38
|
+
"@types/mocha": "^10.0.10",
|
|
39
|
+
"chai": "^4.5.0",
|
|
40
|
+
"hardhat": "^2.22.17",
|
|
41
|
+
"ts-node": "^10.9.2",
|
|
42
|
+
"typescript": "^5.7.2"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sealed-precision-oracle
|
|
3
|
+
description: Cognitive backbone for verifiable market resolutions. Uses TEE-attested AI inference (0G Compute), long-context state persistence (0G Storage), and ERC-7857 agent identity (0G Chain).
|
|
4
|
+
metadata: {"openclaw":{"emoji":"🔮","track":"Track 1: Agentic Infrastructure","requires":{"bins":["node","npx"],"env":["ORACLE_CONTRACT_ADDRESS"],"os":["darwin","linux"]},"optionalEnv":["PRIVATE_KEY"]}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Sealed Precision Oracle (Track 1: Agentic Infrastructure)
|
|
8
|
+
|
|
9
|
+
## Core Focus: Cognitive Backbone
|
|
10
|
+
The Sealed Precision Oracle acts as the cognitive backbone for autonomous intelligence on the 0G Network. It provides other agents with a source of **Verifiable Truth** by orchestrating real-time data, hardware-secured AI reasoning, and immutable state persistence.
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
Resolves complex, data-dependent questions using a multi-layered verifiable pipeline:
|
|
15
|
+
|
|
16
|
+
1. **Autonomous Data Fetching**: Retrieves ground-truth data (e.g., ETH gas, token prices) from public RPCs and APIs.
|
|
17
|
+
2. **Hardware-Secured Reasoning (TEE)**: Executes AI inference inside a **Trusted Execution Environment (TEE)** using the `0g-compute-ts-sdk`. This ensures the AI model cannot be tampered with.
|
|
18
|
+
3. **Cryptographic Attestation**: Fetches and verifies a **per-inference ECDSA signature** from the TEE provider, proving that the specific response was generated by the specific hardware.
|
|
19
|
+
4. **Long-Context Memory (0G Storage)**: Archives the full resolution context—including reasoning, data snapshots, and signatures—into **0G Storage**. This serves as the agent's immutable memory and "Proof of Reasoning."
|
|
20
|
+
5. **On-Chain Settlement**: Anchors the resolution's Merkle root to the **0G Mainnet** via an **ERC-7857 (AI Agent NFT)** contract, enabling other smart contracts to verify the outcome.
|
|
21
|
+
|
|
22
|
+
## Integration with OpenClaw
|
|
23
|
+
|
|
24
|
+
This Oracle is designed to be orchestrated by OpenClaw. It supports two execution modes:
|
|
25
|
+
|
|
26
|
+
1. **Managed Mode (Default/Zero-Config):** Ideal for judges and rapid demos. If no local private key is found, the skill automatically calls a hosted **API Bridge** that handles 0G fees and transaction signing using our infrastructure.
|
|
27
|
+
2. **Autonomous Mode (Self-Sovereign):** If you provide a funded `PRIVATE_KEY`, the skill runs entirely on your local machine, using your own funds for 0G Compute, 0G Storage, and 0G Chain recording.
|
|
28
|
+
|
|
29
|
+
## Workflow
|
|
30
|
+
|
|
31
|
+
1. Trigger the oracle via ClawHub or CLI:
|
|
32
|
+
```bash
|
|
33
|
+
# Managed Mode (Zero-Config)
|
|
34
|
+
sealed-precision-oracle "Is ETH gas below 30 gwei?"
|
|
35
|
+
|
|
36
|
+
# Autonomous Mode (Local Signing)
|
|
37
|
+
export PRIVATE_KEY="your_funded_key"
|
|
38
|
+
sealed-precision-oracle "Is ETH gas below 30 gwei?"
|
|
39
|
+
```
|
|
40
|
+
2. The pipeline handles:
|
|
41
|
+
- Real-time data injection.
|
|
42
|
+
- TEE-attested AI inference.
|
|
43
|
+
- 0G Storage state archival.
|
|
44
|
+
- 0G Mainnet settlement.
|
|
45
|
+
3. Report the verified resolution and cryptographic proofs.
|
|
46
|
+
|
|
47
|
+
## Prerequisites
|
|
48
|
+
|
|
49
|
+
- **Managed Mode:** No prerequisites! Just install and run.
|
|
50
|
+
- **Autonomous Mode:**
|
|
51
|
+
1. Export `PRIVATE_KEY` in your terminal or create a `.env` file locally.
|
|
52
|
+
2. Ensure your wallet has **0G Tokens** for gas and storage fees.
|
|
53
|
+
3. The skill automatically handles Compute Ledger funding and TEE handshakes.
|
|
54
|
+
|
|
55
|
+
## Mainnet Addresses
|
|
56
|
+
- **Oracle ID (ERC-7857):** `0xf25E765eF573c26d6314Fd83822564E7AF11C9Ac`
|
|
57
|
+
- **Network:** 0G Mainnet (Chain ID: 16661)
|
|
58
|
+
|
|
59
|
+
## Guardrails
|
|
60
|
+
|
|
61
|
+
- **Zero-Hallucination**: The oracle is grounded in real-time fetched data.
|
|
62
|
+
- **Hardware-Enforced**: TEE signatures are verified on-chain and off-chain.
|
|
63
|
+
- **Immutable State**: Every decision is archived permanently on 0G Storage.
|
|
64
|
+
|
|
65
|
+
## Output format
|
|
66
|
+
|
|
67
|
+
- **Resolution**: YES/NO with confidence score.
|
|
68
|
+
- **AI Reasoning**: Step-by-step logic from the TEE.
|
|
69
|
+
- **TEE Proof**: Verified signature and signer address.
|
|
70
|
+
- **0G Storage Root**: Permanent pointer to the resolution memory.
|
|
71
|
+
- **0G Chain TX**: Blockchain anchor for the proof.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-market real-time data fetcher.
|
|
3
|
+
* Supports: ETH gas, ETH price, BTC price, and arbitrary token prices.
|
|
4
|
+
* Uses public RPCs and free APIs — no API keys required.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface MarketData {
|
|
8
|
+
marketType: string;
|
|
9
|
+
dataPoints: Record<string, number | string | null>;
|
|
10
|
+
source: string;
|
|
11
|
+
fetchedAt: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
async function jsonFetch(url: string, options?: RequestInit): Promise<any> {
|
|
17
|
+
const res = await fetch(url, {
|
|
18
|
+
...options,
|
|
19
|
+
signal: AbortSignal.timeout(10_000),
|
|
20
|
+
headers: { "Content-Type": "application/json", ...options?.headers },
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
|
|
23
|
+
return res.json();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function rpcCall(url: string, method: string, params: unknown[] = []): Promise<any> {
|
|
27
|
+
const data = await jsonFetch(url, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
body: JSON.stringify({ jsonrpc: "2.0", method, params, id: 1 }),
|
|
30
|
+
});
|
|
31
|
+
if (data.error) throw new Error(`RPC: ${data.error.message}`);
|
|
32
|
+
return data.result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── ETH Gas ────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
const ETH_RPCS = [
|
|
38
|
+
"https://eth.llamarpc.com",
|
|
39
|
+
"https://rpc.ankr.com/eth",
|
|
40
|
+
"https://ethereum-rpc.publicnode.com",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export async function fetchEthGasData(): Promise<MarketData> {
|
|
44
|
+
let lastError: Error | null = null;
|
|
45
|
+
|
|
46
|
+
for (const rpc of ETH_RPCS) {
|
|
47
|
+
try {
|
|
48
|
+
const [gasPrice, blockHex] = await Promise.all([
|
|
49
|
+
rpcCall(rpc, "eth_gasPrice"),
|
|
50
|
+
rpcCall(rpc, "eth_blockNumber"),
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const gasPriceGwei = Number(BigInt(gasPrice)) / 1e9;
|
|
54
|
+
const blockNumber = Number(BigInt(blockHex));
|
|
55
|
+
|
|
56
|
+
let baseFeeGwei: number | null = null;
|
|
57
|
+
try {
|
|
58
|
+
const block = await rpcCall(rpc, "eth_getBlockByNumber", ["latest", false]);
|
|
59
|
+
if (block?.baseFeePerGas) {
|
|
60
|
+
baseFeeGwei = Number(BigInt(block.baseFeePerGas)) / 1e9;
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
marketType: "eth_gas",
|
|
66
|
+
dataPoints: {
|
|
67
|
+
gas_price_gwei: Math.round(gasPriceGwei * 100) / 100,
|
|
68
|
+
base_fee_gwei: baseFeeGwei ? Math.round(baseFeeGwei * 100) / 100 : null,
|
|
69
|
+
block_number: blockNumber,
|
|
70
|
+
},
|
|
71
|
+
source: rpc,
|
|
72
|
+
fetchedAt: new Date().toISOString(),
|
|
73
|
+
};
|
|
74
|
+
} catch (err) {
|
|
75
|
+
lastError = err as Error;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`All ETH RPCs failed. Last: ${lastError?.message}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Crypto Prices (CoinGecko free API — no key) ───────────────────────────
|
|
83
|
+
|
|
84
|
+
const COINGECKO_IDS: Record<string, string> = {
|
|
85
|
+
btc: "bitcoin",
|
|
86
|
+
bitcoin: "bitcoin",
|
|
87
|
+
eth: "ethereum",
|
|
88
|
+
ethereum: "ethereum",
|
|
89
|
+
sol: "solana",
|
|
90
|
+
solana: "solana",
|
|
91
|
+
bnb: "binancecoin",
|
|
92
|
+
matic: "matic-network",
|
|
93
|
+
polygon: "matic-network",
|
|
94
|
+
avax: "avalanche-2",
|
|
95
|
+
dot: "polkadot",
|
|
96
|
+
link: "chainlink",
|
|
97
|
+
uni: "uniswap",
|
|
98
|
+
aave: "aave",
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export async function fetchCryptoPrice(symbol: string): Promise<MarketData> {
|
|
102
|
+
const id = COINGECKO_IDS[symbol.toLowerCase()];
|
|
103
|
+
if (!id) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Unknown symbol "${symbol}". Supported: ${Object.keys(COINGECKO_IDS).join(", ")}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const data = await jsonFetch(
|
|
110
|
+
`https://api.coingecko.com/api/v3/simple/price?ids=${id}&vs_currencies=usd&include_24hr_change=true&include_market_cap=true&include_24hr_vol=true`
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const coin = data[id];
|
|
114
|
+
if (!coin) throw new Error(`No price data for ${id}`);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
marketType: "crypto_price",
|
|
118
|
+
dataPoints: {
|
|
119
|
+
symbol: symbol.toUpperCase(),
|
|
120
|
+
price_usd: coin.usd,
|
|
121
|
+
change_24h_pct: coin.usd_24h_change ? Math.round(coin.usd_24h_change * 100) / 100 : null,
|
|
122
|
+
market_cap_usd: coin.usd_market_cap || null,
|
|
123
|
+
volume_24h_usd: coin.usd_24h_vol || null,
|
|
124
|
+
},
|
|
125
|
+
source: "api.coingecko.com",
|
|
126
|
+
fetchedAt: new Date().toISOString(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Auto-detect market type from question ─────────────────────────────────
|
|
131
|
+
|
|
132
|
+
export async function fetchMarketData(question: string): Promise<MarketData> {
|
|
133
|
+
const q = question.toLowerCase();
|
|
134
|
+
|
|
135
|
+
// Gas price questions
|
|
136
|
+
if (q.includes("gas") || q.includes("gwei")) {
|
|
137
|
+
return fetchEthGasData();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Crypto price questions — detect the token
|
|
141
|
+
for (const [symbol, _id] of Object.entries(COINGECKO_IDS)) {
|
|
142
|
+
if (q.includes(symbol.toLowerCase())) {
|
|
143
|
+
return fetchCryptoPrice(symbol);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Default: fetch both ETH gas and ETH price for general ETH questions
|
|
148
|
+
if (q.includes("eth")) {
|
|
149
|
+
return fetchEthGasData();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Fallback: try BTC if price is mentioned
|
|
153
|
+
if (q.includes("price") || q.includes("exceed") || q.includes("above") || q.includes("below")) {
|
|
154
|
+
// Try to find any crypto mention
|
|
155
|
+
return fetchEthGasData(); // Default to ETH gas
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return fetchEthGasData();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─── Format data for AI prompt ─────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
export function formatMarketDataForPrompt(data: MarketData): string {
|
|
164
|
+
const lines = [`Market type: ${data.marketType}`];
|
|
165
|
+
for (const [key, value] of Object.entries(data.dataPoints)) {
|
|
166
|
+
if (value !== null) {
|
|
167
|
+
lines.push(`${key}: ${value}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
lines.push(`Source: ${data.source}`);
|
|
171
|
+
lines.push(`Fetched at: ${data.fetchedAt}`);
|
|
172
|
+
return lines.join("\n");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
if (require.main === module) {
|
|
178
|
+
const query = process.argv[2] || "eth gas";
|
|
179
|
+
console.log(`Fetching data for: "${query}"\n`);
|
|
180
|
+
|
|
181
|
+
fetchMarketData(query)
|
|
182
|
+
.then((data) => console.log(JSON.stringify(data, null, 2)))
|
|
183
|
+
.catch((err) => {
|
|
184
|
+
console.error("Failed:", err.message);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
|
187
|
+
}
|