Skip to content

Commit 384fdbe

Browse files
authored
Merge pull request #14 from aji70/main
feat: created register, reterieve and verify user closes #1
2 parents 77b0d87 + 96596f8 commit 384fdbe

File tree

4 files changed

+245
-31
lines changed

4 files changed

+245
-31
lines changed

src/base/types.cairo

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,32 @@ pub struct TokenBoundAccount {
99
pub created_at: u64,
1010
pub updated_at: u64,
1111
}
12+
#[derive(Drop, Serde, starknet::Store)]
13+
pub struct User {
14+
pub id: u256,
15+
pub username: felt252,
16+
pub wallet_address: ContractAddress,
17+
pub role: Role,
18+
pub rank: Rank,
19+
pub verified: bool,
20+
pub metadata: felt252,
21+
}
22+
23+
24+
#[derive(Drop, Serde, starknet::Store, Clone, PartialEq)]
25+
pub enum Role {
26+
#[default]
27+
NIL,
28+
READER,
29+
WRITER,
30+
}
31+
32+
33+
#[derive(Drop, Serde, starknet::Store, Clone, PartialEq)]
34+
pub enum Rank {
35+
#[default]
36+
BEGINNER,
37+
INTERMEDIATE,
38+
EXPERT,
39+
}
40+

src/chainlib/ChainLib.cairo

Lines changed: 117 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,73 +6,91 @@ pub mod ChainLib {
66
};
77
use starknet::{ContractAddress, get_block_timestamp, get_caller_address};
88
use crate::interfaces::IChainLib::IChainLib;
9-
use crate::base::types::{TokenBoundAccount};
9+
use crate::base::types::{TokenBoundAccount, User, Role, Rank};
1010

1111

1212
#[storage]
1313
struct Storage {
1414
// Contract addresses for component management
15-
deployed: bool,
15+
admin: ContractAddress,
1616
current_account_id: u256,
1717
accounts: Map<u256, TokenBoundAccount>,
1818
accountsaddr: Map<ContractAddress, TokenBoundAccount>,
1919
next_course_id: u256,
20-
nuum: Map<u8, u8>,
20+
user_id: u256,
21+
users: Map<u256, User>,
2122
}
2223

2324

2425
#[constructor]
25-
fn constructor(ref self: ContractState) {
26+
fn constructor(ref self: ContractState, admin: ContractAddress) {
2627
// Store the values in contract state
27-
self.deployed.write(true);
28+
self.admin.write(admin);
2829
}
2930

3031
#[event]
3132
#[derive(Drop, starknet::Event)]
3233
pub enum Event {
33-
TokenBountAccountcreated: TokenBountAccountcreated,
34+
TokenBoundAccountCreated: TokenBoundAccountCreated,
35+
UserCreated: UserCreated,
3436
}
3537

3638
#[derive(Drop, starknet::Event)]
37-
pub struct TokenBountAccountcreated {
39+
pub struct TokenBoundAccountCreated {
40+
pub id: u256,
41+
}
42+
43+
#[derive(Drop, starknet::Event)]
44+
pub struct UserCreated {
3845
pub id: u256,
3946
}
4047

4148
#[abi(embed_v0)]
4249
impl ChainLibNetImpl of IChainLib<ContractState> {
50+
/// @notice Creates a new token-bound account.
51+
/// @dev This function generates a unique ID, initializes the account, and emits an event.
52+
/// @param self The contract state reference.
53+
/// @param user_name The unique username associated with the token-bound account.
54+
/// @param init_param1 An initialization parameter required for the account setup.
55+
/// @param init_param2 An additional initialization parameter.
56+
/// @return account_id The unique identifier assigned to the token-bound account.
4357
fn create_token_account(
44-
ref self: ContractState, user_name: felt252, init_param1: felt252, init_param2: felt252,
58+
ref self: ContractState, user_name: felt252, init_param1: felt252, init_param2: felt252
4559
) -> u256 {
46-
// Validate input parameters.
60+
// Ensure that the username is not empty.
4761
assert!(user_name != 0, "User name cannot be empty");
62+
63+
// Validate initialization parameters.
4864
assert!(init_param1 != 0, "Initialization parameter 1 cannot be empty");
4965

5066
// Retrieve the current account ID before incrementing.
5167
let account_id = self.current_account_id.read();
5268

53-
// Create a new token bound account struct.
69+
// Create a new token-bound account with the provided parameters.
5470
let new_token_bound_account = TokenBoundAccount {
5571
id: account_id,
56-
address: get_caller_address(),
72+
address: get_caller_address(), // Assign the caller's address.
5773
user_name: user_name,
5874
init_param1: init_param1,
5975
init_param2: init_param2,
60-
created_at: get_block_timestamp(),
61-
updated_at: get_block_timestamp(),
76+
created_at: get_block_timestamp(), // Capture the creation timestamp.
77+
updated_at: get_block_timestamp() // Set initial updated timestamp.
6278
};
6379

64-
// Store the new account in the accounts map.
80+
// Store the new account in the accounts mapping.
6581
self.accounts.write(account_id, new_token_bound_account);
6682

67-
// Increment the account ID counter after using the current value.
83+
// Increment the account ID counter for the next registration.
6884
self.current_account_id.write(account_id + 1);
6985

70-
// Emit an event to signal the creation of the token bound account.
71-
self.emit(TokenBountAccountcreated { id: account_id });
86+
// Emit an event to notify about the new token-bound account creation.
87+
self.emit(TokenBoundAccountCreated { id: account_id });
7288

89+
// Return the assigned account ID.
7390
account_id
7491
}
7592

93+
7694
fn get_token_bound_account(ref self: ContractState, id: u256) -> TokenBoundAccount {
7795
let token_bound_account = self.accounts.read(id);
7896
token_bound_account
@@ -83,8 +101,88 @@ pub mod ChainLib {
83101
let token_bound_account = self.accountsaddr.read(address);
84102
token_bound_account
85103
}
86-
fn test_deployment(ref self: ContractState) -> bool {
87-
self.deployed.read()
104+
105+
106+
/// @notice Registers a new user in the system.
107+
/// @dev This function assigns a unique ID to the user, stores their profile, and emits an
108+
/// event.
109+
/// @param self The contract state reference.
110+
/// @param username The unique username of the user.
111+
/// @param wallet_address The blockchain address of the user.
112+
/// @param role The role of the user (READER or WRITER).
113+
/// @param rank The rank/level of the user.
114+
/// @param metadata Additional metadata associated with the user.
115+
/// @return user_id The unique identifier assigned to the user.
116+
fn register_user(
117+
ref self: ContractState, username: felt252, role: Role, rank: Rank, metadata: felt252
118+
) -> u256 {
119+
// Ensure that the username is not empty.
120+
assert!(username != 0, "User name cannot be empty");
121+
122+
// Retrieve the current user ID before incrementing.
123+
let user_id = self.user_id.read();
124+
125+
// Create a new user profile with provided details.
126+
let new_user = User {
127+
id: user_id,
128+
username: username,
129+
wallet_address: get_caller_address(), // Assign the caller's address.
130+
role: role,
131+
rank: rank,
132+
verified: false, // Default verification status is false.
133+
metadata: metadata
134+
};
135+
136+
// Store the new user in the users mapping.
137+
self.users.write(user_id, new_user);
138+
139+
// Increment the user ID counter for the next registration.
140+
self.current_account_id.write(user_id + 1);
141+
142+
// Emit an event to notify about the new user registration.
143+
self.emit(UserCreated { id: user_id });
144+
145+
// Return the assigned user ID.
146+
user_id
147+
}
148+
149+
150+
/// @notice Verifies a user in the system.
151+
/// @dev Only an admin can verify a user.
152+
/// @param self The contract state reference.
153+
/// @param user_id The unique identifier of the user to be verified.
154+
/// @return bool Returns true if the user is successfully verified.
155+
fn verify_user(ref self: ContractState, user_id: u256) -> bool {
156+
let caller = get_caller_address();
157+
// Ensure that only an admin can verify users.
158+
assert((self.admin.read() == caller), 'Only admin can verify users');
159+
let mut user = self.users.read(user_id);
160+
user.verified = true;
161+
self.users.write(user.id, user);
162+
true
163+
}
164+
/// @notice Retrieves a user's profile from the system.
165+
/// @dev This function fetches the user profile based on the provided user ID.
166+
/// @param self The contract state reference.
167+
/// @param user_id The unique identifier of the user whose profile is being retrieved.
168+
/// @return User The user profile associated with the given user ID.
169+
fn retrieve_user_profile(ref self: ContractState, user_id: u256) -> User {
170+
// Read the user profile from the storage mapping.
171+
let user = self.users.read(user_id);
172+
173+
// Return the retrieved user profile.
174+
user
175+
}
176+
177+
fn is_verified(ref self: ContractState, user_id: u256) -> bool {
178+
let mut user = self.users.read(user_id);
179+
user.verified
180+
}
181+
182+
183+
fn getAdmin(self: @ContractState) -> ContractAddress {
184+
let admin = self.admin.read();
185+
admin
88186
}
89187
}
90188
}

src/interfaces/IChainLib.cairo

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use starknet::ContractAddress;
2-
use crate::base::types::{TokenBoundAccount};
2+
use crate::base::types::{TokenBoundAccount, User, Role, Rank};
33

44
#[starknet::interface]
55
pub trait IChainLib<TContractState> {
@@ -12,5 +12,13 @@ pub trait IChainLib<TContractState> {
1212
fn get_token_bound_account_by_owner(
1313
ref self: TContractState, address: ContractAddress
1414
) -> TokenBoundAccount;
15-
fn test_deployment(ref self: TContractState) -> bool;
15+
16+
fn register_user(
17+
ref self: TContractState, username: felt252, role: Role, rank: Rank, metadata: felt252
18+
) -> u256;
19+
fn verify_user(ref self: TContractState, user_id: u256) -> bool;
20+
fn retrieve_user_profile(ref self: TContractState, user_id: u256) -> User;
21+
fn getAdmin(self: @TContractState) -> ContractAddress;
22+
fn is_verified(ref self: TContractState, user_id: u256) -> bool;
1623
}
24+

tests/test_ChainLib.cairo

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,134 @@ use starknet::ContractAddress;
77
use starknet::class_hash::ClassHash;
88
use starknet::contract_address::contract_address_const;
99
use starknet::testing::{set_caller_address, set_contract_address};
10+
use chain_lib::base::types::{Role, Rank};
1011

1112

12-
fn setup() -> ContractAddress {
13+
fn setup() -> (ContractAddress, ContractAddress) {
1314
let declare_result = declare("ChainLib");
1415
assert(declare_result.is_ok(), 'Contract declaration failed');
16+
let admin_address: ContractAddress = contract_address_const::<'admin'>();
1517

1618
let contract_class = declare_result.unwrap().contract_class();
17-
let mut calldata = array![];
19+
let mut calldata = array![admin_address.into()];
1820

1921
let deploy_result = contract_class.deploy(@calldata);
2022
assert(deploy_result.is_ok(), 'Contract deployment failed');
2123

2224
let (contract_address, _) = deploy_result.unwrap();
2325

24-
(contract_address)
26+
(contract_address, admin_address)
2527
}
2628

2729
#[test]
2830
fn test_initial_data() {
29-
let contract_address = setup();
31+
let (contract_address, admin_address) = setup();
3032

3133
let dispatcher = IChainLibDispatcher { contract_address };
3234

3335
// Ensure dispatcher methods exist
34-
let deployed = dispatcher.test_deployment();
36+
let admin = dispatcher.getAdmin();
3537

36-
assert(deployed == true, 'deployment failed');
38+
assert(admin == admin_address, 'deployment failed');
3739
}
3840

3941

4042
#[test]
4143
fn test_create_token_bount_account() {
42-
let contract_address = setup();
44+
let (contract_address, _) = setup();
4345
let dispatcher = IChainLibDispatcher { contract_address };
4446

4547
// Test input values
4648
let user_name: felt252 = 'John';
4749
let init_param1: felt252 = '[email protected]';
4850
let init_param2: felt252 = 'john is a boy';
4951

50-
// Call create_claim
52+
// Call account
5153
let account_id = dispatcher.create_token_account(user_name, init_param1, init_param2);
5254

53-
// Validate that the claim ID is correctly incremented
55+
// Validate that the account ID is correctly incremented
5456
assert(account_id == 0, 'account_id should start from 0');
5557

56-
// Retrieve the claim to verify it was stored correctly
58+
// Retrieve the account to verify it was stored correctly
5759
let token_bound_account = dispatcher.get_token_bound_account(account_id);
5860
assert(token_bound_account.user_name == user_name, 'namemismatch');
5961
assert(token_bound_account.init_param1 == init_param1, 'init_param1 mismatch');
6062
assert(token_bound_account.init_param2 == init_param2, 'init_param2 mismatch');
6163
}
64+
65+
66+
#[test]
67+
fn test_create_user() {
68+
let (contract_address, _) = setup();
69+
let dispatcher = IChainLibDispatcher { contract_address };
70+
71+
// Test input values
72+
let username: felt252 = 'John';
73+
let role: Role = Role::READER;
74+
let rank: Rank = Rank::BEGINNER;
75+
let metadata: felt252 = 'john is a boy';
76+
77+
// Call create_user
78+
let account_id = dispatcher.register_user(username, role.clone(), rank.clone(), metadata);
79+
80+
// Validate that the claim ID is correctly incremented
81+
assert(account_id == 0, 'account_id should start from 0');
82+
83+
// Retrieve the user to verify it was stored correctly
84+
let user = dispatcher.retrieve_user_profile(account_id);
85+
assert(user.username == username, 'username mismatch');
86+
assert(user.role == role, 'role mismatch');
87+
assert(user.rank == rank, 'rank mismatch');
88+
assert(user.metadata == metadata, 'metadata mismatch');
89+
assert(!user.verified, 'already verified');
90+
}
91+
92+
#[test]
93+
fn test_verify_user() {
94+
let (contract_address, admin_address) = setup();
95+
let dispatcher = IChainLibDispatcher { contract_address };
96+
97+
// Test input values
98+
let username: felt252 = 'John';
99+
let role: Role = Role::READER;
100+
let rank: Rank = Rank::BEGINNER;
101+
let metadata: felt252 = 'john is a boy';
102+
103+
// Call register user
104+
let account_id = dispatcher.register_user(username, role.clone(), rank.clone(), metadata);
105+
// Retrieve the user to verify it was stored correctly
106+
let user = dispatcher.retrieve_user_profile(account_id);
107+
assert(!user.verified, 'already verified');
108+
109+
cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite);
110+
let verify = dispatcher.verify_user(user.id);
111+
assert(verify, 'Verification Falied');
112+
113+
let verified_user = dispatcher.is_verified(account_id);
114+
assert(verified_user, 'Not Verified');
115+
}
116+
117+
#[test]
118+
#[should_panic(expected: 'Only admin can verify users')]
119+
fn test_verify_user_not_admin() {
120+
let (contract_address, _) = setup();
121+
let dispatcher = IChainLibDispatcher { contract_address };
122+
123+
// Test input values
124+
let username: felt252 = 'John';
125+
let role: Role = Role::READER;
126+
let rank: Rank = Rank::BEGINNER;
127+
let metadata: felt252 = 'john is a boy';
128+
129+
// Call register
130+
let account_id = dispatcher.register_user(username, role.clone(), rank.clone(), metadata);
131+
// Retrieve the user to verify user was stored correctly
132+
let user = dispatcher.retrieve_user_profile(account_id);
133+
assert(!user.verified, 'already verified');
134+
135+
let verify = dispatcher.verify_user(user.id);
136+
assert(verify, 'Verification Falied');
137+
138+
let verified_user = dispatcher.retrieve_user_profile(account_id);
139+
assert(verified_user.verified, 'Not Verified');
140+
}

0 commit comments

Comments
 (0)