Skip to content

Commit dd39de8

Browse files
authored
Fix ByteArray handling in ERC20 predeployment (#744)
* [skip ci]
1 parent ee161fa commit dd39de8

File tree

2 files changed

+75
-17
lines changed

2 files changed

+75
-17
lines changed

crates/starknet-devnet-core/src/starknet/predeployed.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use starknet_rs_core::types::Felt;
33
use starknet_rs_core::utils::cairo_short_string_to_felt;
44
use starknet_types::contract_address::ContractAddress;
55
use starknet_types::felt::felt_from_prefixed_hex;
6+
use starknet_types_core::hash::Poseidon;
67

78
use crate::constants::{
89
CHARGEABLE_ACCOUNT_ADDRESS, UDC_CONTRACT, UDC_CONTRACT_ADDRESS, UDC_CONTRACT_CLASS_HASH,
@@ -22,6 +23,40 @@ pub(crate) fn create_erc20_at_address_extended(
2223
Ok(erc20_fee_contract)
2324
}
2425

26+
fn store_short_string_as_byte_array(
27+
state: &mut StarknetState,
28+
contract_address: ContractAddress,
29+
storage_var_name: &str,
30+
short_str: &str,
31+
) -> DevnetResult<()> {
32+
let storage_var_address = get_storage_var_address(storage_var_name, &[])?.try_into()?;
33+
34+
let felt_value =
35+
cairo_short_string_to_felt(short_str).map_err(|_| Error::UnexpectedInternalError {
36+
msg: format!("Cannot create a ByteArray from {short_str}"),
37+
})?;
38+
39+
state.set_storage_at(
40+
contract_address.try_into()?,
41+
storage_var_address,
42+
short_str.len().into(),
43+
)?;
44+
45+
// That's how ByteArray is defined
46+
let capacity_arg = cairo_short_string_to_felt("ByteArray")
47+
.map_err(|e| Error::UnexpectedInternalError { msg: e.to_string() })?;
48+
49+
let chunk_index = Felt::ZERO;
50+
let mut hashable = [storage_var_address.into(), chunk_index, capacity_arg];
51+
Poseidon::hades_permutation(&mut hashable);
52+
let chunk_base = starknet_api::state::StorageKey::try_from(hashable[0])?;
53+
54+
// no offset in chunk because the word is expected to be short
55+
state.set_storage_at(contract_address.try_into()?, chunk_base, felt_value)?;
56+
57+
Ok(())
58+
}
59+
2560
/// Set initial values of ERC20 contract storage
2661
pub(crate) fn initialize_erc20_at_address(
2762
state: &mut StarknetState,
@@ -31,17 +66,11 @@ pub(crate) fn initialize_erc20_at_address(
3166
) -> DevnetResult<()> {
3267
let contract_address = ContractAddress::new(contract_address)?;
3368

69+
for (storage_var_name, string) in [("ERC20_name", erc20_name), ("ERC20_symbol", erc20_symbol)] {
70+
store_short_string_as_byte_array(state, contract_address, storage_var_name, string)?;
71+
}
72+
3473
for (storage_var_name, storage_value) in [
35-
(
36-
"ERC20_name",
37-
cairo_short_string_to_felt(erc20_name)
38-
.map_err(|err| Error::UnexpectedInternalError { msg: err.to_string() })?,
39-
),
40-
(
41-
"ERC20_symbol",
42-
cairo_short_string_to_felt(erc20_symbol)
43-
.map_err(|err| Error::UnexpectedInternalError { msg: err.to_string() })?,
44-
),
4574
("ERC20_decimals", 18.into()),
4675
// necessary to set - otherwise minting txs cannot be executed
4776
("Ownable_owner", felt_from_prefixed_hex(CHARGEABLE_ACCOUNT_ADDRESS)?),

tests/integration/general_integration_tests.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use reqwest::StatusCode;
22
use serde_json::json;
3-
use starknet_rs_core::types::{BlockId, BlockTag, Felt};
4-
use starknet_rs_core::utils::{get_storage_var_address, parse_cairo_short_string};
3+
use starknet_rs_core::codec::Decode;
4+
use starknet_rs_core::types::{BlockId, BlockTag, ByteArray, Felt, FunctionCall};
5+
use starknet_rs_core::utils::{get_selector_from_name, get_storage_var_address};
56
use starknet_rs_providers::Provider;
67

78
use crate::common::background_devnet::BackgroundDevnet;
@@ -186,14 +187,14 @@ async fn test_config() {
186187
assert_eq!(fetched_config, expected_config);
187188
}
188189

190+
/// Part of the responsibility of this test was transferred to test
191+
/// `predeployed_erc20_tokens_return_expected_values_from_property_getters`
189192
#[tokio::test]
190193
async fn predeployed_erc20_tokens_have_expected_storage() {
191194
let devnet = BackgroundDevnet::spawn().await.unwrap();
192195
for (token_address, var_name, expected_value) in [
193-
(ETH_ERC20_CONTRACT_ADDRESS, "ERC20_name", "Ether"),
194-
(ETH_ERC20_CONTRACT_ADDRESS, "ERC20_symbol", "ETH"),
195-
(STRK_ERC20_CONTRACT_ADDRESS, "ERC20_name", "StarkNet Token"),
196-
(STRK_ERC20_CONTRACT_ADDRESS, "ERC20_symbol", "STRK"),
196+
(ETH_ERC20_CONTRACT_ADDRESS, "ERC20_decimals", Felt::from(18)),
197+
(STRK_ERC20_CONTRACT_ADDRESS, "ERC20_decimals", Felt::from(18)),
197198
] {
198199
let actual_value = devnet
199200
.json_rpc_client
@@ -205,6 +206,34 @@ async fn predeployed_erc20_tokens_have_expected_storage() {
205206
.await
206207
.unwrap();
207208

208-
assert_eq!(parse_cairo_short_string(&actual_value).unwrap().as_str(), expected_value);
209+
assert_eq!(actual_value, expected_value);
210+
}
211+
}
212+
213+
#[tokio::test]
214+
async fn predeployed_erc20_tokens_return_expected_values_from_property_getters() {
215+
let devnet = BackgroundDevnet::spawn().await.unwrap();
216+
for (token_address, getter_name, expected_value) in [
217+
(ETH_ERC20_CONTRACT_ADDRESS, "name", "Ether"),
218+
(ETH_ERC20_CONTRACT_ADDRESS, "symbol", "ETH"),
219+
(STRK_ERC20_CONTRACT_ADDRESS, "name", "StarkNet Token"),
220+
(STRK_ERC20_CONTRACT_ADDRESS, "symbol", "STRK"),
221+
] {
222+
let actual_felts = devnet
223+
.json_rpc_client
224+
.call(
225+
FunctionCall {
226+
contract_address: token_address,
227+
entry_point_selector: get_selector_from_name(getter_name).unwrap(),
228+
calldata: vec![],
229+
},
230+
BlockId::Tag(BlockTag::Latest),
231+
)
232+
.await
233+
.unwrap();
234+
235+
// We expect the felt vector to contain the encoded ByteArray, thus we decode it
236+
let actual_string: String = ByteArray::decode(&actual_felts).unwrap().try_into().unwrap();
237+
assert_eq!(&actual_string, expected_value);
209238
}
210239
}

0 commit comments

Comments
 (0)