- Native collections (e.g.
Array,Map,Set), provided by the language - SDK collections (e.g.
IterableMap,Vector), provided by the NEAR SDK
Storage Management
Each time the contract is executed, the first thing it will do is to read the values and deserialize them into memory, and after the function finishes, it will serialize and write the values back to the database. For native collections, the contract will fully load the collection into memory before any method executes. This happens even if the method you invoke does not use the collection. Know that this will have impact on GAS you spend for methods in your contract.Storage Cost
Storage Cost
Your contract needs to lock a portion of their balance proportional to the amount of data they stored in the blockchain. This means that:
- If more data is added the storage increases โ, and your contractโs balance decreases โ.
- If data is deleted the storage decreases โ, and your contractโs balance increases โ.
Storage Constraints on NEAR
Storage Constraints on NEAR
For storing data on-chain itโs important to keep in mind the following:
- There is a 4mb limit on how much you can upload at once
$NEAR per 100kb of storage used.Users will be limited to 4MB per contract call upload due to MAX_GAS constraints. The maximum amount of gas one can attach to a given functionCall is 300TGas.Be mindful of potential small deposit attacks
Native Collections
Native collections are those provided by the language, such asArray, Map, Set in Javascript, or Vec, HashMap, HashSet in Rust.
All entries in a native collection are serialized into a single value and stored together into the state. This means that every time a function execute, the SDK will read and deserialize all entries in the native collection.
Serialization & Storage Example
Serialization & Storage Example
The array
[1,2,3,4] will be serialized into the JSON string "[1,2,3,4]" in Javascript, and the Borsh byte-stream [0,0,0,4,1,2,3,4] in Rust before being storedKeep Native Collections SmallAs the native collection grows, deserializing it from memory will cost more and more gas. If the collections grows too large, your contract might expend all the gas trying to read its state, making it fail on each function call
SDK Collections
The NEAR SDKs expose collections that are optimized for random access of large amounts of data. SDK collections are instantiated using a โprefixโ, which is used as an index to split the data into chunks. This way, SDK collections can defer reading and writing to the store until needed.Serialization & Storage Example
Serialization & Storage Example
The sdk array
[1,2,3,4] with prefix "p" will be stored as the string "p" in the contractโs attribute, and create four entries in the contractโs storage: p-0:1, p-1:2โฆSDK Collections' Features
SDK Collections' Features
โ ๏ธ Legacy collections from
near_sdk::collections. Prefer near_sdk::store equivalents for new contracts.SDK Collections' Time Complexities
SDK Collections' Time Complexities
* - to insert at the end of the vector using
push_back (or push_front for deque)
** - to delete from the end of the vector using pop (or pop_front for deque), or delete using swap_remove which swaps the element with the last element of the vector and then removes it.Instantiation
All structures need to be initialized using a uniqueprefix, which will be used to index the collectionโs values in the accountโs state
- ๐ฆ Rust
- ๐ JavaScript
- ๐ Python
- ๐น GO
Be careful of not using the same prefix in two collections, otherwise, their storage space will collide, and you might overwrite information from one collection when writing in the other
Vector
Implements a vector/array which persists in the contractโs storage. Please refer to the Rust and JS SDKโs for a full reference on their interfaces.- ๐ฆ Rust
- ๐ JavaScript
- ๐ Python
- ๐น GO
LookupMap
Implements a map/dictionary which persists in the contractโs storage. Please refer to the Rust and JS SDKโs for a full reference on their interfaces.- ๐ฆ Rust
- ๐ JavaScript
- ๐ Python
- ๐น GO
UnorderedMap / IterableMap
Implements a map/dictionary which persists in the contractโs storage. Please refer to the Rust and JS SDKโs for a full reference on their interfaces.- ๐ฆ Rust
- ๐ JavaScript
- ๐ Python
- ๐น GO
LookupSet
Implements a set which persists in the contractโs storage. Please refer to the Rust and JS SDKโs for a full reference on their interfaces.- ๐ฆ Rust
- ๐ JavaScript
- ๐ Python
- ๐น GO
UnorderedSet / IterableSet
Implements a set which persists in the contractโs storage. Please refer to the Rust and JS SDKโs for a full reference on their interfaces.- ๐ฆ Rust
- ๐ JavaScript
- ๐ Python
- ๐น GO
Tree
An ordered equivalent of Map. The underlying implementation is based on an AVL. You should use this structure when you need to: have a consistent order, or access the min/max keys.- ๐ฆ Rust
- ๐ Python
- ๐น GO
Nesting Collections
When nesting SDK collections, be careful to use different prefixes for all collections, including the nested ones.- ๐ฆ Rust
- ๐ JavaScript
- ๐ Python
- ๐น GO
Error prone patterns
Because the values are not kept in memory and are lazily loaded from storage, itโs important to make sure if a collection is replaced or removed, that the storage is cleared. In addition, it is important that if the collection is modified, the collection itself is updated in state because most collections will store some metadata. Some error-prone patterns to avoid that cannot be restricted at the type level are:- ๐ฆ Rust
- ๐ JavaScript
- ๐น GO
Pagination
Persistent collections such asIterableMap/UnorderedMap, IterableSet/UnorderedSet and Vector may
contain more elements than the amount of gas available to read them all.
In order to expose them all through view calls, we can use pagination.