Submit AI task din dApp
Exemplu complet de cum un dApp poate folosi rețeaua de mineri OmbraChain pentru a procesa task-uri AI fără să ruleze propriul GPU sau să plătească Anthropic/OpenAI direct.
Use case
DApp-ul tău (ex: "ChatBot decentralizat") vrea ca user-ii să întrebe AI fără ca tu (dev) să plătești factura. User-ul plătește direct cu OMBRA, miner-ul procesează, validatorul atestă.
Flow general
┌──────┐ submitTask ┌─────────┐
│ User │ ───────────────→ │ Mainnet │
│ │ prompt + fee │ mempool │
└──────┘ └─────────┘
│
▼
┌───────────┐
│ Miner │ → procesează cu Claude/GPT/Ollama
│ (random) │
└───────────┘
│
▼
┌───────────┐
│ Validator │ → atestă rezultat
└───────────┘
│
▼
┌──────┐ getTask ┌───────────┐
│ User │ ←──────────────── │ Result │
└──────┘ poll/event │ on-chain │
└───────────┘
Cod complet (React)
import { useState } from "react";
export function AIChat() {
const [prompt, setPrompt] = useState("");
const [taskId, setTaskId] = useState<string | null>(null);
const [result, setResult] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function submit() {
if (!window.ombra) return alert("Install Ombra Wallet ext.");
setLoading(true);
// 1. Connect dacă nu e deja
await window.ombra.request({ method: "ombra_connect" });
// 2. Submit task
const { taskId } = await window.ombra.request({
method: "ombra_submitTask",
params: {
prompt,
taskType: "chat",
fee: "500000", // 0.5 OMBRA
maxTokens: 2000
}
});
setTaskId(taskId);
// 3. Poll pentru rezultat (every 5s)
const interval = setInterval(async () => {
const task = await window.ombra.request({
method: "ombra_getTask",
params: { taskId }
});
if (task.status === "completed") {
setResult(task.result);
setLoading(false);
clearInterval(interval);
} else if (task.status === "failed") {
alert("Task failed: " + task.error);
setLoading(false);
clearInterval(interval);
}
}, 5000);
}
return (
<div>
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Întreabă orice..."
/>
<button onClick={submit} disabled={loading}>
{loading ? "Procesare..." : "Submit (0.5 OMBRA)"}
</button>
{taskId && <p>Task ID: {taskId}</p>}
{result && <pre>{result}</pre>}
</div>
);
}
Parametrii ombra_submitTask
| Parametru | Tip | Required | Descriere |
|---|---|---|---|
prompt | string | da | Text-ul promptului AI |
taskType | "chat" | "code" | "analysis" | da | Hint pentru routing miner |
fee | string (micro-OMBRA) | da | Min 500000 (0.5 OMBRA), recomandat 1000000+ |
maxTokens | number | nu | Cap pe output (default 4000) |
model | string | nu | Hint preferințe model (ex: "claude-opus") |
Cost real
Fee-ul tău (ex: 0.5 OMBRA) se distribuie:
| Destinație | % | Cantitate (la 0.5 OMBRA) |
|---|---|---|
| Miner (cel care procesează) | 70% | 0.35 OMBRA |
| Burn (deflație) | 25% | 0.125 OMBRA |
| Validator (atestă blocul) | 5% | 0.025 OMBRA |
În plus, miner-ul primește reward natural (output+thinking)/1M OMBRA — dar NU iese din buzunarul tău, e nou-emis on-chain.
Async vs sync
Pentru UX bun:
- Polling (every 5s) e simplu dar generează request-uri inutile
- WebSocket subscription (mai performant):
const ws = new WebSocket("wss://api.ombra-net.com/ws");
ws.send(JSON.stringify({ type: "subscribe_task", taskId }));
ws.onmessage = (event) => {
const { status, result } = JSON.parse(event.data);
if (status === "completed") setResult(result);
};
Error handling
Task-ul poate fail cu:
INSUFFICIENT_BALANCE— user nu are destul OMBRAINVALID_PROMPT— prompt gol sau prea lung (>10K chars)NO_MINERS_AVAILABLE— niciun miner online (rar)TIMEOUT— niciun miner nu a răspuns în 5 minuteALL_MINERS_REJECTED— output validators marcat invalid de >3 mineri
try {
await window.ombra.request({ method: "ombra_submitTask", params: { ... } });
} catch (err) {
if (err.code === "INSUFFICIENT_BALANCE") {
alert("Nu ai destul OMBRA. Top up wallet.");
} else {
console.error(err);
}
}
Best practices
-
Verifică balance înainte de submit:
const balance = BigInt(await window.ombra.request({ method: "ombra_balance" }));if (balance < 500000n) return alert("Top up wallet"); -
Cache rezultate — task-urile identice nu se cache-uiesc on-chain (e wasteful să re-submit același prompt)
-
Fee dinamic — pentru task-uri urgente, fee mai mare = prioritate mempool mai mare
const urgentFee = "2000000"; // 2 OMBRA pentru priority -
maxTokens conservator — limitează
maxTokenspentru predictabilitate cost