From 769caacfbe26f905f73c20e4f37a218310cb3584 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Tue, 8 Sep 2026 13:11:48 +0000 Subject: [PATCH] Fix broken code in the choosing-the-right-storage guide The temporary storage snippet could not compile. It called `e.ledger()` twice while its `Env` parameter is named `env`, declared no `AuctionContract` type for the `impl` block, took a `Symbol` where `DataKey::Bid` holds an `Address`, returned `i64` for an `i128` bid value, and annotated `storage().temporary().get()` as `Bid` when it returns `Option`. The same snippet also had its expiry check inverted. It returned the bid value when `expiration_ledger_seq` had already passed, and 0 while the bid was still valid, which is the opposite of what its own comments describe. The persistent and instance snippets were missing the `contract` and `contracttype` imports that they use. --- .../storage/choosing-the-right-storage.mdx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/build/guides/storage/choosing-the-right-storage.mdx b/docs/build/guides/storage/choosing-the-right-storage.mdx index 7519e1a36..34db8bf85 100644 --- a/docs/build/guides/storage/choosing-the-right-storage.mdx +++ b/docs/build/guides/storage/choosing-the-right-storage.mdx @@ -43,7 +43,7 @@ Examples of data that may be stored on persistent storage include user balances, Let's look at a contract for a loyalty points system where users can accumulate points and redeem them for rewards. Each user's point balance will be stored in persistent storage. ```rust -use soroban_sdk::{contractimpl, contracttype, Address, Env}; +use soroban_sdk::{contract, contractimpl, contracttype, Address, Env}; #[contracttype] pub enum DataKey { @@ -97,12 +97,14 @@ Instance storage is best suited for data that has a well-known size limit and is Let's look at additional functions for the loyalty points contract from the previous section, specifically the functions that define the contract admin and add points to the users: ```rust -use soroban_sdk::{contractimpl, Address, Env}; +use soroban_sdk::{contractimpl, contracttype, Address, Env}; +// This is the `DataKey` from the previous section, with `Admin` added. Declare +// it only once in your contract. #[contracttype] pub enum DataKey { Points(Address), - Admin + Admin, } #[contractimpl] @@ -152,10 +154,10 @@ It is also unsafe to rely on an entry expiring as it can be extended by anyone. Temporary storage is best suited for easily replaceable data, or data that is only relevant within a certain time period. For example, oracle price feed data that is only relevant for a few minutes, or limited time authorizations such as token allowances, session tokens, auctions, timelocks, etc. Nonces for the Soroban signatures are also stored in the temporary storage, at least until the signature itself expires. -Let's look at how temporary storage may be implemented in a contract that runs auctions periodically, and users can place bids that are only valid only until some user-defined time point: +Let's look at how temporary storage may be implemented in a contract that runs auctions periodically, and users can place bids that are valid only until some user-defined time point: ```rust -use soroban_sdk::{contracttype, contractimpl, Env, Address}; +use soroban_sdk::{contract, contractimpl, contracttype, Address, Env}; #[contracttype] pub enum DataKey { @@ -171,6 +173,9 @@ pub struct Bid { expiration_ledger_seq: u32, } +#[contract] +pub struct AuctionContract; + #[contractimpl] impl AuctionContract { // This function lets a user place a bid that lives only until the auction ends. @@ -185,21 +190,21 @@ impl AuctionContract { }); // Compute the TTL that the bid requires. let bid_ttl = bid_expiration_ledger_seq - .checked_sub(e.ledger().sequence()) + .checked_sub(env.ledger().sequence()) .unwrap(); // Extend the TTL for the bid, such that it's guaranteed to live at // least until the `bid_expiration_ledger_seq` that the user has - // requested. This operation is will fail in - // case if extension is longer than the protocol allows, so there is - // no need to further validate `bid_ttl`. + // requested. This operation will fail if the extension is longer than + // the protocol allows, so there is no need to further validate + // `bid_ttl`. env.storage().temporary().extend_ttl(&bid_key, bid_ttl, bid_ttl); } // This function returns a user's bid (0 if it has expired). - pub fn get_bid(env: Env, user: Symbol) -> i64 { - let maybe_bid: Bid = env.storage().temporary().get(&DataKey::Bid(user)); + pub fn get_bid(env: Env, user: Address) -> i128 { + let maybe_bid: Option = env.storage().temporary().get(&DataKey::Bid(user)); if let Some(bid) = maybe_bid { - if bid.expiration_ledger_seq <= e.ledger().sequence() { + if bid.expiration_ledger_seq >= env.ledger().sequence() { bid.value } else { // Even though the entry is still in the storage, it has @@ -207,7 +212,6 @@ impl AuctionContract { // order to trick our contract, so return 0. 0 } - } else { // There is no bid for the user - it either hasn't existed at all, // or has been removed from the temporary storage. In either case,