Your First Smart Contract
Create your first smart contract in Rust and deploy it to the NEAR testnet.
Welcome! NEAR accounts can store small apps known as smart contracts. In this tutorial, we'll guide you through creating your first contract on the NEAR testnet.
Create an auction contract that allows users to place bids, track the highest bidder, and claim tokens at the end of the auction.
[accordion] Prefer an online IDE?
Want to jump right into the code without setting up a local dev environment?
Check out NEAR Playground for an easy-to-use online IDE with pre-configured templates.
Prerequisites
Before starting, make sure to set up your development environment.
# Install Rust: https://www.rust-lang.org/tools/install
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Contracts will be compiled to wasm, so we need to add the wasm target
rustup target add wasm32-unknown-unknown
# Install NEAR CLI-RS to deploy and interact with the contract
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh
# Install cargo near to help building the contract
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/cargo-near/releases/latest/download/cargo-near-installer.sh | sh
Creating the contract
Create a smart contract with the cargo near scaffolding tool by following its instructions:
# Create a new contract called "auction"
cargo near new auction

Creating a project using cargo near new
[!TIP]
For this tutorial, we chose to name the project
auction, but feel free to use any name you prefer.
Build and deploy the contract
Let's build the contract, create a NEAR testnet account, and deploy the contract to it.
# Run the sandbox tests to verify the contract works as expected
cargo test
# Build the contract
cargo near build non-reproducible-wasm
# Create a free testnet account, replace `<account.testnet>` with your desired account name
near create-account <account.testnet> --useFaucet
# Deploy the contract to the account
near deploy <account.testnet> ./target/near/auction.wasm
[!TIP]
Already have a testnet account?
If you already have a
testnetaccount and would like to use it instead, you can log in with the commandnear login.
[accordion] Got an error on Windows?
When working in
WSL—or another headless Linux environment—you might encounter issues creating an account because the CLI tries to save keys to the system keychain.In such cases, you can try the following command to create the account:
near account create-account sponsor-by-faucet-service <your-account-id.testnet> autogenerate-new-keypair save-to-legacy-keychain network-config testnet create
Congrats! Your contract now lives in the NEAR testnet network.
Initialize the auction
The contract stores the highest bid, auction end time, auctioneer address, and a flag to track whether proceeds have been claimed. Its init function sets these values when the contract is first initialized:
pub struct Bid {
pub bidder: AccountId,
pub bid: NearToken,
}
#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Contract {
highest_bid: Bid,
auction_end_time: U64,
auctioneer: AccountId,
claimed: bool,
}
#[near]
impl Contract {
#[init]
pub fn init(end_time: U64, auctioneer: AccountId) -> Self {
Self {
highest_bid: Bid {
bidder: env::current_account_id(),
bid: NearToken::from_yoctonear(1),
},
auction_end_time: end_time,
claimed: false,
auctioneer,
}
}
Initialize the auction by setting its end time and designating the auctioneer to receive the funds:
# Get a timestamp for 5 minutes from now (in nanoseconds)
FIVE_MINUTES_FROM_NOW=$(( $(date +%s%N) + 5 * 60 * 1000000000 ))
# Initialize the auction
near call <account.testnet> init "{\"end_time\": \"$FIVE_MINUTES_FROM_NOW\", \"auctioneer\": \"influencer.testnet\"}" --useAccount <account.testnet>
[!TIP]
Feel free to replace
influencer.testnetwith any valid testnet account—this is where the winning bid will be sent.
Place and view bids
Place a bid in the auction by calling the bid method and attaching a NEAR deposit. The function checks whether the auction is ongoing and whether the bid is higher than the stored amount. If it is, the contract records the new bid and refunds the previous bidder.
#[payable]
pub fn bid(&mut self) -> Promise {
// Assert the auction is still ongoing
require!(
env::block_timestamp() < self.auction_end_time.into(),
"Auction has ended"
);
// Current bid
let bid = env::attached_deposit();
let bidder = env::predecessor_account_id();
// Last bid
let Bid {
bidder: last_bidder,
bid: last_bid,
} = self.highest_bid.clone();
// Check if the deposit is higher than the current bid
require!(bid > last_bid, "You must place a higher bid");
// Update the highest bid
self.highest_bid = Bid { bidder, bid };
// Transfer tokens back to the last bidder
Promise::new(last_bidder).transfer(last_bid)
}
# Create a new account to place the bid
near create-account <bidder-account.testnet> --useFaucet
# Place a bid of 0.01 NEAR
near call <account.testnet> bid '{}' --deposit 0.01 --useAccount <bidder-account.testnet>
[!NOTE]
In this example, use the
<bidder-account.testnet>account (remember to rename it) to call thebidfunction and attach a deposit of0.01NEAR.
View the highest bid
The get_highest_bid function only reads from the contract state, so it does not require a transaction or signature. You can also use the contract's other view methods to query the auction end time, auctioneer, and claim status:
pub fn get_highest_bid(&self) -> Bid {
self.highest_bid.clone()
}
pub fn get_auction_end_time(&self) -> U64 {
self.auction_end_time
}
pub fn get_auctioneer(&self) -> AccountId {
self.auctioneer.clone()
}
pub fn get_claimed(&self) -> bool {
self.claimed
}
near view <account.testnet> get_highest_bid '{}'
[accordion] Expected Output
{ "bidder": "<bidder-account.testnet>", "bid": "10000000000000000000000" }
The amount is shown in yoctoNEAR, the smallest unit of NEAR. Since 1 NEAR equals 10^24 yoctoNEAR, the displayed amount is 0.01 NEAR.
[!TIP]
Feel free to create more bidder accounts and place bids to see how the highest bid changes.
Claim the proceeds
After the auction ends, anyone can call the claim method. It transfers the amount of the highest bid to the auctioneer and ends the auction:
pub fn claim(&mut self) -> Promise {
require!(
env::block_timestamp() > self.auction_end_time.into(),
"Auction has not ended yet"
);
require!(!self.claimed, "Auction has already been claimed");
self.claimed = true;
// Transfer tokens to the auctioneer
Promise::new(self.auctioneer.clone()).transfer(self.highest_bid.bid)
}
near call <account.testnet> claim '{}' --useAccount <account.testnet>
[!NOTE]
Who won?
After the auction ends, you can determine the highest bidder by calling the
get_highest_bidmethod again.
Frequently Asked Questions
[accordion] What about mainnet?
You can deploy a contract to mainnet using the same commands. Create a mainnet account and use the
--networkId mainnetflag in thenearCLI commands.
[accordion] How much does it cost to deploy?
The cost of deploying a contract depends on its size: approximately 1 Ⓝ per 100 KB.
[accordion] Can I update a contract after deploying?
Yes. Redeploy with
near deploy <account> <wasm-file>. The account stays the same, and the code is updated.
[accordion] How do I test without deploying?
Use the sandbox tests shown in this guide. They run locally in a simulated NEAR environment.
[accordion] Which SDK should I use to write smart contracts?
Use near-sdk-rs, NEAR's only official smart-contract SDK. It provides the Rust APIs used throughout this guide.
Moving forward
[card] Create a Frontend
Check the auction frontend tutorial to learn how to build a simple web app that interacts with the auction contract.
[card] Extend the Contract
Follow the auction NFT tutorial to award the highest bidder a Non-Fungible Token (NFT) and allow users to bid using Fungible Tokens (FT).
[card] Learn More about the SDK
Check our Anatomy of a Contract page to understand the different components that make up a NEAR smart contract.
[accordion] Versioning for this article
At the time of this writing, this example works with the following versions:
- rustc:
1.86.0- near-cli-rs:
0.22.0- cargo-near:
0.16.1
