How Akash Implements Bid Matching and Lease Creation in the Market Module

Akash implements bid matching and lease creation through a state-transition workflow in the market module that marks winning orders and bids as active, creates payment escrows, stores lease records, and closes all competing bids as lost.

The bid matching and lease creation process in the Akash Network is coordinated by the market module, which manages the lifecycle of deployment orders, provider bids, and active leases. Located in the akash-network/node repository, this module ensures atomic transactions that either complete a lease creation entirely or roll back without side effects. Understanding this flow is essential for developers building on Akash or auditing its decentralized compute marketplace.

The Lease Creation Orchestration

The entry point for bid matching is the CreateLease method in the message handler server. When a user submits a MsgCreateLease transaction, the handler validates the entities, establishes payment infrastructure, and executes state transitions.

The high-level flow follows four distinct phases:

  1. Validation – Load and verify the bid, order, and deployment group are in the correct open state.
  2. Escrow Setup – Create a payment escrow to lock funds for the lease duration.
  3. State Activation – Mark the order and winning bid as matched (active).
  4. Cleanup – Close all competing bids and their escrow accounts.

The implementation in x/market/handler/server.go (lines 78-226) handles this sequence:

func (ms msgServer) CreateLease(goCtx context.Context, msg *types.MsgCreateLease) (*types.MsgCreateLeaseResponse, error) {
    ctx := sdk.UnwrapSDKContext(goCtx)

    // 1️⃣ Load and validate the bid, order and deployment group
    bid,   _ := ms.keepers.Market.GetBid(ctx, msg.BidID)
    order, _ := ms.keepers.Market.GetOrder(ctx, msg.BidID.OrderID())
    group, _ := ms.keepers.Deployment.GetGroup(ctx, order.ID.GroupID())

    // 2️⃣ Create the escrow payment that will hold the lease fee
    err := ms.keepers.Escrow.PaymentCreate(ctx,
        msg.BidID.LeaseID().ToEscrowPaymentID(),
        provider,            // provider address extracted from the bid ID
        bid.Price)           // price agreed in the winning bid
    if err != nil { return nil, err }

    // 3️⃣ Store the lease record
    if err = ms.keepers.Market.CreateLease(ctx, bid); err != nil {
        return nil, err
    }

    // 4️⃣ Mark the order and the winning bid as matched (active)
    ms.keepers.Market.OnOrderMatched(ctx, order)
    ms.keepers.Market.OnBidMatched(ctx, bid)

    // … (losing bids handling – see next section)
    return &types.MsgCreateLeaseResponse{}, nil
}

Matching State Transitions

Once the lease record is created, the system must update the market state to reflect that the order is fulfilled and the specific bid has won.

Activating the Order

The OnOrderMatched function in x/market/keeper/keeper.go (lines 79-84) transitions the order from open to active. This helper reads the current order state, sets order.State = types.OrderActive, and delegates storage to updateOrder, which uses collections.IndexedMap to maintain secondary indexes.

Activating the Winning Bid

Similarly, OnBidMatched (lines 86-90) handles the bid state transition by setting bid.State = types.BidActive before calling updateBid. Both functions ensure that the underlying IndexedMap structures remain consistent, allowing efficient lookups by state and provider.

Closing Competing Bids

After establishing the winning lease, the system must invalidate all other open bids for the same order. The handler iterates through competing bids using WithBidsForOrder, marks each as lost, and closes their escrow accounts.

The cleanup logic in x/market/handler/server.go:

// close losing bids
ms.keepers.Market.WithBidsForOrder(ctx, msg.BidID.OrderID(),
    types.BidOpen, func(cbid types.Bid) bool {
        ms.keepers.Market.OnBidLost(ctx, cbid)          // ⇢ set state to BidLost
        // Immediately close the escrow account for the lost bid
        if err = ms.keepers.Escrow.AccountClose(ctx,
            cbid.ID.ToEscrowAccountID()); err != nil {
            return true // stop iteration on error
        }
        return false
})

The OnBidLost function (lines 93-98 in keeper.go) sets bid.State = types.BidLost using the same updateBid helper as the winning path. This ensures that failed bids release their locked funds immediately through Escrow.AccountClose.

Key Implementation Files

Understanding bid matching and lease creation requires familiarity with these core files in the akash-network/node repository:

  • x/market/keeper/keeper.go – Core state-transition helpers including OnOrderMatched, OnBidMatched, OnBidLost, and CreateLease.
  • x/market/handler/server.go – gRPC message server that orchestrates the lease creation transaction flow.
  • x/market/keeper/keys – Key encoding utilities for the IndexedMap structures managing order, bid, and lease prefixes.
  • upgrades/software/v1.2.0/market.go – Migration logic for transitioning legacy KV-store data to the current collections.IndexedMap schema.

Summary

  • Atomic Execution: The CreateLease handler performs validation, escrow creation, lease storage, and state transitions in a single transaction that rolls back completely on any failure.
  • State Machines: Orders transition from Open to Active via OnOrderMatched, while winning bids move from Open to Active via OnBidMatched and losing bids move to BidLost via OnBidLost.
  • Escrow Management: The system creates a payment escrow for the winning lease via Escrow.PaymentCreate while closing escrow accounts for all losing bids via Escrow.AccountClose.
  • Data Integrity: All state changes utilize collections.IndexedMap to ensure secondary indexes remain synchronized with primary storage.

Frequently Asked Questions

What triggers the bid matching process in Akash?

The bid matching process initiates when a deployment owner submits a MsgCreateLease transaction referencing a specific bid ID. This message triggers the CreateLease handler in x/market/handler/server.go, which validates the bid against the order and executes the matching logic if all conditions are met.

How does Akash ensure losing bids do not retain locked funds?

After creating the winning lease, the handler iterates through all remaining BidOpen states for the order using WithBidsForOrder. For each losing bid, it calls OnBidLost to update the state and immediately invokes Escrow.AccountClose to release the provider's locked collateral, ensuring no funds remain trapped in failed bids.

What happens if the escrow payment creation fails during lease creation?

If Escrow.PaymentCreate returns an error during the CreateLease execution, the transaction aborts immediately and returns the error to the caller. Because Cosmos SDK transactions are atomic, any state changes made earlier in the function are discarded, leaving the order and bid in their original Open state.

Where is the lease record actually stored during the matching process?

The lease record is persisted via ms.keepers.Market.CreateLease(ctx, bid) within the CreateLease handler. This method stores the lease object in the keeper's IndexedMap structure, making it queryable by lease ID while maintaining indexes for efficient provider and tenant lookups.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →