The @yoorquezt/sdk-mev package provides TypeScript clients for both the Q AI API and the MEV Gateway WebSocket.
Installation
npm install @yoorquezt/sdk-mev
# or
yarn add @yoorquezt/sdk-mev
# or
pnpm add @yoorquezt/sdk-mev
QMEVClient
The QMEVClient connects to the Q AI API for natural language chat and tool access.
import { QMEVClient } from "@yoorquezt/sdk-mev";
const client = new QMEVClient({
baseUrl: "http://localhost:9100",
apiKey: "your-api-key",
role: "operator",
});
chat
Send a message and receive a complete response.
const response = await client.chat("What's the profit from the last 24 hours?");
console.log(response.content);
// "Total profit (24h): 2.847 ETH across 142 landed bundles."
console.log(response.toolsUsed);
// ["analyst_profit"]
chatStream
Stream a response token-by-token.
const stream = client.chatStream("Analyze relay performance");
for await (const event of stream) {
switch (event.type) {
case "content_delta":
process.stdout.write(event.delta);
break;
case "tool_use":
console.log(`\n[Using tool: ${event.tool}]`);
break;
case "tool_result":
console.log(`[Tool result: ${event.tool}]`);
break;
case "confirmation_required":
console.log(`\nAction requires confirmation: ${event.message}`);
break;
case "message_end":
console.log(`\n[Tokens: ${event.usage.inputTokens} in, ${event.usage.outputTokens} out]`);
break;
}
}
listTools
List all tools available to your role.
const tools = await client.listTools();
for (const tool of tools) {
console.log(`${tool.name} (${tool.category}) - ${tool.description}`);
if (tool.mutating) console.log(" [MUTATING - requires confirmation]");
}
health
Check Q AI API health.
const health = await client.health();
console.log(health.status); // "healthy"
MEVGatewayClient
The MEVGatewayClient connects to the MEV Gateway via WebSocket for direct engine interaction.
import { MEVGatewayClient } from "@yoorquezt/sdk-mev";
const gw = new MEVGatewayClient("ws://localhost:9099", {
apiKey: "your-api-key",
});
submitBundle
const result = await gw.submitBundle({
txs: ["0xsigned_tx_1", "0xsigned_tx_2"],
blockNumber: 19482300,
chain: "ethereum",
});
console.log(result.bundleHash); // "0x9f8e7d..."
console.log(result.status); // "pending"
getBundleStatus
const status = await gw.getBundleStatus("0x9f8e7d...");
console.log(status.status); // "landed"
console.log(status.block); // 19482301
console.log(status.profit); // "0.0312 ETH"
simulateBundle
const sim = await gw.simulateBundle({
txs: ["0xsigned_tx_1", "0xsigned_tx_2"],
blockNumber: 19482300,
chain: "ethereum",
});
console.log(sim.success); // true
console.log(sim.gasUsed); // 248000
console.log(sim.profit); // "0.0312 ETH"
console.log(sim.logs); // [...transaction logs]
getMempoolSnapshot
const mempool = await gw.getMempoolSnapshot();
console.log(mempool.pending); // 142
console.log(mempool.txs); // [...pending transactions]
getRelayStats
const stats = await gw.getRelayStats();
for (const relay of stats.relays) {
console.log(`${relay.name}: ${relay.status} (${relay.latencyMs}ms)`);
}
getOFAStats
const ofa = await gw.getOFAStats();
console.log(ofa.protectedTxs); // 1423
console.log(ofa.totalRebates); // "12.34 ETH"
getProfitHistory
const history = await gw.getProfitHistory({
range: "7d",
chain: "ethereum",
groupBy: "day",
});
for (const entry of history) {
console.log(`${entry.date}: ${entry.profitEth} ETH (${entry.bundles} bundles)`);
}
subscribe
Subscribe to real-time events.
const sub = await gw.subscribe("auction");
sub.on("data", (event) => {
console.log("Auction event:", event.type, event);
});
sub.on("error", (err) => {
console.error("Subscription error:", err);
});
// Unsubscribe
await sub.unsubscribe();
Available topics: auction, bundles, blocks, mempool, protection.
Types
| Type | Description |
|---|---|
QMEVClientConfig | Configuration for QMEVClient (baseUrl, apiKey, role, model) |
ChatResponse | Response from chat() (id, content, toolsUsed, usage) |
StreamEvent | Streaming event (content_delta, tool_use, tool_result, etc.) |
Tool | Tool definition (name, description, category, mutating, parameters) |
BundleSubmitRequest | Bundle submission parameters |
BundleSubmitResult | Bundle submission result |
BundleStatus | Bundle status response |
SimulationResult | Bundle simulation result |
MempoolSnapshot | Mempool state snapshot |
RelayStats | Relay statistics |
OFAStats | OFA protection statistics |
ProfitEntry | Profit history entry |
SubscriptionEvent | Real-time subscription event |
Error Handling
The SDK throws QMEVError for all error conditions:
import { QMEVError } from "@yoorquezt/sdk-mev";
try {
await gw.getBundleStatus("0xinvalid");
} catch (err) {
if (err instanceof QMEVError) {
console.error(`Error ${err.code}: ${err.message}`);
// Error 1002: Bundle not found
switch (err.code) {
case 1001: // Engine unavailable
console.log("Retry later");
break;
case 1002: // Not found
console.log("Resource does not exist");
break;
case 1003: // Rate limited
console.log(`Retry after ${err.retryAfter}s`);
break;
}
}
}
Full Example
import { QMEVClient, MEVGatewayClient } from "@yoorquezt/sdk-mev";
async function main() {
// Q AI for natural language
const ai = new QMEVClient({
baseUrl: "http://localhost:9100",
apiKey: process.env.YQMEV_API_KEY!,
role: "operator",
});
// Gateway for direct engine access
const gw = new MEVGatewayClient("ws://localhost:9099", {
apiKey: process.env.YQMEV_API_KEY!,
});
// Check health
const health = await ai.health();
console.log("Q AI:", health.status);
// Ask Q AI about profit
const response = await ai.chat("Show me today's profit by strategy");
console.log(response.content);
// Stream a complex query
const stream = ai.chatStream("Generate a forensic report for the last 100 blocks");
for await (const event of stream) {
if (event.type === "content_delta") {
process.stdout.write(event.delta);
}
}
// Direct bundle submission via gateway
const result = await gw.submitBundle({
txs: ["0xsigned_tx_1", "0xsigned_tx_2"],
blockNumber: 19482300,
chain: "ethereum",
});
console.log("\nBundle submitted:", result.bundleHash);
// Watch for the bundle to land
const sub = await gw.subscribe("bundles");
sub.on("data", (event) => {
if (event.bundleHash === result.bundleHash) {
console.log("Bundle status:", event.status);
if (event.status === "landed" || event.status === "failed") {
sub.unsubscribe();
}
}
});
}
main().catch(console.error);