Build an in-memory REST API for a Solana-style timed auction system. Model wallets with escrow tracking, time-bound auctions, deterministic bidding rules, settlement, and cancellation.
- GET /api/health ->
200with{ "status": "ok" }
- POST /api/wallets/deposit
- Body:
{ wallet, amount } wallet: Solana wallet addressamount: positive integer- Returns
201with{ wallet, amount, balance, createdAt } - Multiple deposits to the same wallet accumulate.
- Body:
- GET /api/wallets/:wallet/balance
- Returns
{ wallet, balance, escrowed, available } balance: net funds owned (total deposits + seller earnings from settlements - amounts paid for won auctions)escrowed: funds currently locked as the highest bidder on unsettled auctionsavailable:balance - escrowed- Unknown wallets:
{ wallet, balance: 0, escrowed: 0, available: 0 }
- Returns
- POST /api/auctions
- Body:
seller: Solana wallet addressitem: non-empty stringstartAt: full ISO datetimeendAt: full ISO datetime, must be strictly afterstartAtstartingPrice: integer >= 0minIncrement: positive integer
startAtandendAtmust be full ISO datetimes with time and timezone (date-only values like2026-03-20are invalid).- Returns
201with the auction including computed fields (status,currentPrice,bidCount,highestBidder).
- Body:
- GET /api/auctions/:id
- Optional query
nowas a full ISO datetime override for status/field computation. - Returns auction with computed fields:
statuscurrentPrice: highest bid amount, orstartingPriceif no bidsbidCount: total number of bids placedhighestBidder: wallet of current highest bidder, ornull
- Return
404if not found.
- Optional query
- GET /api/auctions
- Query params:
seller?,status?,now? - Filters combined with logical AND.
- Response:
{ auctions: [...] }with computed fields on each. - Sort by
createdAtascending.
- Query params:
- POST /api/auctions/:id/bid
- Body:
{ bidder, amount, now? } bidder: Solana wallet address, must NOT be the seller.amount: positive integer.- Auction must be
ACTIVEat the givennow. - First bid:
amount >= startingPrice. - Subsequent bids:
amount >= currentPrice + minIncrement. - Bidder must have sufficient
availablebalance. If the bidder already holds the highest bid (raising), their existing escrow on this auction is released before checking availability. - On success:
- If there was a different previous highest bidder, release their escrow.
- Escrow the new bid amount from the bidder.
- Return
200with updated auction snapshot. - Return
400for validation failures,404for missing auction.
- Body:
- POST /api/auctions/:id/settle
- Body:
{ now? } - Only allowed when status is
ENDED. - If bids exist: transfer the winning bid amount to the seller's balance, deducting from the winner's balance and escrow.
- If no bids: mark as settled with no transfers.
- Return
200with{ auction, winner, winningBid }(winnerandwinningBidarenullwhen there are no bids). - Return
400if the auction is not inENDEDstatus.
- Body:
- POST /api/auctions/:id/cancel
- Body:
{ now? } - Only allowed when status is
UPCOMING. - Return
200with the cancelled auction. - Return
400if notUPCOMINGor already cancelled.
- Body:
- GET /api/auctions/:id/bids
- Returns
{ bids: [...] }sorted byplacedAtascending. - Each bid includes
bidder,amount, andplacedAt. - Return
404for missing auction.
- Returns
UPCOMING: now <startAtACTIVE:startAt<= now <endAt, and not cancelled or settledENDED: now >=endAt, and not settled or cancelledSETTLED: after settlementCANCELLED: after cancellation
- Use in-memory storage only. No database required.
- IDs can be any unique string format.
- All timestamps in responses must be ISO strings.
- Any supplied
nowmust be a full ISO datetime string with both time and timezone. Date-only values like2026-03-20are invalid. - Return
400when a suppliednowoverride is not a valid ISO datetime. - Return
404for missing auction IDs on GET, bid, settle, cancel, and bids endpoints.
npm install && npm start