diff --git a/.gitignore b/.gitignore index eb5a316..c835cb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +student-registry-dapp-starter \ No newline at end of file diff --git a/Scarb.lock b/Scarb.lock index 62b5925..200a370 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -5,9 +5,112 @@ version = 1 name = "cairo_bootcamp_3" version = "0.1.0" dependencies = [ + "openzeppelin", "snforge_std", ] +[[package]] +name = "openzeppelin" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_account" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_introspection" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_presets" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", +] + +[[package]] +name = "openzeppelin_security" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_token" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_account", + "openzeppelin_governance", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_utils" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + [[package]] name = "snforge_scarb_plugin" version = "0.31.0" diff --git a/assignments/README.md b/assignments/README.md new file mode 100644 index 0000000..d52b1ab --- /dev/null +++ b/assignments/README.md @@ -0,0 +1,12 @@ + + +# ASSIGNMENT 5 + +## Student Attendance Component +### Implemented: + - get_attendance(student_account: ContractAddress, date: felt252). + + + - mark_attendance(_student_account: ContractAddress, _date: felt252, _present: bool). + +- Contrack address: 0x55abee888d949203f3973ad9ad725fd7c57c8d5da79a6756ef0473af86af863 diff --git a/snfoundry.toml b/snfoundry.toml index 336b432..0bada5c 100644 --- a/snfoundry.toml +++ b/snfoundry.toml @@ -1,5 +1,5 @@ -[sncast.cohort_dev] -account = "cohort_dev" +[sncast.stark1] +account = "stark1" accounts-file = "~/.starknet_accounts/starknet_open_zeppelin_accounts.json" url = "https://free-rpc.nethermind.io/sepolia-juno/" diff --git a/src/lib.cairo b/src/lib.cairo index 238e0f4..ae36b44 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -9,3 +9,4 @@ pub mod ownable_counter; pub mod addition; pub mod student_registry_manager; +pub mod student_attendance; diff --git a/src/student_attendance.cairo b/src/student_attendance.cairo new file mode 100644 index 0000000..3a63fec --- /dev/null +++ b/src/student_attendance.cairo @@ -0,0 +1,100 @@ +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IStudentAttendance { + fn mark_attendance( + ref self: T, _student_account: ContractAddress, _date: felt252, _present: bool + ) -> bool; + + fn get_attendance(self: @T, student_account: ContractAddress, date: felt252) -> bool; +} + +#[starknet::component] +pub mod StudentAttendanceComponent { + use OwnableComponent::InternalTrait; + use starknet::ContractAddress; + use core::num::traits::Zero; + use openzeppelin::access::ownable::{OwnableComponent, OwnableComponent::InternalImpl}; + + use super::{IStudentAttendance}; + use starknet::storage::{ + StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry, Map + }; + use crate::errors::Errors; + + #[storage] + struct Storage { + admin_account: ContractAddress, + attendance_map: Map::>, + } + + #[event] + #[derive(Drop, starknet::Event)] + pub enum Event { + AttendanceMarked: AttendanceMarked + } + + #[derive(Drop, starknet::Event)] + struct AttendanceMarked { + marked_student: ContractAddress, + } + + #[embeddable_as(StudentAttendance)] + pub impl StudenAttendanceImpl< + TContractState, + +HasComponent, + +Drop, + impl Ownable: OwnableComponent::HasComponent + > of IStudentAttendance> { + fn mark_attendance( + ref self: ComponentState, + _student_account: ContractAddress, + _date: felt252, + _present: bool + ) -> bool { + assert(self.is_zero_address(_student_account), Errors::ZERO_ADDRESS); + + // assert only owner + let ownable_comp = get_dep_component!(@self, Ownable); + ownable_comp.assert_only_owner(); + + self.attendance_map.entry(_student_account).entry(_date).write(_present); + + self.emit(AttendanceMarked { marked_student: _student_account }); + + true + } + + fn get_attendance( + self: @ComponentState, student_account: ContractAddress, date: felt252 + ) -> bool { + let attendance = self.attendance_map.entry(student_account).entry(date).read(); + return attendance; + } + } + + + #[generate_trait] + pub impl Private< + TContractState, + +HasComponent, + +Drop, + impl Ownable: OwnableComponent::HasComponent + > of PrivateTrait { + fn initializer(ref self: ComponentState, _admin: ContractAddress) { + // validation to check if admin account has valid address and not 0 address + assert(self.is_zero_address(_admin) == false, Errors::ZERO_ADDRESS); + let mut ownable_comp = get_dep_component_mut!(ref self, Ownable); + ownable_comp.initializer(_admin); + } + + fn is_zero_address( + self: @ComponentState, account: ContractAddress + ) -> bool { + if account.is_zero() { + return true; + } + return false; + } + } +} diff --git a/src/student_registry_manager.cairo b/src/student_registry_manager.cairo index bd6931e..e41b510 100644 --- a/src/student_registry_manager.cairo +++ b/src/student_registry_manager.cairo @@ -10,12 +10,16 @@ pub mod StudentRegistryManager { use super::IStudentRegistryManager; use crate::student_registry::StudentRegistryComponent; use openzeppelin::access::ownable::OwnableComponent; + use crate::student_attendance::StudentAttendanceComponent; // Declare component component!( path: StudentRegistryComponent, storage: studentRegistry, event: StudentRegistryEvent ); component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!( + path: StudentAttendanceComponent, storage: studentAttendance, event: AttendanceEvent + ); #[abi(embed_v0)] impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; @@ -27,8 +31,13 @@ pub mod StudentRegistryManager { impl studentRegistryImpl = StudentRegistryComponent::StudentRegistry; + #[abi(embed_v0)] + impl studentAttendanceImpl = + StudentAttendanceComponent::StudentAttendance; + // instantiate component's private implementation impl studentRegisterPrivateImpl = StudentRegistryComponent::Private; + impl studentAttendancePrivateImpl = StudentAttendanceComponent::Private; #[storage] struct Storage { @@ -36,7 +45,9 @@ pub mod StudentRegistryManager { #[substorage(v0)] // component's storage variable must be annotated with this attribute studentRegistry: StudentRegistryComponent::Storage, #[substorage(v0)] - ownable: OwnableComponent::Storage + ownable: OwnableComponent::Storage, + #[substorage(v0)] + studentAttendance: StudentAttendanceComponent::Storage } #[event] @@ -46,7 +57,9 @@ pub mod StudentRegistryManager { #[flat] // component's event must be annotated with this attribute StudentRegistryEvent: StudentRegistryComponent::Event, #[flat] - OwnableEvent: OwnableComponent::Event + OwnableEvent: OwnableComponent::Event, + #[flat] + AttendanceEvent: StudentAttendanceComponent::Event } #[derive(Drop, starknet::Event)] @@ -61,6 +74,7 @@ pub mod StudentRegistryManager { // initialize component self.studentRegistry.initializer(_admin); + self.studentAttendance.initializer(_admin); } #[abi(embed_v0)]