Skip to main content
Build a complete auction application on NEAR, from smart contract to frontend, learning key concepts along the way.

Overview

This comprehensive tutorial guides you through building a full-featured auction dApp. You’ll learn:
  • Creating smart contracts with state management
  • Writing and running tests
  • Deploying contracts to testnet
  • Building a React frontend
  • Querying historical data with indexers
  • Integrating NFTs and FTs
  • Using factory patterns

GitHub Repository

View the complete auction example

What you’ll build

A complete auction platform where users can:
  • Create auctions for items or NFTs
  • Place bids using NEAR or FT tokens
  • Automatically transfer assets to winners
  • View auction history and past bids

Prerequisites

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Install cargo-near
cargo install cargo-near

# Install NEAR CLI
curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh \
  | sh
Create a testnet account:
near account create-account sponsor-by-faucet-service your-name.testnet \
  autogenerate-new-keypair save-to-keychain network-config testnet create

Tutorial series

Follow these tutorials in order to build the complete application:

Part 1: Smart contracts

1

Basic auction contract

Create the core auction contract with bidding functionalityView tutorial
2

Testing

Write comprehensive tests using NEAR WorkspacesView tutorial
3

Deployment

Deploy to testnet and lock the contractView deployment guide

Part 2: Frontend

1

React setup

Create a React app and integrate NEAR walletView tutorial
2

Contract integration

Connect frontend to smart contractView integration guide
3

Indexing historical data

Use indexers to show auction historyView indexing guide

Part 3: Advanced features

1

NFT auctions

Add support for auctioning NFTsView NFT guide
2

FT bidding

Allow bids in fungible tokensView FT guide
3

Factory pattern

Deploy multiple auction contractsView factory guide

Contract structure

Here’s the core auction contract:
use near_sdk::{
    near, env, require, AccountId, NearToken,
    collections::UnorderedMap,
    json_types::U128,
};

#[near(serializers = [json, borsh])]
pub struct Bid {
    pub bidder: AccountId,
    pub amount: NearToken,
}

#[near(contract_state)]
pub struct AuctionContract {
    pub owner: AccountId,
    pub end_time: u64,
    pub highest_bid: Option<Bid>,
    pub bids: UnorderedMap<AccountId, NearToken>,
    pub claimed: bool,
}

#[near]
impl AuctionContract {
    #[init]
    pub fn new(end_time: u64) -> Self {
        Self {
            owner: env::predecessor_account_id(),
            end_time,
            highest_bid: None,
            bids: UnorderedMap::new(b"b"),
            claimed: false,
        }
    }
    
    #[payable]
    pub fn bid(&mut self) {
        require!(
            env::block_timestamp() < self.end_time,
            "Auction has ended"
        );
        
        let bidder = env::predecessor_account_id();
        let amount = env::attached_deposit();
        
        // Check if bid is high enough
        if let Some(ref highest) = self.highest_bid {
            require!(
                amount > highest.amount,
                "Bid must be higher than current highest bid"
            );
            
            // Refund previous highest bidder
            Promise::new(highest.bidder.clone())
                .transfer(highest.amount);
        }
        
        // Record bid
        self.highest_bid = Some(Bid { bidder: bidder.clone(), amount });
        self.bids.insert(bidder, amount);
    }
    
    pub fn claim(&mut self) -> Promise {
        require!(
            env::block_timestamp() >= self.end_time,
            "Auction has not ended yet"
        );
        require!(!self.claimed, "Already claimed");
        require!(
            env::predecessor_account_id() == self.owner,
            "Only owner can claim"
        );
        
        self.claimed = true;
        
        if let Some(ref highest) = self.highest_bid {
            Promise::new(self.owner.clone())
                .transfer(highest.amount)
        } else {
            env::panic_str("No bids placed");
        }
    }
    
    pub fn get_highest_bid(&self) -> Option<Bid> {
        self.highest_bid.clone()
    }
}

Frontend integration

Connect to the contract from React:
import { Wallet } from '@near-wallet-selector/core';
import { Contract } from 'near-api-js';

class AuctionApp {
  constructor() {
    this.wallet = new Wallet({ createAccessKeyFor: '' });
    this.contract = null;
  }
  
  async init() {
    await this.wallet.startUp();
    
    this.contract = new Contract(
      this.wallet.account(),
      'auction.testnet',
      {
        viewMethods: ['get_highest_bid'],
        changeMethods: ['bid', 'claim'],
      }
    );
  }
  
  async placeBid(amount) {
    return await this.contract.bid({
      args: {},
      amount: amount,
    });
  }
  
  async getHighestBid() {
    return await this.contract.get_highest_bid();
  }
}

export default AuctionApp;

Key concepts learned

By completing this tutorial, you’ll master:

State management

Storing and updating contract state efficiently

Payable functions

Accepting and handling NEAR token deposits

Cross-contract calls

Interacting with NFT and FT contracts

Frontend integration

Building React apps connected to NEAR

Testing

Comprehensive contract testing strategies

Indexing

Querying historical blockchain data

Next steps

Start the tutorial

Begin building the auction dApp

NFT tutorial

Learn to build NFT contracts

FT tutorial

Master fungible token contracts

More examples

Explore other NEAR examples

Build docs developers (and LLMs) love