import Tabs from ‘@theme/Tabs’;
import TabItem from ‘@theme/TabItem’;
import {CodeTabs, Language, Github} from "@site/components/codetabs"
When writing smart contracts you will leverage common programming concepts such as types, collections, modules, interfaces, objects and more.
While language-specific implementation may vary, the main anatomy of a smart contract usually follows the same patterns.
First Example: A Donation Contract
Let’s look at a simple contract whose main purpose is to allow users to donate $NEAR to a specific account. Particularly, the contract stores a beneficiary
account, and exposes a method to give them money while keeping track of the donation.
Take a quick peek at the snippet bellow and then continue to the modules section.
Modules
When writing smart contracts you will leverage modules to organize your code, and reuse third-party libraries.
The main library you will see present in all contracts is the NEAR SDK. You can find it for example in the donation contract among the first lines:
“`js
import { NearContract, NearBindgen, near, call, view, UnorderedMap, Vector } from ‘near-sdk-js’
“`
“`rust
use near_sdk::collections::Vector;
use near_sdk::{env, log, near_bindgen, AccountId, Promise, Balance};
“`
“`ts
import { u128, context, logging, ContractPromiseBatch } from “near-sdk-as”;
import { STORAGE_COST, Donation, add_donation, get_donation,
set_beneficiary, get_beneficiary, get_number_of_donation } from “./model”;
“`
The NEAR SDK defines methods to, among other things:
- Understand the context of an execution (e.g. who started it, how much money they sent, etc…).
- Handle the state (storage) of the smart contract.
- Transfer money to other users/contracts.
- Call methods in other contracts.
:::info Using external libraries
- As a general rule of thumb for Rust, anything that supports
wasm32-unknown-unknown
will be compatible with your smart contract. - However, we do have a size limit for a compiled binary of a contract which is ~4.19 MB so it is possible that certain large libraries will not be compatible.
-
::
Contract’s Interface
Smart contracts expose an interface so users in the blockchain can interact with them. A contract’s interface is made of all the callable functions that live in the codebase.
Initialization Functions
When contracts are deployed to the blockchain, their variables must be initialized. There are two ways to initialize contracts: with an init
method, or using a default
initialization.
Init Method
init
methods define the parameters needed to initialize the contract and need to be manually called. In general you will
want to make these methods private programmatically.
🌐 – In JavaScript you need to call the `init` method to invoke the contract’s constructor.
🦀 – Notice that the `new` method has two macros at the top: `#[init]` and `#[private]`. `#[init]` limits the method to be callable only once, meanwhile `#[private]` makes the method only callable by the contract’s account.
“`ts
// Public – init function, define the beneficiary of donations
export function init(beneficiary: string): void {
assert(context.predecessor == context.contractName, “Method new is private”);
set_beneficiary(beneficiary);
}
“`
🚀 – In AssemblyScript there is no `#[init]` macro. You can create one yourself, as in the example above, but be mindful that, as any other method, it could be called multiple times. You can force the function to work only once by adding the following code:
“`ts
const initialized: bool = storage.getPrimitive
assert(!initialized, “Already initialized”)
storage.set
“`
Default Method
The default
method defines the default parameters to initialize the contract. If any method is invoked before a call to init
happens, then contract will use the default
values.
**Note:** The `default` method is still a work in progress in javascript.
Public and Private methods
All public methods that are exposed will be callable by all users in the blockchain. In the donation contract above, such methods are:
donate
: A method in which the users attaches NEAR in to donate.get_donation_by_number
: Returns a recorded donation, stating how much a user donated.new
: Enables to initialize the contract with a specificbeneficiary
. This function is made private by enforcing that the caller is the contract account itself.
All the other private functions can only be called from within the contract itself.
Contract’s State (Storage)
Smart contracts store typed values and data structures within them. We cover this topic in depth on Storage & Data Structures, but basically:
- The contracts natively handle
u8
,u16
,u32
,u64
,u128
and their signed counterparts. - The NEAR SDK exposes collections such as
Vector
andMap
to simplify handling complex data.
- In reality, contracts use a key-value storage and the SDK handles serializing objects for you.
-
::warning
- Always make sure to check for underflow and overflow errors. For Rust, simply add
overflow-checks=true
in yourCargo
. -
::
NEAR Bindgen and Serialization
You might have notice in the donation example that some structures use the NEAR Bindgen
decorator/macro and, in Rust, derive Borsh or serde serialization.
The NEAR Bindgen
decorator/macro generates the necessary code to:
- Transform the code into a valid NEAR contract.
- Expose public methods, so they can be called externally.
- Serialize objects for internal storage and communication with external actors.
With respect to the serialization, it is important to know that:
- In Javascript the storage is serialized using JSON, as well as the contract’s input and output.
- In Rust, the objects are stored internally using Borsh, while the input/output is serialized using JSON.
- :::tip
- Contracts communicate using values encoded in JSON.
- ::