Skip to content

Commit

Permalink
feat: argument resolution for storage command
Browse files Browse the repository at this point in the history
Adds the `storage:` scheme, and allows falling back to it in the
`storage` command:

    starkli storage eth ERC20_name
  • Loading branch information
xJonathanLEI committed Oct 15, 2023
1 parent 884279e commit d0e96eb
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 8 deletions.
9 changes: 7 additions & 2 deletions book/src/argument-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ The `const` scheme uses `content` as the key to look up a hard-coded table to co

### `selector`

The `selector` calculates the _Starknet Keccak_ hash for the content to derive the function entryponit.
The `selector` scheme calculates the _Starknet Keccak_ hash for the content to derive the function entryponit.

### `storage`

This scheme is currently the same as `selector`, but support for offsets and maps (e.g. `ERC20_balances[0x1234]`) might be added in the future to differentiate it.

## Scheme omission

Normally, the `scheme:` prefix is required for opting in to argument resolution. However, there are a few exceptions:

- the `addr:` prefix can be omitted when an address is expected;
- the `selector:` prefix can be omitted when a selector is expected.
- the `selector:` prefix can be omitted when a selector is expected;
- the `storage:` prefix can be omitted in the `starkli storage` command.

As an example, consider the `starkli invoke` command. To use the `addr` and `selector` schemes, one would run:

Expand Down
27 changes: 26 additions & 1 deletion src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anyhow::Result;
use num_bigint::BigUint;
use starknet::core::{
types::FieldElement,
utils::{cairo_short_string_to_felt, get_selector_from_name},
utils::{cairo_short_string_to_felt, get_selector_from_name, get_storage_var_address},
};

use crate::{address_book::AddressBookResolver, chain_id::ChainIdSource};
Expand All @@ -15,6 +15,7 @@ pub struct FeltDecoder<S> {
enum FallbackOption {
Address,
Selector,
Storage,
None,
}

Expand Down Expand Up @@ -56,6 +57,19 @@ where
}
}

pub async fn decode_single_with_storage_fallback(&self, raw: &str) -> Result<FieldElement> {
let decoded = self.decode_inner(raw, FallbackOption::Storage).await?;

if decoded.len() == 1 {
Ok(decoded[0])
} else {
Err(anyhow::anyhow!(
"expected 1 element but found {}",
decoded.len()
))
}
}

pub async fn decode(&self, raw: &str) -> Result<Vec<FieldElement>> {
self.decode_inner(raw, FallbackOption::None).await
}
Expand Down Expand Up @@ -128,6 +142,11 @@ where
Ok(vec![cairo_short_string_to_felt(short_string)?])
} else if let Some(selector) = raw.strip_prefix("selector:") {
Ok(vec![get_selector_from_name(selector)?])
} else if let Some(storage) = raw.strip_prefix("storage:") {
if storage.contains('[') || storage.contains(']') {
anyhow::bail!("cannot resolve storage address: maps not supported yet")
}
Ok(vec![get_storage_var_address(storage, &[])?])
} else {
match raw.parse::<FieldElement>() {
Ok(value) => Ok(vec![value]),
Expand All @@ -137,6 +156,12 @@ where
Err(_) => Err(err.into()),
},
FallbackOption::Selector => Ok(vec![get_selector_from_name(raw)?]),
FallbackOption::Storage => {
if raw.contains('[') || raw.contains(']') {
anyhow::bail!("cannot resolve storage address: maps not supported yet")
}
Ok(vec![get_storage_var_address(raw, &[])?])
}
FallbackOption::None => Err(err.into()),
},
}
Expand Down
20 changes: 15 additions & 5 deletions src/subcommands/storage.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use std::sync::Arc;

use anyhow::Result;
use clap::Parser;
use starknet::{
core::types::{BlockId, BlockTag, FieldElement},
core::types::{BlockId, BlockTag},
providers::Provider,
};

use crate::{verbosity::VerbosityArgs, ProviderArgs};
use crate::{
address_book::AddressBookResolver, decode::FeltDecoder, verbosity::VerbosityArgs, ProviderArgs,
};

#[derive(Debug, Parser)]
pub struct Storage {
Expand All @@ -23,9 +27,15 @@ impl Storage {
pub async fn run(self) -> Result<()> {
self.verbosity.setup_logging();

let provider = self.provider.into_provider();
let address = FieldElement::from_hex_be(&self.address)?;
let key = FieldElement::from_hex_be(&self.key)?;
let provider = Arc::new(self.provider.into_provider());
let felt_decoder = FeltDecoder::new(AddressBookResolver::new(provider.clone()));

let address = felt_decoder
.decode_single_with_addr_fallback(&self.address)
.await?;
let key = felt_decoder
.decode_single_with_storage_fallback(&self.key)
.await?;

// TODO: allow custom block
let value = provider
Expand Down

0 comments on commit d0e96eb

Please sign in to comment.