#[near]Expand description
This attribute macro is used on a struct/enum and its implementations
to generate the necessary code to expose pub methods from the contract as well
as generating the glue code to be a valid NEAR contract.
The macro is a syntactic sugar for near_bindgen and expands to the near_bindgen macro invocations. Both of them share the same attributes, except for those that are explicitly marked as specific to the near macro. (1, 2)
§Attributes
§#[near(contract_state)] (annotates structs/enums)
The attribute prepares a struct/enum to be a contract state. Only one contract state is allowed per crate.
Custom storage key can be set via #[near(contract_state(key = b"CUSTOM"))].
A contract type is usually acompanied by an impl block, annotated with #[near].
This attribute is also required to make the #[near(contract_metadata(...))] attribute work.
contract_state is specific to the near macro only, not available for near_bindgen.
§Basic example
use near_sdk::near;
#[near(contract_state)]
pub struct Contract {
greeting: String,
}which usually comes paired with at least one impl block for the contract type,
annotated with a plain #[near] attribute:
§Using SDK collections for storage
If contract state becomes large, collections from following modules can be used:
§store module:
use near_sdk::store::IterableMap;
#[near(contract_state)]
pub struct StatusMessage {
records: IterableMap<String, String>,
}- list of host functions used for
storeimplementation - FAQ: mutating state of collections from
storemodule is only finally persisted on runningDrop/flush
§collections module:
use near_sdk::collections::LookupMap;
#[near(contract_state)]
pub struct StatusMessage {
records: LookupMap<String, String>,
}- list of host functions used for
collectionsimplementation
§Reference to Implementation of #[near(contract_state)] attribute (How does it work?)
§Auto-generated <ContractType>Ext struct
This attribute also generates the <ContractType>Ext struct definition for cross-contract calls.
See <ContractType>Ext documentation for details.
§#[near] (annotates impl blocks)
This macro is used to define the code for view-only and mutating methods for contract types,
annotated by #[near(contract_state)].
§Basic example
use near_sdk::{near, log};
#[near]
impl Contract {
// view method
pub fn get_greeting(&self) -> String {
self.greeting.clone()
}
// mutating method
pub fn set_greeting(&mut self, greeting: String) {
log!("Saving greeting: {greeting}");
self.greeting = greeting;
}
}§Reference to Implementation of #[near] macro (How does it work?)
§Auto-generated method wrappers on <ContractType>Ext
This macro also generates method wrappers on the <ContractType>Ext struct for each public method,
enabling cross-contract calls.
See <ContractType>Ext documentation for details.
§<ContractType>Ext struct (auto-generated for cross-contract calls)
When you annotate a struct with #[near(contract_state)]
and define methods in an #[near] impl block, the macro automatically
generates a companion struct called <ContractType>Ext (e.g., ContractExt for a contract named Contract).
This struct provides a builder pattern API for making cross-contract calls to your contract’s methods,
returning a Promise that can be chained with other promises.
§Generated structure
For a contract like:
use near_sdk::near;
#[near(contract_state)]
pub struct CrossContract {
greeting: String,
}
#[near]
impl CrossContract {
pub fn method_one(&self, n: u32) -> u32 { n }
pub fn method_two(&mut self, message: String) { }
}The macro generates (approximately):
#[must_use]
pub struct CrossContractExt {
pub(crate) promise_or_create_on: PromiseOrValue<AccountId>,
pub(crate) deposit: NearToken,
pub(crate) static_gas: Gas,
pub(crate) gas_weight: GasWeight,
}
impl CrossContract {
/// API for calling this contract's functions in a subsequent execution.
pub fn ext(account_id: AccountId) -> CrossContractExt { /* ... */ }
/// API for calling this contract's functions as a callback on a promise.
pub fn ext_on(promise: Promise) -> CrossContractExt { /* ... */ }
}
impl CrossContractExt {
/// Attach NEAR tokens to the cross-contract call.
pub fn with_attached_deposit(mut self, amount: NearToken) -> Self { /* ... */ }
/// Specify the amount of static gas to attach to this call.
pub fn with_static_gas(mut self, static_gas: Gas) -> Self { /* ... */ }
/// Specify the weight for distributing unused gas to this call.
pub fn with_unused_gas_weight(mut self, gas_weight: u64) -> Self { /* ... */ }
// Methods mirroring contract methods:
pub fn method_one(self, n: u32) -> Promise { /* ... */ }
pub fn method_two(self, message: String) -> Promise { /* ... */ }
}§What gets generated where
#[near(contract_state)]generates the<ContractType>Extstruct definition with its fields and theext()/ext_self()/ext_on()constructor methods.#[near]on impl blocks generates method wrappers on<ContractType>Extthat mirror each public method in the impl block, returning aPromise.
§Usage example: Cross-contract calls
use near_sdk::{near, env, Promise};
#[near]
impl Contract {
pub fn some_method(&self) -> u32 { 42 }
pub fn call_other_contract(&self) -> Promise {
// Call another contract's method using the Ext struct
Self::ext(self.other_contract_id.clone())
.with_attached_deposit(near_sdk::NearToken::from_near(1))
.with_static_gas(near_sdk::Gas::from_tgas(5))
.some_method()
}
pub fn call_with_callback(&self) -> Promise {
// Chain multiple calls: call self, then callback.
// `ext_self()` is a shorthand for `ext(env::current_account_id())`.
Self::ext_self()
.some_method()
.then(
Self::ext_self()
.callback_method()
)
}
#[private]
pub fn callback_method(&self, #[callback_unwrap] result: u32) {
// Handle the result from the previous call
}
}§Discovering method signatures
To explore all available methods on your <ContractType>Ext struct, run:
cargo doc --lib --openThis generates documentation for your contract, including the auto-generated
<ContractType>Ext struct with all its methods and their signatures.
§See also
Promise- The type returned by<ContractType>Extmethods#[callback_unwrap]- For handling results from cross-contract calls#[private]- For restricting callback methods- NEAR Cross-Contract Calls Documentation
§#[near(serializers=[...]) (annotates structs/enums)
The attribute makes the struct or enum serializable with either json or borsh. By default, borsh is used.
serializers is specific to the near macro only, not available for near_bindgen.
§Make struct/enum serializable with borsh
use near_sdk::near;
#[near(serializers=[borsh])]
pub enum MyEnum {
Variant1,
}
#[near(serializers=[borsh])]
pub struct MyStruct {
pub name: String,
}
// Since [borsh] is the default value, you can simply skip serializers:
#[near]
pub enum MyEnum2 {
Variant1,
}
#[near]
pub struct MyStruct2 {
pub name: String,
}§Make struct/enum serializable with json
use near_sdk::near;
#[near(serializers=[json])]
pub enum MyEnum {
Variant1,
}
#[near(serializers=[json])]
pub struct MyStruct {
pub name: String,
}§Make struct/enum serializable with both borsh and json
use near_sdk::near;
#[near(serializers=[borsh, json])]
pub enum MyEnum {
Variant1,
}
#[near(serializers=[borsh, json])]
pub struct MyStruct {
pub name: String,
}§Customize borsh serializer
The #[near(serializers = [borsh(...)])] macro allows you to pass configuration parameters to the borsh serializer.
This is useful for customizing borsh serialization parameters since, unlike serde, borsh macros do not support repetitive attributes.
use near_sdk::near;
#[near(serializers = [borsh(use_discriminant = true)])]
pub enum MyEnum {
Variant1,
Variant2,
}§Customize json serializer
The #[near(serializers = [json])] macro does not support passing configuration parameters to the json serializer.
Yet, you can just use #[serde(...)] attributes as if #[derive(Serialize, Deserialize)] is added to the struct (which is what actually happens under the hood of #[near(serializers = [json])] implementation).
use near_sdk::near;
#[near(serializers = [json])]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum MyEnum {
Variant1,
#[serde(alias = "VARIANT_2")]
Variant2,
}You can also use #[serde_as(as = "...")] attributes
as if #[serde_as] is added to the type (which is what actually happens under the hood of #[near(serializers = [json])] implementation).
Note: When using the abi feature with base64/hex encoding, prefer the SDK’s json_types
like Base64VecU8, which have full JSON Schema support.
For hex encoding with ABI support, you can use #[serde_as(as = "serde_with::hex::Hex")] by enabling
the serde_with/schemars_1 feature in your Cargo.toml (this requires upgrading to schemars 1.x).
use near_sdk::{
near,
serde_json::json,
serde_with::{json::JsonString, DisplayFromStr},
json_types::Base64VecU8,
};
#[near(serializers = [json])]
pub struct MyStruct {
#[serde_as(as = "DisplayFromStr")]
pub amount: u128,
pub base64_bytes: Base64VecU8,
#[serde_as(as = "BTreeMap<DisplayFromStr, Vec<DisplayFromStr>>")]
pub collection: BTreeMap<u128, Vec<u128>>,
#[serde_as(as = "JsonString")]
pub json_string: serde_json::Value,
}§#[serializer(...)] (annotates function arguments)
The attribute makes the function argument deserializable from Vec<u8> with either json or borsh. By default, json is used.
Please, note that all the arguments of the function should be using the same deserializer.
NOTE: a more correct name for the attribute would be argument_deserializer, but it’s serializer for historic reasons.
§Basic example
use near_sdk::near;
#[near]
impl Contract {
pub fn borsh_arguments(&self, #[serializer(borsh)] a: String, #[serializer(borsh)] b: String) {}
}§Implementation of #[serializer(...)] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
using the attribute allows to replace default serde_json::from_slice with borsh::from_slice.
A bit more thoroughly the effect of the attribute is described in (step 3.1, #[near] on mutating method).
§#[init] (annotates methods of a type in its impl block)
Contract initialization method annotation. More details can be found here
By default, the Default::default() implementation of a contract will be used to initialize a contract.
There can be a custom initialization function which takes parameters or performs custom logic with the following #[init] annotation.
You can provide several initialization functions.
§Basic example
use near_sdk::{log, near};
#[near(contract_state)]
#[derive(Default)]
pub struct Counter {
value: u64,
}
#[near]
impl Counter {
#[init]
pub fn new(value: u64) -> Self {
log!("Custom counter initialization!");
Self { value }
}
}§Implementation of #[init] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
For a method annotated with #[init]:
- Before invoking the constructor, the macro checks if the contract state already exists by calling
state::ContractState::state_exists, which internally usesenv::storage_has_keyto check for the state key - If the state already exists,
env::panic_strhost function is called with the message"The contract has already been initialized" - Otherwise, the constructor method is called to create the contract instance
- The newly created contract state is written using
env::state_writehost function
The #[init(ignore_state)] variant skips the state existence check in step 1-2, allowing
re-initialization of the contract.
§#[payable] (annotates methods of a type in its impl block)
Specifies that the method can accept NEAR tokens. More details can be found here
Methods can be annotated with #[payable] to allow tokens to be transferred with the method invocation. For more information, see payable methods.
To declare a function as payable, use the #[payable] annotation as follows:
§Basic example
use near_sdk::near;
#[near(contract_state)]
#[derive(Default)]
pub struct Counter {
val: i8,
}
#[near]
impl Counter {
#[payable]
pub fn my_method(&mut self) {
//...
}
}§Implementation of #[payable] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
For methods without the #[payable] attribute, the macro generates a deposit check at the beginning
of the method:
env::attached_deposithost function is called to get the amount of NEAR tokens attached to the call- If the attached deposit is not zero,
env::panic_strhost function is called with the message"Method {method_name} doesn't accept deposit"
When a method is annotated with #[payable], this deposit check is skipped, allowing the method
to accept NEAR token transfers along with the function call.
§#[private] (annotates methods of a type in its impl block)]
The attribute forbids to call the method except from within the contract. This is useful for internal methods that should not be called from outside the contract.
More details can be found here
§Basic example
use near_sdk::near;
#[near(contract_state)]
#[derive(Default)]
pub struct Counter {
val: u64,
}
#[near]
impl Counter {
#[private]
pub fn my_method(&mut self) {
// ...
}
}§Implementation of #[private] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
For methods annotated with #[private], the macro generates a caller check at the beginning
of the method:
env::current_account_idhost function is called to get the contract’s own account IDenv::predecessor_account_idhost function is called to get the caller’s account ID- If the caller’s account ID does not match the contract’s account ID,
env::panic_strhost function is called with the message"Method {method_name} is private"
This ensures that only the contract itself (through cross-contract calls from its own methods) can invoke the private method.
§#[deny_unknown_arguments] (annotates methods of a type in its impl block)]
Specifies that the method call should error during deserialization if any unknown fields are present in the input. This helps ensure data integrity by rejecting potentially malformed input.
Without this attribute, unknown fields are silently ignored during deserialization.
Implementation uses deny_unknown_fields serde’s attribute.
In the following example call of my_method with
{
"description": "value of description"
}payload works, but call of my_method with
{
"description": "value of description",
"unknown_field": "what"
}payload is declined with a FunctionCallError(ExecutionError("Smart contract panicked: Failed to deserialize input from JSON.")) error.
§Basic example
use near_sdk::near;
#[near(contract_state)]
#[derive(Default)]
pub struct Counter {
val: u64,
}
#[near]
impl Counter {
#[deny_unknown_arguments]
pub fn my_method(&mut self, description: String) {
// ...
}
}This attribute is not supposed to be used together with #[serializer(borsh)]
arguments’ serializer, and assumes that default json is used.
If borsh is used on arguments, usage of deny_unknown_arguments on method is a no-op.
§#[result_serializer(...)] (annotates methods of a type in its impl block)
The attribute defines the serializer for function return serialization.
Only one of borsh or json can be specified.
use near_sdk::near;
#[near]
impl MyContract {
#[result_serializer(borsh)]
pub fn borsh_return_value(&self) -> String {
"hello_world".to_string()
}
}§Implementation of #[result_serializer(...)] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
using the attribute allows to replace default serde_json::to_vec with borsh::to_vec.
A bit more thoroughly the effect of the attribute is described in (step 4.1, #[near] on view method).
§#[handle_result] (annotates methods of a type in its impl block)
Have #[handle_result] to Support Result types regardless of how they’re referred to
Function marked with #[handle_result] should return Result<T, E> (where E implements FunctionError).
If you’re trying to use a type alias for Result, try #[handle_result(aliased)]
§Basic error handling with Result
use near_sdk::{near, AccountId, Promise, PromiseError};
#[near(contract_state)]
#[derive(Default)]
pub struct Counter {
val: u64,
}
#[near]
impl Counter {
#[handle_result]
pub fn some_function2(
&self,
) -> Result<(), &'static str> {
Err("error")
}
}§Typed error handling
This example shows how to use error handling in a contract when the error are defined in the contract. This way the contract can utilize result types and panic with the type using its ToString implementation
use near_sdk::{near, FunctionError};
#[derive(FunctionError)]
pub enum MyError {
SomePanicError,
}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MyError::SomePanicError => write!(f, "Panic error message that would be displayed to the user"),
}
}
}
#[near]
impl Counter {
#[handle_result]
pub fn some_function(&self) -> Result<(), MyError> {
if self.val == 0 {
return Err(MyError::SomePanicError);
}
Ok(())
}
}§Implementation of #[handle_result] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
For methods annotated with #[handle_result], the macro modifies how the return value is processed:
- The method is expected to return
Result<T, E>whereEimplementsFunctionError - After the method executes, the macro checks if the result is:
Result::Ok: The inner value is serialized and returned viaenv::value_returnhost functionResult::Err: The error’sFunctionError::panicmethod is called, which typically callsenv::panic_strhost function with the error message converted viaToString
The #[handle_result(aliased)] variant allows using type aliases for Result types, enabling
custom result type definitions while maintaining the same behavior.
§#[callback_unwrap] (annotates function arguments)
Automatically unwraps the successful result of a callback from a cross-contract call. Used on parameters in callback methods that are invoked as part of a cross-contract call chain. If the promise fails, the method will panic with the error message.
This attribute is commonly used with Promise or PromiseOrValue<T> as the return type of another contract method,
whose return value will be passed as argument to #[callback_unwrap]-annotated argument
§Example with Cross-Contract Factorial:
In the example:
- lower level
env::promise_create,env::promise_thenandenv::promise_returnare used infactorialmethod to set up a callback offactorial_multwith result of factorial for(n-1) #[private]onfactorial_multis used to to allow only callingfactorial_multfrom factorial contract method byCrossContractitself and disallow for it to be called externally by users
use near_sdk::{near, env, log, NearToken, Gas};
// Prepaid gas for a single (not inclusive of recursion) `factorial` call.
const FACTORIAL_CALL_GAS: Gas = Gas::from_tgas(20);
// Prepaid gas for a single `factorial_mult` call.
const FACTORIAL_MULT_CALL_GAS: Gas = Gas::from_tgas(10);
#[near(contract_state)]
#[derive(Default)]
pub struct CrossContract {}
#[near]
impl CrossContract {
pub fn factorial(&self, n: u32) {
if n <= 1 {
env::value_return(&serde_json::to_vec(&1u32).unwrap());
return;
}
let account_id = env::current_account_id();
let prepaid_gas = env::prepaid_gas().saturating_sub(FACTORIAL_CALL_GAS);
let promise0 = env::promise_create(
account_id.clone(),
"factorial",
&serde_json::to_vec(&(n - 1,)).unwrap(),
NearToken::from_near(0),
prepaid_gas.saturating_sub(FACTORIAL_MULT_CALL_GAS),
);
let promise1 = env::promise_then(
promise0,
account_id,
"factorial_mult",
&serde_json::to_vec(&(n,)).unwrap(),
NearToken::from_near(0),
FACTORIAL_MULT_CALL_GAS,
);
env::promise_return(promise1);
}
#[private]
pub fn factorial_mult(&self, n: u32, #[callback_unwrap] factorial_n_minus_one_result: u32) -> u32 {
log!("Received n: {:?}", n);
log!("Received factorial_n_minus_one_result: {:?}", factorial_n_minus_one_result);
let result = n * factorial_n_minus_one_result;
log!("Multiplied {:?}", result.clone());
result
}
}which has the following lines in a factorial’s view call log:
logs: [
"Received n: 5",
"Received factorial_n_minus_one_result: 24",
"Multiplied 120",
],§Other examples within repo:
Cross-Contract Factorialagain examples/cross-contract-calls- same example as above, but uses
Promise::theninstead ofenvhost functions calls to set up a callback offactorial_mult
- same example as above, but uses
- examples/callback-results
§Reference to Implementation of #[callback_unwrap] attribute
§#[callback_result] (annotates function arguments)
Similar to #[callback_unwrap], but instead of panicking on promise failure,
it wraps the result in a Result<T, PromiseError>, allowing the callback to handle both success and failure cases.
This is useful when you want to handle failed cross-contract calls gracefully instead of panicking.
§Basic example
use near_sdk::{near, PromiseError};
#[near(contract_state)]
#[derive(Default)]
pub struct Contract {}
#[near]
impl Contract {
#[private]
pub fn callback_method(&self, #[callback_result] result: Result<String, PromiseError>) {
match result {
Ok(value) => {
// Handle successful cross-contract call
near_sdk::log!("Received value: {}", value);
}
Err(_) => {
// Handle failed cross-contract call
near_sdk::log!("Cross-contract call failed");
}
}
}
}§Implementation of #[callback_result] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
For arguments annotated with #[callback_result]:
- Arguments are not expected to be included in the regular input deserialization
- For each
#[callback_result]argument:env::promise_result_checkedhost function is called with the corresponding index (0 for the first callback argument, 1 for the second, etc.)- If successful, the data is deserialized and wrapped in
Result::Ok - If the promise failed,
Result::Err(PromiseError)is returned (no panic occurs, unlike#[callback_unwrap])
- The resulting
Result<T, PromiseError>is passed to the method, allowing error handling
The optional max_bytes parameter (e.g., #[callback_result(max_bytes = 100)]) limits the
maximum size of the callback data to prevent excessive memory usage.
§#[callback_vec] (annotates function arguments)
Collects results from multiple promises into a Vec<T>. This is useful when you have
multiple cross-contract calls via Promise::and and want to process all their results.
§Basic example
use near_sdk::near;
#[near(contract_state)]
#[derive(Default)]
pub struct Contract {}
#[near]
impl Contract {
#[private]
pub fn aggregate_callback(&self, #[callback_vec] results: Vec<u64>) -> u64 {
// Sum all results from multiple cross-contract calls
results.iter().sum()
}
}§Implementation of #[callback_vec] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
For the argument annotated with #[callback_vec]:
env::promise_results_counthost function is called to get the total number of promise results- For each promise result (from index 0 to count-1):
env::promise_result_checkedhost function is called with the index- If successful, the data is deserialized and added to the vector
- If any promise failed,
env::panic_strhost function is called with the message"Callback computation {index} was not successful"
- The collected
Vec<T>is passed to the method
Note: Only one #[callback_vec] parameter is allowed per method.
The optional max_bytes parameter limits the maximum size of each callback result.
§#[near(event_json(...))] (annotates enums)
By passing event_json as an argument near will generate the relevant code to format events
according to NEP-297
For parameter serialization, this macro will generate a wrapper struct to include the NEP-297 standard fields standard and version
as well as include serialization reformatting to include the event and data fields automatically.
The standard and version values must be included in the enum and variant declaration (see example below).
By default this will be JSON deserialized with serde
The version is required to allow backward compatibility. The older back-end will use the version field to determine if the event is supported.
§Basic example
use near_sdk::{near, AccountId};
#[near(event_json(standard = "nepXXX"))]
pub enum MyEvents {
#[event_version("1.0.0")]
Swap { token_in: AccountId, token_out: AccountId, amount_in: u128, amount_out: u128 },
#[event_version("2.0.0")]
StringEvent(String),
#[event_version("3.0.0")]
EmptyEvent
}
#[near]
impl Contract {
pub fn some_function(&self) {
MyEvents::StringEvent(
String::from("some_string")
).emit();
}
}§Implementation of #[near(event_json(...))] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
The #[near(event_json(standard = "..."))] macro transforms an enum into a NEP-297 compliant event:
- The macro adds
#[derive(Serialize, EventMetadata)]to the enum - Serde attributes are added for proper JSON serialization:
#[serde(tag = "event", content = "data")]for the NEP-297 format#[serde(rename_all = "snake_case")]for event name formatting
- A constant
{EnumName}_event_standardis generated with the standard name - The
EventMetadataderive macro generates:emit()method: Serializes the event to JSON and callsenv::log_strwith the formatEVENT_JSON:{json}as specified by NEP-297to_json()method: Returns the event as aserde_json::Valuestandard(),version(),event()methods for accessing metadata
- Each variant must have
#[event_version("x.x.x")]to specify the version
§#[near(contract_metadata(...))] (annotates structs/enums)
By using contract_metadata as an argument near will populate the contract metadata
according to NEP-330 standard. This still applies even when #[near] is used without
any arguments.
All fields(version, link) are optional and will be populated with defaults from the Cargo.toml file if not specified.
The standard will be populated with nep330 by default.
Any additional standards can be added and should be specified using the standard attribute.
The contract_source_metadata() view function will be added and can be used to retrieve the source metadata.
Also, the source metadata will be stored as a constant, CONTRACT_SOURCE_METADATA, in the contract code.
Please note that the contract_metadata will be ignored if #[near(contract_state)] is not used.
§Basic example
use near_sdk::near;
#[near(contract_state, contract_metadata(
version = "39f2d2646f2f60e18ab53337501370dc02a5661c",
link = "https://github.com/near-examples/nft-tutorial",
standard(standard = "nep171", version = "1.0.0"),
standard(standard = "nep177", version = "2.0.0"),
))]
struct Contract {}§Implementation of #[near(contract_metadata(...))] attribute and host functions calls used
In a nutshell and if the details of ABI generation layer are put aside,
The #[near(contract_metadata(...))] attribute works in conjunction with #[near(contract_state)]:
- The metadata is extracted from the attribute arguments or defaults from
Cargo.toml:version: Defaults to theNEP330_VERSIONenvironment variable, or if unset, toCARGO_PKG_VERSIONlink: Defaults to theNEP330_LINKenvironment variable, falling back toCARGO_PKG_REPOSITORYif unsetstandard: Additional standards the contract implements (e.g., NEP-171, NEP-177)
- A
CONTRACT_SOURCE_METADATAconstant is generated containing the JSON-serialized metadata - A
contract_source_metadata()view function is generated that:- Calls
env::setup_panic_hookhost function - Calls
env::value_returnhost function with theCONTRACT_SOURCE_METADATAbytes
- Calls
This follows the NEP-330 standard for contract source metadata, allowing tools and users to discover information about the deployed contract.
§Implementation of #[near(contract_state)] attribute and host functions calls used
This heading describes #[near(contract_state)].
In a nutshell and if the details of ABI generation layer are put aside,
#[near(contract_state)]
pub struct Contract { /* .. */ }- Macro adds derived implementations of
borsh::BorshSerialize/borsh::BorshSerializeforContracttype - Macro defines a global
CONTRACT_SOURCE_METADATAvariable, which is a string of json serialization ofnear_contract_standards::contract_metadata::ContractSourceMetadata. - Macro defines
contract_source_metadatafunction:which#[unsafe(no_mangle)] pub extern "C" fn contract_source_metadata() { /* .. */ }- calls
env::setup_panic_hookhost function - calls
env::value_returnhost function with bytes ofCONTRACT_SOURCE_METADATAfrom step 2.
- calls
§using cargo-expand to view actual macro results
The above is an approximate description of what macro performs.
Running the following in a contract’s crate is a way to introspect more details of its operation:
cargo expand --lib --target wasm32-unknown-unknown
# this has additional code generated for ABI layer
cargo expand --lib --features near-sdk/__abi-generate§Implementation of #[near] macro and host functions calls used
This heading describes #[near] on impl blocks.
In a nutshell and if the details of ABI generation layer are put aside,
#[near]
impl Contract {
pub fn view_method(&self) -> String { todo!("method body") }
pub fn mutating_method(&mut self, argument: String) { /* .. */ }
}§for above view method #[near] macro defines the following function:
#[unsafe(no_mangle)]
pub extern "C" fn view_method() { /* .. */ }which
- calls
env::setup_panic_hookhost function - calls
env::state_readhost function to loadContractinto astatevariableenv::state_read’s result is unwrapped withOption::unwrap_or_defaultPanicOnDefaultmay be used to NOT let implementation ofDefaultforContractvalue become the outcomeContract’sstate, whenenv::state_readreturnsOption::None
- calls original
Contract::view_method(&state)as defined in#[near]annotated impl block and saves the returned value into aresultvariable - calls
serde_json::to_vecon obtainedresultand saves returned value toserialized_resultvariablejsonformat can be changed to serializing withborsh::to_vecby using#[result_serializer(...)]
- if the
serialized_resultis anResult::Errerror, thenenv::panic_strhost function is called to signal result serialization error - otherwise, if the
serialized_resultis aResult::Ok, thenenv::value_returnhost function is called with unwrappedserialized_result
§for above mutating method #[near] macro defines the following function:
#[unsafe(no_mangle)]
pub extern "C" fn mutating_method() { /* ..*/ }which
- calls
env::setup_panic_hookhost function - calls
env::inputhost function and saves it toinputvariable - deserializes
Contract::mutating_methodarguments by callingserde_json::from_sliceoninputvariable and saves it todeserialized_inputvariablejsonformat can be changed to deserializing withborsh::from_sliceby using#[serializer(...)]
- if the
deserialized_inputis anResult::Errerror, thenenv::panic_strhost function is called to signal input deserialization error - otherwise, if the
deserialized_inputis aResult::Ok,deserialized_inputis unwrapped and saved todeserialized_input_successvariable - calls
env::state_readhost function to loadContractinto astatevariableenv::state_read’s result is unwrapped withOption::unwrap_or_defaultPanicOnDefaultmay be used to NOT let implementation ofDefaultforContractvalue become the outcomeContract’sstate, whenenv::state_readreturnsOption::None
- calls original
Contract::mutating_method(&mut state, deserialized_input_success.argument)as defined in#[near]annotated impl block - calls
env::state_writewith&stateas argument.
§Implementation of #[callback_unwrap] attribute and host functions calls used
This heading describes #[callback_unwrap].
In a nutshell and if the details of ABI generation layer are put aside,
#[near]
impl Contract {
pub fn method(
&mut self,
regular: String,
#[callback_unwrap] one: String,
#[callback_unwrap(max_bytes = 100)] two: String
) { /* .. */ }
}For above method using the attribute on arguments, changes the body of function generated in #[near] on mutating method
#[unsafe(no_mangle)]
pub extern "C" fn method() { /* .. */ }in the following way:
- arguments, annotated with
#[callback_unwrap], are no longer expected to be included intoinput, deserialized in (step 3,#[near]on mutating method). - for each argument, annotated with
#[callback_unwrap]:env::promise_result_checkedhost function is called with corresponding index, starting from 0 (0u64for argumentone,1u64for argumenttwoabove), and saved intopromise_resultvariable- if the
promise_resultis anErr(due to failed promise to too long result), thenenv::panic_strhost function is called to signal callback computation error - otherwise, if the
promise_resultisOk, it’s unwrapped and saved to adatavariable datais deserialized similar to that as usual (step 3,#[near]on mutating method), and saved todeserialized_n_promisevariable
- counterpart of (step 7,
#[near]on mutating method): original method is calledContract::method(&mut state, deserialized_input_success.regular, deserialized_0_promise, deserialized_1_promise), as defined in#[near]annotated impl block