diff --git a/content/stellar-contracts/access/access-control.mdx b/content/stellar-contracts/access/access-control.mdx index 06682f86..a1fdde80 100644 --- a/content/stellar-contracts/access/access-control.mdx +++ b/content/stellar-contracts/access/access-control.mdx @@ -46,48 +46,54 @@ Roles exist only through their relationships with accounts, so a role with zero Here’s a simple example of using the Access Control module: ```rust -use soroban_sdk::contract, contractimpl, symbol_short, Address, Env; -use stellar_access::access_control::self as access_control, AccessControl; -use stellar_macros::has_role, only_admin; +use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env}; +use stellar_access::access_control::{self as access_control, AccessControl}; +use stellar_macros::{has_role, only_admin}; #[contract] pub struct MyContract; #[contractimpl] -impl MyContract - pub fn __constructor(e: &Env, admin: Address) +impl MyContract { + pub fn __constructor(e: &Env, admin: Address) { // Set the contract admin access_control::set_admin(e, &admin); // Create a "minter" role with admin as its admin access_control::set_role_admin_no_auth(e, &symbol_short!("minter"), &symbol_short!("admin")); - + } + #[only_admin] - pub fn admin_restricted_function(e: &Env) -> Vec + pub fn admin_restricted_function(e: &Env) -> Vec { vec![&e, String::from_str(e, "seems sus")] - + } + // we want `require_auth()` provided by the macro, since there is no // `require_auth()` in `Base::mint`. #[only_role(caller, "minter")] - pub fn mint(e: &Env, caller: Address, to: Address, token_id: u32) + pub fn mint(e: &Env, caller: Address, to: Address, token_id: u32) { Base::mint(e, &to, token_id) - + + } + // allows either minter or burner role, does not enforce `require_auth` in the macro #[has_any_role(caller, ["minter", "burner"])] - pub fn multi_role_action(e: &Env, caller: Address) -> String + pub fn multi_role_action(e: &Env, caller: Address) -> String { caller.require_auth(); String::from_str(e, "multi_role_action_success") - + } + // allows either minter or burner role AND enforces `require_auth` in the macro #[only_any_role(caller, ["minter", "burner"])] - pub fn multi_role_auth_action(e: &Env, caller: Address) -> String - String::from_str(e, "multi_role_auth_action_success") - + pub fn multi_role_auth_action(e: &Env, caller: Address) -> String { + String::from_str(e, "multi_role_auth_action_success") + } +} ``` ## Benefits and Trade-offs diff --git a/content/stellar-contracts/access/ownable.mdx b/content/stellar-contracts/access/ownable.mdx index b713e700..907e11c7 100644 --- a/content/stellar-contracts/access/ownable.mdx +++ b/content/stellar-contracts/access/ownable.mdx @@ -29,17 +29,13 @@ This feature is useful for contracts that need to become fully decentralized aft ### Procedural Macro -The module includes a procedural macro to simplify owner authorization checks: - -#### @only_owner - -Ensures the caller is the owner before executing the function: +The module includes the `#[only_owner]` procedural macro to simplify authorization checks by ensuring the owner is properly authorized. ```rust #[only_owner] -pub fn restricted_function(e: &Env, other_param: u32) +pub fn restricted_function(e: &Env, other_param: u32) { // Function body - only accessible to owner - +} ``` This expands to code that retrieves the owner from storage and requires authorization before executing the function body. @@ -49,32 +45,32 @@ This expands to code that retrieves the owner from storage and requires authoriz Here’s a simple example of using the Ownable module: ```rust -use soroban_sdk::contract, contractimpl, Address, Env; -use stellar_access::ownable::self as ownable, Ownable; +use soroban_sdk::{contract, contractimpl, Address, Env}; +use stellar_access::ownable::{self as ownable, Ownable}; use stellar_macros::only_owner; #[contract] pub struct MyContract; #[contractimpl] -impl MyContract - pub fn __constructor(e: &Env, initial_owner: Address) +impl MyContract { + pub fn __constructor(e: &Env, initial_owner: Address) { // Set the contract owner ownable::set_owner(e, &initial_owner); - + } #[only_owner] - pub fn update_config(e: &Env, new_value: u32) + pub fn update_config(e: &Env, new_value: u32) { // Only the owner can call this function // Implementation... - + } // This function is accessible to anyone - pub fn get_config(e: &Env) -> u32 + pub fn get_config(e: &Env) -> u32 { // Implementation... 42 - - + } +} ``` ## Benefits and Trade-offs diff --git a/content/stellar-contracts/helpers/default-impl-macro.mdx b/content/stellar-contracts/helpers/default-impl-macro.mdx index 1b1ea059..0733f90a 100644 --- a/content/stellar-contracts/helpers/default-impl-macro.mdx +++ b/content/stellar-contracts/helpers/default-impl-macro.mdx @@ -43,17 +43,17 @@ developers carefully consider and explicitly define their authorization logic ra ## Usage -To use the `[default_impl]` macro, place it above the `[contractimpl]` macro when implementing one of the supported traits: +To use the `#[default_impl]` macro, place it above the `#[contractimpl]` macro when implementing one of the supported traits: ```rust #[default_impl] // IMPORTANT: place this above `#[contractimpl]` #[contractimpl] -impl NonFungibleToken for MyContract +impl NonFungibleToken for MyContract { type ContractType = Base; // Only override the methods you need to customize // All other methods will be automatically implemented with their default behavior - +} ``` ## How It Works @@ -73,7 +73,7 @@ This process ensures that all trait methods are available to the client generate ### Fungible Token Example ```rust -use soroban_sdk::contract, contractimpl, Address, Env; +use soroban_sdk::{contract, contractimpl, Address, Env}; use stellar_tokens::fungible::FungibleToken; use stellar_macros::default_impl; @@ -82,14 +82,14 @@ pub struct MyToken; #[default_impl] #[contractimpl] -impl FungibleToken for MyToken +impl FungibleToken for MyToken { type ContractType = Base; // Only override methods that need custom behavior - fn transfer(e: &Env, from: Address, to: Address, amount: i128) + fn transfer(e: &Env, from: Address, to: Address, amount: i128) { // custom transfer logic here - + } // All other FungibleToken methods will be automatically implemented - +} ``` diff --git a/content/stellar-contracts/index.mdx b/content/stellar-contracts/index.mdx index 71d1ed93..3f98ed9a 100644 --- a/content/stellar-contracts/index.mdx +++ b/content/stellar-contracts/index.mdx @@ -2,21 +2,28 @@ title: Stellar Smart Contracts Suite --- -A comprehensive collection of secure, scalable smart contracts and utilities for the Stellar network, -supporting Fungible, Non-Fungible, and Multi-Token standards. +Explore our comprehensive suite of secure and scalable smart contract utilities for Stellar Soroban. Our libraries provide robust implementations for fungible and non-fungible tokens, along with powerful tools for access control and contract management. ## Tokens -Explore our implementations for token standards on Stellar Soroban: -* ***[Fungible Tokens](/stellar-contracts/tokens/fungible/fungible)***: Digital assets representing a fixed or dynamic supply of identical units. -* ***[Non-Fungible Tokens](/stellar-contracts/tokens/non-fungible/non-fungible)***: Unique digital assets with verifiable ownership. -* ***Multi-Token***: Hybrid tokens enabling both fungible and non-fungible token functionalities (work in progress). +* **[Fungible Tokens](/stellar-contracts/tokens/fungible/fungible)**: Digital assets representing a fixed or dynamic supply of identical units. +* **[Non-Fungible Tokens (NFTs)](/stellar-contracts/tokens/non-fungible/non-fungible)**: Unique digital assets with verifiable ownership. +* **Multi-Tokens**: Hybrid tokens enabling both fungible and non-fungible token functionalities (work in progress). + +## Access Control + +* **[Ownable](/stellar-contracts/access/ownable)**: A simple mechanism with a single account authorized for all privileged actions. +* **[Role-Based Access Control](/stellar-contracts/access/access-control)**: A flexible mechanism with distinct roles for each privileged action. ## Utilities -Discover our utility contracts for Stellar Soroban, applicable to all token standards mentioned above: -* ***[Pausable](/stellar-contracts/utils/pausable)*** -* ***[Upgrades and Migrations](/stellar-contracts/utils/upgradeable)*** +* **[Pausable](/stellar-contracts/utils/pausable)**: Pause and unpause contract functions, useful for emergency response. +* **[Upgradeable](/stellar-contracts/utils/upgradeable)**: Manage contract upgrades and data migrations seamlessly. +* **[Cryptography](/stellar-contracts/utils/crypto)**: A set of cryptographic primitives and utilities for Soroban contracts. + +## Security and Audits + +Our contracts are built with security as a top priority. You can find our audit reports [here](https://github.com/OpenZeppelin/stellar-contracts/tree/main/audits). ## Error Codes In Stellar Soroban, each error variant is assigned an integer. To prevent duplication of error codes, diff --git a/content/stellar-contracts/tokens/fungible/fungible.mdx b/content/stellar-contracts/tokens/fungible/fungible.mdx index 51ab8708..c3d99493 100644 --- a/content/stellar-contracts/tokens/fungible/fungible.mdx +++ b/content/stellar-contracts/tokens/fungible/fungible.mdx @@ -31,17 +31,17 @@ and players can transfer tokens between accounts. Here’s what a basic fungible token contract might look like: ```rust -use soroban_sdk::contract, contractimpl, Address, Env, String; -use stellar_tokens::fungible::burnable::FungibleBurnable, Base, ContractOverrides, FungibleToken; -use stellar_access::ownable::self as ownable, Ownable; -use stellar_macros::default_impl, only_owner; +use soroban_sdk::{contract, contractimpl, Address, Env, String}; +use stellar_tokens::fungible::{burnable::FungibleBurnable, Base, ContractOverrides, FungibleToken}; +use stellar_access::ownable::{self as ownable, Ownable}; +use stellar_macros::{default_impl, only_owner}; #[contract] pub struct GameCurrency; #[contractimpl] -impl GameCurrency - pub fn __constructor(e: &Env, initial_owner: Address) +impl GameCurrency { + pub fn __constructor(e: &Env, initial_owner: Address) { // Set token metadata Base::set_metadata( e, @@ -52,24 +52,24 @@ impl GameCurrency // Set the contract owner ownable::set_owner(e, &initial_owner); - + } #[only_owner] - pub fn mint_tokens(e: &Env, to: Address, amount: i128) + pub fn mint_tokens(e: &Env, to: Address, amount: i128) { // Mint tokens to the recipient Base::mint(e, &to, amount); - - + } +} #[default_impl] #[contractimpl] -impl FungibleToken for GameCurrency +impl FungibleToken for GameCurrency { type ContractType = Base; - +} #[default_impl] #[contractimpl] -impl FungibleBurnable for GameCurrency +impl FungibleBurnable for GameCurrency {} ``` ## Extensions @@ -103,12 +103,6 @@ The `FungibleBlockList` trait extends the `FungibleToken` trait to provide a blo can be managed by an authorized account. This extension ensures that blocked accounts cannot transfer/receive tokens, or approve token transfers. -### TokenInterface Macro - -For contracts that implement both `FungibleToken` and `FungibleBurnable` and also need to implement -`soroban_sdk::token::TokenInterface`, we provide the `impl_token_interface!` macro. This macro automatically -generates the required boilerplate, simplifying the implementation process. - ## Utility Modules The package includes utility modules to help with common token implementation patterns: diff --git a/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx b/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx index b7ac5e4d..f423c6e3 100644 --- a/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx +++ b/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx @@ -39,44 +39,44 @@ Here’s a simplified example of a SAC Admin Generic contract: #[contracterror] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[repr(u32)] -pub enum SACAdminGenericError +pub enum SACAdminGenericError { Unauthorized = 1, InvalidContext = 2, MintingLimitExceeded = 3, - +} #[contracttype] #[derive(Clone)] -pub struct Signature +pub struct Signature { pub public_key: BytesN<32>, pub signature: BytesN<64>, - +} #[contracttype] -pub enum SacDataKey +pub enum SacDataKey { Chief, Operator(BytesN<32>), // -> true/false MintingLimit(BytesN<32>), // -> (max_limit, curr) - +} #[contract] pub struct SacAdminExampleContract; #[contractimpl] -impl SacAdminExampleContract - pub fn __constructor(e: Env, sac: Address, chief: BytesN<32>, operator: BytesN<32>) +impl SacAdminExampleContract { + pub fn __constructor(e: Env, sac: Address, chief: BytesN<32>, operator: BytesN<32>) { set_sac_address(&e, &sac); e.storage().instance().set(&SacDataKey::Chief, &chief); e.storage().instance().set(&SacDataKey::Operator(operator.clone()), &true); e.storage() .instance() .set(&SacDataKey::MintingLimit(operator), &(1_000_000_000i128, 0i128)); - + } - pub fn get_sac_address(e: &Env) -> Address + pub fn get_sac_address(e: &Env) -> Address { get_sac_address(e) - - + } +} ``` ### Custom Authorization Logic @@ -85,15 +85,15 @@ The key feature of the Generic approach is the ability to implement custom autho function: ```rust -use soroban_sdk:: +use soroban_sdk::{ auth::Context, CustomAccountInterface, contract, contracterror, contractimpl, contracttype, crypto::Hash, Address, BytesN, Env, IntoVal, Val, Vec, -; +}; #[contractimpl] -impl CustomAccountInterface for SacAdminExampleContract +impl CustomAccountInterface for SacAdminExampleContract { type Error = SACAdminGenericError; type Signature = Signature; @@ -102,7 +102,7 @@ impl CustomAccountInterface for SacAdminExampleContract payload: Hash<32>, signature: Self::Signature, auth_context: Vec, - ) -> Result<(), SACAdminGenericError> + ) -> Result<(), SACAdminGenericError> { // authenticate e.crypto().ed25519_verify( &signature.public_key, @@ -112,64 +112,64 @@ impl CustomAccountInterface for SacAdminExampleContract let caller = signature.public_key.clone(); // extract from context and check required permissions for every function - for ctx in auth_context.iter() - let context = match ctx + for ctx in auth_context.iter() { + let context = match ctx { Context::Contract(c) => c, _ => return Err(SACAdminGenericError::InvalidContext), - ; + }; - match extract_sac_contract_context(&e, &context) - SacFn::Mint(amount) => + match extract_sac_contract_context(&e, &context) { + SacFn::Mint(amount) => { // ensure caller has required permissions ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?; // ensure operator has minting limit ensure_minting_limit(&e, &caller, amount)?; - - SacFn::Clawback(_amount) => + } + SacFn::Clawback(_amount) => { // ensure caller has required permissions ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?; - - SacFn::SetAuthorized(_) => + } + SacFn::SetAuthorized(_) => { // ensure caller has required permissions ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?; - - SacFn::SetAdmin => + } + SacFn::SetAdmin => { // ensure caller has required permissions ensure_caller_chief(&e, &caller, &SacDataKey::Chief)?; - - SacFn::Unknown => + } + SacFn::Unknown => { // ensure only chief can call other functions ensure_caller_chief(&e, &caller, &SacDataKey::Chief)? - - - + } + } + } Ok(()) - - + } +} // Helper functions fn ensure_caller_chief>( e: &Env, caller: &BytesN<32>, key: &K, -) -> Result<(), SACAdminGenericError> +) -> Result<(), SACAdminGenericError> { let operator: BytesN<32> = e.storage().instance().get(key).expect("chief or operator not set"); - if *caller != operator + if *caller != operator { return Err(SACAdminGenericError::Unauthorized); - + } Ok(()) - +} fn ensure_caller_operator>( e: &Env, key: &K, -) -> Result<(), SACAdminGenericError> - match e.storage().instance().get::<_, bool>(key) +) -> Result<(), SACAdminGenericError> { + match e.storage().instance().get::<_, bool>(key) { Some(is_op) if is_op => Ok(()), _ => Err(SACAdminGenericError::Unauthorized), - - + } +} ``` ## Benefits and Trade-offs diff --git a/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx b/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx index 63eecd4f..9bec9a6f 100644 --- a/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx +++ b/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx @@ -29,12 +29,12 @@ The Wrapper approach to SAC Admin implementation: The `SACAdminWrapper` trait defines the interface for the wrapper approach: ```rust -pub trait SACAdminWrapper +pub trait SACAdminWrapper { fn set_admin(e: Env, new_admin: Address, operator: Address); fn set_authorized(e: Env, id: Address, authorize: bool, operator: Address); fn mint(e: Env, to: Address, amount: i128, operator: Address); fn clawback(e: Env, from: Address, amount: i128, operator: Address); - +} ``` ### Example Implementation @@ -46,14 +46,14 @@ Here’s a simplified example of a SAC Admin Wrapper contract using the OpenZepp pub struct ExampleContract; #[contractimpl] -impl ExampleContract +impl ExampleContract { pub fn __constructor( e: &Env, default_admin: Address, manager1: Address, manager2: Address, sac: Address, - ) + ) { access_control::set_admin(e, &default_admin); // create a role "manager" and grant it to `manager1` @@ -63,31 +63,31 @@ impl ExampleContract access_control::grant_role_no_auth(e, &default_admin, &manager2, &symbol_short!("manager")); fungible::sac_admin_wrapper::set_sac_address(e, &sac); - - + } +} #[contractimpl] -impl SACAdminWrapper for ExampleContract +impl SACAdminWrapper for ExampleContract { #[only_admin] - fn set_admin(e: Env, new_admin: Address, _operator: Address) + fn set_admin(e: Env, new_admin: Address, _operator: Address) { fungible::sac_admin_wrapper::set_admin(&e, &new_admin); - + } #[only_role(operator, "manager")] - fn set_authorized(e: Env, id: Address, authorize: bool, operator: Address) + fn set_authorized(e: Env, id: Address, authorize: bool, operator: Address) { fungible::sac_admin_wrapper::set_authorized(&e, &id, authorize); - + } #[only_role(operator, "manager")] - fn mint(e: Env, to: Address, amount: i128, operator: Address) + fn mint(e: Env, to: Address, amount: i128, operator: Address) { fungible::sac_admin_wrapper::mint(&e, &to, amount); - + } #[only_role(operator, "manager")] - fn clawback(e: Env, from: Address, amount: i128, operator: Address) + fn clawback(e: Env, from: Address, amount: i128, operator: Address) { fungible::sac_admin_wrapper::clawback(&e, &from, amount); - - + } +} ``` ### Integration with Access Control diff --git a/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx b/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx index 653ce27c..38b7a856 100644 --- a/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx +++ b/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx @@ -16,43 +16,43 @@ to `award_items`. Please note any account can call `award_items` and we might wa implement access control to restrict who can mint. ```rust -use soroban_sdk::contract, contractimpl, Address, Env, String; +use soroban_sdk::{contract, contractimpl, Address, Env, String}; use stellar_macros::default_impl; -use stellar_tokens::non_fungible:: - consecutive::Consecutive, NonFungibleConsecutive, +use stellar_tokens::non_fungible::{ + consecutive::{Consecutive, NonFungibleConsecutive}, Base, ContractOverrides, NonFungibleToken, -; +}; #[contract] pub struct GameItem; #[contractimpl] -impl GameItem - pub fn __constructor(e: &Env) +impl GameItem { + pub fn __constructor(e: &Env) { Base::set_metadata( e, String::from_str(e, "www.mygame.com"), String::from_str(e, "My Game Items Collection"), String::from_str(e, "MGMC"), ); - + } - pub fn award_items(e: &Env, to: Address, amount: u32) -> u32 + pub fn award_items(e: &Env, to: Address, amount: u32) -> u32 { // access control might be needed Consecutive::batch_mint(e, &to, amount) - + } - pub fn burn(e: &Env, from: Address, token_id: u32) + pub fn burn(e: &Env, from: Address, token_id: u32) { Consecutive::burn(e, &from, token_id); - - + } +} #[default_impl] #[contractimpl] -impl NonFungibleToken for GameItem +impl NonFungibleToken for GameItem { type ContractType = Consecutive; - +} // no entry-point functions required, marker impl -impl NonFungibleConsecutive for GameItem +impl NonFungibleConsecutive for GameItem {} ``` diff --git a/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx b/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx index f553602f..a37a85e1 100644 --- a/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx +++ b/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx @@ -15,46 +15,46 @@ and modify the contract so that all tokens an address own can be listed. Please can call `award_item` and we might want to implement access control to restrict who can mint. ```rust -use soroban_sdk::contract, contractimpl, Address, Env, String; +use soroban_sdk::{contract, contractimpl, Address, Env, String}; use stellar_macros::default_impl; -use stellar_tokens::non_fungible:: - enumerable::Enumerable, NonFungibleEnumerable, +use stellar_tokens::non_fungible::{ + enumerable::{Enumerable, NonFungibleEnumerable}, Base, ContractOverrides, NonFungibleToken, -; +}; #[contract] pub struct GameItem; #[contractimpl] -impl GameItem - pub fn __constructor(e: &Env) +impl GameItem { + pub fn __constructor(e: &Env) { Base::set_metadata( e, String::from_str(e, "www.mygame.com"), String::from_str(e, "My Game Items Collection"), String::from_str(e, "MGMC"), ); - + } - pub fn award_item(e: &Env, to: Address) -> u32 + pub fn award_item(e: &Env, to: Address) -> u32 { // access control might be needed Enumerable::sequential_mint(e, &to) - + } - pub fn burn(e: &Env, from: Address, token_id: u32) + pub fn burn(e: &Env, from: Address, token_id: u32) { Enumerable::sequential_burn(e, &from, token_id); - - + } +} #[default_impl] #[contractimpl] -impl NonFungibleToken for GameItem +impl NonFungibleToken for GameItem { type ContractType = Enumerable; - +} #[default_impl] #[contractimpl] -impl NonFungibleEnumerable for GameItem +impl NonFungibleEnumerable for GameItem {} ``` The extension exposes additionally the following entry-point functions, automatically implemented by `#[default_impl]`: diff --git a/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx b/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx index ff95f123..415eac81 100644 --- a/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx +++ b/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx @@ -35,42 +35,42 @@ want to implement access control to restrict who can mint. Here’s what a contract for tokenized items might look like: ```rust -use soroban_sdk::contract, contractimpl, Address, Env, String; +use soroban_sdk::{contract, contractimpl, Address, Env, String}; use stellar_macros::default_impl; -use stellar_tokens::non_fungible:: +use stellar_tokens::non_fungible::{ burnable::NonFungibleBurnable, Base, ContractOverrides, NonFungibleToken, -; +}; #[contract] pub struct GameItem; #[contractimpl] -impl GameItem - pub fn __constructor(e: &Env) +impl GameItem { + pub fn __constructor(e: &Env) { Base::set_metadata( e, String::from_str(e, "www.mygame.com"), String::from_str(e, "My Game Items Collection"), String::from_str(e, "MGMC"), ); - + } - pub fn award_item(e: &Env, to: Address) -> u32 + pub fn award_item(e: &Env, to: Address) -> u32 { // access control might be needed Base::sequential_mint(e, &to) - - + } +} #[default_impl] #[contractimpl] -impl NonFungibleToken for GameItem +impl NonFungibleToken for GameItem { type ContractType = Base; - +} #[default_impl] #[contractimpl] -impl NonFungibleBurnable for GameItem +impl NonFungibleBurnable for GameItem {} ``` ## Extensions diff --git a/content/stellar-contracts/utils/crypto.mdx b/content/stellar-contracts/utils/crypto.mdx index 1b2bd66d..b50af954 100644 --- a/content/stellar-contracts/utils/crypto.mdx +++ b/content/stellar-contracts/utils/crypto.mdx @@ -32,13 +32,13 @@ Provides a generic `Hasher` trait and implementations for common hash functions: Each hasher follows the same interface: ```rust -pub trait Hasher +pub trait Hasher { type Output; fn new(e: &Env) -> Self; fn update(&mut self, input: Bytes); fn finalize(self) -> Self::Output; - +} ``` #### Hashable @@ -46,9 +46,9 @@ pub trait Hasher The `Hashable` trait allows types to be hashed with any `Hasher` implementation: ```rust -pub trait Hashable +pub trait Hashable { fn hash(&self, hasher: &mut H); - +} ``` Built-in implementations are provided for `BytesN<32>` and `Bytes`. @@ -66,11 +66,11 @@ The `Verifier` struct provides functionality to verify Merkle proofs: impl Verifier where H: Hasher, - - pub fn verify(e: &Env, proof: Vec, root: Bytes32, leaf: Bytes32) -> bool +{ + pub fn verify(e: &Env, proof: Vec, root: Bytes32, leaf: Bytes32) -> bool { // Implementation verifies that the leaf is part of the tree defined by root - - + } +} ``` ### Usage Examples @@ -78,7 +78,7 @@ where #### Hashing Data ```rust -use soroban_sdk::Bytes, Env; +use soroban_sdk::{Bytes, Env}; use stellar_contract_utils::crypto::keccak::Keccak256; use stellar_contract_utils::crypto::hasher::Hasher; @@ -94,7 +94,7 @@ let hash = hasher.finalize(); #### Verifying a Merkle Proof ```rust -use soroban_sdk::BytesN, Env, Vec; +use soroban_sdk::{BytesN, Env, Vec}; use stellar_crypto::keccak::Keccak256; use stellar_crypto::merkle::Verifier; @@ -119,9 +119,9 @@ other assets using Merkle proofs for verification. The `IndexableLeaf` trait defines the structure for nodes in the Merkle tree: ```rust -pub trait IndexableLeaf +pub trait IndexableLeaf { fn index(&self) -> u32; - +} ``` Each node must include a unique index that identifies its position in the Merkle tree. @@ -137,49 +137,49 @@ The `MerkleDistributor` struct provides functionality for: ### Usage Example ```rust -use soroban_sdk::contract, contractimpl, contracttype, Address, BytesN, Env, Vec; +use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, Vec}; use stellar_contract_utils::crypto::keccak::Keccak256; -use stellar_contract_utils::merkle_distributor::IndexableLeaf, MerkleDistributor; +use stellar_contract_utils::merkle_distributor::{IndexableLeaf, MerkleDistributor}; // Define a leaf node structure #[contracttype] -struct LeafData +struct LeafData { pub index: u32, pub address: Address, pub amount: i128, - +} // Implement IndexableLeaf for the leaf structure -impl IndexableLeaf for LeafData - fn index(&self) -> u32 +impl IndexableLeaf for LeafData { + fn index(&self) -> u32 { self.index - - + } +} #[contract] pub struct TokenDistributor; #[contractimpl] -impl TokenDistributor +impl TokenDistributor { // Initialize the distributor with a Merkle root - pub fn initialize(e: &Env, root: BytesN<32>) + pub fn initialize(e: &Env, root: BytesN<32>) { MerkleDistributor::::set_root(e, root); - + } // Claim tokens by providing a proof - pub fn claim(e: &Env, leaf: LeafData, proof: Vec>) + pub fn claim(e: &Env, leaf: LeafData, proof: Vec>) { // Verify the proof and mark as claimed MerkleDistributor::::verify_and_set_claimed(e, leaf.clone(), proof); // Transfer tokens or perform other actions based on leaf data // ... - + } // Check if an index has been claimed - pub fn is_claimed(e: &Env, index: u32) -> bool + pub fn is_claimed(e: &Env, index: u32) -> bool { MerkleDistributor::::is_claimed(e, index) - - + } +} ``` ## Use Cases diff --git a/content/stellar-contracts/utils/pausable.mdx b/content/stellar-contracts/utils/pausable.mdx index 3c03df84..a462ba09 100644 --- a/content/stellar-contracts/utils/pausable.mdx +++ b/content/stellar-contracts/utils/pausable.mdx @@ -17,17 +17,17 @@ To make it easier to spot when inspecting the code, we turned this simple functi An example: ```rust #[when_paused] -pub fn emergency_reset(e: &Env) +pub fn emergency_reset(e: &Env) { e.storage().instance().set(&DataKey::Counter, &0); - +} ``` Which will expand into the below code: ```rust -pub fn emergency_reset(e: &Env) +pub fn emergency_reset(e: &Env) { when_paused(e); e.storage().instance().set(&DataKey::Counter, &0); - +} ``` diff --git a/content/stellar-contracts/utils/upgradeable.mdx b/content/stellar-contracts/utils/upgradeable.mdx index e8332bdf..9a974545 100644 --- a/content/stellar-contracts/utils/upgradeable.mdx +++ b/content/stellar-contracts/utils/upgradeable.mdx @@ -27,7 +27,7 @@ It consists of two main components: 2. ***[`UpgradeableMigratable`](#upgrade_and_migrate)*** for more advanced scenarios where, in addition to the WASM binary, specific storage entries must be modified (migrated) during the upgrade process. -The recommended way to use this module is through the `\#[derive(Upgradeable)]` and `#[derive(UpgradeableMigratable)]` +The recommended way to use this module is through the `#[derive(Upgradeable)]` and `#[derive(UpgradeableMigratable)]` macros. They handle the implementation of the necessary functions, allowing developers to focus solely on managing authorizations @@ -56,17 +56,18 @@ specifying who can perform the upgrade. This minimal implementation keeps the fo permissions. ```rust -use soroban_sdk:: +use soroban_sdk::{ contract, contracterror, contractimpl, panic_with_error, symbol_short, Address, Env, -; +}; use stellar_contract_utils::upgradeable::UpgradeableInternal; use stellar_macros::Upgradeable; #[contracterror] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[repr(u32)] -pub enum ExampleContractError +pub enum ExampleContractError { Unauthorized = 1, +} #[derive(Upgradeable)] @@ -74,23 +75,23 @@ pub enum ExampleContractError pub struct ExampleContract; #[contractimpl] -impl ExampleContract - pub fn __constructor(e: &Env, admin: Address) +impl ExampleContract { + pub fn __constructor(e: &Env, admin: Address) { e.storage().instance().set(&symbol_short!("OWNER"), &admin); - + } +} - -impl UpgradeableInternal for ExampleContract - fn _require_auth(e: &Env, operator: &Address) +impl UpgradeableInternal for ExampleContract { + fn _require_auth(e: &Env, operator: &Address) { operator.require_auth(); // `operator` is the invoker of the upgrade function and can be used // to perform a role-based access control if implemented let owner: Address = e.storage().instance().get(&symbol_short!("OWNER")).unwrap(); - if *operator != owner + if *operator != owner { panic_with_error!(e, ExampleContractError::Unauthorized) - - - + } + } +} ``` ### Upgrade and Migrate @@ -104,50 +105,50 @@ The `#[derive(UpgradeableMigratable)]` macro manages the sequencing of operation only be invoked after a successful upgrade, preventing potential state inconsistencies and storage corruption. ```rust -use soroban_sdk:: +use soroban_sdk::{ contract, contracterror, contracttype, panic_with_error, symbol_short, Address, Env, -; +}; use stellar_contract_utils::upgradeable::UpgradeableMigratableInternal; use stellar_macros::UpgradeableMigratable; #[contracterror] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[repr(u32)] -pub enum ExampleContractError +pub enum ExampleContractError { Unauthorized = 1, - +} #[contracttype] -pub struct Data +pub struct Data { pub num1: u32, pub num2: u32, - +} #[derive(UpgradeableMigratable)] #[contract] pub struct ExampleContract; -impl UpgradeableMigratableInternal for ExampleContract +impl UpgradeableMigratableInternal for ExampleContract { type MigrationData = Data; - fn _require_auth(e: &Env, operator: &Address) + fn _require_auth(e: &Env, operator: &Address) { operator.require_auth(); let owner: Address = e.storage().instance().get(&symbol_short!("OWNER")).unwrap(); - if *operator != owner + if *operator != owner { panic_with_error!(e, ExampleContractError::Unauthorized) - - + } + } - fn _migrate(e: &Env, data: &Self::MigrationData) + fn _migrate(e: &Env, data: &Self::MigrationData) { e.storage().instance().set(&symbol_short!("DATA_KEY"), data); - - + } +} ``` If a rollback is required, the contract can be upgraded to a newer version where the rollback-specific logic - is defined and performed as a migration. + #### Atomic upgrade and migration @@ -158,21 +159,21 @@ atomic upgrade-and-migrate process. This approach ensures that the migration log upgrade without requiring a separate transaction. ```rust -use soroban_sdk::contract, contractimpl, symbol_short, Address, BytesN, Env, Val; +use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, Val}; use stellar_contract_utils::upgradeable::UpgradeableClient; #[contract] pub struct Upgrader; #[contractimpl] -impl Upgrader +impl Upgrader { pub fn upgrade_and_migrate( env: Env, contract_address: Address, operator: Address, wasm_hash: BytesN<32>, migration_data: soroban_sdk::Vec, - ) + ) { operator.require_auth(); let contract_client = UpgradeableClient::new(&env, &contract_address); @@ -180,6 +181,6 @@ impl Upgrader // The types of the arguments to the migrate function are unknown to this // contract, so we need to call it with invoke_contract. env.invoke_contract::<()>(&contract_address, &symbol_short!("migrate"), migration_data); - - + } +} ``` diff --git a/src/navigation/stellar.json b/src/navigation/stellar.json index 35721844..f82984f8 100644 --- a/src/navigation/stellar.json +++ b/src/navigation/stellar.json @@ -39,7 +39,7 @@ "children": [ { "type": "page", - "name": "Fungible Tokens", + "name": "Fungible Token", "url": "/stellar-contracts/tokens/fungible/fungible" }, { @@ -60,7 +60,7 @@ "children": [ { "type": "page", - "name": "Non-Fungible Tokens", + "name": "Non-Fungible Token", "url": "/stellar-contracts/tokens/non-fungible/non-fungible" }, {