POST /api/v1/mev/orders
Submit a signed transaction for MEV-protected execution through private mempool routing.
This endpoint submits your signed transaction through MEV-protected routing to prevent frontrunning and sandwich attacks.
Transaction Signing
Sign the transaction using quote.mevProtection.transactionParams and preserve quote.mevProtection.providerMetadata for order submission. Both are required.
Endpoint Details
POST https://api.unikron.ch/api/v1/mev/ordersAuthentication Required: Yes (API Key via X-API-Key header)
Request Parameters
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
orderParams | object | Yes | Order parameters from quote (see below) |
signedTransaction | string | Yes | RLP-encoded signed transaction (hex string starting with 0x) |
providerMetadata | object | Yes | Provider metadata from quote (mevProtection.providerMetadata) |
Order Parameters Object
| Field | Type | Required | Description |
|---|---|---|---|
fromToken | string | Yes | Token address to sell (EIP-55 checksummed) |
toToken | string | Yes | Token address to buy (EIP-55 checksummed) |
amount | string | Yes | Sell amount in smallest units |
minReturnAmount | string | Yes | Minimum buy amount (from quote’s minBuyAmount) |
userAddress | string | Yes | User’s wallet address |
chainId | number | Yes | Chain ID (1 for Ethereum mainnet) |
deadline | number | Yes | Unix timestamp deadline (quote expiry) |
slippageBps | number | Yes | Slippage in basis points (50 = 0.5%) |
selectedTier | number | Yes | MEV protection tier (2 = MEV Blocker) |
threatAnalysis | object | Yes | MEV analysis from quote |
transactionValueUsd | number | Yes | Transaction value in USD |
dexRoute | object | Yes | DEX route from quote |
referrer | string | No | Referrer identifier (optional) |
Headers
| Header | Required | Description | Example |
|---|---|---|---|
X-API-Key | Yes | Your UNIKRON API key | uk_live_abc123... |
Content-Type | Yes | Must be application/json | application/json |
Important Requirements
Sign Transaction Parameters Exactly
The signedTransaction must be signed using exactly the transaction parameters from quote.mevProtection.transactionParams. Do not modify any fields (to, data, value, gasLimit, etc.) before signing.
Important: You must also include providerMetadata from quote.mevProtection.providerMetadata in your order submission. This contains the MEV Blocker endpoint configuration, referral codes, and rebate settings.
Token Approval Required Before submitting an order, ensure the sell token
is approved for the router address: javascript await tokenContract.approve(quote.dexRoute.target, amount);
Fresh Quotes Only Quotes expire after ~30 seconds. Always request a fresh quote immediately before submitting an order.
Code Examples
JavaScript
JavaScript / TypeScript
import { ethers } from "ethers";
const API_KEY = process.env.UNIKRON_API_KEY;
const API_URL = "https://api.unikron.ch";
async function submitOrder(quote: any, signer: ethers.Signer) {
// 1. Sign transaction with exact parameters from quote
const signedTx = await signer.signTransaction(
quote.mevProtection.transactionParams
);
// 2. Build threat analysis from MEV analysis
const threatAnalysis = {
threatScore: quote.mevAnalysis?.threatLevel === "HIGH" ? 0.8 :
quote.mevAnalysis?.threatLevel === "MEDIUM" ? 0.5 : 0.2,
threatLevel: quote.mevAnalysis?.threatLevel || "LOW",
confidence: 0.8,
riskFactors: [],
estimatedSavings: {
estimatedProtection: {
amount: quote.mevAnalysis?.estimatedProtectionUsd || 0,
currency: "USD"
}
},
timestamp: Date.now()
};
// 3. Prepare order parameters
const orderParams = {
fromToken: quote.sellToken,
toToken: quote.buyToken,
amount: quote.sellAmount,
minReturnAmount: quote.minBuyAmount,
userAddress: await signer.getAddress(),
chainId: quote.mevProtection.transactionParams.chainId,
deadline: Math.floor(Date.now() / 1000) + 1800, // 30 min from now
slippageBps: 50, // 0.5%
selectedTier: 2, // MEV Blocker
threatAnalysis: threatAnalysis,
transactionValueUsd: quote.scoring.netValueUsd,
dexRoute: {
source: quote.source,
target: quote.dexRoute.target,
estimatedGas: quote.estimatedGas,
data: quote.dexRoute.data,
},
};
// 4. Submit order with providerMetadata
const response = await fetch(`${API_URL}/api/v1/mev/orders`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
orderParams,
signedTransaction: signedTx,
providerMetadata: quote.mevProtection.providerMetadata, // Required!
}),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`HTTP ${response.status}: ${error}`);
}
const { ok, data } = await response.json();
if (!ok) {
throw new Error(`API Error: ${data.error}`);
}
return data;
}
// Example usage
const quote = await getQuote(...); // From previous step
const order = await submitOrder(quote, signer);
console.log("=== Order Submitted ===");
console.log(`Order ID: ${order.order.orderId}`);
console.log(`Provider: ${order.order.provider}`);
console.log(`Status: ${order.order.status}`);
console.log(`Transaction: ${order.order.txHash}`);
console.log(`Tracking: ${order.order.trackingUrl}`);
console.log(`\nPoll status at: ${order.tracking.statusEndpoint}`);
console.log(`Poll interval: ${order.tracking.pollingInterval}s`);Response Format
Success Response (200 OK)
{
"ok": true,
"data": {
"order": {
"orderId": "ord_abc123xyz",
"provider": "MEV Blocker",
"tier": 2,
"status": "submitted",
"submittedAt": "2025-11-04T13:48:40.123Z",
"expiresAt": "2025-11-04T14:18:40.123Z",
"estimatedExecutionTime": "15-45 seconds",
"trackingUrl": "https://rpc.mevblocker.io/tx/0x1234...",
"providerOrderId": "mev_blocker_xyz789",
"txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
},
"tracking": {
"statusEndpoint": "/api/v1/mev/orders/ord_abc123xyz",
"pollingInterval": 10
}
}
}Response Fields Reference
Order Object
| Field | Type | Description |
|---|---|---|
orderId | string | Unique order identifier (use for status polling) |
provider | string | MEV protection provider name (e.g., “MEV Blocker”) |
tier | number | Protection tier (2, 4, etc.) |
status | string | Order status (submitted, pending, mining, etc.) |
submittedAt | string | ISO 8601 submission timestamp |
expiresAt | string | ISO 8601 expiration timestamp |
estimatedExecutionTime | string | Human-readable estimated time to confirmation |
trackingUrl | string | External MEV Blocker tracking URL |
providerOrderId | string | Provider’s internal order identifier |
txHash | string | Transaction hash (use for blockchain explorers) |
Tracking Object
| Field | Type | Description |
|---|---|---|
statusEndpoint | string | API endpoint to poll for order status |
pollingInterval | number | Recommended polling interval (in seconds) |
Error Responses
400 Bad Request - Missing Parameters
{
"ok": false,
"error": "MISSING_PARAMETER",
"message": "orderParams is required",
"details": {
"field": "orderParams",
"required": true
}
}400 Bad Request - Invalid Signature
{
"ok": false,
"error": "INVALID_SIGNATURE",
"message": "Transaction signature is invalid",
"details": {
"reason": "Recovered address does not match userAddress"
}
}400 Bad Request - Missing Allowance
{
"ok": false,
"error": "INSUFFICIENT_ALLOWANCE",
"message": "Token allowance is insufficient",
"details": {
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"router": "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5",
"required": "1000000000000000000",
"current": "0"
},
"suggestion": "Approve token before submitting order"
}409 Conflict - Nonce Too Low
{
"ok": false,
"error": "NONCE_TOO_LOW",
"message": "Transaction nonce has already been used",
"details": {
"provided": 35,
"expected": 36
},
"suggestion": "Get a fresh quote with updated nonce"
}Common Error Codes
| HTTP | Error Code | Reason | Solution |
|---|---|---|---|
| 400 | MISSING_PARAMETER | Required field missing | Include all required fields |
| 400 | INVALID_SIGNATURE | Transaction signature invalid | Re-sign with correct private key |
| 400 | INSUFFICIENT_ALLOWANCE | Token not approved | Approve token for router address |
| 400 | INSUFFICIENT_BALANCE | Not enough tokens | Check user token balance |
| 400 | DEADLINE_EXPIRED | Quote deadline passed | Get fresh quote |
| 409 | NONCE_TOO_LOW | Transaction already mined | Get fresh quote with updated nonce |
| 409 | SIGNATURE_REPLAY | Signature already used | Get new quote and sign again |
| 422 | VALIDATION_ERROR | Order params don’t match quote | Use exact params from quote |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests | Wait and retry with backoff |
| 503 | PROVIDER_UNAVAILABLE | MEV Blocker temporarily down | Retry after a few seconds |
Order Status Flow
After submitting an order, it goes through these states: submitted → pending → mining → confirmed ↓ failed
Status Descriptions
| Status | Description | Next Action |
|---|---|---|
submitted | Order received by UNIKRON API | Wait for pending |
pending | Transaction sent to MEV Blocker | Wait for mining |
mining | Transaction included in block builder auction | Wait for confirmed |
confirmed | Transaction mined successfully | Done - order complete |
failed | Transaction reverted or rejected | Check error reason, retry |
Integration Workflow
Complete Flow
// 1. Get quote
const quote = await getQuote(...);
// 2. Check and approve token (if needed)
const allowance = await token.allowance(userAddress, quote.dexRoute.target);
if (allowance < sellAmount) {
const approveTx = await token.approve(quote.dexRoute.target, ethers.MaxUint256);
await approveTx.wait(); // Wait for approval to confirm
}
// 3. Sign transaction
const signedTx = await signer.signTransaction(
quote.mevProtection.transactionParams
);
// 4. Submit order
const order = await submitOrder(quote, signer);
// 5. Poll for status
const finalStatus = await waitForConfirmation(order.order.orderId);Best Practices
Transaction Signing and Provider Metadata
Never Modify Transaction Parameters
Sign the transaction using exactly the parameters from quote.mevProtection.transactionParams. Any modification will cause the transaction to fail.
Critical: Always include providerMetadata from quote.mevProtection.providerMetadata when submitting orders. This contains the MEV Blocker endpoint configuration, referral codes, and rebate settings.
// ✅ CORRECT
const signedTx = await signer.signTransaction(
quote.mevProtection.transactionParams
);
const orderPayload = {
orderParams: { /* ... */ },
signedTransaction: signedTx,
providerMetadata: quote.mevProtection.providerMetadata, // Required!
};
// ❌ WRONG - Don't modify transaction params
const modifiedParams = { ...quote.mevProtection.transactionParams };
modifiedParams.gasLimit = "600000"; // Don't do this!
const signedTx = await signer.signTransaction(modifiedParams);
// ❌ WRONG - Missing providerMetadata
const orderPayload = {
orderParams: { /* ... */ },
signedTransaction: signedTx,
// Missing providerMetadata - order will fail!
};Threat Analysis Construction
The threatAnalysis field must be properly constructed from the mevAnalysis data in the quote:
const threatAnalysis = {
threatScore: quote.mevAnalysis?.threatLevel === "HIGH" ? 0.8 :
quote.mevAnalysis?.threatLevel === "MEDIUM" ? 0.5 : 0.2,
threatLevel: quote.mevAnalysis?.threatLevel || "LOW",
confidence: 0.8,
riskFactors: [],
estimatedSavings: {
estimatedProtection: {
amount: quote.mevAnalysis?.estimatedProtectionUsd || 0,
currency: "USD"
}
},
timestamp: Date.now()
};Error Handling
try {
const order = await submitOrder(quote, signer);
} catch (error) {
if (error.message.includes('INSUFFICIENT_ALLOWANCE')) {
// Token not approved
console.log('Please approve token first');
} else if (error.message.includes('NONCE_TOO_LOW')) {
// Stale quote
console.log('Quote expired, getting fresh quote...');
const newQuote = await getQuote(...);
} else if (error.message.includes('INSUFFICIENT_BALANCE')) {
// Not enough tokens
console.log('Insufficient token balance');
} else {
// Other errors
console.error('Order submission failed:', error);
}
}Order Expiry
Expiration Policy
- Orders expire after 30 minutes by default
- Always check
order.expiresAttimestamp - If order expires, get a new quote and resubmit
Related Endpoints
Next Steps
-
GET /api/v1/quote - Get quote before submitting order
-
GET /api/v1/mev/orders/:orderId - Poll order status
-
GET /api/v1/mev/orders - Get all user orders