Skip to content

Commit fa67da5

Browse files
committed
EthAccount tests for initialization & upgrade
1 parent 5ca984b commit fa67da5

3 files changed

Lines changed: 210 additions & 0 deletions

File tree

eth_712_account/src/lib.cairo

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ pub mod eth_712_account;
22
pub mod eth_712_utils;
33
pub mod interface;
44
pub mod register_interfaces_eic;
5+
6+
#[cfg(test)]
7+
mod test;
8+
#[cfg(test)]
9+
mod test_utils;

eth_712_account/src/test.cairo

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use openzeppelin::account::extensions::src9::interface::ISRC9_V2_ID;
2+
use openzeppelin::account::interface::ISRC6_ID;
3+
use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait};
4+
use snforge_std::{EventSpyTrait, spy_events, start_cheat_caller_address, stop_cheat_caller_address};
5+
use crate::interface::{IAccount712AdminDispatcher, IAccount712AdminDispatcherTrait};
6+
use crate::test_utils::{
7+
TEST_ETH_ADDRESS, WRONG_ETH_ADDRESS, deploy_eth712_account, get_invalid_signature,
8+
get_ownership_signature,
9+
};
10+
11+
// ================================
12+
// initialize tests
13+
// ================================
14+
15+
#[test]
16+
fn test_initialize_success() {
17+
let contract_address = deploy_eth712_account();
18+
let dispatcher = IAccount712AdminDispatcher { contract_address };
19+
20+
// Initialize with valid signature
21+
dispatcher.initialize(TEST_ETH_ADDRESS(), get_ownership_signature());
22+
23+
// Verify interfaces are registered
24+
let src5 = ISRC5Dispatcher { contract_address };
25+
assert!(src5.supports_interface(ISRC9_V2_ID), "ISRC9_V2_ID not registered");
26+
assert!(src5.supports_interface(ISRC6_ID), "ISRC6_ID not registered");
27+
}
28+
29+
#[test]
30+
#[should_panic(expected: 'ALREADY_INITIALIZED')]
31+
fn test_initialize_already_initialized_reverts() {
32+
let contract_address = deploy_eth712_account();
33+
let dispatcher = IAccount712AdminDispatcher { contract_address };
34+
35+
// First initialization should succeed
36+
dispatcher.initialize(TEST_ETH_ADDRESS(), get_ownership_signature());
37+
38+
// Second initialization should fail
39+
dispatcher.initialize(TEST_ETH_ADDRESS(), get_ownership_signature());
40+
}
41+
42+
#[test]
43+
#[should_panic(expected: 'INVALID_OWNERSHIP_SIGNATURE')]
44+
fn test_initialize_invalid_signature_reverts() {
45+
let contract_address = deploy_eth712_account();
46+
let dispatcher = IAccount712AdminDispatcher { contract_address };
47+
48+
// Initialize with invalid signature
49+
dispatcher.initialize(TEST_ETH_ADDRESS(), get_invalid_signature());
50+
}
51+
52+
#[test]
53+
#[should_panic(expected: 'INVALID_OWNERSHIP_SIGNATURE')]
54+
fn test_initialize_wrong_address_reverts() {
55+
let contract_address = deploy_eth712_account();
56+
let dispatcher = IAccount712AdminDispatcher { contract_address };
57+
58+
// Initialize with correct signature but wrong address
59+
dispatcher.initialize(WRONG_ETH_ADDRESS(), get_ownership_signature());
60+
}
61+
62+
// ================================
63+
// upgrade tests
64+
// ================================
65+
66+
#[test]
67+
#[should_panic(expected: 'UNAUTHORIZED')]
68+
fn test_upgrade_unauthorized_reverts() {
69+
let contract_address = deploy_eth712_account();
70+
let dispatcher = IAccount712AdminDispatcher { contract_address };
71+
72+
// Initialize first
73+
dispatcher.initialize(TEST_ETH_ADDRESS(), get_ownership_signature());
74+
75+
// Try to upgrade from external caller (not self)
76+
let new_class_hash = crate::test_utils::declare_eth712_account();
77+
dispatcher.upgrade(new_class_hash, Option::None);
78+
}
79+
80+
#[test]
81+
fn test_upgrade_from_self_succeeds() {
82+
let contract_address = deploy_eth712_account();
83+
let dispatcher = IAccount712AdminDispatcher { contract_address };
84+
85+
// Initialize first
86+
dispatcher.initialize(TEST_ETH_ADDRESS(), get_ownership_signature());
87+
88+
// Spoof caller as self
89+
start_cheat_caller_address(contract_address, contract_address);
90+
91+
let mut spy = spy_events();
92+
let new_class_hash = crate::test_utils::declare_eth712_account();
93+
dispatcher.upgrade(new_class_hash, Option::None);
94+
95+
// Verify an event was emitted (Upgraded event)
96+
let events = spy.get_events();
97+
assert!(events.events.len() > 0, "No events emitted");
98+
99+
// Verify the event data contains the new class hash
100+
let (from, event) = events.events.at(0);
101+
assert!(*from == contract_address, "Event from wrong address");
102+
assert!(event.data.len() > 0, "Event has no data");
103+
assert!(*event.data.at(0) == new_class_hash.into(), "Wrong class hash in event");
104+
105+
stop_cheat_caller_address(contract_address);
106+
}
107+
108+
#[test]
109+
fn test_upgrade_with_eic() {
110+
let contract_address = deploy_eth712_account();
111+
let dispatcher = IAccount712AdminDispatcher { contract_address };
112+
113+
// Initialize first
114+
dispatcher.initialize(TEST_ETH_ADDRESS(), get_ownership_signature());
115+
116+
// Spoof caller as self
117+
start_cheat_caller_address(contract_address, contract_address);
118+
119+
let new_class_hash = crate::test_utils::declare_eth712_account();
120+
let eic_class_hash = crate::test_utils::declare_register_interfaces_eic();
121+
122+
// Register a custom interface via EIC
123+
let custom_interface_id: felt252 = 0x12345678;
124+
let eic_data: Span<felt252> = array![custom_interface_id].span();
125+
126+
dispatcher.upgrade(new_class_hash, Option::Some((eic_class_hash, eic_data)));
127+
128+
// Verify the custom interface was registered
129+
let src5 = ISRC5Dispatcher { contract_address };
130+
assert!(src5.supports_interface(custom_interface_id), "Custom interface not registered");
131+
132+
stop_cheat_caller_address(contract_address);
133+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use snforge_std::{ContractClassTrait, DeclareResultTrait};
2+
use starknet::secp256_trait::Signature;
3+
use starknet::{ClassHash, ContractAddress, EthAddress, SyscallResultTrait};
4+
5+
/// Test Ethereum address corresponding to private key:
6+
/// 0xa6d86467b6ec9e161649b27edfd8519e75a2e1cf5f4c309c628706e6999780e8
7+
/// Address: 0xbF60187c5dFfA627249f1C3000A4168dbB9D7A1A
8+
pub fn TEST_ETH_ADDRESS() -> EthAddress {
9+
0xbF60187c5dFfA627249f1C3000A4168dbB9D7A1A_felt252.try_into().unwrap()
10+
}
11+
12+
/// A different Ethereum address for testing invalid cases.
13+
pub fn WRONG_ETH_ADDRESS() -> EthAddress {
14+
0x1234567890123456789012345678901234567890_felt252.try_into().unwrap()
15+
}
16+
17+
/// Returns the ownership signature for TEST_ETH_ADDRESS.
18+
/// This signature was generated by signing the OWNERSHIP_TRANSFER_MSG_HASH
19+
/// (0x3ce976d55131cd0bdd49f20afbded052d8e907dc6034d95cdf117a8fd7752e3c)
20+
/// with the test private key.
21+
///
22+
/// Values from: split_raw_eth_sig_to_felts output
23+
/// '0xda47d5165a4577a024d18b8d61c5fe53 0xe994c0e202b390bddaffacf04bdea826
24+
/// 0x46dd0af587023ccad738aa0a82b05f98 0x2e117624d8cc474d0641c3168944eb67 0x1'
25+
pub fn get_ownership_signature() -> Signature {
26+
Signature {
27+
r: u256 {
28+
low: 0xda47d5165a4577a024d18b8d61c5fe53, high: 0xe994c0e202b390bddaffacf04bdea826,
29+
},
30+
s: u256 {
31+
low: 0x46dd0af587023ccad738aa0a82b05f98, high: 0x2e117624d8cc474d0641c3168944eb67,
32+
},
33+
y_parity: true // v % 2 == 0
34+
}
35+
}
36+
37+
/// Returns an invalid signature for testing.
38+
pub fn get_invalid_signature() -> Signature {
39+
Signature {
40+
r: u256 { low: 0x1234567890abcdef, high: 0xfedcba0987654321 },
41+
s: u256 { low: 0xabcdef1234567890, high: 0x1234567890abcdef },
42+
y_parity: false,
43+
}
44+
}
45+
46+
/// Declare and deploy the StarknetEth712Account contract.
47+
pub fn deploy_eth712_account() -> ContractAddress {
48+
let contract_class = snforge_std::declare("StarknetEth712Account")
49+
.unwrap_syscall()
50+
.contract_class();
51+
let (contract_address, _) = contract_class.deploy(@array![]).unwrap_syscall();
52+
contract_address
53+
}
54+
55+
/// Declare the StarknetEth712Account contract and return its class hash.
56+
pub fn declare_eth712_account() -> ClassHash {
57+
*snforge_std::declare("StarknetEth712Account").unwrap_syscall().contract_class().class_hash
58+
}
59+
60+
/// Declare and deploy the RegisterInterfacesEIC contract.
61+
pub fn deploy_register_interfaces_eic() -> ContractAddress {
62+
let contract_class = snforge_std::declare("RegisterInterfacesEIC")
63+
.unwrap_syscall()
64+
.contract_class();
65+
let (contract_address, _) = contract_class.deploy(@array![]).unwrap_syscall();
66+
contract_address
67+
}
68+
69+
/// Declare the RegisterInterfacesEIC contract and return its class hash.
70+
pub fn declare_register_interfaces_eic() -> ClassHash {
71+
*snforge_std::declare("RegisterInterfacesEIC").unwrap_syscall().contract_class().class_hash
72+
}

0 commit comments

Comments
 (0)