Skip to Content
API ReferenceSubmit Order

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/orders

Authentication Required: Yes (API Key via X-API-Key header)


Request Parameters

Request Body (JSON)

ParameterTypeRequiredDescription
orderParamsobjectYesOrder parameters from quote (see below)
signedTransactionstringYesRLP-encoded signed transaction (hex string starting with 0x)
providerMetadataobjectYesProvider metadata from quote (mevProtection.providerMetadata)

Order Parameters Object

FieldTypeRequiredDescription
fromTokenstringYesToken address to sell (EIP-55 checksummed)
toTokenstringYesToken address to buy (EIP-55 checksummed)
amountstringYesSell amount in smallest units
minReturnAmountstringYesMinimum buy amount (from quote’s minBuyAmount)
userAddressstringYesUser’s wallet address
chainIdnumberYesChain ID (1 for Ethereum mainnet)
deadlinenumberYesUnix timestamp deadline (quote expiry)
slippageBpsnumberYesSlippage in basis points (50 = 0.5%)
selectedTiernumberYesMEV protection tier (2 = MEV Blocker)
threatAnalysisobjectYesMEV analysis from quote
transactionValueUsdnumberYesTransaction value in USD
dexRouteobjectYesDEX route from quote
referrerstringNoReferrer identifier (optional)

Headers

HeaderRequiredDescriptionExample
X-API-KeyYesYour UNIKRON API keyuk_live_abc123...
Content-TypeYesMust be application/jsonapplication/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 / 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

FieldTypeDescription
orderIdstringUnique order identifier (use for status polling)
providerstringMEV protection provider name (e.g., “MEV Blocker”)
tiernumberProtection tier (2, 4, etc.)
statusstringOrder status (submitted, pending, mining, etc.)
submittedAtstringISO 8601 submission timestamp
expiresAtstringISO 8601 expiration timestamp
estimatedExecutionTimestringHuman-readable estimated time to confirmation
trackingUrlstringExternal MEV Blocker tracking URL
providerOrderIdstringProvider’s internal order identifier
txHashstringTransaction hash (use for blockchain explorers)

Tracking Object

FieldTypeDescription
statusEndpointstringAPI endpoint to poll for order status
pollingIntervalnumberRecommended 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

HTTPError CodeReasonSolution
400MISSING_PARAMETERRequired field missingInclude all required fields
400INVALID_SIGNATURETransaction signature invalidRe-sign with correct private key
400INSUFFICIENT_ALLOWANCEToken not approvedApprove token for router address
400INSUFFICIENT_BALANCENot enough tokensCheck user token balance
400DEADLINE_EXPIREDQuote deadline passedGet fresh quote
409NONCE_TOO_LOWTransaction already minedGet fresh quote with updated nonce
409SIGNATURE_REPLAYSignature already usedGet new quote and sign again
422VALIDATION_ERROROrder params don’t match quoteUse exact params from quote
429RATE_LIMIT_EXCEEDEDToo many requestsWait and retry with backoff
503PROVIDER_UNAVAILABLEMEV Blocker temporarily downRetry after a few seconds

Order Status Flow

After submitting an order, it goes through these states: submitted → pending → mining → confirmed ↓ failed

Status Descriptions

StatusDescriptionNext Action
submittedOrder received by UNIKRON APIWait for pending
pendingTransaction sent to MEV BlockerWait for mining
miningTransaction included in block builder auctionWait for confirmed
confirmedTransaction mined successfullyDone - order complete
failedTransaction reverted or rejectedCheck 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.expiresAt timestamp
  • If order expires, get a new quote and resubmit

Next Steps


Need Help?

Last updated on