Skip to content

Commit

Permalink
doc: improved documentation for near-sdk and near-sdk-macros crates (#…
Browse files Browse the repository at this point in the history
…1262)

* tmp

* tmp

* nearschema

* link for store crate, neargas and neartoken types

* promises in env

* fmt and env intro

* promise_index and promise structs

* storage and functionerror

* small

* denbite review

* comments from PR 1259

* fmt

* clippy

* fmt

* docs test fix

* fix doc

* fix docs

* fix docs

* fix

* fix

* fix ci test

* fmt

* ignore to no_run

* remove unnecessary near-primitives and near-vm-runner for docs

* fix no-run tests

* fmt

* fix tests

* fmt

* fix tests

* checkj

* review

* added link to the cargo-near-new-project-template and remove not-actual information

---------

Co-authored-by: Artur Yurii Korchynskyi <[email protected]>
Co-authored-by: akorchyn <[email protected]>
  • Loading branch information
3 people authored Jan 30, 2025
1 parent 0027201 commit 25414c6
Show file tree
Hide file tree
Showing 11 changed files with 445 additions and 147 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ impl StatusMessage {
}
```

## Github Codespaces template
* Quick start your project with the [template repository](https://github.com/near/cargo-near-new-project-template) generated by `cargo near new` command
* The associated [template repository](https://github.com/near/cargo-near-new-project-template)'s README contains a `Code > Codespaces > Create codespace on main` screenshot.
* Clicking the button will result in using [Github Codespaces devcontainer](https://docs.github.com/en/codespaces). The containers allow you to start building without the need to install any dependencies on your machine and provide the actual version.

## Features

### Unit-testable
Expand Down Expand Up @@ -288,12 +293,6 @@ cargo near deploy

Builds the smart contract (equivalent to `cargo near build`) and guides you to deploy it to the blockchain.

### Using cargo build

```sh
RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
```

## Building with reproducible builds

Since WebAssembly compiler includes a bunch of debug information into the binary, the resulting binary might be
Expand Down
292 changes: 172 additions & 120 deletions near-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,57 +43,140 @@ struct NearMacroArgs {
inside_nearsdk: Option<bool>,
}

/// This attribute macro is used to enhance the near_bindgen macro.
/// It is used to add Borsh and Serde derives for serialization and deserialization.
/// It also adds `BorshSchema` and `JsonSchema` if needed
///
/// If you would like to add Borsh or Serde serialization and deserialization to your contract,
/// you can use the abi attribute and pass in the serializers you would like to use.
/// This attribute macro is used on a struct 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.
///
/// # Example
/// ## Example
///
/// ```ignore
/// #[near(serializers=[borsh, json])]
/// struct MyStruct {
/// pub name: String,
/// }
/// ```
/// effectively becomes:
///
/// This macro will generate code to load and deserialize state if the `self` parameter is included
/// as well as saving it back to state if `&mut self` is used.
///
/// # Parameter and result serialization
/// If the macro is used with Impl section, for parameter serialization, this macro will generate a struct with all of the parameters as
/// fields and derive deserialization for it. By default this will be JSON deserialized with `serde`
/// but can be overwritten by using `#[serializer(borsh)]`:
/// ```ignore
/// use borsh::{BorshSerialize, BorshDeserialize};
/// use serde::{Serialize, Deserialize};
/// use near_sdk_macro::NearSchema;
/// #[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, NearSchema)]
/// #[borsh(crate = "near_sdk::borsh")]
/// #[serde(crate = "near_sdk::serde")]
/// struct MyStruct {
/// pub name: String,
/// #[near]
/// impl Adder {
/// #[result_serializer(borsh)]
/// pub fn borsh_parameters(&self, #[serializer(borsh)] a: Pair, #[serializer(borsh)] b: Pair) -> Pair {
/// /// ...
/// }
/// }
/// ```
///
/// `#[near]` will also handle serializing and setting the return value of the
/// function execution based on what type is returned by the function. By default, this will be
/// done through `serde` serialized as JSON, but this can be overwritten using
/// `#[result_serializer(borsh)]`:
/// ```ignore
/// #[near]
/// impl Adder {
/// #[result_serializer(borsh)]
/// pub fn borsh_parameters(&self) -> Pair {
/// /// ...
/// }
/// }
/// ```
/// Please note that `BorshSchema` and `JsonSchema` are added inside NearSchema whenever you use near macro for struct or enum.
/// By default, if no serializers are passed, Borsh is used.
///
/// If you want this struct to be a contract state, you can pass in the contract_state argument.
/// # Usage for enum / struct
///
/// If the macro is used with struct or enum, it will make the struct or enum serializable with either
/// Borsh or Json depending on serializers passed. Use `#[near(serializers=[borsh])]` to make it serializable with Borsh.
/// Or use `#[near(serializers=[json])]` to make it serializable with Json. By default, borsh is used.
/// You can also specify both and none. BorshSchema or JsonSchema are always generated.
///
/// # Example
/// If you want the struct to be a contract state, you can pass in the contract_state argument.
///
/// ## Example
/// ```ignore
/// use near_sdk::near;
///
/// #[near(contract_state)]
/// struct MyStruct {
/// pub name: String,
/// pub struct Contract {
/// data: i8,
/// }
///
/// #[near]
/// impl Contract {
/// pub fn some_function(&self) {}
/// }
/// ```
/// becomes:
/// As well, the macro supports arguments like `event_json` and `contract_metadata`.
///
/// # Events Standard:
///
/// By passing `event_json` as an argument `near_bindgen` 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`
///
///
/// ## Examples
///
/// ```ignore
/// #[near_bindgen]
/// #[derive(BorshSerialize, BorshDeserialize, NearSchema)]
/// #[borsh(crate = "near_sdk::borsh")]
/// struct MyStruct {
/// pub name: String,
/// use near_sdk::near;
///
/// #[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();
/// }
///
/// }
/// ```
///
/// As well, the macro supports arguments like `event_json` and `contract_metadata`.
/// # Contract Source Metadata Standard:
///
/// By using `contract_metadata` as an argument `near` will populate the contract metadata
/// according to [`NEP-330`](<https://github.com/near/NEPs/blob/master/neps/nep-0330.md>) standard. This still applies even when `#[near]` is used without
/// any arguments.
///
/// All fields(version, link, standard) are optional and will be populated with defaults from the Cargo.toml file if not specified.
///
/// 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.
///
/// ## Examples
/// ```ignore
/// use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
/// use near_sdk::near;
///
/// #[derive(Default, BorshSerialize, BorshDeserialize)]
/// #[near(contract_metadata(
/// version = "39f2d2646f2f60e18ab53337501370dc02a5661c",
/// link = "https://github.com/near-examples/nft-tutorial",
/// standard(standard = "nep330", version = "1.1.0"),
/// standard(standard = "nep171", version = "1.0.0"),
/// standard(standard = "nep177", version = "2.0.0"),
/// ))]
/// struct Contract {}
/// ```
#[proc_macro_attribute]
pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
if attr.to_string().contains("event_json") {
Expand Down Expand Up @@ -235,103 +318,23 @@ pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}

/// This attribute macro is used on a struct 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.
///
/// This macro will generate code to load and deserialize state if the `self` parameter is included
/// as well as saving it back to state if `&mut self` is used.
///
/// For parameter serialization, this macro will generate a struct with all of the parameters as
/// fields and derive deserialization for it. By default this will be JSON deserialized with `serde`
/// but can be overwritten by using `#[serializer(borsh)]`.
///
/// `#[near_bindgen]` will also handle serializing and setting the return value of the
/// function execution based on what type is returned by the function. By default, this will be
/// done through `serde` serialized as JSON, but this can be overwritten using
/// `#[result_serializer(borsh)]`.
///
/// # Examples
///
/// This macro is deprecated. Use [#\[near\]](./attr.near.html) instead. The difference between #\[near\] and #\[near_bindgen\] is that
/// with #\[near_bindgen\] you have to manually add boilerplate code for structs and enums so that they become Json- and Borsh-serializable:
/// ```ignore
/// use near_sdk::near_bindgen;
///
/// #[near_bindgen]
/// pub struct Contract {
/// data: i8,
/// }
///
/// #[near_bindgen]
/// impl Contract {
/// pub fn some_function(&self) {}
/// #[derive(BorshSerialize, BorshDeserialize, NearSchema)]
/// #[borsh(crate = "near_sdk::borsh")]
/// struct MyStruct {
/// pub name: String,
/// }
/// ```
///
/// Events Standard:
///
/// By passing `event_json` as an argument `near_bindgen` 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`
///
///
/// # Examples
///
/// Instead of:
/// ```ignore
/// use near_sdk::near_bindgen;
///
/// #[near_bindgen(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_bindgen]
/// impl Contract {
/// pub fn some_function(&self) {
/// MyEvents::StringEvent(
/// String::from("some_string")
/// ).emit();
/// }
///
/// #[near]
/// struct MyStruct {
/// pub name: String,
/// }
/// ```
///
/// Contract Source Metadata Standard:
///
/// By using `contract_metadata` as an argument `near_bindgen` will populate the contract metadata
/// according to [`NEP-330`](<https://github.com/near/NEPs/blob/master/neps/nep-0330.md>) standard. This still applies even when `#[near_bindgen]` is used without
/// any arguments.
///
/// All fields(version, link, standard) are optional and will be populated with defaults from the Cargo.toml file if not specified.
///
/// 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.
///
/// # Examples
/// ```ignore
/// use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
/// use near_sdk::near_bindgen;
///
/// #[derive(Default, BorshSerialize, BorshDeserialize)]
/// #[near_bindgen(contract_metadata(
/// version = "39f2d2646f2f60e18ab53337501370dc02a5661c",
/// link = "https://github.com/near-examples/nft-tutorial",
/// standard(standard = "nep330", version = "1.1.0"),
/// standard(standard = "nep171", version = "1.0.0"),
/// standard(standard = "nep177", version = "2.0.0"),
/// ))]
/// struct Contract {}
/// ```
#[proc_macro_attribute]
pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
if attr.to_string().contains("event_json") {
Expand Down Expand Up @@ -459,7 +462,7 @@ fn process_impl_block(
/// Each of these static methods takes positional arguments defined by the Trait,
/// then the receiver_id, the attached deposit and the amount of gas and returns a new Promise.
///
/// # Examples
/// ## Examples
///
/// ```ignore
/// use near_sdk::ext_contract;
Expand All @@ -469,7 +472,19 @@ fn process_impl_block(
/// fn mult(&self, a: u64, b: u64) -> u128;
/// fn sum(&self, a: u128, b: u128) -> u128;
/// }
///
/// #[near]
/// impl Contract {
/// pub fn multiply_by_five(&mut self, number: u64) -> Promise {
/// ext_calculator::ext(self.calculator_account.clone())
/// .with_static_gas(Gas::from_tgas(5))
/// .mult(number, 5)
/// }
/// }
///
/// ```
///
/// See more information about role of ext_contract in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/crosscontract)
#[proc_macro_attribute]
pub fn ext_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
if let Ok(mut input) = syn::parse::<ItemTrait>(item) {
Expand Down Expand Up @@ -553,6 +568,18 @@ struct DeriveNearSchema {
borsh: Option<bool>,
}

/// `NearSchema` is a derive macro that generates `BorshSchema` and / or `JsonSchema` implementations.
/// Use `#[abi(json)]` attribute to generate code for `JsonSchema`. And `#[abi(borsh)]` for `BorshSchema`.
/// You can use both and none as well.
/// ## Example
/// ```ignore
/// #[derive(NearSchema)]
/// #[abi(borsh)]
/// struct Value {
/// field: InnerValue,
/// }
/// ```
/// In this example, BorshSchema will be generated for `Value` struct.
#[proc_macro_derive(NearSchema, attributes(abi, serde, borsh, schemars, validate, inside_nearsdk))]
pub fn derive_near_schema(#[allow(unused)] input: TokenStream) -> TokenStream {
#[cfg(not(feature = "abi"))]
Expand Down Expand Up @@ -787,6 +814,31 @@ pub fn derive_no_default(item: TokenStream) -> TokenStream {
/// `BorshStorageKey` generates implementation for `BorshIntoStorageKey` trait.
/// It allows the type to be passed as a unique prefix for persistent collections.
/// The type should also implement or derive `BorshSerialize` trait.
///
/// More information about storage keys in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/storage)
/// ## Example
/// ```ignore
/// #[derive(BorshSerialize, BorshDeserialize, BorshStorageKey)]
/// #[borsh(crate = "near_sdk::borsh")]
/// pub enum StorageKey {
/// Messages,
/// }
///
/// // Define the contract structure
/// #[near(contract_state)]
/// pub struct Contract {
/// messages: Vector<String>
/// }
///
/// // Define the default, which automatically initializes the contract
/// impl Default for Contract {
/// fn default() -> Self {
/// Self {
/// messages: Vector::new(StorageKey::Messages)
/// }
/// }
/// }
/// ```
#[proc_macro_derive(BorshStorageKey)]
pub fn borsh_storage_key(item: TokenStream) -> TokenStream {
let (name, generics) = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
Expand Down
Loading

0 comments on commit 25414c6

Please sign in to comment.