Skip to main content

neo_devpack/
storage.rs

1// Copyright (c) 2025-2026 R3E Network
2// Licensed under the MIT License
3
4//! Storage utilities for Neo N3 smart contracts.
5//!
6//! This module provides convenient functions for storing and retrieving
7//! structured data using JSON serialization.
8
9use crate::{NeoStorage, NeoStorageContext};
10use neo_types::{NeoByteString, NeoIterator, NeoResult, NeoStruct, NeoValue};
11use serde::{Deserialize, Serialize};
12use serde_json::Value as JsonValue;
13
14/// Reads a JSON-serialized value from a NeoByteString.
15///
16/// # Type Parameters
17/// * `T` - The type to deserialize
18///
19/// # Arguments
20/// * `bytes` - The bytes containing JSON data
21///
22/// # Returns
23/// * `Some(T)` if deserialization succeeds
24/// * `None` if deserialization fails
25pub fn read_json<T: for<'de> Deserialize<'de>>(bytes: &NeoByteString) -> Option<T> {
26    serde_json::from_slice(bytes.as_slice()).ok()
27}
28
29/// Writes a value as JSON to a NeoByteString.
30///
31/// # Type Parameters
32/// * `T` - The type to serialize
33///
34/// # Arguments
35/// * `value` - The value to serialize
36///
37/// # Returns
38/// * `Ok(NeoByteString)` containing the JSON representation
39/// * `Err(NeoError)` if serialization fails
40pub fn write_json<T: Serialize>(value: &T) -> NeoResult<NeoByteString> {
41    let data = serde_json::to_vec(value).map_err(|err| {
42        neo_types::NeoError::new(&format!("failed to serialize storage JSON: {err}"))
43    })?;
44    Ok(NeoByteString::from_slice(&data))
45}
46
47/// Loads a typed value from storage.
48///
49/// # Type Parameters
50/// * `T` - The type to load
51///
52/// # Arguments
53/// * `ctx` - The storage context
54/// * `key` - The storage key
55///
56/// # Returns
57/// * `Some(T)` if the key exists and deserialization succeeds
58/// * `None` if the key doesn't exist or deserialization fails
59pub fn load<T: for<'de> Deserialize<'de>>(ctx: &NeoStorageContext, key: &[u8]) -> Option<T> {
60    let key_bytes = NeoByteString::from_slice(key);
61    let bytes = NeoStorage::get(ctx, &key_bytes).ok()?;
62    if bytes.is_empty() {
63        None
64    } else {
65        read_json(&bytes)
66    }
67}
68
69/// Stores a typed value to storage.
70///
71/// # Type Parameters
72/// * `T` - The type to store
73///
74/// # Arguments
75/// * `ctx` - The storage context
76/// * `key` - The storage key
77/// * `value` - The value to store
78///
79/// # Returns
80/// * `Ok(())` on success
81/// * `Err(NeoError)` if serialization or storage fails
82pub fn store<T: Serialize>(ctx: &NeoStorageContext, key: &[u8], value: &T) -> NeoResult<()> {
83    let key_bytes = NeoByteString::from_slice(key);
84    let value_bytes = write_json(value)?;
85    NeoStorage::put(ctx, &key_bytes, &value_bytes)
86}
87
88/// Deletes a key from storage.
89///
90/// # Arguments
91/// * `ctx` - The storage context
92/// * `key` - The storage key to delete
93///
94/// # Returns
95/// * `Ok(())` on success
96/// * `Err(NeoError)` if deletion fails (e.g., read-only context)
97pub fn delete(ctx: &NeoStorageContext, key: &[u8]) -> NeoResult<()> {
98    let key_bytes = NeoByteString::from_slice(key);
99    NeoStorage::delete(ctx, &key_bytes)
100}
101
102/// Finds all storage entries with the given prefix.
103///
104/// # Arguments
105/// * `ctx` - The storage context
106/// * `prefix` - The key prefix to search for
107///
108/// # Returns
109/// * `Ok(NeoIterator<NeoValue>)` containing matching entries
110/// * `Err(NeoError)` if the search fails
111pub fn find_prefix(ctx: &NeoStorageContext, prefix: &[u8]) -> NeoResult<NeoIterator<NeoValue>> {
112    let prefix_bytes = NeoByteString::from_slice(prefix);
113    NeoStorage::find(ctx, &prefix_bytes)
114}
115
116/// Creates a NeoStruct entry with key and value fields.
117///
118/// This is useful for returning storage entries from contract methods.
119pub fn struct_entry(key: NeoByteString, value: NeoByteString) -> NeoValue {
120    let mut s = NeoStruct::new();
121    s.set_field("key", NeoValue::from(key));
122    s.set_field("value", NeoValue::from(value));
123    NeoValue::from(s)
124}
125
126/// Converts a NeoValue containing a ByteString to a JSON value.
127///
128/// # Arguments
129/// * `value` - The NeoValue to convert
130///
131/// # Returns
132/// * `Some(JsonValue)` if the value is a valid ByteString containing JSON
133/// * `None` otherwise
134pub fn value_to_json(value: &NeoValue) -> Option<JsonValue> {
135    value
136        .as_byte_string()
137        .and_then(|bytes| serde_json::from_slice(bytes.as_slice()).ok())
138}