testnet.
DocumentationDuring this tutorial, we will be relying on the Smart Contract Documentation and its different sections
Cloning the contract
To get started we’ll clone the tutorial’s repository from GitHub. The repository contains the smart contracts written in Rust (./contract-rs).
Navigate to the contract-rs folder, and then to the 01-basic-auction folder.
FrontendThe repository also contains a frontend application that interacts with the contract. You can find it in the
frontends folder. We will cover the frontend in a future sectionThe Contract’s State
The contract allows users to place bids using$NEAR tokens and keeps track of the highest bidder. Lets start by looking at how we define the contract’s state, this is, the data that the contract will store.
Macros
A first thing to notice is the use of the#[near(contract_state)] macro to denote the main structure and derive the PanicOnDefault to specify that the contract must be initialized before being used.
We also use the #[near(serializers = [json, borsh])] macro to enable both borsh and JSON (de)serialization of the Bid structure. As a rule of thumb: use the json serializer for structs that will be used as input / output of functions, and borsh for those that will be saved to state.
Storage (aka State)
Another important information revealed by the code is that the contract can store different types of data.highest_bidis an instance of aBidwhich stores:bid: aNearTokenwhich simplifies handling$NEARtoken amountsbidder: theAccountIdthat placed the bid
auction_end_timeis aU64representing aunix timestampin nanosecondsauctioneeranAccountIdthat states who can withdraw the funds at the end of the auctionclaimedabooleanthat tracks if the auctioneer has claimed the funds
Initialization Function
Lets now take a look at the initialization function, which we need to call to determine the time at which the auction will end.Macros
We denote the initialization function using the#[init] macro. Notice that the initialization function needs to return an instance of Self, i.e. the contract’s structure.
Meanwhile, the #[private] denotes that the function can only be called by the account on which the contract is deployed.
End Time
The end time is represented using aunix timestamp in nano seconds, and needs to be given as a String when calling the initialization function. This is because smart contracts cannot receive numbers larger than 52 bits and unix timestamps are represented in 64 bits.
Initial Bid
Notice that we initialize the contract with a1 yoctonear bid, made from the current account id. This means that, after the contract is initialized, the first bid will be placed by the contract at 10^-24 NEAR.
Claimed
Theclaimed field is initialized as false, as the auctioneer has not claimed the funds yet.
Auctioneer
The auctioneer is set by the deployer on initialization and is the account that will be able to claim the funds at the end of the auction.Read-only Functions
The contract implements four functions to give access to its stored data, i.e. the highest bid so far (the amount and by whom), the time at which the auction ends, the auctioneer, and whether the auction has been claimed. Functions that do not change the contract’s state (i.e. that only read from it) are calledview functions and take a non-mutable reference to self (&self).
View functions are free to call, and do not require a NEAR account to sign a transaction in order to call them.
Bidding Function
An auction is not an auction if you can’t place a bid! For this, the contract includes abid function, which users will call attaching some $NEAR tokens.
The function is quite simple: it verifies if the auction is still active and compares the attached deposit with the current highest bid. If the bid is higher, it updates the highest_bid and refunds the previous bidder.
Payable Functions
The first thing to notice is that the function changes the state, and thus takes as input a mutable reference to self (&mut self). To call this function, a NEAR account needs to sign a transaction and expend GAS.
Second, the function is marked as payable, this is because by default functions do not accept $NEAR tokens! If a user attaches tokens while calling a function that is not marked as payable, the transaction will fail.
The Environment
Notice that the function can access information about the environment in which it is running, such as who called the function (predecessor account), how many tokens they attached as deposit (attached deposit), and the approximate unix timestamp at which the function is executing (block timestamp).
Token Transfer
The function finishes by creating aPromise to transfer tokens to the previous bidder. This token amount will be deducted immediately and transferred in the next block after the current function has finished executing.
Note that on the first bid, the contract will send 1 yoctonear to itself, this is fine as we can safely assume that the contract will have the lowest denomination of $NEAR available to send to itself.
Handling Funds
Handling Funds
When a user attaches tokens to a call, the tokens are deposited to the contract’s account before the function is executed. However, if the function raises an error during its execution, the tokens are immediately refunded to the user.
Claim function
You’ll notice that the contract has a final function calledclaim, this allows the auctioneer to claim the funds from the contract at the end of the auction. Since, on NEAR, a smart contract account and user account are the same, contracts can still have keys when they are deployed, thus a user could just claim the funds from the contract via a wallet. However, this presents a security issue since by having a key the key holder can take the funds from the contract at any point, maliciously change the contract’s state or just delete the contract as a whole. By implementing a claim function we can later lock the contract by removing all access keys and have the auctioneer claim the funds via preset conditions via code.
This function is quite simple it does four things:
- Checks that the auction has ended (the current timestamp is past the auction end time).
- Checks that the auction has not yet been claimed.
- Sets the auction as now claimed.
- And if these conditions hold true it transfers
$NEARequal to the highest bid to the auctioneer.
Conclusion
In this part of the tutorial, we’ve seen how a smart contract stores data, mutates the stored data, and views the data. In the next part, we will cover how to test the contract, so we can ensure it works as expected before deploying it totestnet.