KIP 249: Slot-Based Auction on Kaia
Author | Joseph, Lewis, Lake |
---|---|
Discussions-To | https://devforum.kaia.io/t/discussion-on-kip-249-slot-based-auction-on-kaia/8075 |
Status | Draft |
Type | Core |
Created | 2025-02-24 |
Simple Summary
This KIP defines a slot-based mechanism in Kaia, where the auction-winning call is placed immediately after its target transaction to capture backrunning MEV. Searchers deposit funds in a vault, ensuring coverage for bid + gas, and an AuctionEntryPoint contract handles bid payment, execution of backrun logic, and gas refunds atomically. Collected fees are routed to a governance-controlled vault, providing transparency and flexibility in distributing MEV proceeds.
Abstract
MEV (Maximal Extractable Value) commonly arises when a backrun exploits state changes immediately after a target transaction. This KIP enforces a slot-based adjacency, ensuring the winning call is placed directly after its target in a block, minimizing reorder opportunities. Searchers maintain a deposit in a vault, guaranteeing coverage for both bid and gas. They submit off-chain bids to an Auctioneer, and the final transaction is crafted and signed by the block proposer, enforcing adjacency. An AuctionEntryPoint contract handles the atomic collection of bids, execution of backrun logic, and gas reimbursement in a single transaction. Any fees collected are routed to a governance-controlled AuctionFeeVault
, enabling Kaia to capture and distribute MEV proceeds transparently.
Motivation
Currently, the MEV on Kaia network has the following issues:
- The profit generated from MEV is centralized.
- The spamming transactions inefficiently congest the network.
Also, during transition to the permissionless network, we’d expect these issues will have even more impact. To address these, we need a structured MEV approach so we can enhance user experience and decentralization while maintaining a sustainable and stable network.
Specification
Key Mechanisms
-
Hidden Bidding
- Searchers privately submit bids to the off-chain Auctioneer.
- The Auctioneer chooses the highest valid bid and sends the auction result to the validators.
-
Slot-Based Adjacency
- Ensures if a target transaction is placed at index i, the backrun (
AuctionEntryPoint.call
) call is at i+1. - Reduces reorder exploits by preventing free-form placement of backrun transactions elsewhere in the block.
- If no target transaction is found, the backrun transaction will be dropped.
- Ensures if a target transaction is placed at index i, the backrun (
-
Bundle
- The winner’s bid will be signed by proposer and bundled based on the KIP-245.
- Bypasses the block generation limit during the execution of BidTx.
- Discard the bundle if it encounters revert during the execution of BidTx to prevent balance loss of proposer.
-
Deposit & Cooldown
- A dedicated
AuctionDepositVault
requires each searcher to lock up enough funds for their bid and gas. - A cooldown on withdrawals prevents immediate removal of deposits, enforcing fund coverage for the bids.
- A dedicated
-
Governance-Controlled Fees
- Proceeds from winning bids go into an
AuctionFeeVault
. - Kaia’s on-chain governance decides the split (e.g., treasury vs. proposer) and any further uses of these funds.
- Proceeds from winning bids go into an
Terminology
Term | Description |
---|---|
Slot | The position immediately following a target transaction in a block’s transaction list. |
Target Tx | A user transaction that triggers a backrun opportunity. |
Auctioneer | Off-chain entity coordinating hidden bids among searchers and picking the winning call. |
Searcher | A participant who provides a deposit and hidden bid, including the target reference, to the Auctioneer. |
AuctionEntryPoint | A contract that handles bid payment, execution of backrun logic, and gas refunds atomically. |
AuctionDepositVault | A contract storing each searcher’s deposit, allowing a two-step withdrawal. |
AuctionFeeVault | A governance-owned contract capturing the bid amounts. |
Auction Rules
Hidden Bidding
- Searchers sign their intended
bid
offline. - The Auctioneer selects the highest valid bid, then sends the winning bid to the proposer for forming one transaction calling
AuctionEntryPoint
in the final block.
Searcher Bid Limit
- Each searcher may place one winning valid bid per block.
- Any additional bids submitted by the same searcher address for the same block will be disregarded by the Auctioneer and proposer.
Slot Concept
- If the targetTx is at index i, the
AuctionEntryPoint
call is placed at i+1.
Target and Backrun Adjacency
- The backrun call is at index i+1 if the targetTx is at i.
- If the proposer does not have the targetTx on the same block of the backrun tx, it removes the backrun Tx automatically.
- If the target transaction has been executed without revert, the bid will be paid regardless of execution result of backrun logic.
Early Deadline
- The block proposer will enforce an early deadline for normal user transactions to ensure the duration of an auction.
- After that, only the winning backrun call and its target can still be appended.
# Filters out the late non-target transactions exceed earlyDeadline.
def filter_tx(txs, bidTargetTxs, earlyDeadline):
txs[:] = [tx for tx in txs if not (tx.time > earlyDeadline and tx not in bidTargetTxs)]
Auction Result Validation
The auction result will be BidDetails
where the bid is RLP-serialization of the following:
rlp([target_tx_hash, block_num, sender, nonce, to_addr, call_gas_limit, call_data, bid_amount, from_signature, auctioneer_signature])
auction_type_hash = keccak256("AuctionTx(bytes32 targetTxHash,uint256 blockNumber,address sender,address to,uint256 nonce,uint256 bid,uint256 callGasLimit,bytes data)")
Please note that it’s not the transaction, but the bid.
@dataclass
class BidDetails:
target_tx_hash: Hash32 # Equivalanet to common.Hash in Go
block_num: int # The target block number
sender: str # The searcher address who has enough deposit balances
nonce: int # The nonce of searcher in the AuctionEntryPoint, not blockchain account's nonce
to_addr: str # The target address of backrun logic
call_gas_limit: bytes # The gas limit of backrun logic
call_data: bytes # The call data of backrun logic
bid_amount: bytes # The bid amount in Kei
from_signature: bytes # ECDSA.sign the hash typed data V4 with auction_type_hash based on EIP-712 by searcher's private key.
auc_signature: bytes # ECDSA.sign the from_signature based on EIP-191 by auctioneer's private key
The proposer verifies the BidDetails
in the following order.
- Verify the both signatures from searcher and auctioneer.
- The auctioneer address will come from the AuctionEntryPoint contract.
- Verify the
currBlockNum < Bid.blockNum <= currBlockNum + 2
and can be inserted into the block.- If the proposer already started mining of
Bid.blockNum
, it will be discarded.
- If the proposer already started mining of
- Verify the
Bid.bid_amount > 0
.
Block Building
The verified bids will wait for the next block mining. When the mining starts, the proposer will place the bid results into block following the KIP-245.
BidTx
The proposer shall inject a transaction denoted by BidTx, which calls the AuctionEntryPoint contract. The BidTx will follow the below rules:
- BidTx.type is 0x7802, which is the Ethereum Dynamic Fee transaction type.
- BidTx.to is an AuctionEntryPoint contract.
- BidTx.value is 0
- BidTx.data shall be correctly encoded for
AuctionEntyPoint.call
. - BidTx.maxFeePerGas, BidTx.maxPriorityFeePerGas shall be the same as those of target tx.
- BidTx.gasLimit shall be set large enough to cover the gas fee including
Bid.call_gas_limit
.
After building the BidTx, the proposer will make a BidTx bundle based on the KIP-245.
BidTx Bundling
After making the BidTx, it will be bundled based on the KIP-245. The bid bundle doesn’t include target tx but single bid tx so that the target tx can’t be delayed/canceled due to bundling. If there’s no target transaction or it’s reverted, the bid will be discarded.
Original tx list: [Tx1, Tx2, Tx3, Tx4]
Target tx: Tx2
BidTx bundle: {targetTx: Tx2, bundleTxs: [B2]} // not [Tx2, B2]
Final tx list: [Tx1, Tx2, [B2], Tx3, Tx4]
Please note that the proposer doesn’t sign the BidTx yet and it’ll be done right before executing the BidTx. This is because we can’t finalize the nonce of the proposer before executing all previous transactions and bundles when there’re more than one bundle in the same block.
Smart Contract
AuctionEntryPoint
The AuctionEntrypoint is a singleton smart contract that implements a function that includes all logic for paying the bid, executing the backrun, and refunding gas. It also manages the address of a valid auctioneer which will be used to verify the auctioneer’s signature. The address of AuctionEntrypoint will be registered in the system registry defined in KIP-149.
![]() |
---|
The execution sequence of BidTx |
The AuctionEntryPoint will verify auctionTx and will return/revert the transaction. Since the BidTx is bundled, the propose won’t pay the gas fee if it’s reverted, otherwise it will pay the gas fee.
interface IAuctionEntryPoint {
/* ========== STRUCT ========== */
struct AuctionTx {
bytes32 targetTxHash;
uint256 blockNumber;
address sender;
address to;
uint256 nonce;
uint256 bid;
uint256 callGasLimit;
bytes data;
bytes auctioneerSig;
bytes searcherSig;
}
// @dev Returns the address of current auctioneer.
function auctioneer() external view returns (address);
// @dev Returns the current nonce of address.
function nonces(address) external view returns (uint256);
// @dev Executes the all bid operations.
function call(
AuctionTx calldata auctionTx,
) external
}
AuctionDepositVault
The AuctionDepositVault is a singleton smart contract that implements a function that returns the current deposit balance of a searcher. It’s used when the proposer verifies if a searcher has enough deposit balance to pay all the bids and gasFee.
interface IAuctionDepositVault {
// @dev Returns the current deposit balance of address.
function depositBalances(address) external view returns (uint256);
// @dev Deposits KAIA into vault.
function deposit() external payable;
// @dev Starts cooldown period to withdraw KAIA.
function reserveWithdraw() external;
// @dev Withdraws KAIA after cooldown period.
function withdraw() external;
}
AuctionFeeVault
The AuctionFeeVault is a governance-owned smart contract that captures and distributes the bid amounts. After executing each bid, the bidAmount will be sent to AuctionFeeVault.
interface IAuctionFeeVault {
// @dev Receives KAIA
receive() payable external;
// @dev Distributes the KAIA to multiple receivers.
// @dev Please note that this function must be called by governance-owned sender (e.g., KIP-81 Voting contract)
function distributeFee(address[] memory, uint256[] amounts) external;
}
JSON-RPC API
The following JSON-RPC methods for the Kaia node should be added to receive the auction result from the auctioneer node and share the auction-related data with the auctioneer node.
-
auction_sendAuctionResult
- TBD
-
auction_subscribe["newPendingTransactionBodies"]
- WebSocket subscription for new pending transaction bodies.
- Please note that the existing
newPendingTransactions
subscription only returns the transaction hash, but thenewPendingTransactionBodies
will return the full transaction data.
-
auction_subscribe["newMiningStarted"]
- WebSocket subscription for new mining started.
- Returns the
(txHashes, blockNumber)
wheretxHashes
is the list of all transaction hashes andblockNumber
is the mining block number.
Auctioneer Node
The auctioneer is the independent node that relays the auction process. It validates the searcher’s bid, selects the winning bidder, and sends it to the validators.
Once the searchers deposit the KAIA into AuctionDepositVault, the auctioneer node will catch the event and recognize the searcher address.
The auctioneer node will implement the following json-rpc APIs for searchers.
auction_submitBid
- Input:
rlp([target_tx, block_num, sender, nonce, to_addr, call_gas_limit, call_data, bid_amount, from_signature])
- Output:
(bid_hash, error)
- Input:
After receiving the bid, the auctioneer verifies the the Bid
in the following order:
- Verify if the searcher has no winning bid in the same block.
- Verify the signature from searcher
- Verify the
currBlockNum + 1 <= Bid.blockNum <= currBlockNum + 2
. - Verify the
Bid.bidAmount >= minBidAmount
.- The auctioneer will check the minimum bid amount, while the proposer will check the
Bid.bidAmount > 0
.
- The auctioneer will check the minimum bid amount, while the proposer will check the
- Verify the
Bid.nonce == AuctionEntryPoint.nonce
. - Verify if the searcher has enough deposit balance to pay the
bidAmount
andgasFee
.- The
gasFee
is estimated by summing up theBid.callGasLimit
andGAS_BUFFER
that covers the gas fee for theBidTx
. - The
effetiveGasPrice
is estimated from the target transaction’smaxFeePerGas
andmaxPriorityFeePerGas
.
- The
If the validation passes, the auctioneer will insert the bid into auction.
Early Deadline Calculation
The early deadline secures the enough auction time for all transactions, but currently it’s not enforced at the consensus level. But, the auctioneer will report all activities of the validators to the public. From the reported data, the ecosystem participants (e.g., searchers, validators, users) can detect the malicious validator who doesn’t follow the early deadline.
To calculate the early deadline, we already introduced two new subscriptions in the JSON-RPC API.
The auctioneer will receive below two messages via websocket from each validators and it records message with the timestamp.
- Pending transactions: By
newPendingTransactionBodies
subscription. - Mining start message (MSM): By
newMiningStarted
subscription.
Since we have the timestamp of the pending transactions and the MSM, we can calculate the early deadline by subtracting the timestamp of the last pending transaction in MSM from the MSM timestamp.
// In auctioneer's local time
Tx D: 00:40
Tx E: 00:63
Tx F: 00:85
<- Early deadline
Tx G: 00:93
MSM : 01:05 {D, E, F}
In this case, we can assume that the early deadline is 01:05 - 00:93 = 00:12
. Of course, it’s affected by the network condition so any repeated pattern or suspicious timing can be detected by the ecosystem participants.
And if the proposer inserts any arbitrary transactions (c.f. bundle transactions) not specified in the MSM, we can have 100% confidence that the proposer is malicious.
Governance
Fee Distribution
- Kaia governance configures how
bidAmount
is split (e.g., partial to the treasury or proposer). - Withdrawals from
AuctionFeeVault
occur solely through governance proposals (see KIP-81).
Rationale
Hidden Auction + Slot Adjacency
- Defends against front-running by placing the backrun call directly after its target, minimizing reorder attacks.
Early Deadline
- The early deadline secures the enough auction time for all transactions.
- The malicious validator who doesn’t follow the early deadline can be detected by the auctioneer off-chain. Please see the Security Considerations for more details.
BidTx Bundling
- Thanks to bundling, the winning bid is guaranteed to be executed if it encounters the block generation limit during the execution. Also, the proposer can prevent any balance loss due to unexpected revert on
BidTx
. Since we impose a callGasLimit on the bid logic, the additional processing overhead will have a minimal impact compared to the bidAmount.
Single EntryPoint Call
- Bid payment, backrun logic, and gas refunds are handled atomically in a single
AuctionEntryPoint
transaction, preventing partial failures or inconsistencies.
Non-zero Bid
- The bid amount must be greater than 0 to prevent any misuse of the auction system.
Singleton AuctionEntryPoint and AuctionDepositVault
- The biggest concern of the auction system is that searchers can bypass the auction system to not pay any bid.
- The AuctionEntryPoint and AuctionDepositVault force searchers to submit correct auction bids by taking the deposit of the searcher once it wins the auction and is correctly ordered in a block.
- Even though the backrun logic was not executed successfully, the searcher forfeits all the bid payment.
Minimal On-chain Changes
- Only adjacency enforcement in block-building plus deposit logic in separate contracts. The off-chain Auctioneer coordinates hidden bids and determines the winning searcher.
Security considerations
-
Front-Running / “Sandwich” Attack
- Feasibility: Since each searcher is limited to one valid bid per block, a single searcher cannot both front-run and back-run the same transaction in a single block. However, an entity could attempt to coordinate a “sandwich” by using two separate searcher addresses, for example:
(target-1) (bid from searcher A) (target) (bid from searcher B)
- Deposit Risk: Each searcher address must lock up enough deposit to cover its own bid + gas. If the target transaction reverts, the deposit for the front-run slot (Searcher A) is forfeited.
-
Malicious Auctioneer
- Trusted Auctioneer: In this initial design, the Auctioneer is trusted, substantially reducing malicious risk.
- Future iterations could allow more permissionless auctioneers. In such scenarios, an auctioneer could theoretically attempt to cheat. However, the proposer checks the auction results, generating the real transaction. If the auctioneer produced a fraudulent result, it would fail the verification.
-
Malicious Validator
- The proposer is responsible for creating the final transaction (BidTx) after receiving the highest valid bid. While one might suspect a proposer could copy or imitate the backrun logic, the AuctionEntryPoint contract requires a valid auctioneer signature. Any attempt to replay a backrun call without that signature will not be accepted.
- If the validator doesn’t follow the early deadline, it can be detected by the auctioneer off-chain as mentioned in the Early Deadline Calculation.
Backward Compatibility
- No fundamental consensus changes, as adjacency remains a block-building policy.
- It is strongly recommended to upgrade the client to apply this auction protocol, so that the validators can benefit from the auction.
References
Copyright
Copyright and related
rights waived via CC0 1.0 Universal.