From aba3ed15d4c52ad411e6cd320f7daf1ef85715ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Pobiar=C5=BCyn?= Date: Wed, 25 May 2022 20:36:02 +0200 Subject: [PATCH] Fix logic nft contract, docs update (#69) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update nft token logic, add some documentation * ensure whitelisted before mint/burn * allow a user to have at most one token * init AccessControl module * add and refactor documentation Co-authored-by: Maciej Zieliński * set fixed version of cargo-expand Co-authored-by: Maciej Zieliński --- Makefile | 2 +- dao-contracts/src/admin.rs | 18 ++--- dao-contracts/src/dao_nft.rs | 80 ++++++++++++++++++++ dao-contracts/src/kyc_voter.rs | 52 ++++++++++--- dao-contracts/src/onboarding_voter.rs | 55 +++++++++++--- dao-contracts/src/repo_voter.rs | 18 ++--- dao-contracts/src/reputation.rs | 17 +---- dao-contracts/src/variable_repository.rs | 24 +----- dao-contracts/src/voting/kyc_info.rs | 15 +++- dao-contracts/src/voting/onboarding_info.rs | 15 ++++ dao-contracts/tests/test_kyc_voter.rs | 6 +- dao-contracts/tests/test_onboarding_voter.rs | 8 +- dao-modules/src/access_control.rs | 37 +++++++++ dao-utils/src/bytes.rs | 1 - dao-utils/src/lib.rs | 1 - dao-utils/src/parts/error.rs | 1 + 16 files changed, 264 insertions(+), 86 deletions(-) delete mode 100644 dao-utils/src/bytes.rs diff --git a/Makefile b/Makefile index 5a84d250..146e5390 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ CARGO_TEST = cargo test --features=test-support --no-default-features prepare: rustup target add wasm32-unknown-unknown - cargo install cargo-expand + cargo install cargo-expand --version 1.0.17 build-proxy-getter: $(CARGO_BUILD) -p casper-dao-utils --bin getter_proxy diff --git a/dao-contracts/src/admin.rs b/dao-contracts/src/admin.rs index 11de9b1e..ac428b62 100644 --- a/dao-contracts/src/admin.rs +++ b/dao-contracts/src/admin.rs @@ -14,7 +14,7 @@ use delegate::delegate; #[casper_contract_interface] pub trait AdminContractInterface { - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::init()) fn init(&mut self, variable_repo: Address, reputation_token: Address); /// Creates new admin voting. @@ -31,21 +31,21 @@ pub trait AdminContractInterface { address: Address, stake: U256, ); - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::vote()) fn vote(&mut self, voting_id: VotingId, choice: Choice, stake: U256); - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::finish_voting()) fn finish_voting(&mut self, voting_id: VotingId); - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_dust_amount()) fn get_dust_amount(&self) -> U256; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_variable_repo_address()) fn get_variable_repo_address(&self) -> Address; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_reputation_token_address()) fn get_reputation_token_address(&self) -> Address; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_voting()) fn get_voting(&self, voting_id: U256) -> Option; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_ballot()) fn get_ballot(&self, voting_id: U256, address: Address) -> Option; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_voter()) fn get_voter(&self, voting_id: U256, at: u32) -> Option
; } diff --git a/dao-contracts/src/dao_nft.rs b/dao-contracts/src/dao_nft.rs index c9920db1..51333791 100644 --- a/dao-contracts/src/dao_nft.rs +++ b/dao-contracts/src/dao_nft.rs @@ -5,6 +5,7 @@ use casper_dao_modules::AccessControl; use casper_dao_utils::{ casper_contract::unwrap_or_revert::UnwrapOrRevert, casper_dao_macros::{casper_contract_interface, Instance}, + casper_env::{self, caller}, Address, Error, Mapping, }; use casper_types::U256; @@ -12,26 +13,91 @@ use delegate::delegate; #[casper_contract_interface] pub trait DaoOwnedNftContractInterface { + /// Contract constructor. + /// + /// Initializes modules. Sets the deployer as the owner. + /// + /// See [MetadataERC721](MetadataERC721::init()), [AccessControl](AccessControl::init()) fn init(&mut self, name: String, symbol: String, base_uri: TokenUri); + /// Change ownership of the contract. Transfer the ownership to the `owner`. Only current owner + /// is permited to call this method. + /// + /// See [AccessControl](AccessControl::change_ownership()) fn change_ownership(&mut self, owner: Address); + /// Add new address to the whitelist. + /// + /// See [AccessControl](AccessControl::add_to_whitelist()) fn add_to_whitelist(&mut self, address: Address); + /// Remove address from the whitelist. + /// + /// See [AccessControl](AccessControl::remove_from_whitelist()) fn remove_from_whitelist(&mut self, address: Address); + /// Returns the address of the current owner. fn get_owner(&self) -> Option
; + /// Checks whether the given address is added to the whitelist. fn is_whitelisted(&self, address: Address) -> bool; + /// Returns a descriptive name for a collection of tokens in this contract fn name(&self) -> String; + /// Gets an abbreviated name for tokens in this contract fn symbol(&self) -> String; + /// Returns the address of the owner of the token. + /// + /// If the given `token_id` does not exist the None value is returned. fn owner_of(&self, token_id: TokenId) -> Option
; + /// Returns a token id for the given the `address`. + /// + /// If the `owner` does not own any token the None value is returned. fn token_id(&self, address: Address) -> Option; + /// Returns the number of tokens owned by `owner`. fn balance_of(&self, owner: Address) -> U256; + /// Returns the total number of tokens. fn total_supply(&self) -> U256; + /// Returns a distinct Uniform Resource Identifier (URI) for a given asset. fn token_uri(&self, token_id: TokenId) -> TokenUri; + /// Returns a URI prefix that is used by all the assets. fn base_uri(&self) -> TokenUri; + /// Creates a new token with the given id and transfers it to a new owner. + /// Increments the total supply and the balance of the `to` address. + /// + /// # Note + /// Only whitelisted addresses are permited to call this + /// method. + /// + /// Each user is entitled to own only one token. + /// + /// # Errors + /// Throws [`TokenAlreadyExists`](Error::TokenAlreadyExists) if a token with + /// the `token_id` has been minted already. + /// + /// Throws [`UserAlreadyOwnsToken`](Error::UserAlreadyOwnsToken) if the `to` address + /// already owns a token. + /// + /// # Events + /// Emits [`Transfer`](casper_dao_erc721::events::Transfer) event when minted successfully. fn mint(&mut self, to: Address, token_id: TokenId); + /// Burns a token with the given id. Decrements the balance of the token owner + /// and decrements the total supply. + /// + /// # Errors + /// Throws [`NotWhitelisted`](casper_dao_utils::Error::NotWhitelisted) if caller + /// is not whitelisted. + /// + /// # Events + /// Emits [`Burn`](casper_dao_modules::events::Burn) event. fn burn(&mut self, token_id: TokenId); + /// Change or confirm the approved address for a token with the given id. fn approve(&mut self, approved: Option
, token_id: TokenId); + /// Enables or disables approval for a third party (`operator`) to manage + /// all of the caller assets. fn set_approval_for_all(&mut self, operator: Address, approved: bool); } +/// Dao Owned Nft contract acts like an erc-721 token and derives most of erc-721 standards from +/// [ERC721Token](ERC721Token) module. +/// +/// Dao Owned Nft token is mintable and burnable but the caller needs to have permissions to perform those actions. +/// +/// For details see [DaoOwnedNftContractInterface](DaoOwnedNftContractInterface) #[derive(Instance)] pub struct DaoOwnedNftContract { token: ERC721Token, @@ -42,7 +108,9 @@ pub struct DaoOwnedNftContract { impl DaoOwnedNftContractInterface for DaoOwnedNftContract { fn init(&mut self, name: String, symbol: String, base_uri: TokenUri) { + let deployer = caller(); self.metadata.init(name, symbol, base_uri); + self.access_control.init(deployer); } delegate! { @@ -74,11 +142,15 @@ impl DaoOwnedNftContractInterface for DaoOwnedNftContract { } fn mint(&mut self, to: Address, token_id: TokenId) { + self.access_control.ensure_whitelisted(); + self.assert_does_not_own_token(&to); + MintableERC721::mint(&mut self.token, to, token_id); self.tokens.set(&to, Some(token_id)); } fn burn(&mut self, token_id: TokenId) { + self.access_control.ensure_whitelisted(); let owner = self .token .owner_of(token_id) @@ -91,3 +163,11 @@ impl DaoOwnedNftContractInterface for DaoOwnedNftContract { self.tokens.get(&address).unwrap_or(None) } } + +impl DaoOwnedNftContract { + fn assert_does_not_own_token(&self, address: &Address) { + if self.tokens.get(address).is_some() { + casper_env::revert(Error::UserAlreadyOwnsToken) + } + } +} diff --git a/dao-contracts/src/kyc_voter.rs b/dao-contracts/src/kyc_voter.rs index 6ee4ee46..6c7456f0 100644 --- a/dao-contracts/src/kyc_voter.rs +++ b/dao-contracts/src/kyc_voter.rs @@ -13,22 +13,50 @@ use delegate::delegate; #[casper_contract_interface] pub trait KycVoterContractInterface { + /// Contract constructor + /// + /// Initializes modules. + /// + /// See [GovernanceVoting](GovernanceVoting::init()), [KycInfo](KycInfo::init()) fn init(&mut self, variable_repo: Address, reputation_token: Address, kyc_token: Address); - // Require no voting for a given `address` is on. - // Precondition: KycNft.balance_of(address_to_onboard) == 0; - // Action: KycNft.mint(address_to_onboard, next_token_id) - fn create_voting(&mut self, address_to_onboard: Address, document_hash: U256, stake: U256); + /// Creates new kyc voting. Once the voting passes a kyc token is minted to the `subject_address`. + /// + /// # Prerequisites + /// + /// * no voting on the given `subject_address` is in progress, + /// * `subject_address` does not own a kyc token. + /// + /// # Note + /// + /// `subject_address` - [address](Address) of a user to be verified. + /// `document_hash` - a hash of a document that vefify the user. The hash is used as an id of a freshly minted kyc token. + /// `subject_address` - an [Address](Address) to be on/offboarded. + fn create_voting(&mut self, subject_address: Address, document_hash: U256, stake: U256); + /// see [GovernanceVoting](GovernanceVoting::vote()) fn vote(&mut self, voting_id: VotingId, choice: Choice, stake: U256); + /// see [GovernanceVoting](GovernanceVoting::finish_voting()) fn finish_voting(&mut self, voting_id: VotingId); + /// see [GovernanceVoting](GovernanceVoting::get_dust_amount()) fn get_dust_amount(&self) -> U256; + /// see [GovernanceVoting](GovernanceVoting::get_variable_repo_address()) fn get_variable_repo_address(&self) -> Address; + /// see [GovernanceVoting](GovernanceVoting::get_reputation_token_address()) fn get_reputation_token_address(&self) -> Address; + /// see [GovernanceVoting](GovernanceVoting::get_voting()) + fn get_voting(&self, voting_id: U256) -> Option; + /// see [GovernanceVoting](GovernanceVoting::get_ballot()) + fn get_ballot(&self, voting_id: U256, address: Address) -> Option; + /// see [GovernanceVoting](GovernanceVoting::get_voter()) + fn get_voter(&self, voting_id: U256, at: u32) -> Option
; + /// see [KycInfo](KycInfo::get_kyc_token_address()) fn get_kyc_token_address(&self) -> Address; - fn get_voting(&self, voting_id: VotingId) -> Option; - fn get_ballot(&self, voting_id: VotingId, address: Address) -> Option; - fn get_voter(&self, voting_id: VotingId, at: u32) -> Option
; } +/// KycVoterContract +/// +/// It is responsible for managing kyc tokens (see [DaoOwnedNftContract](crate::DaoOwnedNftContract). +/// +/// When the voting passes, a kyc token is minted. #[derive(Instance)] pub struct KycVoterContract { kyc: KycInfo, @@ -56,14 +84,14 @@ impl KycVoterContractInterface for KycVoterContract { } } - fn create_voting(&mut self, address_to_onboard: Address, document_hash: U256, stake: U256) { - self.assert_no_ongoing_voting(&address_to_onboard); - self.assert_not_kyced(&address_to_onboard); + fn create_voting(&mut self, subject_address: Address, document_hash: U256, stake: U256) { + self.assert_no_ongoing_voting(&subject_address); + self.assert_not_kyced(&subject_address); let creator = caller(); let contract_to_call = self.get_kyc_token_address(); let runtime_args = runtime_args! { - consts::ARG_TO => address_to_onboard, + consts::ARG_TO => subject_address, consts::ARG_TOKEN_ID => document_hash, }; let entry_point = consts::EP_MINT.to_string(); @@ -71,7 +99,7 @@ impl KycVoterContractInterface for KycVoterContract { self.voting .create_voting(creator, stake, contract_to_call, entry_point, runtime_args); - self.kyc.set_voting(&address_to_onboard); + self.kyc.set_voting(&subject_address); } fn vote(&mut self, voting_id: VotingId, choice: Choice, stake: U256) { diff --git a/dao-contracts/src/onboarding_voter.rs b/dao-contracts/src/onboarding_voter.rs index 39bef86c..386cce62 100644 --- a/dao-contracts/src/onboarding_voter.rs +++ b/dao-contracts/src/onboarding_voter.rs @@ -19,6 +19,11 @@ use delegate::delegate; #[casper_contract_interface] pub trait OnboardingVoterContractInterface { + /// Contract constructor + /// + /// Initializes modules. + /// + /// See [GovernanceVoting](GovernanceVoting::init()), [KycInfo](KycInfo::init()), [OnboardingInfo](OnboardingInfo::init()) fn init( &mut self, variable_repo: Address, @@ -26,28 +31,54 @@ pub trait OnboardingVoterContractInterface { kyc_token: Address, va_token: Address, ); - - // - Require no voting for a given `address` is in progress. - // For Adding new VA: - // - Check if VA is not onboarderd. - // - Check if `address` is KYCed. - // - Check if `address` has positive reputation amount. - // For Removing existing VA: - // - Check if VA is already onboarderd. - // - Check if `address` has positive reputation amount. + /// Creates new Onboarding\Offboarding voting. + /// + /// # Prerequisites + /// + /// * no voting on the given `subject_address` is in progress. + /// + /// To onboard a new VA: + /// * `subject_address` must not be already onboarded, + /// * `subject_address` must be KYCd, + /// * `subject_address` must have a positive reputation token amount. + /// + /// To offboard a VA: + /// * `subject_address` must be already onboarded, + /// * `subject_address` must have a positive reputation token amount. + /// + /// # Note + /// + /// `action` - the mode of newly created voting can be either onboarding ([Add](OnboardingAction::Add)) or offboarding ([Remove](OnboardingAction::Remove)). + /// + /// `subject_address` - an [Address](Address) to be on/offboarded. fn create_voting(&mut self, action: OnboardingAction, subject_address: Address, stake: U256); + /// see [GovernanceVoting](GovernanceVoting::vote()) fn vote(&mut self, voting_id: VotingId, choice: Choice, stake: U256); + /// see [GovernanceVoting](GovernanceVoting::finish_voting()) fn finish_voting(&mut self, voting_id: VotingId); + /// see [GovernanceVoting](GovernanceVoting::get_dust_amount()) fn get_dust_amount(&self) -> U256; + /// see [GovernanceVoting](GovernanceVoting::get_variable_repo_address()) fn get_variable_repo_address(&self) -> Address; + /// see [GovernanceVoting](GovernanceVoting::get_reputation_token_address()) fn get_reputation_token_address(&self) -> Address; + /// see [GovernanceVoting](GovernanceVoting::get_voting()) + fn get_voting(&self, voting_id: U256) -> Option; + /// see [GovernanceVoting](GovernanceVoting::get_ballot()) + fn get_ballot(&self, voting_id: U256, address: Address) -> Option; + /// see [GovernanceVoting](GovernanceVoting::get_voter()) + fn get_voter(&self, voting_id: U256, at: u32) -> Option
; + /// see [KycInfo](KycInfo::get_kyc_token_address()) fn get_kyc_token_address(&self) -> Address; + /// see [OnboardingInfo](OnboardingInfo::get_va_token_address()) fn get_va_token_address(&self) -> Address; - fn get_voting(&self, voting_id: VotingId) -> Option; - fn get_ballot(&self, voting_id: VotingId, address: Address) -> Option; - fn get_voter(&self, voting_id: VotingId, at: u32) -> Option
; } +/// OnboardingVoterContract +/// +/// It is responsible for managing va tokens (see [DaoOwnedNftContract](crate::DaoOwnedNftContract)). +/// +/// When the voting passes, a va token is minted (onboarding) or burned (offboarding). #[derive(Instance)] pub struct OnboardingVoterContract { onboarding: OnboardingInfo, diff --git a/dao-contracts/src/repo_voter.rs b/dao-contracts/src/repo_voter.rs index 80b9e651..053efafe 100644 --- a/dao-contracts/src/repo_voter.rs +++ b/dao-contracts/src/repo_voter.rs @@ -11,7 +11,7 @@ use delegate::delegate; #[casper_contract_interface] pub trait RepoVoterContractInterface { - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::init()) fn init(&mut self, variable_repo: Address, reputation_token: Address); /// Creates new RepoVoter voting. /// @@ -26,21 +26,21 @@ pub trait RepoVoterContractInterface { activation_time: Option, stake: U256, ); - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::vote()) fn vote(&mut self, voting_id: VotingId, choice: Choice, stake: U256); - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::finish_voting()) fn finish_voting(&mut self, voting_id: VotingId); - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_dust_amount()) fn get_dust_amount(&self) -> U256; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_variable_repo_address()) fn get_variable_repo_address(&self) -> Address; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_reputation_token_address()) fn get_reputation_token_address(&self) -> Address; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_voting()) fn get_voting(&self, voting_id: U256) -> Option; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_ballot()) fn get_ballot(&self, voting_id: U256, address: Address) -> Option; - /// see [GovernanceVoting](GovernanceVoting) + /// see [GovernanceVoting](GovernanceVoting::get_voter()) fn get_voter(&self, voting_id: U256, at: u32) -> Option
; } diff --git a/dao-contracts/src/reputation.rs b/dao-contracts/src/reputation.rs index 8c4c1911..9af96ad2 100644 --- a/dao-contracts/src/reputation.rs +++ b/dao-contracts/src/reputation.rs @@ -59,28 +59,17 @@ pub trait ReputationContractInterface { /// Change ownership of the contract. Transfer the ownership to the `owner`. Only current owner /// is permited to call this method. /// - /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller - /// is not the current owner. - /// - /// It emits [`OwnerChanged`](casper_dao_modules::events::OwnerChanged), - /// [`AddedToWhitelist`](casper_dao_modules::events::AddedToWhitelist) events. + /// See [AccessControl](AccessControl::change_ownership()) fn change_ownership(&mut self, owner: Address); /// Add new address to the whitelist. /// - /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller - /// is not the current owner. - /// - /// It emits [`AddedToWhitelist`](casper_dao_modules::events::AddedToWhitelist) event. + /// See [AccessControl](AccessControl::add_to_whitelist()) fn add_to_whitelist(&mut self, address: Address); /// Remove address from the whitelist. /// - /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller - /// is not the current owner. - /// - /// It emits [`RemovedFromWhitelist`](casper_dao_modules::events::RemovedFromWhitelist) - /// event. + /// See [AccessControl](AccessControl::remove_from_whitelist()) fn remove_from_whitelist(&mut self, address: Address); /// Stake `amount` of tokens for the `address`. It decrements `address`'s balance by `amount`. diff --git a/dao-contracts/src/variable_repository.rs b/dao-contracts/src/variable_repository.rs index 87976c04..5909d8c3 100644 --- a/dao-contracts/src/variable_repository.rs +++ b/dao-contracts/src/variable_repository.rs @@ -37,35 +37,17 @@ pub trait VariableRepositoryContractInterface { /// Changes the ownership of the contract. Transfers the ownership to the `owner`. /// Only the current owner is permited to call this method. /// - /// # Events - /// * [`OwnerChanged`](casper_dao_modules::events::OwnerChanged), - /// * [`AddedToWhitelist`](casper_dao_modules::events::AddedToWhitelist). - /// - /// # Errors - /// Throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if the caller - /// is not the current owner. + /// See [`AccessControl`](AccessControl::change_ownership()) fn change_ownership(&mut self, owner: Address); /// Adds a new address to the whitelist. /// - /// # Events - /// Emits [`AddedToWhitelist`](casper_dao_modules::events::AddedToWhitelist) event. - /// - /// # Errors - /// Throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller - /// is not the current owner. - /// + /// See [`AccessControl`](AccessControl::add_to_whitelist()) fn add_to_whitelist(&mut self, address: Address); /// Remove address from the whitelist. /// - /// # Events - /// Emits [`RemovedFromWhitelist`](casper_dao_modules::events::RemovedFromWhitelist) - /// event. - /// - /// # Errors - /// Throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller - /// is not the current owner. + /// See [`AccessControl`](AccessControl::remove_from_whitelist()) fn remove_from_whitelist(&mut self, address: Address); /// Inserts or updates the value under the given key. diff --git a/dao-contracts/src/voting/kyc_info.rs b/dao-contracts/src/voting/kyc_info.rs index a27ca512..3991ec40 100644 --- a/dao-contracts/src/voting/kyc_info.rs +++ b/dao-contracts/src/voting/kyc_info.rs @@ -2,10 +2,10 @@ use casper_dao_utils::{ casper_contract::unwrap_or_revert::UnwrapOrRevert, casper_dao_macros::Instance, Address, Error, Mapping, Variable, }; -use casper_types::U256; use crate::{DaoOwnedNftContractCaller, DaoOwnedNftContractInterface}; +/// A utility module that provides information about the current status of the KYC process. #[derive(Instance)] pub struct KycInfo { kyc_token: Variable
, @@ -13,29 +13,38 @@ pub struct KycInfo { } impl KycInfo { + /// Initializes `kyc_token` contract address. pub fn init(&mut self, kyc_token: Address) { self.kyc_token.set(kyc_token); } + /// Returns the `address` of kyc token contract. + /// + /// If the variable is not initialized, reverts with [VariableValueNotSet](Error::VariableValueNotSet) pub fn get_kyc_token_address(&self) -> Address { self.kyc_token .get() .unwrap_or_revert_with(Error::VariableValueNotSet) } + /// Returns true if the `address` has a non-zero balance of kyc token, false otherwise. pub fn is_kycd(&self, &address: &Address) -> bool { - DaoOwnedNftContractCaller::at(self.get_kyc_token_address()).balance_of(address) - > U256::zero() + !DaoOwnedNftContractCaller::at(self.get_kyc_token_address()) + .balance_of(address) + .is_zero() } + /// Sets a flag indicating there is ongoing voting for the given `address`. pub(crate) fn set_voting(&self, address: &Address) { self.votings.set(address, true); } + /// Clears the flag indicating there is ongoing voting for the given `address`. pub(crate) fn clear_voting(&self, address: &Address) { self.votings.set(address, false); } + /// Indicates whether there is ongoing voting for the given `address`. pub(crate) fn exists_ongoing_voting(&self, address: &Address) -> bool { self.votings.get(address).unwrap_or(false) } diff --git a/dao-contracts/src/voting/onboarding_info.rs b/dao-contracts/src/voting/onboarding_info.rs index fbda5637..cf8fb41b 100644 --- a/dao-contracts/src/voting/onboarding_info.rs +++ b/dao-contracts/src/voting/onboarding_info.rs @@ -6,6 +6,7 @@ use casper_dao_utils::{ Address, Error, Mapping, Variable, }; +/// A utility module that provides information about the current status of the onboarding process. #[derive(Instance)] pub struct OnboardingInfo { va_token: Variable
, @@ -13,38 +14,52 @@ pub struct OnboardingInfo { } impl OnboardingInfo { + /// Initializes `va_token` contract address. pub fn init(&mut self, va_token: Address) { self.va_token.set(va_token); } + /// Returns the `address` of kyc token contract. + /// + /// If the variable is not initialized, reverts with [VariableValueNotSet](Error::VariableValueNotSet) pub fn get_va_token_address(&self) -> Address { self.va_token .get() .unwrap_or_revert_with(Error::VariableValueNotSet) } + /// Sets a flag indicating there is ongoing voting for the given `address`. pub fn set_voting(&mut self, address: &Address) { self.votings.set(address, true); } + /// Clears the flag indicating there is ongoing voting for the given `address`. pub fn clear_voting(&mut self, address: &Address) { self.votings.set(address, false); } + /// Indicates whether there is ongoing voting for the given `address`. pub fn exists_ongoing_voting(&self, address: &Address) -> bool { self.votings.get(address).unwrap_or(false) } + /// Returns true if the `address` has a non-zero balance of va token, false otherwise. pub fn is_onboarded(&self, &address: &Address) -> bool { !self.dao_nft_caller().balance_of(address).is_zero() } + /// Returns the `token id` of the `address`. + /// + /// If the `address` does not own any token, reverts with [`InvalidTokenOwner`](Error:InvalidTokenOwner) error. pub fn token_id_of(&self, address: &Address) -> TokenId { self.dao_nft_caller() .token_id(*address) .unwrap_or_revert_with(Error::InvalidTokenOwner) } + /// Returns the owner of a token with the given id. + /// + /// If the `token_id` does not have an owner, None value is return. pub fn owner_of(&self, token_id: TokenId) -> Option
{ self.dao_nft_caller().owner_of(token_id) } diff --git a/dao-contracts/tests/test_kyc_voter.rs b/dao-contracts/tests/test_kyc_voter.rs index 5d89be9b..c3189efb 100644 --- a/dao-contracts/tests/test_kyc_voter.rs +++ b/dao-contracts/tests/test_kyc_voter.rs @@ -156,7 +156,7 @@ fn setup() -> ( ) { let env = TestEnv::new(); - let kyc_token = DaoOwnedNftContractTest::new( + let mut kyc_token = DaoOwnedNftContractTest::new( &env, "kyc token".to_string(), "kyt".to_string(), @@ -180,6 +180,10 @@ fn setup() -> ( reputation_token .change_ownership(onboarding_voter.address()) .unwrap(); + + kyc_token + .change_ownership(onboarding_voter.address()) + .unwrap(); let applicant = env.get_account(1); let another_applicant = env.get_account(2); let voter = env.get_account(3); diff --git a/dao-contracts/tests/test_onboarding_voter.rs b/dao-contracts/tests/test_onboarding_voter.rs index e82cae39..098839b1 100644 --- a/dao-contracts/tests/test_onboarding_voter.rs +++ b/dao-contracts/tests/test_onboarding_voter.rs @@ -288,7 +288,7 @@ fn setup() -> ( ) { let env = TestEnv::new(); - let va_token = DaoOwnedNftContractTest::new( + let mut va_token = DaoOwnedNftContractTest::new( &env, "user token".to_string(), "usert".to_string(), @@ -311,7 +311,7 @@ fn setup() -> ( va_token.address(), ); - // Voter Contract becomes the owner of Variable Repo and Reputation Token + // Voter Contract becomes the owner of Variable Repo, Reputation Token and VA Token variable_repo .change_ownership(onboarding_voter.address()) .unwrap(); @@ -319,6 +319,10 @@ fn setup() -> ( reputation_token .change_ownership(onboarding_voter.address()) .unwrap(); + + va_token + .change_ownership(onboarding_voter.address()) + .unwrap(); let user = env.get_account(1); let va = env.get_account(2); let second_va = env.get_account(3); diff --git a/dao-modules/src/access_control.rs b/dao-modules/src/access_control.rs index 98caec47..1703d11c 100644 --- a/dao-modules/src/access_control.rs +++ b/dao-modules/src/access_control.rs @@ -12,35 +12,72 @@ pub struct AccessControl { } impl AccessControl { + /// Module constructor. + /// + /// Initializes submodules. + /// + /// See [`Owner`] and [`Whitelist`]. pub fn init(&mut self, address: Address) { self.owner.init(address); self.whitelist.add_to_whitelist(address); } + /// Changes ownership of the contract. Transfer the ownership to the `owner`. Only the current owner + /// is permited to call this method. + /// + /// # Errors + /// Throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller + /// is not the current owner. + /// + /// # Events + /// Emits [`OwnerChanged`](crate::events::OwnerChanged), + /// [`AddedToWhitelist`](crate::events::AddedToWhitelist) events. pub fn change_ownership(&mut self, owner: Address) { self.owner.ensure_owner(); self.owner.change_ownership(owner); self.whitelist.add_to_whitelist(owner); } + /// Adds a new address to the whitelist. + /// + /// # Errors + /// Throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if the caller + /// is not the current owner. + /// + /// # Events + /// Emits [`AddedToWhitelist`](crate::events::AddedToWhitelist) event. pub fn add_to_whitelist(&mut self, address: Address) { self.owner.ensure_owner(); self.whitelist.add_to_whitelist(address); } + /// Removes the `address` from the whitelist. + /// + /// # Errors + /// Throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller + /// is not the current owner. + /// + /// # Events + /// It emits [`RemovedFromWhitelist`](crate::events::RemovedFromWhitelist) pub fn remove_from_whitelist(&mut self, address: Address) { self.owner.ensure_owner(); self.whitelist.remove_from_whitelist(address); } + /// Checks whether the given address is added to the whitelist. + /// See [`Whitelist`]. pub fn is_whitelisted(&self, address: Address) -> bool { self.whitelist.is_whitelisted(&address) } + /// Verifys whether the current caller address is added to the whitelist. pub fn ensure_whitelisted(&self) { self.whitelist.ensure_whitelisted(); } + /// Returns the address of the current owner. + /// + /// See [`Owner`]. pub fn get_owner(&self) -> Option
{ self.owner.get_owner() } diff --git a/dao-utils/src/bytes.rs b/dao-utils/src/bytes.rs deleted file mode 100644 index 8b137891..00000000 --- a/dao-utils/src/bytes.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/dao-utils/src/lib.rs b/dao-utils/src/lib.rs index 6d4c15f2..c44cc529 100644 --- a/dao-utils/src/lib.rs +++ b/dao-utils/src/lib.rs @@ -3,7 +3,6 @@ extern crate alloc; // Reexport of casper-contract crate. pub use casper_contract; -pub mod bytes; pub mod casper_env; mod events; pub mod instance; diff --git a/dao-utils/src/parts/error.rs b/dao-utils/src/parts/error.rs index 53b1858d..f1327882 100644 --- a/dao-utils/src/parts/error.rs +++ b/dao-utils/src/parts/error.rs @@ -56,6 +56,7 @@ dao_errors!( TransferFromIncorrectOwner => 1706, ApproveToCaller => 1707, InvalidTokenOwner => 1708, + UserAlreadyOwnsToken => 1709, InformalVotingTimeNotReached => 2101, FormalVotingTimeNotReached => 2102, VoteOnCompletedVotingNotAllowed => 2103,