Skip to content

Commit

Permalink
Pad addresses to correct length (#2486)
Browse files Browse the repository at this point in the history
<!-- Reference any GitHub issues resolved by this PR -->

Closes #2248 
Closes #2424 

## Introduced changes

<!-- A brief description of the changes -->

- Addresses used in `sncast account create`, `sncast declare` and
`sncast deploy` are now correctly padded to 66 (64 after `0x`)
characters length.

## Checklist

<!-- Make sure all of these are complete -->

- [x] Linked relevant issue
- [x] Updated relevant documentation
- [x] Added relevant tests
- [x] Performed self-review of the code
- [x] Added changes to `CHANGELOG.md`

---------

Co-authored-by: ddoktorski <[email protected]>
  • Loading branch information
cptartur and ddoktorski authored Nov 8, 2024
1 parent c8331be commit 84167fa
Show file tree
Hide file tree
Showing 22 changed files with 232 additions and 68 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Added

- You can skip `--name` flag when using `account import` - a default name will be generated.
- Addresses outputted when calling `sncast account create`, `sncast deploy` and `sncast declare` are now padded to 64 characters length and prefixed with `0x0`

## [0.33.0] - 2024-11-04

Expand Down
2 changes: 2 additions & 0 deletions crates/conversions/src/class_hash.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{from_thru_felt252, FromConv, IntoConv};
use conversions::padded_felt::PaddedFelt;
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce};
use starknet_types_core::felt::Felt as Felt252;

Expand All @@ -11,3 +12,4 @@ impl FromConv<Felt252> for ClassHash {
from_thru_felt252!(ContractAddress, ClassHash);
from_thru_felt252!(Nonce, ClassHash);
from_thru_felt252!(EntryPointSelector, ClassHash);
from_thru_felt252!(PaddedFelt, ClassHash);
2 changes: 2 additions & 0 deletions crates/conversions/src/contract_address.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{from_thru_felt252, FromConv};
use conversions::padded_felt::PaddedFelt;
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce, PatriciaKey};
use starknet_api::hash::StarkHash;
use starknet_types_core::felt::Felt as Felt252;
Expand All @@ -12,3 +13,4 @@ impl FromConv<Felt252> for ContractAddress {
from_thru_felt252!(ClassHash, ContractAddress);
from_thru_felt252!(Nonce, ContractAddress);
from_thru_felt252!(EntryPointSelector, ContractAddress);
from_thru_felt252!(PaddedFelt, ContractAddress);
7 changes: 7 additions & 0 deletions crates/conversions/src/felt252.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
string::{TryFromDecStr, TryFromHexStr},
FromConv, IntoConv,
};
use conversions::padded_felt::PaddedFelt;
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce};
use starknet_types_core::felt::{Felt as Felt252, FromStrError};
use std::vec;
Expand Down Expand Up @@ -32,6 +33,12 @@ impl FromConv<EntryPointSelector> for Felt252 {
}
}

impl FromConv<PaddedFelt> for Felt252 {
fn from_(value: PaddedFelt) -> Felt252 {
value.0.into_()
}
}

impl<T> TryFromDecStr for T
where
T: FromConv<Felt252>,
Expand Down
3 changes: 3 additions & 0 deletions crates/conversions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ pub mod entrypoint_selector;
pub mod eth_address;
pub mod felt252;
pub mod nonce;
pub mod padded_felt;
pub mod primitive;
pub mod serde;
pub mod string;

extern crate self as conversions;

pub trait FromConv<T>: Sized {
fn from_(value: T) -> Self;
}
Expand Down
35 changes: 35 additions & 0 deletions crates/conversions/src/padded_felt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::FromConv;
use cairo_serde_macros::CairoSerialize;
use conversions::from_thru_felt252;
use serde::{Deserialize, Serialize, Serializer};
use starknet_api::core::{ClassHash, ContractAddress};
use starknet_types_core::felt::Felt as Felt252;
use std::fmt;
use std::fmt::{Formatter, LowerHex};

#[derive(Clone, Copy, Debug, PartialEq, Deserialize, CairoSerialize)]
pub struct PaddedFelt(pub Felt252);

impl FromConv<Felt252> for PaddedFelt {
fn from_(value: Felt252) -> Self {
Self(value)
}
}

impl Serialize for PaddedFelt {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("{:#064x}", &self.0))
}
}

impl LowerHex for PaddedFelt {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}

from_thru_felt252!(ClassHash, PaddedFelt);
from_thru_felt252!(ContractAddress, PaddedFelt);
1 change: 1 addition & 0 deletions crates/conversions/tests/e2e/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ mod entrypoint_selector;
mod felt252;
mod field_elements;
mod nonce;
mod padded_felt;
mod string;
16 changes: 16 additions & 0 deletions crates/conversions/tests/e2e/padded_felt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[cfg(test)]
mod tests {
use conversions::padded_felt::PaddedFelt;
use conversions::{FromConv, IntoConv};
use starknet_api::core::{ClassHash, ContractAddress};
use starknet_types_core::felt::Felt as Felt252;

#[test]
fn test_padded_felt_conversions_happy_case() {
let felt = Felt252::from_bytes_be(&[1u8; 32]);
let padded_felt = PaddedFelt(felt);

assert_eq!(padded_felt, ContractAddress::from_(padded_felt).into_());
assert_eq!(padded_felt, ClassHash::from_(padded_felt).into_());
}
}
58 changes: 29 additions & 29 deletions crates/sncast/src/helpers/block_explorer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use serde::{Deserialize, Serialize};
use starknet::core::types::Felt;

use crate::{response::explorer_link::ExplorerError, Network};
use conversions::padded_felt::PaddedFelt;
use serde::{Deserialize, Serialize};

const STARKSCAN: &str = "starkscan.co";
const VOYAGER: &str = "voyager.online";
Expand Down Expand Up @@ -33,9 +32,9 @@ impl Service {
}

pub trait LinkProvider {
fn transaction(&self, hash: Felt) -> String;
fn class(&self, hash: Felt) -> String;
fn contract(&self, address: Felt) -> String;
fn transaction(&self, hash: PaddedFelt) -> String;
fn class(&self, hash: PaddedFelt) -> String;
fn contract(&self, address: PaddedFelt) -> String;
}

const fn network_subdomain(network: Network) -> &'static str {
Expand All @@ -50,21 +49,21 @@ pub struct StarkScan {
}

impl LinkProvider for StarkScan {
fn transaction(&self, hash: Felt) -> String {
fn transaction(&self, hash: PaddedFelt) -> String {
format!(
"https://{}{STARKSCAN}/tx/{hash:#x}",
network_subdomain(self.network)
)
}

fn class(&self, hash: Felt) -> String {
fn class(&self, hash: PaddedFelt) -> String {
format!(
"https://{}{STARKSCAN}/class/{hash:#x}",
network_subdomain(self.network)
)
}

fn contract(&self, address: Felt) -> String {
fn contract(&self, address: PaddedFelt) -> String {
format!(
"https://{}{STARKSCAN}/contract/{address:#x}",
network_subdomain(self.network)
Expand All @@ -77,21 +76,21 @@ pub struct Voyager {
}

impl LinkProvider for Voyager {
fn transaction(&self, hash: Felt) -> String {
fn transaction(&self, hash: PaddedFelt) -> String {
format!(
"https://{}{VOYAGER}/tx/{hash:#x}",
network_subdomain(self.network)
)
}

fn class(&self, hash: Felt) -> String {
fn class(&self, hash: PaddedFelt) -> String {
format!(
"https://{}{VOYAGER}/class/{hash:#x}",
network_subdomain(self.network)
)
}

fn contract(&self, address: Felt) -> String {
fn contract(&self, address: PaddedFelt) -> String {
format!(
"https://{}{VOYAGER}/contract/{address:#x}",
network_subdomain(self.network)
Expand All @@ -102,47 +101,47 @@ impl LinkProvider for Voyager {
pub struct ViewBlock;

impl LinkProvider for ViewBlock {
fn transaction(&self, hash: Felt) -> String {
fn transaction(&self, hash: PaddedFelt) -> String {
format!("{VIEWBLOCK}/tx/{hash:#x}")
}

fn class(&self, hash: Felt) -> String {
fn class(&self, hash: PaddedFelt) -> String {
format!("{VIEWBLOCK}/class/{hash:#x}")
}

fn contract(&self, address: Felt) -> String {
fn contract(&self, address: PaddedFelt) -> String {
format!("{VIEWBLOCK}/contract/{address:#x}")
}
}

pub struct OkLink;

impl LinkProvider for OkLink {
fn transaction(&self, hash: Felt) -> String {
fn transaction(&self, hash: PaddedFelt) -> String {
format!("{OKLINK}/tx/{hash:#x}")
}

fn class(&self, hash: Felt) -> String {
fn class(&self, hash: PaddedFelt) -> String {
format!("{OKLINK}/class/{hash:#x}")
}

fn contract(&self, address: Felt) -> String {
fn contract(&self, address: PaddedFelt) -> String {
format!("{OKLINK}/contract/{address:#x}")
}
}

pub struct NftScan;

impl LinkProvider for NftScan {
fn transaction(&self, hash: Felt) -> String {
fn transaction(&self, hash: PaddedFelt) -> String {
format!("{NFTSCAN}/{hash:#x}")
}

fn class(&self, hash: Felt) -> String {
fn class(&self, hash: PaddedFelt) -> String {
format!("{NFTSCAN}/{hash:#x}")
}

fn contract(&self, address: Felt) -> String {
fn contract(&self, address: PaddedFelt) -> String {
format!("{NFTSCAN}/{address:#x}")
}
}
Expand All @@ -154,26 +153,27 @@ mod tests {
response::{explorer_link::OutputLink, structs::DeployResponse},
Network,
};
use conversions::padded_felt::PaddedFelt;
use regex::Regex;
use starknet::macros::felt;
use test_case::test_case;

const MAINNET_RESPONSE: DeployResponse = DeployResponse {
contract_address: felt!(
contract_address: PaddedFelt(felt!(
"0x03241d40a2af53a34274dd411e090ccac1ea80e0380a0303fe76d71b046cfecb"
),
transaction_hash: felt!(
)),
transaction_hash: PaddedFelt(felt!(
"0x7605291e593e0c6ad85681d09e27a601befb85033bdf1805aabf5d84617cf68"
),
)),
};

const SEPOLIA_RESPONSE: DeployResponse = DeployResponse {
contract_address: felt!(
contract_address: PaddedFelt(felt!(
"0x0716b5f1e3bd760c489272fd6530462a09578109049e26e3f4c70492676eae17"
),
transaction_hash: felt!(
)),
transaction_hash: PaddedFelt(felt!(
"0x1cde70aae10f79d2d1289c923a1eeca7b81a2a6691c32551ec540fa2cb29c33"
),
)),
};

async fn assert_valid_links(input: &str) {
Expand Down
45 changes: 45 additions & 0 deletions crates/sncast/src/response/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ impl Format for OutputValue {
if let Ok(field) = Felt::from_str(&input) {
return match numbers {
NumbersFormat::Decimal => OutputValue::String(format!("{field:#}")),
NumbersFormat::Hex if input.len() == 66 && input.starts_with("0x0") => {
OutputValue::String(input)
}
NumbersFormat::Hex => OutputValue::String(format!("{field:#x}")),
NumbersFormat::Default => OutputValue::String(input),
};
Expand Down Expand Up @@ -254,6 +257,48 @@ mod tests {
assert_eq!(actual, expected);
}

#[test]
fn format_address_force_hex() {
let json_value = OutputValue::Array(vec![OutputValue::String(String::from(
"0x0163a86513df426f4fd7ad989b11062769b03d3fd75fb83fae6c0f416b33a3d5",
))]);

let actual = json_value.format_with(NumbersFormat::Hex);
let expected = OutputValue::Array(vec![OutputValue::String(String::from(
"0x0163a86513df426f4fd7ad989b11062769b03d3fd75fb83fae6c0f416b33a3d5",
))]);

assert_eq!(actual, expected);
}

#[test]
fn format_address_force_decimal() {
let json_value = OutputValue::Array(vec![OutputValue::String(String::from(
"0x0163a86513df426f4fd7ad989b11062769b03d3fd75fb83fae6c0f416b33a3d5",
))]);

let actual = json_value.format_with(NumbersFormat::Decimal);
let expected = OutputValue::Array(vec![OutputValue::String(String::from(
"628392926429977811333168641010360117621605580210734736624161546314682966997",
))]);

assert_eq!(actual, expected);
}

#[test]
fn format_address_leave_default() {
let json_value = OutputValue::Array(vec![OutputValue::String(String::from(
"0x0163a86513df426f4fd7ad989b11062769b03d3fd75fb83fae6c0f416b33a3d5",
))]);

let actual = json_value.format_with(NumbersFormat::Default);
let expected = OutputValue::Array(vec![OutputValue::String(String::from(
"0x0163a86513df426f4fd7ad989b11062769b03d3fd75fb83fae6c0f416b33a3d5",
))]);

assert_eq!(actual, expected);
}

#[test]
fn test_struct_value_to_output_data() {
let mut json_value = Map::new();
Expand Down
13 changes: 7 additions & 6 deletions crates/sncast/src/response/structs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::explorer_link::OutputLink;
use crate::helpers::block_explorer::LinkProvider;
use camino::Utf8PathBuf;
use conversions::padded_felt::PaddedFelt;
use conversions::serde::serialize::CairoSerialize;
use indoc::formatdoc;
use serde::{Deserialize, Serialize, Serializer};
Expand Down Expand Up @@ -34,27 +35,27 @@ impl CommandResponse for CallResponse {}

#[derive(Serialize, Deserialize, CairoSerialize, Clone, Debug, PartialEq)]
pub struct InvokeResponse {
pub transaction_hash: Felt,
pub transaction_hash: PaddedFelt,
}
impl CommandResponse for InvokeResponse {}

#[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)]
pub struct DeployResponse {
pub contract_address: Felt,
pub transaction_hash: Felt,
pub contract_address: PaddedFelt,
pub transaction_hash: PaddedFelt,
}
impl CommandResponse for DeployResponse {}

#[derive(Clone, Serialize, Deserialize, CairoSerialize, Debug, PartialEq)]
pub struct DeclareResponse {
pub class_hash: Felt,
pub transaction_hash: Felt,
pub class_hash: PaddedFelt,
pub transaction_hash: PaddedFelt,
}
impl CommandResponse for DeclareResponse {}

#[derive(Serialize)]
pub struct AccountCreateResponse {
pub address: Felt,
pub address: PaddedFelt,
#[serde(serialize_with = "crate::response::structs::serialize_as_decimal")]
pub max_fee: Felt,
pub add_profile: String,
Expand Down
Loading

0 comments on commit 84167fa

Please sign in to comment.