From 746e4280a7e25b2036bd4e2f2c186cd76e1a7cde Mon Sep 17 00:00:00 2001 From: TobiasBond <135626372+TobieTom@users.noreply.github.com> Date: Tue, 25 Feb 2025 20:30:29 +0100 Subject: [PATCH] docs: document `callback_unwrap` attribute (#1313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: document `callback_unwrap` attribute Fixes #1303 Adds documentation for the `callback_unwrap` attribute under the `#[near]` macro docs, including: - Description of functionality and usage - Integration with `PromiseOrValue` - Cross-contract factorial example - Error handling alternatives - Rust doc annotations and references Resolves #1303 --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/near/near-sdk-rs/issues/1303?shareId=XXXX-XXXX-XXXX-XXXX). * comment: resolves https://github.com/near/near-sdk-rs/pull/1313#discussion_r1961570176 * comment: resolves https://github.com/near/near-sdk-rs/pull/1313#discussion_r1961871194 * comment: resolves https://github.com/near/near-sdk-rs/pull/1313#discussion_r1961617678 * comment: resolves https://github.com/near/near-sdk-rs/pull/1313#discussion_r1961729355 * comment: resolves https://github.com/near/near-sdk-rs/pull/1313#discussion_r1961639898 * comment: resolves https://github.com/near/near-sdk-rs/pull/1313#discussion_r1961814911 * comment: resolves https://github.com/near/near-sdk-rs/pull/1313#issuecomment-2682263612 * snapshot: fix compilation test schemars 0.8.21 -> 0.8.22 --------- Co-authored-by: dj8yf0μl --- near-sdk-macros/src/lib.rs | 37 -------- .../schema_derive_invalids.stderr | 2 +- near-sdk/src/environment/env.rs | 14 ++- near-sdk/src/lib.rs | 88 +++++++++++++++++++ near-sdk/src/near_annotations.rs | 27 +++--- 5 files changed, 116 insertions(+), 52 deletions(-) diff --git a/near-sdk-macros/src/lib.rs b/near-sdk-macros/src/lib.rs index a6a257dd8..0a4f4e0cb 100644 --- a/near-sdk-macros/src/lib.rs +++ b/near-sdk-macros/src/lib.rs @@ -374,43 +374,6 @@ pub fn ext_contract(attr: TokenStream, item: TokenStream) -> TokenStream { } } -// The below attributes a marker-attributes and therefore they are no-op. - -/// `callback` is a marker attribute it does not generate code by itself. -#[proc_macro_attribute] -#[deprecated(since = "4.0.0", note = "Case is handled internally by macro, no need to import")] -pub fn callback(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} - -/// `callback_args_vec` is a marker attribute it does not generate code by itself. -#[deprecated(since = "4.0.0", note = "Case is handled internally by macro, no need to import")] -#[proc_macro_attribute] -pub fn callback_vec(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} - -/// `serializer` is a marker attribute it does not generate code by itself. -#[deprecated(since = "4.0.0", note = "Case is handled internally by macro, no need to import")] -#[proc_macro_attribute] -pub fn serializer(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} - -/// `result_serializer` is a marker attribute it does not generate code by itself. -#[deprecated(since = "4.0.0", note = "Case is handled internally by macro, no need to import")] -#[proc_macro_attribute] -pub fn result_serializer(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} - -/// `init` is a marker attribute it does not generate code by itself. -#[deprecated(since = "4.0.0", note = "Case is handled internally by macro, no need to import")] -#[proc_macro_attribute] -pub fn init(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} - #[cfg(feature = "abi")] #[derive(darling::FromDeriveInput, Debug)] #[darling(attributes(abi), forward_attrs(serde, borsh_skip, schemars, validate))] diff --git a/near-sdk/compilation_tests/schema_derive_invalids.stderr b/near-sdk/compilation_tests/schema_derive_invalids.stderr index 9894ab188..dea29ed3a 100644 --- a/near-sdk/compilation_tests/schema_derive_invalids.stderr +++ b/near-sdk/compilation_tests/schema_derive_invalids.stderr @@ -87,7 +87,7 @@ error[E0277]: the trait bound `Inner: JsonSchema` is not satisfied (T0, T1, T2, T3, T4, T5) and $N others note: required by a bound in `SchemaGenerator::subschema_for` - --> $CARGO/schemars-0.8.21/src/gen.rs + --> $CARGO/schemars-0.8.22/src/gen.rs | | pub fn subschema_for(&mut self) -> Schema { | ^^^^^^^^^^ required by this bound in `SchemaGenerator::subschema_for` diff --git a/near-sdk/src/environment/env.rs b/near-sdk/src/environment/env.rs index 1211dc100..e5e95369b 100644 --- a/near-sdk/src/environment/env.rs +++ b/near-sdk/src/environment/env.rs @@ -1,7 +1,11 @@ //! Blockchain-specific methods available to the smart contract that allow to interact with NEAR runtime. -//! This is a wrapper around a low-level [`near_sys`](near_sys). Unless you know what you are doing prefer using `env::*` -//! whenever possible. In case of cross-contract calls prefer using even higher-level API available -//! through `callback_args`, `callback_args_vec`, `ext_contract`, `Promise`, and `PromiseOrValue`. +//! This is a wrapper around a low-level [`near_sys`](near_sys). +//! +//! Unless you know what you are doing prefer using `env::*` +//! whenever possible. +//! +//! In case of cross-contract calls prefer using higher-level API available +//! through [`crate::Promise`], and [`crate::PromiseOrValue`]. use std::convert::TryInto; use std::mem::{size_of, size_of_val}; @@ -754,7 +758,9 @@ pub fn alt_bn128_pairing_check(value: &[u8]) -> bool { /// ``` /// /// More info about promises in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/crosscontract#promises) +/// /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_create`] +/// /// Example usages of this low-level api are and /// pub fn promise_create( @@ -810,6 +816,7 @@ pub fn promise_create( /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_then`] +/// /// Example usages of this low-level api are and pub fn promise_then( promise_idx: PromiseIndex, @@ -1466,6 +1473,7 @@ pub(crate) fn promise_result_internal(result_idx: u64) -> Result<(), PromiseErro /// promise_return(promise); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_return`] +/// /// Example usages: [one](https://github.com/near/near-sdk-rs/tree/master/examples/cross-contract-calls/low-level/src/lib.rs), [two](https://github.com/near/near-sdk-rs/tree/master/examples/factory-contract/low-level/src/lib.rs) pub fn promise_return(promise_idx: PromiseIndex) { unsafe { sys::promise_return(promise_idx.0) } diff --git a/near-sdk/src/lib.rs b/near-sdk/src/lib.rs index 39dab8644..25e88593d 100644 --- a/near-sdk/src/lib.rs +++ b/near-sdk/src/lib.rs @@ -365,6 +365,94 @@ extern crate quickcheck; /// } /// ``` /// +/// ## `#[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`] 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_then`] and [`env::promise_return`] are used in +/// `factorial` method to set up a callback of `factorial_mult` with result of factorial for `(n-1)` +/// - [`#[private]`](near#private-annotates-methods-of-a-type-in-its-impl-block) on `factorial_mult` is used to +/// to allow only calling `factorial_mult` from factorial contract method by `CrossContract` itself +/// and disallow for it to be called externally by users +/// +/// ```rust +/// 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: +/// +/// ```bash,ignore +/// logs: [ +/// "Received n: 5", +/// "Received factorial_n_minus_one_result: 24", +/// "Multiplied 120", +/// ], +/// ``` +/// +/// ### Other examples within repo: +/// +/// - `Cross-Contract Factorial` again [examples/cross-contract-calls](https://github.com/near/near-sdk-rs/blob/9596835369467cac6198e8de9a4b72a38deee4a5/examples/cross-contract-calls/high-level/src/lib.rs?plain=1#L26) +/// - same example as [above](near#example-with-cross-contract-factorial), but uses [`Promise::then`] instead of [`env`](mod@env) host functions calls to set up a callback of `factorial_mult` +/// - [examples/adder](https://github.com/near/near-sdk-rs/blob/9596835369467cac6198e8de9a4b72a38deee4a5/examples/adder/src/lib.rs?plain=1#L30) +/// - [examples/adder](https://github.com/near/near-sdk-rs/blob/9596835369467cac6198e8de9a4b72a38deee4a5/examples/adder/src/lib.rs?plain=1#L31) +/// - [examples/callback-results](https://github.com/near/near-sdk-rs/blob/9596835369467cac6198e8de9a4b72a38deee4a5/examples/callback-results/src/lib.rs?plain=1#L51) +/// /// ## `#[near(event_json(...))]` (annotates enums) /// /// By passing `event_json` as an argument `near` will generate the relevant code to format events diff --git a/near-sdk/src/near_annotations.rs b/near-sdk/src/near_annotations.rs index 1dca4a56c..fb16d0254 100644 --- a/near-sdk/src/near_annotations.rs +++ b/near-sdk/src/near_annotations.rs @@ -3,18 +3,20 @@ //! This is not a real module; here we document the attributes that [`#[near]`](crate::near) //! and [`#[near_bindgen]`](crate::near_bindgen) macro use. //! -//! `near_bindgen` and `near_sdk` shares almost the same attributes: -//! * init -//! * payable -//! * private -//! * handle_result -//! * event_json -//! * contract_metadata -//! * serializer +//! `near_bindgen` and `near_sdk` share most of the attributes: +//! * `init` +//! * `payable` +//! * `private` +//! * `handle_result` +//! * `callback_unwrap` +//! * `event_json` +//! * `contract_metadata` +//! * `serializer` +//! * `result_serializer` //! -//! These attributes are only part of the `near` macro. -//! * serializers -//! * contract_state +//! Following attributes are only part of the `near` macro: +//! * `serializers` +//! * `contract_state` /// See [`near_sdk::near #[init]`](crate::near#init-annotates-methods-of-a-type-in-its-impl-block) pub fn init() {} @@ -31,6 +33,9 @@ pub fn result_serializer() {} /// See [`near_sdk::near #[handle_result]`](crate::near#handle_result-annotates-methods-of-a-type-in-its-impl-block) pub fn handle_result() {} +/// See [`near_sdk::near #[callback_unwrap]`](crate::near#callback_unwrap-annotates-function-arguments) +pub fn callback_unwrap() {} + /// See [`near_sdk::near #[near(event_json(...))]`](crate::near#nearevent_json-annotates-enums) pub fn event_json() {}