Storage string error

@danielbate @Nazeeh21
Help me to resolve this bug. Trait “Hash” is not implemented for type “StorageString”.

contract;

mod errors;
mod interface;

use errors::{MintError, SetError};
use interface::Constructor;
use standards::{src20::SRC20, src3::SRC3, src5::{SRC5, State}, src7::{Metadata, SRC7},};
use sway_libs::{
    asset::{
        base::{
            _name,
            _set_name,
            _set_symbol,
            _symbol,
            _total_assets,
            _total_supply,
            SetAssetAttributes,
        },
        metadata::*,
        supply::{
            _burn,
            _mint,
        },
    },
    ownership::{
        _owner,
        initialize_ownership,
        only_owner,
    },
    pausable::{
        _is_paused,
        _pause,
        _unpause,
        Pausable,
        require_not_paused,
    },
};
use std::{hash::Hash, storage::storage_string::*, string::String};

struct QuestionInfo {
    question: StorageString,
    start_time: StorageString,
    end_time: StorageString,
    yes_count: u256,
    no_count: u256,
}


storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    metadata: StorageMetadata = StorageMetadata {},
    base_token_uri: StorageString = StorageString {},
    question_counter: u256 = 0,
    _next_token_id: u256 = 0,
    question_detail: StorageMap<StorageString, QuestionInfo> = StorageMap {},
    used_id: StorageMap<StorageString, bool> = StorageMap {},
}

abi CricSage {
    #[storage(read)]
    fn question_counter() -> u256;

    #[storage(read)]
    fn base_token_uri() -> StorageString;

    #[storage(read)]
    fn question_detail(a: StorageString) -> QuestionInfo;

    // #[storage(read, write)]
    // fn used_id(question_id: StorageString) -> bool;
}


configurable {
    MAX_SUPPLY: u64 = 300,
}

impl SRC20 for Contract {
    #[storage(read)]
    fn total_assets() -> u64 {
        _total_assets(storage.total_assets)
    }

    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64> {
        _total_supply(storage.total_supply, asset)
    }

    #[storage(read)]
    fn name(asset: AssetId) -> Option<String> {
        _name(storage.name, asset)
    }

    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String> {
        _symbol(storage.symbol, asset)
    }

    #[storage(read)]
    fn decimals(_asset: AssetId) -> Option<u8> {
        Some(0u8)
    }
}

impl SRC3 for Contract {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: SubId, amount: u64) {
        require_not_paused();

        let asset = AssetId::new(ContractId::this(), sub_id);
        require(amount == 1, MintError::CannotMintMoreThanOneNFTWithSubId);
        require(
            storage
                .total_supply
                .get(asset)
                .try_read()
                .is_none(),
            MintError::NFTAlreadyMinted,
        );
        require(
            storage
                .total_assets
                .try_read()
                .unwrap_or(0) + amount <= MAX_SUPPLY,
            MintError::MaxNFTsMinted,
        );

        let _ = _mint(
            storage
                .total_assets,
            storage
                .total_supply,
            recipient,
            sub_id,
            amount,
        );
    }

    #[payable]
    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64) {
        require_not_paused();
        _burn(storage.total_supply, sub_id, amount);
    }
}

impl SRC7 for Contract {
    #[storage(read)]
    fn metadata(asset: AssetId, key: String) -> Option<Metadata> {
        storage.metadata.get(asset, key)
    }
}

impl SRC5 for Contract {
    #[storage(read)]
    fn owner() -> State {
        _owner()
    }
}

impl SetAssetAttributes for Contract {
    #[storage(write)]
    fn set_name(asset: AssetId, name: String) {
        only_owner();
        require(
            storage
                .name
                .get(asset)
                .read_slice()
                .is_none(),
            SetError::ValueAlreadySet,
        );
        _set_name(storage.name, asset, name);
    }

    #[storage(write)]
    fn set_symbol(asset: AssetId, symbol: String) {
        only_owner();
        require(
            storage
                .symbol
                .get(asset)
                .read_slice()
                .is_none(),
            SetError::ValueAlreadySet,
        );
        _set_symbol(storage.symbol, asset, symbol);
    }

    #[storage(write)]
    fn set_decimals(_asset: AssetId, _decimals: u8) {
        require(false, SetError::ValueAlreadySet);
    }
}

impl SetAssetMetadata for Contract {
    #[storage(read, write)]
    fn set_metadata(asset: AssetId, key: String, metadata: Metadata) {
        require(
            storage
                .metadata
                .get(asset, key)
                .is_none(),
            SetError::ValueAlreadySet,
        );
        _set_metadata(storage.metadata, asset, key, metadata);
    }
}

impl Pausable for Contract {
    #[storage(write)]
    fn pause() {
        only_owner();
        _pause();
    }

    #[storage(read)]
    fn is_paused() -> bool {
        _is_paused()
    }

    #[storage(write)]
    fn unpause() {
        only_owner();
        _unpause();
    }
}

impl CricSage for Contract {
    #[storage(read)]
    fn question_counter() -> u256 {
        storage.question_counter.read()
    }

    #[storage(read)]
    fn base_token_uri() -> StorageString {
        storage.base_token_uri.read()
    }

   #[storage(read)]
    fn question_detail(a: StorageString) -> QuestionInfo {
        storage.question_detail.get(a).read()
    }


    // #[storage(read)]
    // fn used_id(question_id: StorageString) -> bool {
    //     storage.used_id.get(question_id).read().unwrap_or(false)
    // }
}

impl Constructor for Contract {
    #[storage(read, write)]
    fn constructor(owner: Identity) {
        initialize_ownership(owner);
    }
}

compile time error :

1 Like

Hey @mahesh_d_luffy, will have a look into it and get back to you. meanwhile can you share the screenshot for the fuelup show?

@Nazeeh21 please check it

1 Like

Can you please try out this solutioon and lmk if it works for you?

I tried Already , facing the same blocker
@Nazeeh21

will make sure the team looks into it

Thanks @Nazeeh21 , looking forward to it.

You have to import the trait use std::hash::Hash

yes I am already having that trait

Thanks for reporting @mahesh_d_luffy, team has identified this as an issue and will work on the fix :slight_smile:

1 Like

Hello @mahesh_d_luffy, we see you are trying to use a string as a key in a StorageMap. Heap types are not allowed in storage and hence the StorageString exists. While you are thinking about the approach correctly, StorageString is just an empty struct and thus does not contain any data:

pub struct StorageString {}

This means that using it as a key will always return the same key, as no underlying data ever changes!

This is what we recommend doing instead;

Update your question_detail storage variable to use a b256 and hash your string. It would look like this:

storage {
    question_detail: StorageMap<b256, QuestionInfo> = StorageMap {},
}

For you base_token_uri() function, have it return a String instead and read the string in storage:

    fn base_token_uri() -> String {
        storage.base_token_uri.read_slice().unwrap_or(String::new())
    }

Then, simply pass your String to question_detail() and use the hash as the key in your StorageMap:

    fn question_detail(string: String) -> QuestionInfo {
        let digest = sha256(string);
        storage.question_detail.get(digest).try_read().unwrap()
    }

This way you can still use your String as a key for your StorageMap.