Skip to content

Fixes for rust sdk #2325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/1.concepts/protocol/access-keys.md
Original file line number Diff line number Diff line change
@@ -11,6 +11,9 @@ NEAR accounts present the **unique** feature of being able to hold multiple [Acc
1. `Full-Access Keys`: Have full control over the account, and should **never be shared**
2. `Function-Call Keys`: Can sign calls to specific contract, and are **meant to be shared**


Currently supported curve types are `ed25519` (default curve type used for public key cryptography in NEAR) and `secp256k1` (supported for compatibility with other blockchains).

---

## Full-Access Keys {#full-access-keys}
2 changes: 1 addition & 1 deletion docs/1.concepts/protocol/gas.md
Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@ The cost of calling a function will depend on how complex the function is, but w
:::

:::tip Deploying a Contract**
Note that this covers the gas cost of uploading and writing bytes to storage, but does **not** cover the cost of holding them in storage (which is `1Ⓝ ~ 100kb`).
Note that this covers the gas cost of uploading and writing bytes to storage, but does **not** cover the cost of holding them in storage (which is `1Ⓝ ~ 100kb`). More information about storage staking [here](../storage/storage-staking.md)
:::

<details className="info">
4 changes: 2 additions & 2 deletions docs/2.build/2.smart-contracts/anatomy/collections.md
Original file line number Diff line number Diff line change
@@ -109,8 +109,8 @@ SDK collections are useful when you are planning to store large amounts of data
|-----------------------------------------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `store::Vector<T>` | `Vec<T>` | A growable array type. The values are sharded in memory and can be used for iterable and indexable values that are dynamically sized. |
| <code>store::LookupMap`<K,&nbsp;V>`</code> | <code>HashMap`<K,&nbsp;V>`</code> | This structure behaves as a thin wrapper around the key-value storage available to contracts. This structure does not contain any metadata about the elements in the map, so it is not iterable. |
| <code>store::IterableMap`<K,&nbsp;V>`</code> | <code>HashMap`<K,&nbsp;V>`</code> | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. |
| <code>store::UnorderedMap`<K,&nbsp;V>`</code> | <code>HashMap`<K,&nbsp;V>`</code> | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. |
| <code>store::IterableMap`<K,&nbsp;V>`</code> | <code>HashMap`<K,&nbsp;V>`</code> | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. To be used when remove is not a frequent operation. |
| <code>store::UnorderedMap`<K,&nbsp;V>`</code> | <code>HashMap`<K,&nbsp;V>`</code> | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. Optimized for fast removal. Other operations become slower. |
| `store::LookupSet<T>` | `HashSet<T>` | A set, which is similar to `LookupMap` but without storing values, can be used for checking the unique existence of values. This structure is not iterable and can only be used for lookups. |
| `store::IterableSet<T>` | `HashSet<T>` | An iterable equivalent of `LookupSet` which stores additional metadata for the elements contained in the set. |
| `store::UnorderedSet<T>` | `HashSet<T>` | An iterable equivalent of `LookupSet` which stores additional metadata for the elements contained in the set. |
44 changes: 42 additions & 2 deletions docs/2.build/2.smart-contracts/anatomy/crosscontract.md
Original file line number Diff line number Diff line change
@@ -41,7 +41,16 @@ While making your contract, it is likely that you will want to query information
<Github fname="lib.rs"
url="https://github.com/near-examples/cross-contract-calls/blob/main/contract-simple-rs/src/lib.rs"
start="22" end="51" />
<Github fname="external.rs"
</Language>

</CodeTabs>

Note that in order for that to work in Rust, you have to implement the interface of the contract you call:

<CodeTabs>
<Language value="rust" language="rust">
<Github fname="external.rs"
url="https://github.com/near-examples/cross-contract-calls/blob/main/contract-simple-rs/src/external.rs"
start="2" end="12" />

@@ -66,6 +75,13 @@ Calling another contract passing information is also a common scenario. Below yo
<Github fname="lib.rs"
url="https://github.com/near-examples/cross-contract-calls/blob/main/contract-simple-rs/src/lib.rs"
start="53" end="80" />

</Language>

</CodeTabs>

<CodeTabs>
<Language value="rust" language="rust">
<Github fname="external.rs"
url="https://github.com/near-examples/cross-contract-calls/blob/main/contract-simple-rs/src/external.rs"
start="2" end="12" />
@@ -149,10 +165,34 @@ You can attach an unused GAS weight by specifying the `.with_unused_gas_weight()
</TabItem>
</Tabs>

:::info
:::caution

If a function returns a promise, then it will delegate the return value and status of transaction execution, but if you return a value or nothing, then the `Promise` result will not influence the transaction status



Unless there is a very good reason not to, all the cross-contract calling functions should return the Promise as the result of its execution, otherwise, the cross-contract calls won't affect the result of the contract execution.

For example:
<Tabs>
<TabItem value="rs" label="🦀 Rust">

```rust
fn destroy(&mut self) {
Promise::...; // notice the `;` here and the empty return value in the function signature
}
fn destroy(&mut self) -> Promise {
Promise::... // notice there is no `;` here and the return value in the function signature Promise
}
```

</TabItem>
</Tabs>

The difference between the two is that the first will schedule the cross-contract call, but its return value will be ignored (even if the call fails, the result of the `destroy` function will be OK), and transaction return value may even be returned earlier than all the in-flight cross-contract calls complete since they do not affect the execution result.

Without linking the cross-contract call with the result of the `destroy` function, it may start removing itself earlier than the cross-contract call is finished and if it fails, it's state will become unreachable.

:::

:::caution
56 changes: 38 additions & 18 deletions docs/2.build/2.smart-contracts/anatomy/environment.md
Original file line number Diff line number Diff line change
@@ -67,23 +67,23 @@ Every method execution has an environment associated with information such as:

## Who is Calling? Who am I?

The environment gives you access to 3 important users: the `current_account`, the `predecessor`, and the `signer`.
The environment gives you access to 3 important users: the `current_account_id()`, the `predecessor_account_id()`, and the `signer_account_id()`.

### Current Account

The `current_account` contains the address in which your contract is deployed. This is very useful to implement ownership, e.g. making a public method only callable by the contract itself.
The `current_account_id()` contains the address in which your contract is deployed. This is very useful to implement ownership, e.g. making a public method only callable by the contract itself.

### Predecessor and Signer

The `predecessor` is the account that called the method in the contract. Meanwhile, the `signer` is the account that _signed_ the initial transaction.
The `predecessor_account_id()` is the account that called the method in the contract. Meanwhile, the `signer_account_id()` is the account that _signed_ the initial transaction.

During a simple transaction (no [cross-contract calls](../anatomy/crosscontract.md)) the `predecessor` is the same as the `signer`. For example, if **alice.near** calls **contract.near**, from the contract's perspective, **alice.near** is both the `signer` and the `predecessor`. However, if **contract.near** creates a [cross-contract call](../anatomy/crosscontract.md), then the `predecessor` changes down the line. In the example below, when **pool.near** executes, it would see **contract.near** as the `predecessor` and **alice.near** as the `signer`.
During a simple transaction (no [cross-contract calls](../anatomy/crosscontract.md)) the `predecessor_account_id()` is the same as the `signer_account_id()`. For example, if **alice.near** calls **contract.near**, from the contract's perspective, **alice.near** is both the `signer_account_id()` and the `predecessor_account_id()`. However, if **contract.near** creates a [cross-contract call](../anatomy/crosscontract.md), then the `predecessor_account_id()` changes down the line. In the example below, when **pool.near** executes, it would see **contract.near** as the `predecessor_account_id()` and **alice.near** as the `signer_account_id()`.

![img](https://miro.medium.com/max/1400/1*LquSNOoRyXpITQF9ugsDpQ.png)
*You can access information about the users interacting with your smart contract*

:::tip
In most scenarios you will **only need to know the predecessor**. However, there are situations in which the signer is very useful. For example, when adding [NFTs](../../5.primitives/nft.md) into [this marketplace](https://github.com/near-examples/nft-tutorial/blob/7fb267b83899d1f65f1bceb71804430fab62c7a7/market-contract/src/nft_callbacks.rs#L42), the contract checks that the `signer`, i.e. the person who generated the transaction chain, is the NFT owner.
In most scenarios you will **only need to know the predecessor**. However, there are situations in which the signer is very useful. For example, when adding [NFTs](../../5.primitives/nft.md) into [this marketplace](https://github.com/near-examples/nft-tutorial/blob/7fb267b83899d1f65f1bceb71804430fab62c7a7/market-contract/src/nft_callbacks.rs#L42), the contract checks that the `signer_account_id()`, i.e. the person who generated the transaction chain, is the NFT owner.
:::

---
@@ -92,27 +92,27 @@ In most scenarios you will **only need to know the predecessor**. However, there
The environment gives you access to 3 token-related parameters, all expressed in yoctoNEAR (1 Ⓝ = 10<sup>24</sup>yⓃ):

### Attached Deposit
`attached_deposit` represents the amount of yoctoNEAR the predecessor attached to the call.
`attached_deposit()` represents the amount of yoctoNEAR the predecessor attached to the call.

This amount is **already deposited** in your contract's account, and is **automatically returned** to the `predecessor` if your **method panics**.
This amount is **already deposited** in your contract's account, and is **automatically returned** to the `predecessor_account_id()` if your **method panics**.

:::warning
If you make a [cross-contract call](../anatomy/crosscontract.md) and it panics, the funds are sent back to **your contract**. See how to handle this situation in the [callback section](../anatomy/crosscontract.md#what-happens-if-the-function-i-call-fails)
:::

### Account Balance

`account_balance` represents the balance of your contract (`current_account`).
`account_balance()` represents the balance of your contract (`current_account_id()`).

It includes the `attached_deposit`, since it was deposited when the method execution started.
It includes the `attached_deposit()`, since it was deposited when the method execution started.

If the contract has any locked $NEAR, it will appear in `account_locked_balance`.
If the contract has any locked $NEAR, it will appear in `account_locked_balance()`.

---

### Storage Used

`storage_used` represents the amount of [storage](../anatomy/storage.md) that is currently being used by your contract.
`storage_used()` represents the amount of [storage](../anatomy/storage.md) that is currently being used by your contract.

:::tip
If you want to know how much storage a structure uses, print the storage before and after storing it.
@@ -126,15 +126,15 @@ The environment exposes three different ways to tell the pass of time, each repr

### Timestamp

The `timestamp` attribute represents the approximated **UNIX timestamp** in **nanoseconds** at which this call was executed. It quantifies time passing in a human way, enabling us to check if a specific date has passed or not.
The `block_timestamp()` attribute represents the approximated **UNIX timestamp** in **nanoseconds** at which this call was executed. It quantifies time passing in a human way, enabling us to check if a specific date has passed or not.

### Current Epoch

The NEAR blockchain groups blocks in [Epochs](../../../1.concepts/basics/epoch.md). The `current_epoch` attribute measures how many epochs have passed so far. It is very useful to coordinate with other contracts that measure time in epochs, such as the [validators](../../../1.concepts/basics/validators.md).
The NEAR blockchain groups blocks in [Epochs](../../../1.concepts/basics/epoch.md). The `current_epoch()` attribute measures how many epochs have passed so far. It is very useful to coordinate with other contracts that measure time in epochs, such as the [validators](../../../1.concepts/basics/validators.md).

### Block Index

The `block_index` represents the index of the block in which this transaction will be added to the blockchain.
The `block_index()` represents the index of the block in which this transaction will be added to the blockchain.

---

@@ -146,13 +146,13 @@ Gas can be thought of as wall time, where 1 PetaGas (1_000 TGas) is ~1 second of

Each code instruction costs a certain amount of Gas, and if you run out of it, the execution halts with the error message `Exceeded the prepaid gas`.

The environment gives you access to two gas-related arguments: `prepaid_gas` and `used_gas`.
The environment gives you access to two gas-related arguments: `prepaid_gas()` and `used_gas()`.

### Prepaid Gas
`prepaid_gas` represents the amount of Gas the `predecessor` attached to this call. It cannot exceed the limit 300TGas (300 * 10<sup>12</sup> Gas).
`prepaid_gas` represents the amount of Gas the `predecessor_account_id()` attached to this call. It cannot exceed the limit 300TGas (300 * 10<sup>12</sup> Gas).

### Used Gas
`used_gas` contains the amount of Gas that has been used so far. It is useful to estimate the Gas cost of running a method.
`used_gas()` contains the amount of Gas that has been used so far. It is useful to estimate the Gas cost of running a method.

:::warning
During [cross-contract calls](./crosscontract.md) always make sure the callback has enough Gas to fully execute.
@@ -203,6 +203,7 @@ Besides environmental variables, the SDK also exposes some functions to perform
| Function Name | SDK method | Description |
|-----------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| SHA 256 | `env::sha256(value)` | Hashes a sequence of bytes using sha256. |
| ED25519 verify | `env::ed25519_verify(signature, message, public_key)` | Verifies if the message was signed with correct public key |
| Keccak 256 | `env::keccak256(value)` | Hashes a sequence of bytes using keccak256. |
| Keccak 512 | `env::keccak512(value)` | Hashes a sequence of bytes using keccak512. |
| SHA 256 (Array) | `env::sha256_array(value)` | Hashes the bytes using the SHA-256 hash function. This returns a 32 byte hash. |
@@ -214,13 +215,32 @@ Besides environmental variables, the SDK also exposes some functions to perform
| Log String | `env::log_str(message)` | Logs the string message. This message is stored on chain. |
| Validator Stake | `env::validator_stake(account_id)` | For a given account return its current stake. If the account is not a validator, returns 0. |
| Validator Total Stake | `env::validator_total_stake()` | Returns the total stake of validators in the current epoch. |

| Random seed | `env::random_seed()` | Returns random number |
| Random seed array | `env::random_seed_array()` | Returns array of random numbers |
</TabItem>

</Tabs>

## Low-level functions for Rust sdk:
<Tabs className="language-tabs" groupId="code-tabs">
<TabItem value="rust" label="🦀 Rust">
| Function Name | SDK method | Description |
|-----------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Setup panic hook | `env::setup_panic_hook()` | Sets up `env::panic_str` method to call when unexpected error occurs.
| Contract call input | `env::input()` | Returns serde-serialized arguments to the method call |
| Create promise | `env::promise_create(account_id, function_name, arguments, amount, gas)` | Makes cross-contract call and returns promise |
| Read storage | `env::storage_read(key)` | Reads value for the specified key from storage |
| Write storage | `env::storage_write(key, value)` | Writes value for the specified key to storage |
| Remove storage | `env::storage_remove(key)` | Removes key with it's value from storage |
</TabItem>
</Tabs>

:::info
In the JS SDK, `throw new Error("message")` mimics the behavior of Rust's `env::panic_str("message")`.
:::

:::info
More information on Rust sdk environment methods you can find in it's [documentation](https://docs.rs/near-sdk/latest/near_sdk/env/index.html)
:::

---
196 changes: 196 additions & 0 deletions docs/2.build/2.smart-contracts/anatomy/gascalc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
---
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks more like a tutorial than a section for Build

If you check all the other pages on Smart Contract / Anatomy, you will see that they only talk about the SDK

Here, we mix multiple tools, therefore it is better suited as a tutorial

id: gascalc
title: Gas estimation
hide_table_of_contents: true
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import {CodeTabs, Language, Github} from "@site/src/components/codetabs";
import CodeBlock from '@theme/CodeBlock';
import {ExplainCode, Block, File} from '@site/src/components/CodeExplainer/code-explainer';

This guide explains how to estimate, allocate, and check gas for cross-contract calls in NEAR. It includes practical examples, tools like near-cli, and API usage to streamline gas estimation. All examples are in Rust.

## Overview of Gas in NEAR
Gas in NEAR is used to measure computational resources for transactions. When calling cross-contract functions, you need to allocate gas carefully to prevent failures or inefficiencies. Key components include:

- Gas burned: Gas consumed during execution.
- Refunded gas: Unused prepaid gas returned to the caller.
- Used gas: The total amount of gas deducted from the transaction (includes burned and refunded gas)
- Prepaid Gas: Gas attached to a transaction upfront, allocated by the caller.

## Cross-Contract Call Workflow
When invoking another contract:

1. Attach prepaid gas to the transaction.
2. Allocate gas for:
- The called contract.
- Any callbacks or subsequent operations.
- The current function’s execution.
3. Test and benchmark, monitor gas usage (burned, used, refunded) to optimize allocation.

Gas to Attach = Gas for Current Function + Gas for Target Contract + Gas for Callback

:::note
Near sets a gas limit per transaction: 300 Tgas (300 × 10^12 gas)
:::

### Example: Simple Cross-Contract Call

```rust
use near_sdk::Promise;

const TGAS: u64 = 1_000_000_000_000; // 1 TeraGas
const GAS_FOR_TARGET: u64 = 50 * TGAS; // Gas for the target contract
const GAS_FOR_CALLBACK: u64 = 20 * TGAS; // Gas reserved for the callback

#[near]
impl MyContract {
/// Initiates a cross-contract call
pub fn call_another_contract(&self, target_contract: AccountId, args: Vec<u8>) {
// Ensure there's enough gas for the call and the callback
assert!(
env::prepaid_gas() > GAS_FOR_TARGET + GAS_FOR_CALLBACK,
"Not enough gas attached"
);

// Call the target contract
Promise::new(target_contract.clone())
.function_call(
"target_function".to_string(),
args,
0, // No attached deposit
GAS_FOR_TARGET, // Gas for this function
)
// Add a callback to handle the response
.then(
Promise::new(env::current_account_id())
.function_call(
"on_callback".to_string(),
vec![],
0, // No attached deposit
GAS_FOR_CALLBACK,
)
);
}

/// Callback handler
pub fn on_callback(&self) {
if let Some(result) = env::promise_result(0) {
match result {
PromiseResult::Successful(data) => {
env::log_str("Callback succeeded");
// Process the data if needed
}
PromiseResult::Failed => {
env::log_str("Callback failed");
}
_ => (),
}
}
}
}
```

## How to Check Gas Usage
### Testnet / mainnet
You can use `nearblocks` explorer to see gas usage. You may first check for existing transactions in mainnet or testnet and see how much gas did they use. Here is an example of a token swap transaction on a decentralized exchange.

For example, for swap operation transaction `s5dL1DTiJ5pJugU2KpyTWLCbCT3pCXM3Pw8zSrTfqD4` in mainnet, go to https://nearblocks.io/txns/s5dL1DTiJ5pJugU2KpyTWLCbCT3pCXM3Pw8zSrTfqD4

There you can see `Usage by Txn: 37.97 Tgas`
That means that the transaction used 37.97 TGas. So the amount of gas you should attach to call the contract is 37.97 Tgas.

### Sandbox
When developing contract, you may want to know how much gas is used every time you change the contract code. In order to do that, you can run your code in sandox and see how much gas is used.

On integrations tests [page](../testing/integration-test.md) you can find how to run sandbox.

Use `env::used_gas()` inside your contract to check how much gas is used
```rust
env::log_str(&format!("Gas used: {}", env::used_gas()));
```


## Setting gas in cross-contract call

The following are the two different ways to make a contract call. Take a look on how the way of setting gas changes.

Example: Using `Promise::new().function_call`
```rust
use near_sdk::Promise;

#[near]
impl Contract {
fn call_other_contract() {
Promise::new("receiver.testnet".to_string())
.function_call(
"target_function".to_string(), // Method name to call on the contract
b"{}".to_vec(), // Arguments to pass as a byte array (empty in this case)
0, // Attached deposit (in yoctoNEAR), set to 0 here
10_000_000_000_000, // Gas attached for this call
)
}
}
```

Example: Using `ext` structure and `with_static_gas` method
```rust
#[ext_contract(ext_target)]
pub trait TargetContract {
fn target_function(&self, arg1: String);
}

#[near]
impl Contract {
pub fn call_with_static_gas(&self, target: AccountId) -> Promise {
ext_target::ext("receiver.testnet".to_string())
.with_static_gas(10_000_000_000_000)
.target_fuction("hello")
}
}
```

## Using prepaid gas

In order to check whether the gas required is attached to a call, you can use `env::prepaid_gas`. It allows to stop the execution in complex workflows where you know in advance that the gas attached may be insufficient:
```rust
#[near]
impl MyContract {
pub fn example_prepaid(&self, target: AccountId, args: Vec<u8>) {
assert!(
env::prepaid_gas() > 80 * TGAS,
"Insufficient prepaid gas"
);
// ...
}
}
```

Sometimes, you just don't know how much gas will be used. It can happen when you make a cross-contract call to a contract that is not predefined and it's gas usage may be unpredictable. Also, if your contract's code is changing rapidly, it's gas usage may change rapidly as well. In order to make your code still work and make the user decide how much gas to attach, you can use a trick with `env::prepaid_gas` when making cross-contract call.

Instead of estimating the gas for a cross-contract call, use a part of `prepaid_gas`, for example, one third:
```rust
#[near]
impl Contract {
pub fn call_with_static_gas(&self, target: AccountId) -> Promise {
...
ext_target::ext("receiver.testnet".to_string())
.with_static_gas(env::prepaid_gas() / 3)
.target_fuction("hello")
...
}
}
```
It will allow you to enable the user to execute contract method when the gas usage unexpectedly increases.

## Common Pitfalls to Avoid
1. Running Out of Gas:
- Ensure that the total gas attached to the transaction is within the 300 Tgas limit.
- Always allocate some gas for callbacks if you expect them.
- Reserve extra gas for scenarios where the target contract performs unexpectedly resource-intensive operations.
2. Over-Allocation:
- While unused gas is refunded, attaching excessive gas may cause unnecessary delays in execution due to prioritization in the network.
3. Mismanaging Deposits:
- Ensure attached deposits are explicitly set to 0 if not required to avoid unexpected costs
149 changes: 149 additions & 0 deletions docs/2.build/2.smart-contracts/anatomy/macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
id: macros
title: Internals of Rust macros
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

Let's talk about what happens internally when `near` macro is used. Consider this example:

```rust
#[near(contract_state)]
pub struct PersonContract {
name: String,
age: u8,
}

#[near]
impl PersonContract {
pub fn set_person(&mut self, name: String, age: u8) {
self.name = name;
self.age = age;
}
}
```

The following is what `near` macro does. The generated code is not a real output, but a demonstration:
```rust
// Using Borsh so that it can be keeped in the blockchain storage.
#[derive(BorshSerialize, BorshDeserialize)]
#[borsh(crate = "::near_sdk::borsh")]
pub struct PersonContract {
name : String,
age : u8,
}

// This function is executed when someone calls a contract method
pub extern "C" fn set_person()
{
// Setting up panic hook so that when error appears, it is handled correctly (env::panic_str() is called)
setup_panic_hook();

// To accept a deposit, you have to mark the function as #[payable]. Otherwise, the execution has to be interrupted.
if env::attached_deposit().as_yoctonear() != 0 {
env::panic_str("Method set_person doesn't accept deposit");
}

// Creating a structure for arguments, so it can be deserialized with serde when the method is called
#[derive(Deserialize)]
#[serde(crate = "::near_sdk::serde")]
struct Input {
name: String,
age: u8,
}

// Getting arguments
let Input { name, age, } = match env::input() {
Some(input) => match serde_json::from_slice(&input) {
Ok(deserialized) => deserialized,
Err(_) => env::panic_str("Failed to deserialize input from JSON.")
},
None => env::panic_str("Expected input since method has arguments.")
};

// Reading contract state
let mut contract: PersonContract = env::state_read().unwrap_or_default();

// Calling contract method
contract.set_person(name, age);

// Writing contract state
env::state_write(& contract);
}
```

In addition, a struct for cross contract calls is generated. It is done so that the contract can easily call itself. Similar code is generated when `#[ext_contract]` macro is used:
```rust
impl PersonContractExt {
// If you execute this method from another contract, it's going to call `pub extern "C" fn set_person()`
pub fn set_person(self, name : String, age : u8,) -> Promise {
let __args =
{
// Creating a structure for arguments, so it can be serialized with serde and passed
#[derive(serde::Serialize)]
#[serde(crate = "::near_sdk::serde")]
struct Input < 'nearinput > {
name : & 'nearinput String,
age : & 'nearinput u8,
}

// Serializing arguments with serde
let __args = Input { name : & name, age : & age, };
serde_json::to_vec(& __args)
};

// Actually calling other contract
Promise::new(self.account_id).function_call_weight(
String::from("set_person"),
__args,
self.deposit,
self.static_gas,
self.gas_weight,
)
}
}
```

In addition, helpers are generated:
```rust
impl PersonContract
{
pub fn ext(account_id : :: near_sdk :: AccountId) -> PersonContractExt
{
PersonContractExt
{
account_id,
deposit: from_near(0),
static_gas: from_gas(0),
gas_weight: default(),
}
}
}


pub struct PersonContractExt
{
pub(crate) account_id: AccountId,
pub(crate) deposit: NearToken,
pub(crate) static_gas: Gas,
pub(crate) gas_weight: GasWeight,
}

impl PersonContractExt
{
pub fn with_attached_deposit(mut self, amount: NearToken) -> Self {
self.deposit = amount;
self
}
pub fn with_static_gas(mut self, static_gas: Gas) -> Self {
self.static_gas = static_gas;
self
}
pub fn with_unused_gas_weight(mut self, gas_weight : u64) -> Self {
self.gas_weight = GasWeight(gas_weight);
self
}
}

```
41 changes: 37 additions & 4 deletions docs/2.build/2.smart-contracts/anatomy/serialization.md
Original file line number Diff line number Diff line change
@@ -110,15 +110,15 @@ When returning the result, the contract will automatically encode the object `B{
into its JSON serialized value: `"{\"success\":true, \"other_number\":0}"` and return this
string.

:::caution JSON Limitations
### JSON Limitations
:::caution
Since JSON is limited to `52 bytes` numbers, you cannot use `u64`/`u128` as input
or output. JSON simply cannot serialize them. Instead, you must use `Strings`.

The `NEAR SDK RS` currently implements the `near_sdk::json_types::{U64, I64, U128, I128}`
that you can use for input / output of data.
:::


---

## Borsh: State Serialization
@@ -127,7 +127,7 @@ Under the hood smart contracts store data using simple **key/value pairs**. This
the contract needs to translate complex states into simple key-value pairs.

For this, NEAR contracts use [borsh](https://borsh.io) which is optimized for (de)serializing
complex objects into smaller streams of bytes.
complex objects into smaller streams of bytes. Borsh is used to serialize and deserialize both keys and values.

:::tip SDK-JS still uses json
The JavaScript SDK uses JSON to serialize objects in the state, but the borsh implementation
@@ -138,7 +138,7 @@ should arrive soon
Let's look at this example, written only for educational purposes:

```rust
#[near(serializers = [json, borsh])]
#[near(contract_state, serializers = [json, borsh])]
#[derive(PanicOnDefault)]
pub struct Contract {
string: String,
@@ -211,6 +211,21 @@ If we initialize the state we can see how Borsh is used to serialize the state
</TabItem>
</Tabs>

Under the hood, every method in smart contract where state is mutated, finishes with writing state to storage, where key is "STATE" and value is contract state, serialized with Borsh. `storage_write` is used for that:

```rust
pub fn init(...) -> Self {
...
storage_write(b"STATE", &borsh::to_vec(&Self {string, vector}));
}
```

Also, when pushing to vector, another `storage_write` is used:
```rust
// here 0 is element index
storage_write(b"prefix:0", &borsh::to_vec(&element))
```

<details>

<summary> Result </summary>
@@ -287,6 +302,24 @@ the vector now has 2 elements.
At the same time, a new key-value was added adding the new vector entry: the `1u8` we just added.
#### BorshSerialize
In order for state reading and writing to work, contract structure should be borsh serializable. That means every field and their fields should be borsh serializable as well. Otherwise, code is not going to compile. For that, `BorshSerialize` and `BorshDeserialize` macros can be used. But `#[near]` macro specifies it internally. So, all the structures and enums, that are going to be used as fields of contract, have to be wrapped with `#[near]` macro.

Example:

```rust
#[near]
pub struct InnerStruct {
name: String,
age: u8
}
#[near(contract_state)]
pub struct Contract {
person: InnerStruct
}
```

<hr className="subsection" />

<!-- We should see where to move/replicate this -->
22 changes: 22 additions & 0 deletions docs/2.build/2.smart-contracts/anatomy/storage.md
Original file line number Diff line number Diff line change
@@ -13,6 +13,28 @@ Smart contracts store data in their account's state, which is public on the chai

It is important to know that the account's **code** and account's **storage** are **independent**. [Updating the code](../release/upgrade.md) does **not erase** the state.

## Key-value storage

Behind the scenes, all data stored on the NEAR blockchain is saved in a key-value database. A storage key is a unique identifier used to access data stored in a smart contract’s persistent storage. The key-value storage model in NEAR allows smart contracts to manage and retrieve data efficiently along with minimizing costs for storage.

Keys can be any binary sequence, but they are typically structured for ease of use (e.g., as human-readable strings).
Data associated with a key persists across contract calls and is stored on-chain until explicitly deleted.

SDK collections are instantiated using a "prefix" so that the elements from different collections access different data.

Here is an example:

```ts
const tokenToOwner = new PersistentMap<TokenId, AccountId>("t2o");
```

That `'t2o'` variable that's passed to `PersistentMap` helps it keep track of all its values. If your account `example.near` owns token with ID `0`, then at the time of writing, here's the data that would get saved to the key-value database:

- key: `t2o::0`
- value: `example.near`

Storage key type should implement the trait `IntoStorageKey`.

<hr class="subsection" />

<ExplainCode languages="js,rust" >
36 changes: 36 additions & 0 deletions docs/2.build/2.smart-contracts/anatomy/types.md
Original file line number Diff line number Diff line change
@@ -113,3 +113,39 @@ To simplify development, the SDK provides the `U64` and `U128` types which are a
start="2" end="84" />

</ExplainCode>


## PublicKey

[`PublicKey`](https://docs.rs/near-sdk/latest/near_sdk/struct.PublicKey.html) is in a binary format with base58 string serialization with human-readable curve.
The curve types currently supported are `secp256k1` and `ed25519` under [`near_sdk::CurveType`](https://docs.rs/near-sdk/latest/near_sdk/struct.PublicKey.html).

Example:
```rust
use near_sdk::{PublicKey, CurveType};

pub fn stake_tokens(
&mut self,
amount: U128,
) -> Promise {
// Compressed ed25519 key
let ed: PublicKey = "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp".parse()
.unwrap();

// Uncompressed secp256k1 key
let secp256k1: PublicKey =
"secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj"
.parse().unwrap();

// Check the curve type of the public key
match ed.curve_type() {
near_sdk::CurveType::ED25519 => env::log_str("Using ED25519 curve."),
near_sdk::CurveType::SECP256K1 => env::log_str("Using SECP256K1 curve."),
_ => env::panic_str("Unsupported curve type!"),
}

// Use the staking contract to stake the tokens (using NEAR's PromiseAction::Stake)
Promise::new(env::predecessor_account_id().clone())
.stake(amount.0, public_key) // stake the tokens using the public key for verification
}
```
6 changes: 3 additions & 3 deletions docs/2.build/2.smart-contracts/anatomy/yield-resume.md
Original file line number Diff line number Diff line change
@@ -29,9 +29,9 @@ Let's look at an example that takes a prompt from a user (e.g. "What is 2+2"), a
</CodeTabs>

#### Creating a Yielded Promise
In the example above, we are creating a [`Promise`](./crosscontract.md#promises) to call the contract's function `return_external_response`.
In the example above, we are creating a [`Promise`](./crosscontract.md#promises) [🦀](https://near.github.io/near-sdk-js/classes/promise.NearPromise.html) [🌐](https://docs.rs/near-sdk/latest/near_sdk/struct.Promise.html) to call the contract's function `return_external_response`.

Notice that we create the `Promise` using `env::promise_yield_create`, which will create an **identifier** for the yielded promise in the `YIELD_REGISTER`.
Notice that we create the `Promise` using `env::promise_yield_create` [🦀](https://docs.rs/near-sdk/latest/near_sdk/env/fn.promise_yield_create.html) , which will create an **identifier** for the yielded promise in the `YIELD_REGISTER`.

#### Retrieving the Yielded Promise ID
We read the `YIELD_REGISTER` to retrieve the `ID` of our yielded promise. We store the `yield_id` and the user's `prompt` so the external service query them (the contract exposes has a function to list all requests).
@@ -88,7 +88,7 @@ The function being resumed will have access to all parameters passed to it, incl
In the example above, the `return_external_response` receives two parameters:

1. A `request_id` - passed on [creation](#creating-a-yielded-promise) - which is used to remove the request from the state
2. A `response` - passed when [signaling to resume](#signaling-the-resume) - which contains the external response, or a `PromiseError` if the contract timed out while waiting
2. A `response` - passed when [signaling to resume](#signaling-the-resume) - which contains the external response, or a `PromiseError` [🦀](https://docs.rs/near-sdk/latest/near_sdk/enum.PromiseError.html) [🌐](https://near.github.io/near-sdk-js/enums/types_vm_types.PromiseError.html) if the contract timed out while waiting

:::tip There's plenty of time

9 changes: 9 additions & 0 deletions docs/2.build/2.smart-contracts/security/checklist.md
Original file line number Diff line number Diff line change
@@ -34,3 +34,12 @@ Check our [security articles](./welcome.md) to understand how to improve the sec
12. Callbacks are free of `panic!`
13. All the callbacks are given enough GAS to execute entirely
14. The contract is not left in an exploitable state between a cross-contract call and its callback

## Cross-contract calls
15. Cross-contract calling functions [should](../anatomy/crosscontract#creating-a-cross-contract-call) return the Promise as the result of its execution
16. Use [integration tests](../testing/integration-test.md) to cover cross contract calls

## Types
16. Use `near_sdk::json_types::{U64, I64, U128, I128}` to avoid deserialization [problems](../anatomy/serialization#json-limitations) with `u64`, etc.
17. Use built in types like `AccountId`, `BlockHeight`, `NearToken` and other types from [near-sdk](https://docs.rs/near-sdk/latest/near_sdk/)
18. When dealing with balances, consider using `checked_mul`, `checked_sub`, etc
4 changes: 2 additions & 2 deletions docs/2.build/2.smart-contracts/testing/integration-test.md
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ All of our [examples](https://github.com/near-examples/docs-examples) come with

## Snippet I: Testing Hello NEAR

Lets take a look at the test of our [Quickstart Project](../quickstart.md) [👋 Hello NEAR](https://github.com/near-examples/hello-near-examples), where we deploy the contract on an account and test it correctly retrieves and sets the greeting.
Lets take a look at the test of our [Quickstart Project](../quickstart.md) [👋 Hello NEAR](https://github.com/near-examples/hello-near-examples), where we deploy the contract on an account and test it correctly retrieves and sets the greeting. Here local `sandbox` is used.

<CodeTabs>
<Language value="js" language="js">
@@ -52,7 +52,7 @@ In most cases we will want to test complex methods involving multiple users and

---

## Sandbox Testing
## Sandbox Features

NEAR Workspaces allows you to write tests once, and run them either on `testnet` or a local `Sandbox`. By **default**, Workspaces will start a **sandbox** and run your tests **locally**. Lets dive into the features of our framework and see how they can help you.

2 changes: 2 additions & 0 deletions website/sidebars.js
Original file line number Diff line number Diff line change
@@ -127,6 +127,7 @@ const sidebar = {
"build/smart-contracts/anatomy/actions",
"build/smart-contracts/anatomy/crosscontract",
"build/smart-contracts/anatomy/yield-resume",
"build/smart-contracts/anatomy/gascalc",
"build/smart-contracts/security/checklist",
{
"type": "html",
@@ -139,6 +140,7 @@ const sidebar = {
"build/smart-contracts/anatomy/serialization-protocols",
"build/smart-contracts/anatomy/reduce-size",
"build/smart-contracts/anatomy/reproducible-builds",
"build/smart-contracts/anatomy/macros",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call adding this here

]
}
]