Skip to content

Commit

Permalink
Add call ops
Browse files Browse the repository at this point in the history
  • Loading branch information
kariy committed Apr 5, 2024
1 parent a2f920f commit f448870
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 57 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ fn main() -> Result<()> {

fn execute(command: Commands) -> Result<()> {
match command {
Commands::Call(args) => ops::call::call(args)?,
Commands::Balance(args) => ops::balance::get(args)?,
Commands::Tx(args) => ops::transaction::get(args)?,
Commands::TxCount(args) => ops::transaction::count(args)?,
Expand Down
13 changes: 5 additions & 8 deletions crates/args/src/commands/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,15 @@ use crate::parser::BlockIdParser;
#[derive(Debug, Parser)]
pub struct CallArgs {
#[arg(display_order = 1)]
contract_address: FieldElement,
pub contract_address: FieldElement,

#[arg(display_order = 2)]
#[arg(help = "The name of the function to be called")]
#[arg(value_name = "FUNCTION_NAME")]
function: String,
pub function: String,

#[arg(short, long)]
#[arg(display_order = 3)]
#[arg(value_delimiter = ',')]
#[arg(help = "Comma seperated values e.g., 0x12345,0x69420,...")]
input: Vec<FieldElement>,
pub input: Vec<FieldElement>,

#[arg(next_line_help = true)]
#[arg(display_order = 5)]
Expand All @@ -28,9 +25,9 @@ pub struct CallArgs {
#[arg(
help = "The hash of the requested block, or number (height) of the requested block, or a block tag (e.g. latest, pending)."
)]
block_id: BlockId,
pub block_id: BlockId,

#[command(flatten)]
#[command(next_help_heading = "Starknet options")]
starknet: StarknetOptions,
pub starknet: StarknetOptions,
}
8 changes: 2 additions & 6 deletions crates/args/src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,12 +485,8 @@ impl Pretty for TransactionStatus {
impl Pretty for TransactionExecutionStatus {
fn prettify(&self) -> String {
match self {
TransactionExecutionStatus::Succeeded => {
"Succeeded".to_string()
}
TransactionExecutionStatus::Reverted => {
"Reverted".to_string()
}
TransactionExecutionStatus::Succeeded => "Succeeded".to_string(),
TransactionExecutionStatus::Reverted => "Reverted".to_string(),
}
}
}
1 change: 1 addition & 0 deletions crates/ops/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rika-args.workspace = true
serde = "1.0.197"
serde_json.workspace = true
starknet.workspace = true
thiserror.workspace = true
tokio = { version = "1.37.0", features = [ "full" ] }

[dev-dependencies]
Expand Down
81 changes: 38 additions & 43 deletions crates/ops/src/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ use eyre::{eyre, ContextCompat, Report, Result};
use rika_args::commands::balance::BalanceArgs;
use starknet::{
core::{
types::{BlockId, FieldElement, FunctionCall, StarknetError},
types::{BlockId, FieldElement, StarknetError},
utils::parse_cairo_short_string,
},
macros::selector,
providers::{Provider, ProviderError},
};

use crate::utils;
use crate::{
call::{contract_call, ContractCallError},
utils,
};

pub fn get(args: BalanceArgs) -> Result<()> {
let BalanceArgs {
Expand Down Expand Up @@ -50,22 +52,27 @@ async fn get_balance<P>(
where
P: Provider,
{
fn handle_error(err: ProviderError) -> Report {
fn handle_error(err: ContractCallError) -> Report {
match err {
ProviderError::StarknetError(StarknetError::ContractNotFound) => {
ContractCallError::Provider(ProviderError::StarknetError(
StarknetError::ContractNotFound,
)) => {
eyre!("token contract not found")
}
e => eyre!(e),
}
}

let call = FunctionCall {
let retdata = contract_call(
provider,
contract_address,
calldata: vec![address],
entry_point_selector: selector!("balanceOf"),
};
"balanceOf",
vec![address],
block_id,
)
.await
.map_err(handle_error)?;

let retdata = provider.call(call, block_id).await.map_err(handle_error)?;
// the convention is to return a u256, which means there are two felts
let low = retdata.first().context("missing low value")?;
let high = retdata.last().context("missing high value")?;
Expand All @@ -81,44 +88,11 @@ async fn get_token_metadata<P>(
where
P: Provider + Sync,
{
async fn get_decimals(
provider: impl Provider,
block_id: BlockId,
contract_address: FieldElement,
) -> Result<u8> {
let request = FunctionCall {
contract_address,
calldata: Vec::new(),
entry_point_selector: selector!("decimals"),
};

let result = provider.call(request, block_id).await?;
let decimals = result.first().context("missing decimals in call retdata")?;
Ok((*decimals).try_into()?)
}

async fn get_symbol(
provider: impl Provider,
block_id: BlockId,
contract_address: FieldElement,
) -> Result<String> {
let request = FunctionCall {
contract_address,
calldata: Vec::new(),
entry_point_selector: selector!("symbol"),
};

let result = provider.call(request, block_id).await?;
let symbol = result.first().context("missing symbol in call retdata")?;
Ok(parse_cairo_short_string(symbol)?)
}

let (symbol, decimals) = join!(
get_symbol(&provider, block_id, contract_address),
get_decimals(&provider, block_id, contract_address)
)
.await;

Ok((symbol?, decimals?))
}

Expand All @@ -135,3 +109,24 @@ fn format_balance(balance: U256, symbol: &str, decimals: u8) -> String {

format!("{decimal} {symbol}")
}

async fn get_decimals(
provider: impl Provider,
block_id: BlockId,
contract_address: FieldElement,
) -> Result<u8> {
let retdata =
contract_call(provider, contract_address, "decimals", Vec::new(), block_id).await?;
let dec = retdata.first().context("missing value in call retdata")?;
Ok((*dec).try_into()?)
}

async fn get_symbol(
provider: impl Provider,
block_id: BlockId,
contract_address: FieldElement,
) -> Result<String> {
let retdata = contract_call(provider, contract_address, "symbol", Vec::new(), block_id).await?;
let symbol = retdata.first().context("missing value in call retdata")?;
Ok(parse_cairo_short_string(symbol)?)
}
60 changes: 60 additions & 0 deletions crates/ops/src/call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use eyre::Result;
use rika_args::{commands::call::CallArgs, fmt::Pretty};
use starknet::{
core::{
types::{BlockId, FieldElement, FunctionCall},
utils::{get_selector_from_name, NonAsciiNameError},
},
providers::{Provider, ProviderError},
};

use crate::utils;

// TODO: parse the return data according to the ABI?
pub fn call(args: CallArgs) -> Result<()> {
let CallArgs {
contract_address,
function,
input,
block_id,
starknet,
} = args;

let provider = starknet.provider();
let retdata = utils::block_on(contract_call(
provider,
contract_address,
&function,
input,
block_id,
))?;
println!("{}", retdata.prettify());

Ok(())
}

#[derive(Debug, thiserror::Error)]
pub(crate) enum ContractCallError {
#[error("invalid contract entrypoint name: {0}")]
Selector(#[from] NonAsciiNameError),
#[error(transparent)]
Provider(#[from] ProviderError),
}

pub(crate) async fn contract_call<P: Provider>(
provider: P,
contract_address: FieldElement,
entrypoint: &str,
calldata: Vec<FieldElement>,
block: BlockId,
) -> Result<Vec<FieldElement>, ContractCallError> {
let entrypoint = get_selector_from_name(entrypoint)?;
let request = FunctionCall {
calldata,
contract_address,
entry_point_selector: entrypoint,
};

let retdata = provider.call(request, block).await?;
Ok(retdata)
}
1 change: 1 addition & 0 deletions crates/ops/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]

pub mod balance;
pub mod call;
pub mod rpc;
pub mod transaction;
mod utils;

0 comments on commit f448870

Please sign in to comment.