diff --git a/Scarb.lock b/Scarb.lock index ecca394..7b026fb 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -58,9 +58,15 @@ name = "allo" version = "0.0.1" dependencies = [ "alexandria_math", + "openzeppelin", "snforge_std", ] +[[package]] +name = "openzeppelin" +version = "0.13.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.13.0#978b4e75209da355667d8954d2450e32bd71fe49" + [[package]] name = "snforge_std" version = "0.23.0" diff --git a/Scarb.toml b/Scarb.toml index 05824a3..8d00e94 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -7,6 +7,7 @@ edition = "2023_11" starknet = "2.6.3" alexandria_math = { git = "https://github.com/keep-starknet-strange/alexandria.git" } alexandria_bytes = { git = "https://github.com/keep-starknet-strange/alexandria.git" } +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.13.0" } [dev-dependencies] snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.23.0" } diff --git a/src/core/libraries.cairo b/src/core/libraries.cairo index 47a2261..f77b474 100644 --- a/src/core/libraries.cairo +++ b/src/core/libraries.cairo @@ -1,2 +1,2 @@ mod clone; -mod errors; +pub mod errors; diff --git a/src/core/libraries/errors.cairo b/src/core/libraries/errors.cairo index d238fdf..39ce545 100644 --- a/src/core/libraries/errors.cairo +++ b/src/core/libraries/errors.cairo @@ -1,6 +1,6 @@ use starknet::{ContractAddress, contract_address_const}; -mod Errors { +pub mod Errors { /// Throws as an general error when input / data is invalid. const INVALID: felt252 = 'Data is invalid'; /// Thrown when mismatch in decoding data. @@ -8,9 +8,9 @@ mod Errors { /// Thrown when not enough funds are available const NOT_ENOUGH_FUNDS: felt252 = 'Not enough funds available'; /// Thrown when user is not authorized - const UNAUTHORIZED: felt252 = 'Not authorized'; + pub const UNAUTHORIZED: felt252 = 'Not authorized'; // // /// Thrown when address is the zero address - // const ZERO_ADDRESS : ContractAddress = contract_address_const::<'0'>(); + pub const ZERO_ADDRESS: felt252 = 'Address is Zero'; /// Thrown when the function is not implemented const NOT_IMPLEMENTED: felt252 = 'Not implemented'; /// Thrown when the value is non-zero diff --git a/src/core/registry.cairo b/src/core/registry.cairo index 4ae23a7..7a3953d 100644 --- a/src/core/registry.cairo +++ b/src/core/registry.cairo @@ -21,34 +21,86 @@ use starknet::{ContractAddress, get_caller_address, get_contract_address, contra /// Registry contract interface /// Interface for the Registry contract. #[starknet::interface] -pub trait IRegistry {} - +pub trait IRegistry { + fn is_owner_of_profile(self: @TContractState, profile_id: u256, owner: ContractAddress) -> bool; +} #[starknet::contract] pub mod Registry { use alexandria_encoding::sol_abi::encode::SolAbiEncodeTrait; -use starknet::ContractAddress; use alexandria_bytes::{Bytes, BytesTrait}; + use starknet::ContractAddress; + use core::poseidon::PoseidonTrait; + use core::hash::HashStateTrait; + use allo::core::libraries::errors::Errors; + use openzeppelin::access::accesscontrol::AccessControlComponent; + use openzeppelin::introspection::src5::SRC5Component; + + component!(path: SRC5Component, storage: SRC5_supported_interfaces, event: SRC5ComponentEvent); + + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + component!( + path: AccessControlComponent, storage: accessControl, event: AccessControlComponentEvent + ); + #[abi(embed_v0)] + impl AccessControlComponentImpl = + AccessControlComponent::AccessControlImpl; + impl AccessControlComponentInternalImpl = AccessControlComponent::InternalImpl; + + // ========================== + // === Structs ============== + // ========================== + + #[derive(Drop, Serde, starknet::Store)] + struct Metadata { + protocol: u256, + pointer: ByteArray, + } + + #[derive(Drop, Serde, starknet::Store)] + struct Profile { + id: u256, + nonce: u256, + name: ByteArray, + metadata: Metadata, + owner: ContractAddress, + anchor: ContractAddress, + } // ========================== // === Storage Variables ==== // ========================== #[storage] - struct Storage {} + struct Storage { + profiles_by_id: LegacyMap, + #[substorage(v0)] + SRC5_supported_interfaces: SRC5Component::Storage, + #[substorage(v0)] + accessControl: AccessControlComponent::Storage, + } /// ====================== /// ======= Events ======= /// ====================== #[event] #[derive(Drop, starknet::Event)] - enum Event {} + enum Event { + #[flat] + SRC5ComponentEvent: SRC5Component::Event, + #[flat] + AccessControlComponentEvent: AccessControlComponent::Event, + } #[constructor] - fn constructor(ref self: ContractState) { // Issue no #19 - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L78C40-L79C9 - // Implement the functionality of making sure the address is not zero and - // grant the Allo owner role to the owner. - // You can use the posiedon hashing for hasing storing Allo owner + fn constructor(ref self: ContractState, _owner: ContractAddress) { + assert(_owner.into() == 0, Errors::ZERO_ADDRESS); + + let allo_owner_role = PoseidonTrait::new().update('ALLO_OWNER').finalize(); + + self.accessControl.initializer(); + self.accessControl._grant_role(allo_owner_role, _owner) } @@ -59,35 +111,40 @@ use starknet::ContractAddress; impl Registry of super::IRegistry< ContractState > { // Issue no. #15 Implement the functionality to retrieve profile by profileId - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L94 - // Use _profileID as u256 - - // Issue no. #14 Implement the functionality to retrieve profile by anchor - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L102 - - // Issue no. #13 Implement the functionality of createProfile - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L118C5-L125C18 - - // Issue no. #12 Implement the functionality of updateProfileName - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L182C14-L182C31 - - // Issue no. #11 Implement the functionality of updateProfileMetadata - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L214C14-L214C35 - - // Issue no. #10 Implement the functionality of isOwnerOrMemberOfProfile - // Use u256 instead of bytes32 - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L229 - - // Issue no. #3 Implement the functionality of isOwnerOfProfile - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L245 - + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L94 + // Use _profileID as u256 + + // Issue no. #14 Implement the functionality to retrieve profile by anchor + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L102 + + // Issue no. #13 Implement the functionality of createProfile + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L118C5-L125C18 + + // Issue no. #12 Implement the functionality of updateProfileName + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L182C14-L182C31 + + // Issue no. #11 Implement the functionality of updateProfileMetadata + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L214C14-L214C35 + + // Issue no. #10 Implement the functionality of isOwnerOrMemberOfProfile + // Use u256 instead of bytes32 + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L229 + + // Issue no. #3 Implement the functionality of isOwnerOfProfile + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L245 + + fn is_owner_of_profile( + self: @ContractState, profile_id: u256, owner: ContractAddress + ) -> bool { + return self._is_owner_of_profile(profile_id, owner); + } // Issue no. #5 Implement the functionality of isMemberOfProfile // Down below is the function that is to be implemented in the contract but in cairo. // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L245 @@ -121,36 +178,40 @@ use starknet::ContractAddress; /// ==================================== #[generate_trait] impl RegistryInternalImpl of RegistryInternalTrait { // Issue no. #19 Implement the functionality of _generateProfileId - // Internal function to create a profile - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L366 - // Reference on how to implement keccak256(abi.encodePacked) - // Solidity - https://github.com/celestiaorg/blobstream-contracts/blob/0b4bcf69d1ce96df000da7f95fba8c03aa15a45e/src/lib/tree/namespace/TreeHasher.sol#L33 - // Cairo - https://github.com/keep-starknet-strange/blobstream-starknet/blob/b74777e5fb479e5b4aa5a1419135e0826343fc37/src/tree/namespace/hasher.cairo#L10 - // More about it - https://github.com/keep-starknet-strange/alexandria/tree/main/src/encoding - fn _generateProfileId(_nonce: u256, owner: ContractAddress) -> u256 { - let profileId = BytesTrait::new_empty() - .encode_packed(_nonce) - .encode_packed(owner) - .keccak(); - return profileId; - } - - // Issue no. #18 Implement the functionality of _generateAnchor - // Internal function to create a _generateAnchor - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L340 - - // Issue no. #17 Implement the functionality of _checkOnlyProfileOwner - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L331 - - // Issue no. #4 Implement the functionality of _generateProfileId - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L375C14-L375C31 - - // Issue no. #3 Implement the functionality of _isOwnerOfProfile - // Down below is the function that is to be implemented in the contract but in cairo. - // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L375C14-L375C31 - + // Internal function to create a profile + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L366 + // Reference on how to implement keccak256(abi.encodePacked) + // Solidity - https://github.com/celestiaorg/blobstream-contracts/blob/0b4bcf69d1ce96df000da7f95fba8c03aa15a45e/src/lib/tree/namespace/TreeHasher.sol#L33 + // Cairo - https://github.com/keep-starknet-strange/blobstream-starknet/blob/b74777e5fb479e5b4aa5a1419135e0826343fc37/src/tree/namespace/hasher.cairo#L10 + // More about it - https://github.com/keep-starknet-strange/alexandria/tree/main/src/encoding + + // Issue no. #18 Implement the functionality of _generateAnchor + // Internal function to create a _generateAnchor + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L340 + fn _generateProfileId(_nonce: u256, owner: ContractAddress) -> u256 { + let profileId = BytesTrait::new_empty() + .encode_packed(_nonce) + .encode_packed(owner) + .keccak(); + return profileId; + } + // Issue no. #17 Implement the functionality of _checkOnlyProfileOwner + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L331 + + // Issue no. #4 Implement the functionality of _generateProfileId + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L375C14-L375C31 + + // Issue no. #3 Implement the functionality of _isOwnerOfProfile + // Down below is the function that is to be implemented in the contract but in cairo. + // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L375C14-L375C31 + + fn _is_owner_of_profile( + self: @ContractState, _profile_id: u256, _owner: ContractAddress + ) -> bool { + return self.profiles_by_id.read(_profile_id).owner == _owner; + } // Issue n. #5 Implement the functionality of _isMemberOfProfile // Down below is the function that is to be implemented in the contract but in cairo. // https://github.com/allo-protocol/allo-v2/blob/4dd0ea34a504a16ac90e80f49a5570b8be9b30e9/contracts/core/Registry.sol#L384C14-L384C32