POST /api/v1/mev/wrap/order
Submit signed wrap/unwrap transaction with MEV-protected execution.
This endpoint submits your signed WETH wrap/unwrap transaction through MEV-protected routing to prevent frontrunning.
Endpoint Details
POST https://api.unikron.ch/api/v1/mev/wrap/orderAuthentication Required: Yes (API Key via X-API-Key header)
Request Parameters
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
quoteId | string | No | Quote ID from quote response (optional, for tracking) |
signedTransaction | string | Yes | Hex-encoded signed transaction (starting with 0x) |
providerMetadata | object | Yes | Provider metadata from quote (mevProtection.providerMetadata) |
operation | string | Yes | "wrap" or "unwrap" |
amount | string | No | Amount for tracking/logging (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
Provider Metadata Required
The providerMetadata object from the quote response is required for order submission. It contains the MEV Blocker endpoint configuration and routing information. Missing this will cause a 400 error.
Sign Transaction Parameters Exactly The signedTransaction must be signed
using exactly the transaction parameters from the quote response (with nonce
added). Do not modify any fields.
Set Nonce Before Signing The quote returns nonce: null. You must set the
nonce to the user’s current nonce before signing:
const nonce = await provider.getTransactionCount(userAddress);
txParams.nonce = nonce;
const signedTx = await signer.signTransaction(txParams);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 submitWrapOrder(quote: any, signer: ethers.Signer) {
// 1. Get current nonce
const nonce = await signer.provider.getTransactionCount(
await signer.getAddress()
);
// 2. Add nonce to transaction params
const txParams = {
...quote.mevProtection.transactionParams,
nonce,
};
// 3. Sign transaction
const signedTx = await signer.signTransaction(txParams);
// 4. Submit order
const response = await fetch(`${API_URL}/api/v1/mev/wrap/order`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
quoteId: quote.quoteId,
signedTransaction: signedTx,
providerMetadata: quote.mevProtection.providerMetadata, // Required!
operation: quote.operation,
amount: quote.amount,
}),
});
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.order;
}
// Example usage - Complete wrap flow
async function wrapEth(amountEth: string, signer: ethers.Signer) {
const userAddress = await signer.getAddress();
// Step 1: Get quote
const quoteResponse = await fetch(`${API_URL}/api/v1/mev/wrap/quote`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
operation: "wrap",
amount: amountEth,
userAddress,
chainId: 1,
}),
});
const { data } = await quoteResponse.json();
const quote = data.quote;
console.log(" Quote received:", quote.quoteId);
// Step 2: Submit order
const order = await submitWrapOrder(quote, signer);
console.log("\n=== Wrap Order Submitted ===");
console.log(`Operation: ${order.operation}`);
console.log(`Amount: ${order.amount} ETH`);
console.log(`TX Hash: ${order.txHash}`);
console.log(`Status: ${order.status}`);
console.log(`Provider: ${order.provider}`);
console.log(`Tracking URL: ${order.trackingUrl}`);
console.log(`Explorer: ${order.explorerUrl}`);
console.log(`Submitted: ${order.submittedAt}`);
return order;
}
// Execute wrap
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
wrapEth("1.0", wallet)
.then((order) => console.log("\n Wrap completed successfully"))
.catch((error) => console.error(" Wrap failed:", error));Response Format
Success Response (200 OK)
{
"ok": true,
"data": {
"order": {
"operation": "wrap",
"txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"from": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"amount": "1.0",
"status": "submitted",
"provider": "MEV Blocker",
"trackingUrl": "https://rpc.mevblocker.io/tx/0x1234567890abcdef...",
"explorerUrl": "https://etherscan.io/tx/0x1234567890abcdef...",
"submittedAt": "2024-01-15T10:30:00.000Z"
}
}
}Response Fields Reference
Order Object
| Field | Type | Description |
|---|---|---|
operation | string | ”wrap” or “unwrap” |
txHash | string | Transaction hash (use for tracking) |
from | string | Transaction sender address |
amount | string | Amount being wrapped/unwrapped (in ETH) |
status | string | Order status (always “submitted” initially) |
provider | string | MEV protection provider (e.g., “MEV Blocker”) |
trackingUrl | string | MEV Blocker tracking URL |
explorerUrl | string | Etherscan transaction URL |
submittedAt | string | Submission timestamp (ISO 8601) |
Error Responses
400 Bad Request - Missing Parameter
{
"ok": false,
"error": "MISSING_PARAMETER",
"message": "signedTransaction is required",
"details": {
"field": "signedTransaction",
"required": true
}
}400 Bad Request - Invalid Signature
{
"ok": false,
"error": "INVALID_SIGNATURE",
"message": "Transaction signature is invalid",
"details": {
"reason": "Failed to recover signer address"
}
}400 Bad Request - Nonce Too Low
{
"ok": false,
"error": "NONCE_TOO_LOW",
"message": "Transaction nonce has already been used",
"details": {
"provided": 35,
"expected": 36
},
"suggestion": "Get fresh quote and use current nonce"
}400 Bad Request - Insufficient Balance
{
"ok": false,
"error": "INSUFFICIENT_BALANCE",
"message": "Insufficient ETH balance for wrap operation",
"details": {
"required": "1000000000000000000",
"available": "500000000000000000"
}
}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 | NONCE_TOO_LOW | Transaction already mined | Use current nonce |
| 400 | INSUFFICIENT_BALANCE | Not enough ETH/WETH | Check wallet balance |
| 400 | INVALID_TRANSACTION | Transaction params invalid | Use exact params from quote |
| 401 | UNAUTHORIZED | Invalid/missing API key | Check X-API-Key header |
| 422 | VALIDATION_ERROR | 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 |
Transaction Monitoring
Track Your Transaction: After submission, monitor transaction status
using: 1. MEV Blocker Tracking: order.trackingUrl 2. Etherscan:
order.explorerUrl 3. RPC Provider: Poll with order.txHash
// Monitor transaction confirmation
async function waitForConfirmation(txHash: string, provider: ethers.Provider) {
console.log(`⏳ Waiting for confirmation: ${txHash}`);
const receipt = await provider.waitForTransaction(txHash, 1); // 1 confirmation
if (receipt.status === 1) {
console.log(" Transaction confirmed!");
console.log(`Block: ${receipt.blockNumber}`);
console.log(`Gas used: ${receipt.gasUsed.toString()}`);
} else {
console.log(" Transaction failed");
}
return receipt;
}
// Usage
const order = await submitWrapOrder(quote, signer);
await waitForConfirmation(order.txHash, provider);Complete Wrap/Unwrap Flow
Step-by-Step Process:
- Get Quote: POST /api/v1/mev/wrap/quote
- Get Nonce: Fetch user’s current transaction nonce
- Sign Transaction: Add nonce and sign transaction params
- Submit Order: POST /api/v1/mev/wrap/order (this endpoint)
- Monitor: Track transaction until confirmed
// Complete flow example
async function completeWrapFlow(amountEth: string, signer: ethers.Signer) {
try {
// 1. Get quote
const quote = await getWrapQuote(
"wrap",
amountEth,
await signer.getAddress()
);
console.log(" Step 1: Quote received");
// 2. Get nonce
const nonce = await signer.provider.getTransactionCount(
await signer.getAddress()
);
console.log(` Step 2: Nonce retrieved (${nonce})`);
// 3. Sign transaction
const txParams = { ...quote.mevProtection.transactionParams, nonce };
const signedTx = await signer.signTransaction(txParams);
console.log(" Step 3: Transaction signed");
// 4. Submit order
const order = await submitWrapOrder(quote, signer);
console.log(" Step 4: Order submitted");
console.log(` TX Hash: ${order.txHash}`);
// 5. Monitor confirmation
await waitForConfirmation(order.txHash, signer.provider);
console.log(" Step 5: Transaction confirmed");
return order;
} catch (error) {
console.error(" Wrap flow failed:", error);
throw error;
}
}Best Practices
Transaction Signing
Never Modify Transaction Parameters Sign the transaction using exactly the parameters from the quote (with nonce added). Any modification will cause the transaction to fail.
// CORRECT - Using transactionParams
const txParams = { ...quote.mevProtection.transactionParams };
txParams.nonce = await signer.provider.getTransactionCount(userAddress);
const signedTx = await signer.signTransaction(txParams);
// Then submit with providerMetadata
const orderPayload = {
quoteId: quote.quoteId,
signedTransaction: signedTx,
providerMetadata: quote.mevProtection.providerMetadata, // Required!
operation: quote.operation,
amount: quote.amount,
};
// WRONG - Don't modify
const txParams = { ...quote.mevProtection.transactionParams };
txParams.gasLimit = "100000"; // Don't do this!
txParams.nonce = await signer.provider.getTransactionCount(userAddress);
// WRONG - Missing providerMetadata
const orderPayload = {
signedTransaction: signedTx,
operation: "wrap",
// Missing providerMetadata - will fail with 400 error!
};Error Handling
try {
const order = await submitWrapOrder(quote, signer);
} catch (error) {
if (error.message.includes("INSUFFICIENT_BALANCE")) {
console.log("Not enough ETH/WETH in wallet");
} else if (error.message.includes("NONCE_TOO_LOW")) {
console.log("Nonce already used, get fresh quote");
} else if (error.message.includes("INVALID_SIGNATURE")) {
console.log("Check signing configuration");
} else {
console.error("Order submission failed:", error);
}
}Gas Estimation
Gas Costs:
- Wrap (ETH → WETH): Approx 46,000 gas (Approx $1-3 at 50 gwei)
- Unwrap (WETH → ETH): Approx 30,000 gas (Approx $0.5-2 at 50 gwei)
Gas estimates include MEV protection overhead.
Related Endpoints
Related Operations:
- POST /api/v1/mev/wrap/quote - Get wrap/unwrap quote
- GET /api/v1/quote - Get swap quote
- POST /api/v1/mev/orders - Submit swap order