diff --git a/contracts/src/access/access_control/access_control.rs b/contracts/src/access/access_control/access_control.rs index 08fe01d63..75d118d77 100644 --- a/contracts/src/access/access_control/access_control.rs +++ b/contracts/src/access/access_control/access_control.rs @@ -26,7 +26,10 @@ use openbrush::{ declare_storage_trait, modifier_definition, modifiers, - storage::Mapping, + storage::{ + Mapping, + ValueGuard, + }, traits::AccountId, }; @@ -38,7 +41,7 @@ pub struct AccessControlData where B: AccessControlMemberManager, { - pub admin_roles: Mapping, + pub admin_roles: Mapping>, pub members: B, pub _reserved: Option<()>, } @@ -72,10 +75,10 @@ where } default fn get_role_admin(&self, role: RoleType) -> RoleType { - get_role_admin(self, &role) + get_role_admin(self, role) } - #[modifiers(only_role(get_role_admin(self, &role)))] + #[modifiers(only_role(get_role_admin(self, role)))] default fn grant_role(&mut self, role: RoleType, account: AccountId) -> Result<(), AccessControlError> { if self.get().members.has_role(role, &account) { return Err(AccessControlError::RoleRedundant) @@ -85,7 +88,7 @@ where Ok(()) } - #[modifiers(only_role(get_role_admin(self, &role)))] + #[modifiers(only_role(get_role_admin(self, role)))] default fn revoke_role(&mut self, role: RoleType, account: AccountId) -> Result<(), AccessControlError> { check_role(self, role, account)?; self._do_revoke_role(role, account); @@ -169,12 +172,12 @@ where } default fn _set_role_admin(&mut self, role: RoleType, new_admin: RoleType) { - let mut entry = self.get_mut().admin_roles.get(&role); + let mut entry = self.get_mut().admin_roles.get(role); if entry.is_none() { entry = Some(Self::_default_admin()); } let old_admin = entry.unwrap(); - self.get_mut().admin_roles.insert(&role, &new_admin); + self.get_mut().admin_roles.insert(role, &new_admin); self._emit_role_admin_changed(role, old_admin, new_admin); } } @@ -190,7 +193,7 @@ where Ok(()) } -pub fn get_role_admin(instance: &T, role: &RoleType) -> RoleType +pub fn get_role_admin(instance: &T, role: RoleType) -> RoleType where B: AccessControlMemberManager, T: AccessControlStorage>, diff --git a/contracts/src/access/access_control_enumerable/mod.rs b/contracts/src/access/access_control/extensions/enumerable.rs similarity index 100% rename from contracts/src/access/access_control_enumerable/mod.rs rename to contracts/src/access/access_control/extensions/enumerable.rs diff --git a/contracts/src/access/access_control/mod.rs b/contracts/src/access/access_control/mod.rs index 4c8b4ca4f..940ce5e08 100644 --- a/contracts/src/access/access_control/mod.rs +++ b/contracts/src/access/access_control/mod.rs @@ -19,6 +19,10 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +pub mod extensions { + pub mod enumerable; +} + mod access_control; mod members; diff --git a/contracts/src/access/mod.rs b/contracts/src/access/mod.rs index b4cb5ea14..3fa3ed4f4 100644 --- a/contracts/src/access/mod.rs +++ b/contracts/src/access/mod.rs @@ -21,7 +21,5 @@ #[cfg(feature = "access_control")] pub mod access_control; -#[cfg(feature = "access_control")] -pub mod access_control_enumerable; #[cfg(feature = "ownable")] pub mod ownable; diff --git a/contracts/src/finance/payment_splitter/mod.rs b/contracts/src/finance/payment_splitter/mod.rs index 1c9612dba..2f814775d 100644 --- a/contracts/src/finance/payment_splitter/mod.rs +++ b/contracts/src/finance/payment_splitter/mod.rs @@ -22,9 +22,9 @@ pub use crate::traits::payment_splitter::*; pub use derive::PaymentSplitterStorage; use ink_prelude::vec::Vec; -use ink_storage::Mapping; use openbrush::{ declare_storage_trait, + storage::Mapping, traits::{ AccountId, AccountIdExt, diff --git a/contracts/src/token/psp22/psp22.rs b/contracts/src/token/psp22/psp22.rs index f79d55f9e..498c487ee 100644 --- a/contracts/src/token/psp22/psp22.rs +++ b/contracts/src/token/psp22/psp22.rs @@ -38,9 +38,12 @@ use ink_prelude::{ string::String, vec::Vec, }; -use ink_storage::Mapping; use openbrush::{ declare_storage_trait, + storage::{ + Mapping, + TypeGuard, + }, traits::{ AccountId, AccountIdExt, @@ -56,10 +59,16 @@ pub const STORAGE_KEY: [u8; 32] = ink_lang::blake2x256!("openbrush::PSP22Data"); pub struct PSP22Data { pub supply: Balance, pub balances: Mapping, - pub allowances: Mapping<(AccountId, AccountId), Balance>, + pub allowances: Mapping<(AccountId, AccountId), Balance, AllowancesKey>, pub _reserved: Option<()>, } +pub struct AllowancesKey; + +impl<'a> TypeGuard<'a> for AllowancesKey { + type Type = &'a (&'a AccountId, &'a AccountId); +} + declare_storage_trait!(PSP22Storage); impl + Flush> PSP22 for T { @@ -72,7 +81,7 @@ impl + Flush> PSP22 for T { } default fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance { - self.get().allowances.get((&owner, &spender)).unwrap_or(0) + self.get().allowances.get(&(&owner, &spender)).unwrap_or(0) } default fn transfer(&mut self, to: AccountId, value: Balance, data: Vec) -> Result<(), PSP22Error> { @@ -256,7 +265,7 @@ impl + Flush> PSP22Internal for T { return Err(PSP22Error::ZeroRecipientAddress) } - self.get_mut().allowances.insert((&owner, &spender), &amount); + self.get_mut().allowances.insert(&(&owner, &spender), &amount); self._emit_approval_event(owner, spender, amount); Ok(()) } diff --git a/contracts/src/token/psp34/extensions/metadata.rs b/contracts/src/token/psp34/extensions/metadata.rs index 5d7074a2d..eb8fa6290 100644 --- a/contracts/src/token/psp34/extensions/metadata.rs +++ b/contracts/src/token/psp34/extensions/metadata.rs @@ -28,23 +28,34 @@ pub use derive::{ PSP34Storage, }; use ink_prelude::vec::Vec; -use ink_storage::Mapping; -use openbrush::declare_storage_trait; +use openbrush::{ + declare_storage_trait, + storage::{ + Mapping, + TypeGuard, + }, +}; pub const STORAGE_KEY: [u8; 32] = ink_lang::blake2x256!("openbrush::PSP32MetadataData"); #[derive(Default, Debug)] #[openbrush::storage(STORAGE_KEY)] pub struct PSP34MetadataData { - pub attributes: Mapping<(Id, Vec), Vec>, + pub attributes: Mapping<(Id, Vec), Vec, AttributesKey>, pub _reserved: Option<()>, } +pub struct AttributesKey; + +impl<'a> TypeGuard<'a> for AttributesKey { + type Type = &'a (&'a Id, &'a Vec); +} + declare_storage_trait!(PSP34MetadataStorage); impl> PSP34Metadata for T { default fn get_attribute(&self, id: Id, key: Vec) -> Option> { - self.get().attributes.get((&id, &key)) + self.get().attributes.get(&(&id, &key)) } } @@ -54,7 +65,7 @@ pub trait PSP34MetadataInternal { impl + PSP34Internal> PSP34MetadataInternal for T { default fn _set_attribute(&mut self, id: Id, key: Vec, value: Vec) { - self.get_mut().attributes.insert((&id, &key), &value); + self.get_mut().attributes.insert(&(&id, &key), &value); self._emit_attribute_set_event(id, key, value); } } diff --git a/contracts/src/token/psp35/extensions/metadata.rs b/contracts/src/token/psp35/extensions/metadata.rs index aa40f8867..fb5e1a98e 100644 --- a/contracts/src/token/psp35/extensions/metadata.rs +++ b/contracts/src/token/psp35/extensions/metadata.rs @@ -25,23 +25,34 @@ pub use crate::{ }; pub use derive::PSP35MetadataStorage; use ink_prelude::vec::Vec; -use ink_storage::Mapping; -use openbrush::declare_storage_trait; +use openbrush::{ + declare_storage_trait, + storage::{ + Mapping, + TypeGuard, + }, +}; pub const STORAGE_KEY: [u8; 32] = ink_lang::blake2x256!("openbrush::PSP35MetadataData"); #[derive(Default, Debug)] #[openbrush::storage(STORAGE_KEY)] pub struct PSP35MetadataData { - pub attributes: Mapping<(Id, Vec), Vec>, + pub attributes: Mapping<(Id, Vec), Vec, AttributesKey>, pub _reserved: Option<()>, } +pub struct AttributesKey; + +impl<'a> TypeGuard<'a> for AttributesKey { + type Type = &'a (&'a Id, &'a Vec); +} + declare_storage_trait!(PSP35MetadataStorage); impl> PSP35Metadata for T { default fn get_attribute(&self, id: Id, key: Vec) -> Option> { - self.get().attributes.get(&(id, key)) + self.get().attributes.get(&(&id, &key)) } } @@ -55,13 +66,13 @@ pub trait PSP35MetadataInternal { impl> PSP35MetadataInternal for T { default fn _set_attribute(&mut self, id: &Id, key: &Vec, data: &Vec) -> Result<(), PSP35Error> { - self.get_mut().attributes.insert((id, key), data); + self.get_mut().attributes.insert(&(&id, &key), data); self._emit_attribute_set_event(id, key, data); Ok(()) } default fn _get_attribute(&self, id: &Id, key: &Vec) -> Option> { - self.get().attributes.get((id, key)) + self.get().attributes.get(&(&id, &key)) } default fn _emit_attribute_set_event(&self, _id: &Id, _key: &Vec, _data: &Vec) {} diff --git a/contracts/src/token/psp35/psp35.rs b/contracts/src/token/psp35/psp35.rs index 2d2f61b41..9f2f3768f 100644 --- a/contracts/src/token/psp35/psp35.rs +++ b/contracts/src/token/psp35/psp35.rs @@ -58,12 +58,6 @@ where pub _reserved: Option<()>, } -pub struct BalancesKey; - -impl<'a> TypeGuard<'a> for BalancesKey { - type Type = &'a (&'a Id, &'a AccountId); -} - pub struct ApprovalsKey; impl<'a> TypeGuard<'a> for ApprovalsKey { diff --git a/contracts/src/upgradability/diamond/diamond.rs b/contracts/src/upgradability/diamond/diamond.rs index fae0d73fc..f036ab24a 100644 --- a/contracts/src/upgradability/diamond/diamond.rs +++ b/contracts/src/upgradability/diamond/diamond.rs @@ -32,7 +32,6 @@ use ink_env::{ Clear, }; use ink_prelude::vec::Vec; -use ink_storage::Mapping; use openbrush::{ modifiers, traits::{ @@ -42,6 +41,7 @@ use openbrush::{ }; pub use derive::DiamondStorage; +use openbrush::storage::Mapping; pub const STORAGE_KEY: [u8; 32] = ink_lang::blake2x256!("openbrush::DiamondData"); @@ -149,7 +149,7 @@ impl + Flush + DiamondCut> DiamondInternal default fn _fallback(&self) -> ! { let selector = ink_env::decode_input::().unwrap_or_else(|_| panic!("Calldata error")); - let delegate_code = DiamondStorage::get(self).selector_to_hash.get(selector); + let delegate_code = DiamondStorage::get(self).selector_to_hash.get(&selector); if delegate_code.is_none() { panic!("Function is not registered"); diff --git a/contracts/src/upgradability/diamond/extensions/diamond_loupe.rs b/contracts/src/upgradability/diamond/extensions/diamond_loupe.rs index 5f5af787a..8f3cce0fe 100644 --- a/contracts/src/upgradability/diamond/extensions/diamond_loupe.rs +++ b/contracts/src/upgradability/diamond/extensions/diamond_loupe.rs @@ -25,13 +25,16 @@ pub use crate::{ traits::diamond::extensions::diamond_loupe::*, }; use ink_prelude::vec::Vec; -use ink_storage::Mapping; use openbrush::{ declare_storage_trait, traits::Hash, }; pub use derive::DiamondLoupeStorage; +use openbrush::storage::{ + Mapping, + ValueGuard, +}; pub const STORAGE_KEY: [u8; 32] = ink_lang::blake2x256!("openbrush::DiamondLoupeData"); @@ -39,11 +42,11 @@ pub const STORAGE_KEY: [u8; 32] = ink_lang::blake2x256!("openbrush::DiamondLoupe #[openbrush::storage(STORAGE_KEY)] pub struct DiamondLoupeData { // number of registered code hashes - pub code_hashes: u16, + pub code_hashes: u32, // mapping of facet to its position in all facets list - pub hash_to_id: Mapping, + pub hash_to_id: Mapping, // mapping of facet id to its facet - pub id_to_hash: Mapping, + pub id_to_hash: Mapping>, pub _reserved: Option<()>, } @@ -53,21 +56,21 @@ impl> DiamondCut for T { default fn _on_add_facet(&mut self, code_hash: Hash) { let hash_id = self.get().code_hashes; self.get_mut().hash_to_id.insert(&code_hash, &hash_id); - self.get_mut().id_to_hash.insert(&hash_id, &code_hash); + self.get_mut().id_to_hash.insert(hash_id, &code_hash); self.get_mut().code_hashes += 1; } default fn _on_remove_facet(&mut self, code_hash: Hash) { let new_hash_id = self.get().code_hashes - 1; let removed_hash_id = self.get().hash_to_id.get(&code_hash).unwrap(); - let last_hash = self.get().id_to_hash.get(&new_hash_id).unwrap(); + let last_hash = self.get().id_to_hash.get(new_hash_id).unwrap(); if last_hash != code_hash { - self.get_mut().id_to_hash.insert(&removed_hash_id, &last_hash); + self.get_mut().id_to_hash.insert(removed_hash_id, &last_hash); self.get_mut().hash_to_id.insert(&last_hash, &removed_hash_id); - self.get_mut().id_to_hash.remove(&new_hash_id); + self.get_mut().id_to_hash.remove(new_hash_id); } else { - self.get_mut().id_to_hash.remove(&removed_hash_id); + self.get_mut().id_to_hash.remove(removed_hash_id); } self.get_mut().hash_to_id.remove(&code_hash); @@ -79,7 +82,7 @@ impl + DiamondStorage Vec { let mut out_vec = Vec::new(); for i in 0..DiamondLoupeStorage::get(self).code_hashes { - let hash = DiamondLoupeStorage::get(self).id_to_hash.get(&i).unwrap(); + let hash = DiamondLoupeStorage::get(self).id_to_hash.get(i).unwrap(); let selectors = DiamondStorage::get(self).hash_to_selectors.get(&hash).unwrap(); out_vec.push(FacetCut { hash, selectors }) } @@ -89,19 +92,19 @@ impl + DiamondStorage Vec { DiamondStorage::get(self) .hash_to_selectors - .get(facet) + .get(&facet) .unwrap_or(Vec::::new()) } default fn facet_code_hashes(&self) -> Vec { let mut out_vec = Vec::new(); for i in 0..DiamondLoupeStorage::get(self).code_hashes { - out_vec.push(DiamondLoupeStorage::get(self).id_to_hash.get(&i).unwrap()) + out_vec.push(DiamondLoupeStorage::get(self).id_to_hash.get(i).unwrap()) } out_vec } default fn facet_code_hash(&self, selector: Selector) -> Option { - DiamondStorage::get(self).selector_to_hash.get(selector) + DiamondStorage::get(self).selector_to_hash.get(&selector) } } diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md index 20b488f63..3b107d3f3 100644 --- a/docs/docs/getting-started.md +++ b/docs/docs/getting-started.md @@ -9,8 +9,12 @@ Welcome to OpenBrush documentation! This documentation aims to guide you through ## What is OpenBrush OpenBrush is a library for smart contract development on ink! -It provides standard contracts ([based on PSP](https://github.com/w3f/PSPs)), as well as useful contracts and macros to help you build ink! smart contracts. +It provides standard contracts ([based on PSP](https://github.com/w3f/PSPs)), +as well as useful contracts and macros to help you build ink! smart contracts. ## Why OpenBrush -OpenBrush attempts to analogize OpenZeppelin perfectly with Rust’s paradigm, enabling users to import contracts implemented by another user without problems and reuse the code. -There was a need to have a library that can provide base implementations of ERCs and to import/reuse them by customizing their own logic. \ No newline at end of file +OpenBrush attempts to analogize OpenZeppelin perfectly with Rust’s paradigm, +enabling users to import contracts implemented by another user without problems +and reuse the code. +There was a need to have a library that can provide base implementations of +ERCs and to import/reuse them by customizing their own logic. \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP22/Extensions/burnable.md b/docs/docs/smart-contracts/PSP22/Extensions/burnable.md index 503f4b846..6b7b9ea5d 100644 --- a/docs/docs/smart-contracts/PSP22/Extensions/burnable.md +++ b/docs/docs/smart-contracts/PSP22/Extensions/burnable.md @@ -4,7 +4,7 @@ title: PSP22 Burnable --- This example shows how you can reuse the implementation of -[PSP22](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22Burnable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22/src/extensions/burnable.rs) extension. +[PSP22](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22Burnable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/burnable.rs) extension. ## How to use this extension diff --git a/docs/docs/smart-contracts/PSP22/Extensions/metadata.md b/docs/docs/smart-contracts/PSP22/Extensions/metadata.md index 41c46bf9a..c41b3aa63 100644 --- a/docs/docs/smart-contracts/PSP22/Extensions/metadata.md +++ b/docs/docs/smart-contracts/PSP22/Extensions/metadata.md @@ -3,7 +3,7 @@ sidebar_position: 1 title: PSP22 Metadata --- -This example shows how you can reuse the implementation of [PSP22](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22) token with the [PSP22Metadata](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22/src/extensions/metadata.rs) extension. +This example shows how you can reuse the implementation of [PSP22](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22) token with the [PSP22Metadata](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/metadata.rs) extension. ## Step 1: Add imports and enable unstable feature diff --git a/docs/docs/smart-contracts/PSP22/Extensions/mintable.md b/docs/docs/smart-contracts/PSP22/Extensions/mintable.md index e400479c2..b9e650701 100644 --- a/docs/docs/smart-contracts/PSP22/Extensions/mintable.md +++ b/docs/docs/smart-contracts/PSP22/Extensions/mintable.md @@ -4,7 +4,7 @@ title: PSP22 Mintable --- This example shows how you can reuse the implementation of -[PSP22](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22Mintable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22/src/extensions/mintable.rs) extension. +[PSP22](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22Mintable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/mintable.rs) extension. ## How to use this extension diff --git a/docs/docs/smart-contracts/PSP34/Extensions/burnable.md b/docs/docs/smart-contracts/PSP34/Extensions/burnable.md index 75b0c2146..1f96acfd2 100644 --- a/docs/docs/smart-contracts/PSP34/Extensions/burnable.md +++ b/docs/docs/smart-contracts/PSP34/Extensions/burnable.md @@ -3,7 +3,7 @@ sidebar_position: 3 title: PSP34 Burnable --- -This example shows how you can reuse the implementation of [PSP34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Burnable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34/src/extensions/burnable.rs) extension. +This example shows how you can reuse the implementation of [PSP34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Burnable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/burnable.rs) extension. ## How to use this extension diff --git a/docs/docs/smart-contracts/PSP34/Extensions/enumerable.md b/docs/docs/smart-contracts/PSP34/Extensions/enumerable.md new file mode 100644 index 000000000..a7ffcd7c2 --- /dev/null +++ b/docs/docs/smart-contracts/PSP34/Extensions/enumerable.md @@ -0,0 +1,50 @@ +--- +sidebar_position: 3 +title: PSP34 Enumerable +--- + +This example shows how you can reuse the implementation of [PSP34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Enumerable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/enumerable.rs) extension. + +## Step 1: Add imports and enable unstable feature + +Import **everything** from `openbrush::contracts::psp34::extensions::enumerable`. + +```rust +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(min_specialization)] + +#[openbrush::contract] +pub mod my_psp34 { + use openbrush::contracts::psp34::extensions::enumerable::*; + use ink_storage::traits::SpreadAllocate; +... +``` + +## Step 2: Define storage + +Declare storage struct and use `EnumerableBalances` instead of common balances to be able to use `PSP34Enumerable` extension in your `PSP34` implementation. + +```rust +#[derive(Default, SpreadAllocate, PSP34Storage)] +#[ink(storage)] +pub struct MyPSP34 { + #[PSP34StorageField] + psp34: PSP34Data, +} +``` + +## Step 3: Inherit logic + +Inherit implementation of the `PSP34Enumerable` trait. You can customize (override) methods in this `impl` block. + +```rust + +impl PSP34 for MyPSP34 {} + +impl PSP34Enumerable for MyPSP34 {} +``` + +And that's it! Your `PSP34` is now extended by the `PSP34Enumerable` extension and ready to use its functions! +You can check an example of the usage of [PSP34 Enumerable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/examples/psp34_extensions/enumerable). + +You can also check the documentation for the basic implementation of [PSP34](/smart-contracts/PSP34). \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP34/Extensions/metadata.md b/docs/docs/smart-contracts/PSP34/Extensions/metadata.md index e5ce5a2e9..93b77e29b 100644 --- a/docs/docs/smart-contracts/PSP34/Extensions/metadata.md +++ b/docs/docs/smart-contracts/PSP34/Extensions/metadata.md @@ -3,7 +3,7 @@ sidebar_position: 1 title: PSP34 Metadata --- -This example shows how you can reuse the implementation of [PSP34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Metadata](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34/src/extensions/metadata.rs) extension. +This example shows how you can reuse the implementation of [PSP34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Metadata](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/metadata.rs) extension. ## Step 1: Include dependencies diff --git a/docs/docs/smart-contracts/PSP34/Extensions/mintable.md b/docs/docs/smart-contracts/PSP34/Extensions/mintable.md index 8c0d4b764..c999438eb 100644 --- a/docs/docs/smart-contracts/PSP34/Extensions/mintable.md +++ b/docs/docs/smart-contracts/PSP34/Extensions/mintable.md @@ -3,7 +3,7 @@ sidebar_position: 2 title: PSP34 Mintable --- -This example shows how you can reuse the implementation of [PSP34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Mintable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34/src/extensions/mintable.rs) extension. +This example shows how you can reuse the implementation of [PSP34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Mintable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/mintable.rs) extension. ## How to use this extension diff --git a/docs/docs/smart-contracts/PSP34/psp34.md b/docs/docs/smart-contracts/PSP34/psp34.md index 401b0ebc2..82d19ed42 100644 --- a/docs/docs/smart-contracts/PSP34/psp34.md +++ b/docs/docs/smart-contracts/PSP34/psp34.md @@ -101,3 +101,5 @@ Also you can use extensions for psp34 token: [PSP34Mintable](/smart-contracts/PSP34/extensions/mintable): creation of new tokens. [PSP34Burnable](/smart-contracts/PSP34/extensions/burnable): destruction of contract's tokens. + +[PSP34Enumerable](/smart-contracts/PSP34/extensions/enumerable): iterating over contract's tokens. diff --git a/docs/docs/smart-contracts/PSP35/Extensions/batch.md b/docs/docs/smart-contracts/PSP35/Extensions/batch.md index 2a2f29ce2..98be8839e 100644 --- a/docs/docs/smart-contracts/PSP35/Extensions/batch.md +++ b/docs/docs/smart-contracts/PSP35/Extensions/batch.md @@ -3,7 +3,7 @@ sidebar_position: 2 title: PSP35 Batch --- -This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Batch](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/src/extensions/batch.rs) extension, which allows batch transferring of PSP35 tokens. +This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Batch](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/extensions/batch.rs) extension, which allows batch transferring of PSP35 tokens. ## How to use this extension diff --git a/docs/docs/smart-contracts/PSP35/Extensions/burnable.md b/docs/docs/smart-contracts/PSP35/Extensions/burnable.md index 6ed73b1a0..61585d200 100644 --- a/docs/docs/smart-contracts/PSP35/Extensions/burnable.md +++ b/docs/docs/smart-contracts/PSP35/Extensions/burnable.md @@ -3,7 +3,7 @@ sidebar_position: 3 title: PSP35 Burnable --- -This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Burnable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/src/extensions/burnable.rs) extension. +This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Burnable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/extensions/burnable.rs) extension. ## How to use this extension diff --git a/docs/docs/smart-contracts/PSP35/Extensions/enumerable.md b/docs/docs/smart-contracts/PSP35/Extensions/enumerable.md index ecd721814..2f1b1b9a2 100644 --- a/docs/docs/smart-contracts/PSP35/Extensions/enumerable.md +++ b/docs/docs/smart-contracts/PSP35/Extensions/enumerable.md @@ -3,7 +3,7 @@ sidebar_position: 1 title: PSP35 Enumerable --- -This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Enumerable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/src/extensions/enumerable.rs) extension. +This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Enumerable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/extensions/enumerable.rs) extension. ## Step 1: Add imports and enable unstable feature diff --git a/docs/docs/smart-contracts/PSP35/Extensions/metadata.md b/docs/docs/smart-contracts/PSP35/Extensions/metadata.md index 0a6728e87..3cd2c7f64 100644 --- a/docs/docs/smart-contracts/PSP35/Extensions/metadata.md +++ b/docs/docs/smart-contracts/PSP35/Extensions/metadata.md @@ -3,7 +3,7 @@ sidebar_position: 1 title: PSP35 Metadata --- -This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Metadata](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/src/extensions/metadata.rs) extension. +This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Metadata](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/extensions/metadata.rs) extension. ## Step 1: Add imports and enable unstable feature diff --git a/docs/docs/smart-contracts/PSP35/Extensions/mintable.md b/docs/docs/smart-contracts/PSP35/Extensions/mintable.md index 5ae72677f..28510e735 100644 --- a/docs/docs/smart-contracts/PSP35/Extensions/mintable.md +++ b/docs/docs/smart-contracts/PSP35/Extensions/mintable.md @@ -3,7 +3,7 @@ sidebar_position: 2 title: PSP35 Mintable --- -This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Mintable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/src/extensions/mintable.rs) extension. +This example shows how you can reuse the implementation of [PSP35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) token with [PSP35Mintable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35/extensions/mintable.rs) extension. ## How to use this extension diff --git a/docs/docs/smart-contracts/PSP35/psp35.md b/docs/docs/smart-contracts/PSP35/psp35.md index b1bc4f972..b9cab15ac 100644 --- a/docs/docs/smart-contracts/PSP35/psp35.md +++ b/docs/docs/smart-contracts/PSP35/psp35.md @@ -118,4 +118,6 @@ Also you can use extensions for PSP35 token: [PSP35Burnable](/smart-contracts/PSP35/extensions/burnable): destruction of contract's tokens. -[PSP35Batch](/smart-contracts/PSP35/extensions/batch): transfer batch of tokens \ No newline at end of file +[PSP35Batch](/smart-contracts/PSP35/extensions/batch): transfer batch of tokens. + +[PSP35Enumerable](/smart-contracts/PSP35/extensions/enumerable): iterates over contract's tokens. \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/implementation.md b/docs/docs/smart-contracts/example/implementation.md index f0255647f..d5c3d2624 100644 --- a/docs/docs/smart-contracts/example/implementation.md +++ b/docs/docs/smart-contracts/example/implementation.md @@ -209,7 +209,7 @@ default fn borrow_assets( let reserve_asset = get_reserve_asset(self, &asset_address)?; // we will find out the price of deposited collateral - let price = get_asset_price(self, amount, collateral_address, asset_address); + let price = get_asset_price(self, &amount, &collateral_address, &asset_address); // we will set the liquidation price to be 75% of current price let liquidation_price = (price * 75) / 100; // borrow amount is 70% of collateral diff --git a/docs/docs/smart-contracts/example/impls.md b/docs/docs/smart-contracts/example/impls.md index 0f6213474..3f414b837 100644 --- a/docs/docs/smart-contracts/example/impls.md +++ b/docs/docs/smart-contracts/example/impls.md @@ -189,22 +189,23 @@ it will be enough for us. We will store prices info in our data struct. // importing everything publicly from traits allows you to import every stuff related to lending // by one import pub use crate::traits::lending::*; -use openbrush::{ - declare_storage_trait, - traits::{ - AccountId, - AccountIdExt, - Balance, - Hash, - ZERO_ADDRESS, - }, +use ink_storage::traits::{ + SpreadAllocate, + SpreadLayout, }; -use ink_storage::{ - traits::{ - SpreadAllocate, - SpreadLayout, - }, +use openbrush::{ + declare_storage_trait, + storage::{ Mapping, + TypeGuard, + }, + traits::{ + AccountId, + AccountIdExt, + Balance, + Hash, + ZERO_ADDRESS, + }, }; // it is public because when you will import the trait you also will import the derive for the trait pub use lending_project_derive::LendingStorage; @@ -237,50 +238,51 @@ pub struct LendingData { /// when we deposit 1 unit of tuple.0 /// we are using this just to simulate an oracle in our example /// in the example the returned balance will be amount of stable coin for an asset - pub asset_price: Mapping<(AccountId, AccountId), Balance>, + pub asset_price: Mapping<(AccountId, AccountId), Balance, AssetPriceKey>, /// code hash of the `SharesContract` pub shares_contract_code_hash: Hash, /// the `AccountId` of the `Loan` pub loan_account: AccountId, } -declare_storage_trait!(LendingStorage, LendingData); +pub struct AssetPriceKey; + +impl<'a> TypeGuard<'a> for AssetPriceKey { + type Type = &'a (&'a AccountId, &'a AccountId); +} + +declare_storage_trait!(LendingStorage); /// this internal function will be used to set price of `asset_in` when we deposit `asset_out` /// we are using this function in our example to simulate an oracle -pub fn set_asset_price(instance: &mut T, asset_in: AccountId, asset_out: AccountId, price: Balance) { - instance.get_mut().asset_price.insert((&asset_in, &asset_out), &price); +pub fn set_asset_price(instance: &mut T, asset_in: &AccountId, asset_out: &AccountId, price: &Balance) +where + T: LendingStorage, +{ + instance.get_mut().asset_price.insert(&(asset_in, asset_out), price); } /// this internal function will be used to set price of `asset_in` when we deposit `asset_out` /// we are using this function in our example to simulate an oracle -pub fn get_asset_price( - instance: &T, - amount_in: Balance, - asset_in: AccountId, - asset_out: AccountId, -) -> Balance { - let price = instance - .get() - .asset_price - .get((&asset_in, &asset_out)) - .cloned() - .unwrap_or(0); +pub fn get_asset_price(instance: &T, amount_in: &Balance, asset_in: &AccountId, asset_out: &AccountId) -> Balance +where + T: LendingStorage, +{ + let price = instance.get().asset_price.get(&(asset_in, asset_out)).unwrap_or(0); price * amount_in } /// Internal function which will return the address of the shares token /// which are minted when `asset_address` is borrowed -pub fn get_reserve_asset( - instance: &T, - asset_address: &AccountId, -) -> Result { +pub fn get_reserve_asset(instance: &T, asset_address: &AccountId) -> Result +where + T: LendingStorage, +{ let reserve_asset = instance - .get() - .asset_shares - .get(asset_address) - .cloned() - .unwrap_or(ZERO_ADDRESS.into()); + .get() + .asset_shares + .get(&asset_address) + .unwrap_or(ZERO_ADDRESS.into()); if reserve_asset.is_zero() { return Err(LendingError::AssetNotSupported) } @@ -289,16 +291,15 @@ pub fn get_reserve_asset( /// internal function which will return the address of asset /// which is bound to `shares_address` shares token -pub fn get_asset_from_shares( - instance: &T, - shares_address: AccountId, -) -> Result { +pub fn get_asset_from_shares(instance: &T, shares_address: &AccountId) -> Result +where + T: LendingStorage, +{ let token = instance - .get() - .shares_asset - .get(&shares_address) - .cloned() - .unwrap_or(ZERO_ADDRESS.into()); + .get() + .shares_asset + .get(shares_address) + .unwrap_or(ZERO_ADDRESS.into()); if token.is_zero() { return Err(LendingError::AssetNotSupported) } @@ -355,114 +356,128 @@ That allows us to use methods from these traits and define the implementation. ```rust pub use super::data::*; use openbrush::{ - contracts::{ - access_control::*, - pausable::PausableStorage, - traits::psp22::PSP22Ref, - }, - modifiers, - traits::{ - AccountId, - Balance, - ZERO_ADDRESS, - }, + contracts::{ + access_control::*, + pausable::{ + PausableData, + PausableStorage, + }, + traits::psp22::PSP22Ref, + }, + modifiers, + traits::{ + AccountId, + Balance, + ZERO_ADDRESS, + }, }; pub const MANAGER: RoleType = ink_lang::selector_id!("MANAGER"); -impl LendingPermissioned -for T +impl LendingPermissioned for T +where + T: LendingStorage + + PausableStorage + + LendingPermissionedInternal + + AccessControlStorage, { - #[modifiers(only_role(MANAGER))] - default fn allow_asset(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - // we will ensure the asset is not accepted already - if self.is_accepted_lending(asset_address) { - return Err(LendingError::AssetSupported) - } + #[modifiers(only_role(MANAGER))] + default fn allow_asset(&mut self, asset_address: AccountId) -> Result<(), LendingError> { + // we will ensure the asset is not accepted already + if self.is_accepted_lending(asset_address) { + return Err(LendingError::AssetSupported) + } - // instantiate the shares of the lended assets - let shares_address = self._instantiate_shares_contract("LendingShares", "LS"); - // instantiate the reserves of the borrowed assets - let reserves_address = self._instantiate_shares_contract("LendingReserves", "LR"); - // accept the asset and map shares and reserves to it + // instantiate the shares of the lended assets + let shares_address = self._instantiate_shares_contract("LendingShares", "LS"); + // instantiate the reserves of the borrowed assets + let reserves_address = self._instantiate_shares_contract("LendingReserves", "LR"); + // accept the asset and map shares and reserves to it - accept_lending(self, asset_address, shares_address, reserves_address); - Ok(()) - } + accept_lending(self, asset_address, shares_address, reserves_address); + Ok(()) + } - #[modifiers(only_role(MANAGER))] - default fn disallow_lending(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - let reserve_asset = get_reserve_asset(self, &asset_address)?; - if PSP22Ref::balance_of(&asset_address, Self::env().account_id()) > 0 + #[modifiers(only_role(MANAGER))] + default fn disallow_lending(&mut self, asset_address: AccountId) -> Result<(), LendingError> { + let reserve_asset = get_reserve_asset(self, &asset_address)?; + if PSP22Ref::balance_of(&asset_address, Self::env().account_id()) > 0 || PSP22Ref::balance_of(&reserve_asset, Self::env().account_id()) > 0 - { - return Err(LendingError::AssetsInTheContract) + { + return Err(LendingError::AssetsInTheContract) + } + disallow_lending(self, asset_address); + Ok(()) } - disallow_lending(self, asset_address); - Ok(()) - } - - #[modifiers(only_role(MANAGER))] - default fn allow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - // we will ensure the asset is not accepted already - if self.is_accepted_collateral(asset_address) { - return Err(LendingError::AssetSupported) + + #[modifiers(only_role(MANAGER))] + default fn allow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError> { + // we will ensure the asset is not accepted already + if self.is_accepted_collateral(asset_address) { + return Err(LendingError::AssetSupported) + } + set_collateral_accepted(self, asset_address, true); + Ok(()) } - set_collateral_accepted(self, asset_address, true); - Ok(()) - } - - #[modifiers(only_role(MANAGER))] - default fn disallow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - // we will ensure the asset is not accepted already - if self.is_accepted_collateral(asset_address) { - set_collateral_accepted(self, asset_address, false); + + #[modifiers(only_role(MANAGER))] + default fn disallow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError> { + // we will ensure the asset is not accepted already + if self.is_accepted_collateral(asset_address) { + set_collateral_accepted(self, asset_address, false); + } + Ok(()) } - Ok(()) - } - #[modifiers(only_role(MANAGER))] - default fn set_asset_price( - &mut self, - asset_in: AccountId, - asset_out: AccountId, - price: Balance, - ) -> Result<(), LendingError> { - set_asset_price(self, asset_in, asset_out, price); - Ok(()) - } + #[modifiers(only_role(MANAGER))] + default fn set_asset_price( + &mut self, + asset_in: AccountId, + asset_out: AccountId, + price: Balance, + ) -> Result<(), LendingError> { + set_asset_price(self, &asset_in, &asset_out, &price); + Ok(()) + } } pub trait LendingPermissionedInternal { - /// internal function which instantiates a shares contract and returns its AccountId - fn _instantiate_shares_contract(&self, contract_name: &str, contract_symbol: &str) -> AccountId; + /// internal function which instantiates a shares contract and returns its AccountId + fn _instantiate_shares_contract(&self, contract_name: &str, contract_symbol: &str) -> AccountId; } -fn accept_lending( - instance: &mut T, - asset_address: AccountId, - share_address: AccountId, - reserve_address: AccountId, +fn accept_lending>( + instance: &mut T, + asset_address: AccountId, + share_address: AccountId, + reserve_address: AccountId, ) { - instance.get_mut().asset_shares.insert(&asset_address, &share_address); - instance.get_mut().shares_asset.insert(&share_address, &asset_address); - instance.get_mut().assets_lended.insert(&asset_address, &reserve_address); + instance.get_mut().asset_shares.insert(&asset_address, &share_address); + instance.get_mut().shares_asset.insert(&share_address, &asset_address); + instance + .get_mut() + .assets_lended + .insert(&asset_address, &reserve_address); } -fn disallow_lending(instance: &mut T, asset_address: AccountId) { - let share_address = instance - .get_mut() - .asset_shares - .get(&asset_address) - .unwrap_or(ZERO_ADDRESS.into()); - instance.get_mut().asset_shares.remove(&asset_address); - instance.get_mut().shares_asset.remove(&share_address); - instance.get_mut().assets_lended.remove(&asset_address); +fn disallow_lending>(instance: &mut T, asset_address: AccountId) { + let share_address = instance + .get_mut() + .asset_shares + .get(&asset_address) + .unwrap_or(ZERO_ADDRESS.into()); + instance.get_mut().asset_shares.remove(&asset_address); + instance.get_mut().shares_asset.remove(&share_address); + instance.get_mut().assets_lended.remove(&asset_address); } /// this function will accept `asset_address` for using as collateral -fn set_collateral_accepted(instance: &mut T, asset_address: AccountId, accepted: bool) { - instance.get_mut().collateral_accepted.insert(&asset_address, &accepted); +fn set_collateral_accepted>( + instance: &mut T, + asset_address: AccountId, + accepted: bool, +) { + instance.get_mut().collateral_accepted.insert(&asset_address, &accepted); } ``` @@ -485,6 +500,7 @@ use crate::traits::{ }, shares::SharesRef, }; +use ink_prelude::vec::Vec; use openbrush::{ contracts::{ pausable::*, @@ -502,11 +518,10 @@ use openbrush::{ ZERO_ADDRESS, }, }; -use ink_prelude::vec::Vec; pub const YEAR: Timestamp = 60 * 60 * 24 * 365; -impl Lending for T { +impl + PausableStorage> Lending for T { default fn total_asset(&self, asset_address: AccountId) -> Result { // get asset from mapping let mapped_asset = LendingStorage::get(self) @@ -610,7 +625,7 @@ impl Lending for T { let reserve_asset = get_reserve_asset(self, &asset_address)?; // we will find out the price of deposited collateral - let price = get_asset_price(self, amount, collateral_address, asset_address); + let price = get_asset_price(self, &amount, &collateral_address, &asset_address); // we will set the liquidation price to be 75% of current price let liquidation_price = (price * 75) / 100; // borrow amount is 70% of collateral @@ -625,9 +640,9 @@ impl Lending for T { } // we will transfer the collateral to the contract PSP22Ref::transfer_from_builder(&collateral_address, borrower, contract, amount, Vec::::new()) - .call_flags(ink_env::CallFlags::default().set_allow_reentry(true)) - .fire() - .unwrap()?; + .call_flags(ink_env::CallFlags::default().set_allow_reentry(true)) + .fire() + .unwrap()?; // create loan info let loan_info = LoanInfo { borrower, @@ -656,12 +671,12 @@ impl Lending for T { let loan_account = LendingStorage::get(self).loan_account; let apy = 1000; // initiator must own the nft - if LoanRef::owner_of(&loan_account, loan_id).unwrap_or(ZERO_ADDRESS.into()) != initiator { + if LoanRef::owner_of(&loan_account, loan_id.clone()).unwrap_or(ZERO_ADDRESS.into()) != initiator { return Err(LendingError::NotTheOwner) } - let loan_info = LoanRef::get_loan_info(&loan_account, loan_id)?; + let loan_info = LoanRef::get_loan_info(&loan_account, loan_id.clone())?; if loan_info.liquidated { - LoanRef::delete_loan(&loan_account, initiator, loan_id)?; + LoanRef::delete_loan(&loan_account, initiator, loan_id.clone())?; return Ok(false) } @@ -679,9 +694,9 @@ impl Lending for T { let reserve_asset = get_reserve_asset(self, &loan_info.borrow_token)?; if repay_amount >= to_repay { PSP22Ref::transfer_from_builder(&loan_info.borrow_token, initiator, contract, to_repay, Vec::::new()) - .call_flags(ink_env::CallFlags::default().set_allow_reentry(true)) - .fire() - .unwrap()?; + .call_flags(ink_env::CallFlags::default().set_allow_reentry(true)) + .fire() + .unwrap()?; PSP22Ref::transfer( &loan_info.collateral_token, initiator, @@ -689,7 +704,7 @@ impl Lending for T { Vec::::new(), )?; LoanRef::delete_loan(&loan_account, initiator, loan_id)?; - SharesRef::burn(&reserve_asset, loan_info.borrow_amount)?; + SharesRef::burn(&reserve_asset, Self::env().caller(), loan_info.borrow_amount)?; } else { PSP22Ref::transfer_from_builder( &loan_info.borrow_token, @@ -710,7 +725,7 @@ impl Lending for T { )?; LoanRef::update_loan( &loan_account, - loan_id, + loan_id.clone(), to_repay - repay_amount, Self::env().block_timestamp(), loan_info.collateral_amount - to_return, @@ -724,21 +739,21 @@ impl Lending for T { shares_address: AccountId, shares_amount: Balance, ) -> Result<(), LendingError> { - let withdraw_asset = get_asset_from_shares(self, shares_address)?; + let withdraw_asset = get_asset_from_shares(self, &shares_address)?; let withdraw_amount = (shares_amount * self.total_asset(withdraw_asset)?) / PSP22Ref::total_supply(&shares_address); if withdraw_amount > PSP22Ref::balance_of(&withdraw_asset, Self::env().account_id()) { return Err(LendingError::InsufficientBalanceInContract) } - SharesRef::burn_from(&shares_address, Self::env().caller(), shares_amount)?; + SharesRef::burn(&shares_address, Self::env().caller(), shares_amount)?; PSP22Ref::transfer(&withdraw_asset, Self::env().caller(), withdraw_amount, Vec::::new())?; Ok(()) } default fn liquidate_loan(&mut self, loan_id: Id) -> Result<(), LendingError> { let loan_account = LendingStorage::get(self).loan_account; - let loan_info = LoanRef::get_loan_info(&loan_account, loan_id)?; + let loan_info = LoanRef::get_loan_info(&loan_account, loan_id.clone())?; if loan_info.liquidated { return Err(LendingError::LoanLiquidated) @@ -746,9 +761,9 @@ impl Lending for T { let price = get_asset_price( self, - loan_info.collateral_amount, - loan_info.collateral_token, - loan_info.borrow_token, + &loan_info.collateral_amount, + &loan_info.collateral_token, + &loan_info.borrow_token, ); if price <= loan_info.liquidation_price { @@ -762,11 +777,12 @@ impl Lending for T { reward, Vec::::new(), )?; - LoanRef::liquidate_loan(&loan_account, loan_id)?; + LoanRef::liquidate_loan(&loan_account, loan_id.clone())?; } else { return Err(LendingError::CanNotBeLiquidated) } Ok(()) } } + ``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/loan.md b/docs/docs/smart-contracts/example/loan.md index c52dc4d64..e01ec760c 100644 --- a/docs/docs/smart-contracts/example/loan.md +++ b/docs/docs/smart-contracts/example/loan.md @@ -33,6 +33,10 @@ so it is defined in the `traits` instead of the body of the contract). `LoanRef` can be used by other developers to do a cross contract call to `LoanContract`. ```rust +use ink_storage::traits::{ + PackedLayout, + SpreadLayout, +}; use openbrush::{ contracts::traits::{ ownable::*, @@ -47,10 +51,6 @@ use openbrush::{ Timestamp, }, }; -use ink_storage::traits::{ - PackedLayout, - SpreadLayout, -}; #[cfg(feature = "std")] use ink_storage::traits::StorageLayout; @@ -99,6 +99,7 @@ pub trait Loan: PSP34 + PSP34Metadata + Ownable { #[ink(message)] fn get_loan_info(&self, loan_id: Id) -> Result; } + ``` ## Add dependencies @@ -121,9 +122,13 @@ so we will add these to our contract. We will add a `openbrush::contract` macro /// This contract will represent the loan of a user #[openbrush::contract] pub mod loan { - use openbrush::contracts::{ - ownable::*, - psp34::extensions::metadata::*, + use ink_storage::traits::SpreadAllocate; + use openbrush::{ + contracts::{ + ownable::*, + psp34::extensions::metadata::*, + }, + storage::Mapping, }; use openbrush::modifiers; @@ -132,10 +137,6 @@ pub mod loan { string::String, vec::Vec, }; - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; use lending_project::traits::loan::*; ``` @@ -204,7 +205,7 @@ impl Loan for LoanContract { #[modifiers(only_owner)] #[ink(message)] fn delete_loan(&mut self, initiator: AccountId, loan_id: Id) -> Result<(), PSP34Error> { - self.loan_info.take(&loan_id); + self.loan_info.remove(&loan_id); self._burn_from(initiator, loan_id) } @@ -232,7 +233,7 @@ impl Loan for LoanContract { if loan_info.is_none() { return Err(PSP34Error::Custom(String::from("Loan does not exist"))) } - Ok(loan_info.cloned().unwrap()) + Ok(loan_info.unwrap()) } } ``` @@ -247,7 +248,7 @@ We will also add several helper functions. ```rust impl LoanContract { /// constructor with name and symbol - #[ink(constructor)] + #[ink(constructor, payable)] pub fn new() -> Self { ink_lang::codegen::initialize_contract(|instance: &mut LoanContract| { instance.last_loan_id = Id::U8(1u8); @@ -274,7 +275,7 @@ impl LoanContract { return Err(PSP34Error::Custom(String::from("This loan does not exist!"))) } - let mut loan_info = loan_info.cloned().unwrap(); + let mut loan_info = loan_info.unwrap(); loan_info.collateral_amount = new_collateral_amount; loan_info.borrow_amount = new_borrow_amount; loan_info.timestamp = new_timestamp; @@ -292,7 +293,7 @@ impl LoanContract { return Err(PSP34Error::Custom(String::from("This loan does not exist!"))) } - let mut loan_info = loan_info.cloned().unwrap(); + let mut loan_info = loan_info.unwrap(); loan_info.liquidated = true; self.loan_info.insert(&loan_id, &loan_info); @@ -305,21 +306,17 @@ impl LoanContract { if self.freed_ids.len() > 0 { return Ok(self.freed_ids.pop().unwrap()) } - let mut current = self.last_loan_id; + let current = self.last_loan_id.clone(); // It is not fully correct implementation of the increasing. but it is only an example - for n in 0..32 { - if current[n] == u8::MAX { - if n == 31 { + match current { + Id::U8(v) => { + if v == u8::MAX { return Err(PSP34Error::Custom(String::from("Max Id reached!"))) - } else { - current[n] = 0; } - } else { - current[n] += 1; - break + self.last_loan_id = Id::U8(v + 1); } - } - self.last_loan_id = current; + _ => {} + }; Ok(current) } } diff --git a/docs/docs/smart-contracts/overview.md b/docs/docs/smart-contracts/overview.md index 98e62dfd7..9067e2c97 100644 --- a/docs/docs/smart-contracts/overview.md +++ b/docs/docs/smart-contracts/overview.md @@ -79,15 +79,18 @@ Also, that doc contains links to the examples of how to reuse and customize the * [PSP34Metadata](PSP34/Extensions/metadata.md): metadata for PSP34. * [PSP34Mintable](PSP34/Extensions/mintable.md): creation of new tokens. * [PSP34Burnable](PSP34/Extensions/burnable.md): destruction of own tokens. + * [PSP34Enumerable](PSP34/Extensions/enumerable.md): iterating over contract's tokens. * [PSP35](PSP35/psp35.md) is an example of how you can reuse the implementation of [psp35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35). You also can find examples of how to reuse extensions. * [PSP35Metadata](PSP35/Extensions/metadata.md): metadata for PSP35. * [PSP35Mintable](PSP35/Extensions/mintable.md): creation of new tokens. * [PSP35Burnable](PSP35/Extensions/burnable.md): destruction of own tokens. - * [PSP35Batch](PSP35/Extensions/batch.md): batch transferring of tokens + * [PSP35Batch](PSP35/Extensions/batch.md): batch transferring of tokens. + * [PSP35Enumerable](PSP35/Extensions/enumerable.md): iterating over contract's tokens. * [Access Control](access-control.md) shows how you can use the implementation of [access-control](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/access/access_control) and [psp34](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/token/psp34) together to provide rights to mint and burn NFT tokens. + * [AccessControlEnumerable](PSP34/Extensions/enumerable.md): iterating over contract's tokens. * [Ownable](ownable.md) shows how you can use the implementation of [ownable](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/access/ownable) and [psp35](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/token/psp35) together to provide rights to mint and burn tokens. @@ -102,4 +105,11 @@ Also, that doc contains links to the examples of how to reuse and customize the to execute a transaction with some delay via governance. * [PaymentSplitter](payment-splitter.md) shows how you can use the implementation of [payment-splitter](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/finance/payment_splitter) - to split received native tokens between participants of the contract. \ No newline at end of file + to split received native tokens between participants of the contract. +* [Diamond](diamond.md) shows how you can use the implementation of + [diamond](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/upgradability/diamond) + pattern to split your contract into small parts and support upgradability. + * [DiamondLoupe](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/upgradability/diamond/extensions): iterating over contract's facets. +* [Proxy](proxy.md) shows how you can use the implementation of + [proxy](https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/upgradability/proxy) + pattern to support upgradability of your contract. \ No newline at end of file diff --git a/example_project_structure/contracts/loan/lib.rs b/example_project_structure/contracts/loan/lib.rs index 437ea06f2..5dfa56d59 100644 --- a/example_project_structure/contracts/loan/lib.rs +++ b/example_project_structure/contracts/loan/lib.rs @@ -4,13 +4,13 @@ /// This contract will represent the loan of a user #[openbrush::contract] pub mod loan { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - use openbrush::contracts::{ - ownable::*, - psp34::extensions::metadata::*, + use ink_storage::traits::SpreadAllocate; + use openbrush::{ + contracts::{ + ownable::*, + psp34::extensions::metadata::*, + }, + storage::Mapping, }; use openbrush::modifiers; diff --git a/example_project_structure/impls/lending/data.rs b/example_project_structure/impls/lending/data.rs index 56df37564..092979a93 100644 --- a/example_project_structure/impls/lending/data.rs +++ b/example_project_structure/impls/lending/data.rs @@ -1,15 +1,16 @@ // importing everything publicly from traits allows you to import every stuff related to lending // by one import pub use crate::traits::lending::*; -use ink_storage::{ - traits::{ - SpreadAllocate, - SpreadLayout, - }, - Mapping, +use ink_storage::traits::{ + SpreadAllocate, + SpreadLayout, }; use openbrush::{ declare_storage_trait, + storage::{ + Mapping, + TypeGuard, + }, traits::{ AccountId, AccountIdExt, @@ -49,44 +50,46 @@ pub struct LendingData { /// when we deposit 1 unit of tuple.0 /// we are using this just to simulate an oracle in our example /// in the example the returned balance will be amount of stable coin for an asset - pub asset_price: Mapping<(AccountId, AccountId), Balance>, + pub asset_price: Mapping<(AccountId, AccountId), Balance, AssetPriceKey>, /// code hash of the `SharesContract` pub shares_contract_code_hash: Hash, /// the `AccountId` of the `Loan` pub loan_account: AccountId, } +pub struct AssetPriceKey; + +impl<'a> TypeGuard<'a> for AssetPriceKey { + type Type = &'a (&'a AccountId, &'a AccountId); +} + declare_storage_trait!(LendingStorage); /// this internal function will be used to set price of `asset_in` when we deposit `asset_out` /// we are using this function in our example to simulate an oracle -pub fn set_asset_price>( - instance: &mut T, - asset_in: AccountId, - asset_out: AccountId, - price: Balance, -) { - instance.get_mut().asset_price.insert((&asset_in, &asset_out), &price); +pub fn set_asset_price(instance: &mut T, asset_in: &AccountId, asset_out: &AccountId, price: &Balance) +where + T: LendingStorage, +{ + instance.get_mut().asset_price.insert(&(asset_in, asset_out), price); } /// this internal function will be used to set price of `asset_in` when we deposit `asset_out` /// we are using this function in our example to simulate an oracle -pub fn get_asset_price>( - instance: &T, - amount_in: Balance, - asset_in: AccountId, - asset_out: AccountId, -) -> Balance { - let price = instance.get().asset_price.get((&asset_in, &asset_out)).unwrap_or(0); +pub fn get_asset_price(instance: &T, amount_in: &Balance, asset_in: &AccountId, asset_out: &AccountId) -> Balance +where + T: LendingStorage, +{ + let price = instance.get().asset_price.get(&(asset_in, asset_out)).unwrap_or(0); price * amount_in } /// Internal function which will return the address of the shares token /// which are minted when `asset_address` is borrowed -pub fn get_reserve_asset>( - instance: &T, - asset_address: &AccountId, -) -> Result { +pub fn get_reserve_asset(instance: &T, asset_address: &AccountId) -> Result +where + T: LendingStorage, +{ let reserve_asset = instance .get() .asset_shares @@ -100,14 +103,14 @@ pub fn get_reserve_asset>( /// internal function which will return the address of asset /// which is bound to `shares_address` shares token -pub fn get_asset_from_shares>( - instance: &T, - shares_address: AccountId, -) -> Result { +pub fn get_asset_from_shares(instance: &T, shares_address: &AccountId) -> Result +where + T: LendingStorage, +{ let token = instance .get() .shares_asset - .get(&shares_address) + .get(shares_address) .unwrap_or(ZERO_ADDRESS.into()); if token.is_zero() { return Err(LendingError::AssetNotSupported) diff --git a/example_project_structure/impls/lending/lending.rs b/example_project_structure/impls/lending/lending.rs index b7ce7bf8c..67ca475a2 100644 --- a/example_project_structure/impls/lending/lending.rs +++ b/example_project_structure/impls/lending/lending.rs @@ -134,7 +134,7 @@ impl + PausableStorage + PausableStorage Result<(), LendingError> { - let withdraw_asset = get_asset_from_shares(self, shares_address)?; + let withdraw_asset = get_asset_from_shares(self, &shares_address)?; let withdraw_amount = (shares_amount * self.total_asset(withdraw_asset)?) / PSP22Ref::total_supply(&shares_address); if withdraw_amount > PSP22Ref::balance_of(&withdraw_asset, Self::env().account_id()) { @@ -270,9 +270,9 @@ impl + PausableStorage Result<(), LendingError> { - set_asset_price(self, asset_in, asset_out, price); + set_asset_price(self, &asset_in, &asset_out, &price); Ok(()) } } diff --git a/examples/access_control_enumerable/.gitignore b/examples/access_control_extensions/enumerable/.gitignore similarity index 100% rename from examples/access_control_enumerable/.gitignore rename to examples/access_control_extensions/enumerable/.gitignore diff --git a/examples/access_control_enumerable/Cargo.toml b/examples/access_control_extensions/enumerable/Cargo.toml similarity index 93% rename from examples/access_control_enumerable/Cargo.toml rename to examples/access_control_extensions/enumerable/Cargo.toml index 6a6733682..89f5d6b58 100644 --- a/examples/access_control_enumerable/Cargo.toml +++ b/examples/access_control_extensions/enumerable/Cargo.toml @@ -17,7 +17,7 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } # These dependencies -openbrush = { path = "../..", default-features = false, features = ["access_control"] } +openbrush = { path = "../../..", default-features = false, features = ["access_control"] } [lib] name = "my_access_control_enumerable" diff --git a/examples/access_control_enumerable/lib.rs b/examples/access_control_extensions/enumerable/lib.rs similarity index 92% rename from examples/access_control_enumerable/lib.rs rename to examples/access_control_extensions/enumerable/lib.rs index 64a0b5aca..045a4c04d 100644 --- a/examples/access_control_enumerable/lib.rs +++ b/examples/access_control_extensions/enumerable/lib.rs @@ -4,10 +4,7 @@ #[openbrush::contract] pub mod my_access_control { use ink_storage::traits::SpreadAllocate; - use openbrush::contracts::{ - access_control::*, - access_control_enumerable::*, - }; + use openbrush::contracts::access_control::extensions::enumerable::*; #[ink(storage)] #[derive(Default, SpreadAllocate, AccessControlStorage)] diff --git a/examples/psp35/lib.rs b/examples/psp35/lib.rs index e0f0c3912..1ebd26425 100644 --- a/examples/psp35/lib.rs +++ b/examples/psp35/lib.rs @@ -7,11 +7,11 @@ pub mod my_psp35 { string::String, vec, }; - use ink_storage::{ - traits::SpreadAllocate, - Mapping, + use ink_storage::traits::SpreadAllocate; + use openbrush::{ + contracts::psp35::*, + storage::Mapping, }; - use openbrush::contracts::psp35::*; #[derive(Default, SpreadAllocate, PSP35Storage)] #[ink(storage)] diff --git a/tests/access_control_enumerable.rs b/tests/access_control_enumerable.rs index 31ba2ae0c..0b6f7daea 100644 --- a/tests/access_control_enumerable.rs +++ b/tests/access_control_enumerable.rs @@ -29,7 +29,7 @@ mod access_control_enumerable { use ink_storage::traits::SpreadAllocate; use openbrush::{ - contracts::access_control_enumerable::*, + contracts::access_control::extensions::enumerable::*, test_utils::accounts, }; diff --git a/tests/e2e/access-control.tests.ts b/tests/e2e/access-control/access-control.tests.ts similarity index 98% rename from tests/e2e/access-control.tests.ts rename to tests/e2e/access-control/access-control.tests.ts index 6c6ab3013..ce3a998d2 100644 --- a/tests/e2e/access-control.tests.ts +++ b/tests/e2e/access-control/access-control.tests.ts @@ -1,6 +1,6 @@ -import { bnArg, expect, fromSigner, setupContract } from './helpers' +import { bnArg, expect, fromSigner, setupContract } from '../helpers' -import { Roles } from './constants' +import { Roles } from '../constants' describe('MY_ACCESS_CONTROL', () => { async function setup() { diff --git a/tests/e2e/access-control-enumerable.tests.ts b/tests/e2e/access-control/extensions/enumerable.tests.ts similarity index 95% rename from tests/e2e/access-control-enumerable.tests.ts rename to tests/e2e/access-control/extensions/enumerable.tests.ts index 06e934f5f..d50fdab49 100644 --- a/tests/e2e/access-control-enumerable.tests.ts +++ b/tests/e2e/access-control/extensions/enumerable.tests.ts @@ -1,6 +1,6 @@ -import { expect, setupContract } from './helpers' +import { expect, setupContract } from '../../helpers' -import { Roles } from './constants' +import { Roles } from '../../constants' describe('MY_ACCESS_CONTROL_ENUMERABLE', () => { async function setup() {