Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit 384f459

Browse files
ERC20 test with cairo2 (#767)
* add erc20 * use exec * use deploy syscall * use proxy contract * use erc class hash * fix test * add asserts * clippy * remove old crate dep * Update starknet_programs/cairo2/deploy_erc20.cairo Co-authored-by: Matías Ignacio González <maigonzalez@fi.uba.ar> * Update starknet_programs/cairo2/deploy_erc20.cairo Co-authored-by: Matías Ignacio González <maigonzalez@fi.uba.ar> * Update src/testing/erc20.rs Co-authored-by: Matías Ignacio González <maigonzalez@fi.uba.ar> * Update src/testing/erc20.rs Co-authored-by: Matías Ignacio González <maigonzalez@fi.uba.ar> * Update src/testing/erc20.rs Co-authored-by: Matías Ignacio González <maigonzalez@fi.uba.ar> * remove duplicated function --------- Co-authored-by: Matías Ignacio González <maigonzalez@fi.uba.ar>
1 parent 57ed54d commit 384f459

File tree

6 files changed

+496
-1
lines changed

6 files changed

+496
-1
lines changed

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub fn call_contract<T: State + StateReader>(
108108
calldata: Vec<Felt252>,
109109
state: &mut T,
110110
block_context: BlockContext,
111+
caller_address: Address,
111112
) -> Result<Vec<Felt252>, TransactionError> {
112113
let contract_address = Address(contract_address);
113114
let class_hash = state.get_class_hash_at(&contract_address)?;
@@ -118,7 +119,6 @@ pub fn call_contract<T: State + StateReader>(
118119
let signature = vec![];
119120
let max_fee = 1000000000;
120121
let initial_gas = 1000000000;
121-
let caller_address = Address(0.into());
122122
let version = 0;
123123

124124
let execution_entrypoint = ExecutionEntryPoint::new(
@@ -319,6 +319,7 @@ mod test {
319319
calldata,
320320
&mut state,
321321
BlockContext::default(),
322+
Address(0.into()),
322323
)
323324
.unwrap();
324325

src/testing/erc20.rs

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
#![allow(unused_imports)]
2+
use std::{collections::HashMap, io::Bytes, path::Path, vec};
3+
4+
use crate::{
5+
call_contract,
6+
definitions::{
7+
block_context::{BlockContext, StarknetChainId},
8+
constants::CONSTRUCTOR_ENTRY_POINT_SELECTOR,
9+
},
10+
execution::{
11+
execution_entry_point::ExecutionEntryPoint, CallType, TransactionExecutionContext,
12+
},
13+
services::api::contract_classes::deprecated_contract_class::ContractClass,
14+
state::{
15+
cached_state::CachedState,
16+
in_memory_state_reader::InMemoryStateReader,
17+
state_api::{State, StateReader},
18+
ExecutionResourcesManager,
19+
},
20+
transaction::{error::TransactionError, DeployAccount, InvokeFunction},
21+
utils::calculate_sn_keccak,
22+
EntryPointType, Felt252,
23+
};
24+
use cairo_lang_starknet::casm_contract_class::CasmContractClass;
25+
use cairo_vm::felt::felt_str;
26+
use lazy_static::lazy_static;
27+
use num_traits::Zero;
28+
pub const ERC20_CONTRACT_PATH: &str = "starknet_programs/cairo2/ERC20.casm";
29+
use crate::{
30+
state::state_cache::StorageEntry,
31+
utils::{felt_to_hash, Address, ClassHash},
32+
};
33+
34+
use super::{
35+
get_contract_class, new_starknet_block_context_for_testing, ACCOUNT_CONTRACT_PATH, ACTUAL_FEE,
36+
TEST_ACCOUNT_CONTRACT_ADDRESS, TEST_ACCOUNT_CONTRACT_CLASS_HASH, TEST_CLASS_HASH,
37+
TEST_CONTRACT_ADDRESS, TEST_CONTRACT_PATH, TEST_ERC20_ACCOUNT_BALANCE_KEY,
38+
TEST_ERC20_CONTRACT_CLASS_HASH,
39+
};
40+
41+
#[test]
42+
fn test_erc20_cairo2() {
43+
// data to deploy
44+
let erc20_class_hash: ClassHash = [2; 32];
45+
let test_data = include_bytes!("../../starknet_programs/cairo2/erc20.casm");
46+
let test_contract_class: CasmContractClass = serde_json::from_slice(test_data).unwrap();
47+
48+
// Create the deploy contract class
49+
let program_data = include_bytes!("../../starknet_programs/cairo2/deploy_erc20.casm");
50+
let contract_class: CasmContractClass = serde_json::from_slice(program_data).unwrap();
51+
let entrypoints = contract_class.clone().entry_points_by_type;
52+
let entrypoint_selector = &entrypoints.external.get(0).unwrap().selector;
53+
54+
// Create state reader with class hash data
55+
let mut contract_class_cache = HashMap::new();
56+
57+
let address = Address(1111.into());
58+
let class_hash: ClassHash = [1; 32];
59+
let nonce = Felt252::zero();
60+
61+
contract_class_cache.insert(class_hash, contract_class);
62+
contract_class_cache.insert(erc20_class_hash, test_contract_class);
63+
64+
let mut state_reader = InMemoryStateReader::default();
65+
state_reader
66+
.address_to_class_hash_mut()
67+
.insert(address.clone(), class_hash);
68+
state_reader
69+
.address_to_nonce_mut()
70+
.insert(address.clone(), nonce);
71+
72+
// Create state from the state_reader and contract cache.
73+
let mut state = CachedState::new(state_reader, None, Some(contract_class_cache));
74+
75+
let name_ = Felt252::from_bytes_be(b"some-token");
76+
let symbol_ = Felt252::from_bytes_be(b"my-super-awesome-token");
77+
let decimals_ = Felt252::from(24);
78+
let initial_supply = Felt252::from(1000);
79+
let recipient =
80+
felt_str!("397149464972449753182583229366244826403270781177748543857889179957856017275");
81+
let erc20_salt = felt_str!("1234");
82+
// arguments of deploy contract
83+
let calldata = vec![
84+
Felt252::from_bytes_be(&erc20_class_hash),
85+
erc20_salt,
86+
recipient,
87+
name_,
88+
decimals_,
89+
initial_supply,
90+
symbol_,
91+
];
92+
// set up remaining structures
93+
94+
let caller_address = Address(0000.into());
95+
let entry_point_type = EntryPointType::External;
96+
97+
let exec_entry_point = ExecutionEntryPoint::new(
98+
address,
99+
calldata,
100+
Felt252::new(entrypoint_selector.clone()),
101+
caller_address,
102+
entry_point_type,
103+
Some(CallType::Delegate),
104+
Some(class_hash),
105+
100_000_000_000,
106+
);
107+
108+
// Execute the entrypoint
109+
let block_context = BlockContext::default();
110+
let mut tx_execution_context = TransactionExecutionContext::new(
111+
Address(0.into()),
112+
Felt252::zero(),
113+
Vec::new(),
114+
0,
115+
10.into(),
116+
block_context.invoke_tx_max_n_steps(),
117+
1.into(),
118+
);
119+
let mut resources_manager = ExecutionResourcesManager::default();
120+
121+
let call_info = exec_entry_point
122+
.execute(
123+
&mut state,
124+
&block_context,
125+
&mut resources_manager,
126+
&mut tx_execution_context,
127+
false,
128+
)
129+
.unwrap();
130+
let erc20_address = call_info.retdata.get(0).unwrap().clone();
131+
132+
// ACCOUNT 1
133+
let program_data_account =
134+
include_bytes!("../../starknet_programs/cairo2/hello_world_account.casm");
135+
let contract_class_account: CasmContractClass =
136+
serde_json::from_slice(program_data_account).unwrap();
137+
138+
state
139+
.set_compiled_class(&felt_str!("1"), contract_class_account)
140+
.unwrap();
141+
142+
let contract_address_salt =
143+
felt_str!("2669425616857739096022668060305620640217901643963991674344872184515580705509");
144+
145+
let internal_deploy_account = DeployAccount::new(
146+
felt_str!("1").to_be_bytes(),
147+
0,
148+
1.into(),
149+
Felt252::zero(),
150+
vec![2.into()],
151+
vec![
152+
felt_str!(
153+
"3233776396904427614006684968846859029149676045084089832563834729503047027074"
154+
),
155+
felt_str!(
156+
"707039245213420890976709143988743108543645298941971188668773816813012281203"
157+
),
158+
],
159+
contract_address_salt,
160+
StarknetChainId::TestNet.to_felt(),
161+
None,
162+
)
163+
.unwrap();
164+
165+
let account_address_1 = internal_deploy_account
166+
.execute(&mut state, &Default::default())
167+
.unwrap()
168+
.validate_info
169+
.unwrap()
170+
.contract_address;
171+
172+
// ACCOUNT 2
173+
let program_data_account =
174+
include_bytes!("../../starknet_programs/cairo2/hello_world_account.casm");
175+
let contract_class_account: CasmContractClass =
176+
serde_json::from_slice(program_data_account).unwrap();
177+
178+
state
179+
.set_compiled_class(&felt_str!("1"), contract_class_account)
180+
.unwrap();
181+
182+
let contract_address_salt = felt_str!("123123123123123");
183+
184+
let internal_deploy_account = DeployAccount::new(
185+
felt_str!("1").to_be_bytes(),
186+
0,
187+
1.into(),
188+
Felt252::zero(),
189+
vec![2.into()],
190+
vec![
191+
felt_str!(
192+
"3233776396904427614006684968846859029149676045084089832563834729503047027074"
193+
),
194+
felt_str!(
195+
"707039245213420890976709143988743108543645298941971188668773816813012281203"
196+
),
197+
],
198+
contract_address_salt,
199+
StarknetChainId::TestNet.to_felt(),
200+
None,
201+
)
202+
.unwrap();
203+
204+
let account_address_2 = internal_deploy_account
205+
.execute(&mut state, &Default::default())
206+
.unwrap()
207+
.validate_info
208+
.unwrap()
209+
.contract_address;
210+
211+
// TRANSFER
212+
let entrypoint_selector = Felt252::from_bytes_be(&calculate_sn_keccak(b"transfer"));
213+
let calldata = vec![account_address_2.clone().0, Felt252::from(123)];
214+
215+
let retdata = call_contract(
216+
erc20_address.clone(),
217+
entrypoint_selector,
218+
calldata,
219+
&mut state,
220+
BlockContext::default(),
221+
account_address_1.clone(),
222+
)
223+
.unwrap();
224+
225+
assert!(retdata.is_empty());
226+
227+
// GET BALANCE ACCOUNT 1
228+
let entrypoint_selector = Felt252::from_bytes_be(&calculate_sn_keccak(b"balance_of"));
229+
let retdata = call_contract(
230+
erc20_address.clone(),
231+
entrypoint_selector,
232+
vec![account_address_1.clone().0],
233+
&mut state,
234+
BlockContext::default(),
235+
account_address_1.clone(),
236+
)
237+
.unwrap();
238+
239+
assert_eq!(retdata, vec![877.into()]);
240+
241+
// GET BALANCE ACCOUNT 2
242+
let entrypoint_selector = Felt252::from_bytes_be(&calculate_sn_keccak(b"balance_of"));
243+
let retdata = call_contract(
244+
erc20_address,
245+
entrypoint_selector,
246+
vec![account_address_2.0],
247+
&mut state,
248+
BlockContext::default(),
249+
account_address_1,
250+
)
251+
.unwrap();
252+
253+
assert_eq!(retdata, vec![123.into()]);
254+
}

src/testing/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod erc20;
12
pub mod state;
23
pub mod state_error;
34
pub mod type_utils;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use starknet::contract_address::ContractAddress;
2+
use starknet::class_hash::ClassHash;
3+
#[starknet::interface]
4+
trait IDeployTest<TContractState> {
5+
fn deploy_test(self: @TContractState, class_hash: ClassHash, contract_address_salt: felt252, recipient: felt252, name: felt252, decimals: felt252, initial_supply: felt252, symbol: felt252) -> (core::starknet::contract_address::ContractAddress, core::array::Span::<core::felt252>);
6+
}
7+
8+
#[starknet::contract]
9+
mod DeployTest {
10+
use core::result::ResultTrait;
11+
use starknet::syscalls::deploy_syscall;
12+
use starknet::class_hash::ClassHash;
13+
use starknet::contract_address::ContractAddress;
14+
use starknet::class_hash::Felt252TryIntoClassHash;
15+
use option::OptionTrait;
16+
use traits::TryInto;
17+
use array::ArrayTrait;
18+
19+
#[storage]
20+
struct Storage {
21+
}
22+
23+
#[external(v0)]
24+
impl DeployTest of super::IDeployTest<ContractState> {
25+
fn deploy_test(self: @ContractState, class_hash: ClassHash, contract_address_salt: felt252, recipient: felt252, name: felt252, decimals: felt252, initial_supply: felt252, symbol: felt252) -> (core::starknet::contract_address::ContractAddress, core::array::Span::<core::felt252>) {
26+
let mut calldata = ArrayTrait::new();
27+
calldata.append(recipient);
28+
calldata.append(name);
29+
calldata.append(decimals);
30+
calldata.append(initial_supply);
31+
calldata.append(symbol);
32+
deploy_syscall(class_hash, contract_address_salt, calldata.span(), false).unwrap()
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)