Replies: 2 comments 6 replies
-
I've got changes coming that might break this a bit (mainly that Ignoring the above fact for a second, one of the things I don't like about this proposal is that it's caught between being strict and loose with what's stored at the specified state-key. I could do this: #[state_keys]
pub enum StateKeys {
#[state_keys(getter = u64)] // u64 here
TotalSupply,
}
#[public]
pub fn get_total_supply(Context { program, .. }: Context<StateKeys>) -> i64 { // but this is an i64
program
.state()
.get(StateKeys::TotalSupply)
.expect("failed to get total supply")
} I'm generally in favour of making things more strict, but regardless, it shouldn't be somewhere in the middle. I do like pre-defined getters and setters, but maybe, instead of an enum, each key gets its own type. If we defined types as something like I suggested in #944 (comment) (but a little different): state_schema! {
MyUnitStruct => i64,
MyTupleStruct(Address) => String,
} we could output separate types for keys and values such that calling state.get(state_keys::MyUnitStruct) // Result<i64, StateError>
state.get(state_keys::MyTupleStruct(Address)) // Result<String, StateError> Of course, this would necessarily change how we do caching, but there are several ways that could be accomplished. |
Beta Was this translation helpful? Give feedback.
-
So far, this seems pretty straightforward to implement with one exception, composition. How should we compose state-keys such that code that requires state-access can be pulled into a different program. The classic example is ownership. state_schema! {
pub OwnerKey => Address,
}
fn is_owner(context: &Context) -> bool {
let Some(owner) = context.program().state().get(OwnerKey) else {
return false;
};
owner == context.actor()
} (or something along those lines) The state-key here is I could do something like: use owner_lib::OwnerKey;
state_schema! {
MyKey => MyValue,
OwnerKey,
}
#[public]
pub fn do_something(ctx: Context) {
if !is_owner(&ctx) {
return;
}
// do something
} OR we force composable code to use traits ///! `owned/lib.rs`
use wasmlanche_sdk::{Address, Context};
pub trait OwnerKey: Schema<Value = Address> {}
pub fn is_owner<Key: OwnerKey>(context: &Context, owner_key: Key) -> bool {
let Some(owner): Option<Address> = context.program().state().get(owner_key) else {
return false;
};
owner == context.actor()
} Where you use the trait as follows: use owned::{is_owner, OwnerKey};
state_schema! {
Owner => Address,
}
impl OwnerKey for Owner {}
#[public]
pub fn do_something(ctx: Context) {
if !is_owner::<Owner>(&ctx) {
return;
}
// do something
} |
Beta Was this translation helpful? Give feedback.
-
I spec'ed an extension of the
state_keys
attribute macro to be able to generate getter functions for the chosen state keys as part of #944As the current state of this PR, we would need to write
And a method would be automatically expanded with
The look of the macro may still be a bit rough on the edges as pointed by @richardpringle on #944 (comment) which is also why this discussion was opened.
We should also decide wether or not we want this feature to land on
main
, since we may have longer term plans regarding how we threat storage retrieval. For instance, the current design of state keys may change because we need to specify the const generic value of theContext
(which is in most cases the state keys).Beta Was this translation helpful? Give feedback.
All reactions