Skip to content

File Storage

Soroban File Storage
#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, BytesN, Env, Map, String, Symbol, Vec, log};
 
#[contracttype]
#[derive(Debug, Clone)]
pub struct Report {
    pub subject: String,
    pub date_of_incident: String,
    pub location: String,
    pub date_of_submission: String,
    pub description_cid: String,
    pub attachments_cid: String,
    pub attachment_count: u32,
}
 
#[contracttype]
#[derive(Debug, Clone)]
pub struct ReportUpdate {
    pub report_secret_key: Symbol,
    pub content_cid: String,
    pub status: String,
    pub date_of_submission: String,
}
 
#[contracttype]
pub struct UpdateCount {
    pub count: u32,
}
 
#[contracttype]
pub enum UpdateCountRegistry {
    UpdateCount(Symbol),
}
 
#[contracttype]
#[derive(Clone)]
enum DataKey {
    Admin,
}
 
const REPORT_KEYS:Symbol = symbol_short!("RPT_KEYS"); // report keys
const UPDATE_KEYS:Symbol = symbol_short!("RUPD_KEYS"); // report update keys
const REPORT_COUNT:Symbol = symbol_short!("RPT_COUNT"); // report count
 
#[contract]
pub struct ReportContract;
 
#[contractimpl]
impl ReportContract {
    // Update a Report
    pub fn upload_report(
        env: Env,
        secret_key: Symbol,
        subject: String,
        date_of_incident: String,
        location: String,
        date_of_submission: String,
        description_cid: String,
        attachments_cid: String,
        attachment_count: u32,
        initial_update_key: String,
    ) {
        let report = Report {
            subject,
            date_of_incident,
            location,
            description_cid,
            attachments_cid,
            date_of_submission: date_of_submission.clone(),
            attachment_count,
        };
 
        let mut report_count: u64 = env.storage().persistent().get(&REPORT_COUNT).unwrap_or(0);
 
        env.storage().persistent().set(&secret_key, &report);
 
        report_count += 1;
 
        // Save the key to the report keys list
        let mut report_keys: Vec<Symbol> = env
            .storage()
            .persistent()
            .get(&REPORT_KEYS)
            .unwrap_or(Vec::new(&env));
 
        report_keys.push_back(secret_key.clone());
 
        env.storage().persistent().set(&REPORT_KEYS, &report_keys);
        env.storage().persistent().set(&REPORT_COUNT, &report_count);
 
        // set default report update status to "submitted"
        Self::upload_report_update(
            env.clone(),
            secret_key,
            initial_update_key,
            String::from_str(&env, "__NULL__"),
            String::from_str(&env, "submitted"),
            date_of_submission,
        );
    }
 
    // Fetch a Report
    pub fn fetch_report(env: Env, secret_key: Symbol) -> Option<Report> {
        env.storage().persistent().get(&secret_key)
    }
 
    // Fetch all Reports
    pub fn fetch_all_reports(env: Env) -> Map<Symbol, Report> {
        let mut reports = Map::new(&env);
 
        let report_keys: Vec<Symbol> = env
            .storage()
            .persistent()
            .get(&REPORT_KEYS)
            .unwrap_or(Vec::new(&env));
 
        for key in report_keys.iter() {
            if let Some(report) = Self::fetch_report(env.clone(), key.clone()) {
                reports.set(key, report);
            }
        }
 
        /// @dev should remove logs before deploying smart contracts
        log!(&env, "Reports: {}", reports);
 
        reports
    }
 
    pub fn get_report_count(env: Env) -> u64 {
        env.storage().persistent().get(&REPORT_COUNT).unwrap_or(0)
    }
 
    pub fn get_all_report_keys(env: Env) -> Vec<Symbol> {
        env.storage()
            .persistent()
            .get(&REPORT_KEYS)
            .unwrap_or(Vec::new(&env))
    }
 
    // Upload a Report Update
    pub fn upload_report_update(
        env: Env,
        report_secret_key: Symbol,
        update_key: String,
        content_cid: String,
        status: String,
        date_of_submission: String,
    ) {
        let mut update_count =
            Self::get_report_update_count(env.clone(), report_secret_key.clone());
 
        let report_update = ReportUpdate {
            report_secret_key: report_secret_key.clone(),
            content_cid,
            status,
            date_of_submission,
        };
 
        env.storage().persistent().set(&update_key, &report_update);
 
        update_count.count = update_count.count + 1;
 
        env.storage().persistent().set(
            &UpdateCountRegistry::UpdateCount(report_secret_key),
            &update_count,
        );
 
        // Save the update key to the report update keys list
        let mut update_keys: Vec<String> = env
            .storage()
            .persistent()
            .get(&UPDATE_KEYS)
            .unwrap_or(Vec::new(&env));
 
        update_keys.push_back(update_key);
        env.storage().persistent().set(&UPDATE_KEYS, &update_keys);
    }
 
    // Fetch a Report Update
    pub fn fetch_report_update(env: Env, update_key: String) -> Option<ReportUpdate> {
        env.storage().persistent().get(&update_key)
    }
 
    // Fetch all Report Updates for a Specific Report
    pub fn fetch_all_report_updates(env: Env, report_secret_key: Symbol) -> Vec<ReportUpdate> {
        let mut updates = Vec::new(&env);
 
        let update_keys: Vec<String> = env
            .storage()
            .persistent()
            .get(&UPDATE_KEYS)
            .unwrap_or(Vec::new(&env));
 
        for key in update_keys.iter() {
            if let Some(update) = env.storage().persistent().get::<String, ReportUpdate>(&key) {
                if update.report_secret_key == report_secret_key {
                    updates.push_back(update);
                }
            }
        }
 
        /// @dev should remove logs before deploying smart contracts
        log!(&env, "Updates: {}", updates);
 
        updates
    }
 
    // Get Report Update Count
    pub fn get_report_update_count(env: Env, report_secret_key: Symbol) -> UpdateCount {
        let key = UpdateCountRegistry::UpdateCount(report_secret_key);
 
        env.storage()
            .persistent()
            .get(&key)
            .unwrap_or(UpdateCount {count: 0})
    }
 
    // Logic for Contract Upgrade
 
    pub fn init(e: Env, admin: Address) {
        e.storage().instance().set(&DataKey::Admin, &admin);
    }
 
    pub fn version() -> u32 {
        1
    }
 
    pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) {
        let admin: Address = e.storage().instance().get(&DataKey::Admin).unwrap();
        admin.require_auth();
 
        e.deployer().update_current_contract_wasm(new_wasm_hash);
    }
}

Explanation

#![no_std] This attribute prevents linking to the standard library, making the code lighter and more efficient for Soroban contracts. It's big so we save on size.

use soroban_sdk::{contract, contractimpl, Env, log} Imports stuffs from the Soroban SDK. Env is basic Soroban type, we need it because we can't use the Rust standard library.

pub Marks a function as external, meaning it can be invoked outside of the context of the contract code itself.

enum This functionality in Rust allows one to define a type with a fixed set of values. It enables pattern matching, discriminant unions, as well as option and return types.

#[contract] Marks the struct as a Soroban smart contract. Soroban smart contracts are defined as Rust structs.

#[contractimpl] Marks the implementation block as containing contract methods and transforms it to code that Soroban can evaluate directly.

Report Management

upload_report This function takes various details about a report (subject, date of incident, location, etc.) and a secret key for access. It creates a Report struct, stores it with the secret key as a key, increments a report count, saves the key in a list of report keys, and finally uploads a default update with "submitted" status.

fetch_report This function retrieves a specific report based on its secret key.

fetch_all_reports This function iterates through all report keys, retrieves each report using the fetch_report function, and stores them in a map with the secret key as the key.

get_report_count This function retrieves the total number of reports stored.

get_all_report_keys This function retrieves the list of all secret keys associated with reports.

Report Update Management

upload_report_update This function allows uploading updates for a report. It takes details like content, status, and date of submission along with the report's secret key and a unique update key. It stores the update information, increments an update count for the report, saves the update key in a list of all update keys, and stores the updated count.

fetch_report_update This function retrieves a specific report update based on its update key.

fetch_all_report_updates This function iterates through all update keys, retrieves each update using the fetch_report_update function, and filters them based on the provided report's secret key, returning only updates for that specific report.

get_report_update_count This function retrieves the total number of updates for a specific report identified by its secret key.

Contract Upgrade

init This function is likely called during contract deployment. It stores the administrator address in the contract storage.

version This function simply returns the current version of the contract (version 1 in this case).

upgrade This function allows upgrading the contract code. It retrieves the administrator address, checks if the caller is the admin using require_auth, and updates the contract's Wasm code with the provided new hash if authorized.

Run in Playground

Loading playground...