diff --git a/contracts/admin/src/lib.rs b/contracts/admin/src/lib.rs index b75bdc24..8e17e10e 100644 --- a/contracts/admin/src/lib.rs +++ b/contracts/admin/src/lib.rs @@ -130,6 +130,9 @@ #![no_std] mod events; +pub mod storage; + +pub use storage::*; use bc_forge_ttl as ttl; use soroban_sdk::{contracterror, contracttype, vec, Address, Env, String, Vec}; @@ -455,6 +458,9 @@ pub fn grant_role(env: &Env, caller: &Address, role: Role, address: &Address) { /// @param address The address to receive the role. fn _grant_role(env: &Env, admin: &Address, role: Role, address: &Address) { require_non_zero_address(env, address); + if has_role(env, role, address) { + soroban_sdk::panic_with_error!(env, AdminError::RoleAlreadyGranted); + } env.storage() .persistent() .set(&AdminKey::Role(role, address.clone()), &true); diff --git a/contracts/admin/src/storage.rs b/contracts/admin/src/storage.rs new file mode 100644 index 00000000..5cf584ef --- /dev/null +++ b/contracts/admin/src/storage.rs @@ -0,0 +1,60 @@ +use soroban_sdk::{contracttype, Address, String, Vec}; + +/// Storage keys for the access-control layer. +/// +/// `#[contracttype]` derives a distinct ledger key for every variant (and, +/// for `Role(Role, Address)`, for every `(Role, Address)` pair), so entries +/// never collide with each other or with the other variants below. +#[derive(Clone)] +#[contracttype] +pub enum AdminKey { + /// The singular contract admin address, set via `set_admin`. + Admin, + /// Maps a `(Role, Address)` pair to `true` when `address` holds `role`. + /// This is the Role-to-Address mapping storage structure: membership is + /// looked up directly by key rather than by scanning a list, and each + /// pair occupies its own ledger entry so grants/revokes for one address + /// never touch another's. + Role(Role, Address), + /// Multi-sig admin pool addresses, set via `set_admin_pool`. + AdminPool, + /// Multi-sig approval threshold, set alongside the pool. + Threshold, + /// Governance proposal data, keyed by proposal ID. + Proposal(u64), + /// Auto-incrementing counter for proposal IDs. + ProposalIdCounter, + /// Super-admin mapping populated by `migrate_admin` for legacy contracts. + SuperAdmin(Address), +} + +/// Roles recognized by the access-control layer. +/// +/// New variants must be appended, never inserted, so that previously +/// persisted `AdminKey::Role(Role, Address)` entries keep decoding to the +/// same variant they were written with. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[contracttype] +pub enum Role { + /// Full administrative control granted via `set_admin`. + Admin, + /// Permission to mint new tokens. + Minter, + /// Highest-privilege role, reserved for owner-level operations. + SuperAdmin, + /// Role allowing emergency pause and unpause operations. + Pauser, +} + +/// The SuperAdmin role constant — can be imported as `SUPER_ADMIN_ROLE` for +/// use in access-control gating without qualifying the full `Role` enum. +pub const SUPER_ADMIN_ROLE: Role = Role::SuperAdmin; + +#[derive(Clone, Debug, PartialEq)] +#[contracttype] +pub struct Proposal { + pub creator: Address, + pub description: String, + pub approvals: Vec
, + pub executed: bool, +} diff --git a/contracts/split/src/lib.rs b/contracts/split/src/lib.rs index 03e89e43..d728f8c4 100644 --- a/contracts/split/src/lib.rs +++ b/contracts/split/src/lib.rs @@ -45,14 +45,20 @@ pub struct Invoice { pub created_at: u32, } +/// Errors returned by the split contract. #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[contracterror] #[repr(u32)] pub enum SplitError { + /// The specified invoice was not found. InvoiceNotFound = 1, + /// The recipient address is invalid. InvalidRecipient = 2, + /// Insufficient balance to perform the operation. InsufficientBalance = 3, + /// The invoice has already been completed. InvoiceAlreadyCompleted = 4, + /// The specified failed payout was not found. FailedPayoutNotFound = 5, } diff --git a/contracts/token/src/lib.rs b/contracts/token/src/lib.rs index 4d1e4731..a61fc971 100644 --- a/contracts/token/src/lib.rs +++ b/contracts/token/src/lib.rs @@ -100,6 +100,7 @@ struct AllowanceData { expiration_ledger: u32, } +/// Errors returned by the token contract. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[contracterror] #[repr(u32)] diff --git a/contracts/vesting/src/lib.rs b/contracts/vesting/src/lib.rs index 48af9b7a..bdda5a6d 100644 --- a/contracts/vesting/src/lib.rs +++ b/contracts/vesting/src/lib.rs @@ -56,17 +56,26 @@ pub struct VestingInfo { pub revoked: bool, } +/// Errors returned by the vesting contract. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[contracterror] #[repr(u32)] pub enum VestingError { + /// The contract has already been initialized. AlreadyInitialized = 1, + /// The contract has not been initialized. NotInitialized = 2, + /// An invalid amount was provided. InvalidAmount = 3, + /// An invalid vesting duration was specified. InvalidDuration = 4, + /// The specified cliff occurs after the vesting end. CliffAfterEnd = 5, + /// The vesting schedule was not found. ScheduleNotFound = 6, + /// The vesting schedule is not revocable. NotRevocable = 7, + /// The vesting schedule has already been revoked. AlreadyRevoked = 8, } diff --git a/contracts/wrapper/src/lib.rs b/contracts/wrapper/src/lib.rs index 95ca6cee..c825f6b8 100644 --- a/contracts/wrapper/src/lib.rs +++ b/contracts/wrapper/src/lib.rs @@ -56,15 +56,22 @@ pub enum DataKey { // ─── Errors ────────────────────────────────────────────────────────────────── +/// Errors returned by the wrapper contract. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[contracterror] #[repr(u32)] pub enum WrapperError { + /// The contract has already been initialized. AlreadyInitialized = 1, + /// The contract has not been initialized. NotInitialized = 2, + /// An invalid amount was provided. InvalidAmount = 3, + /// Insufficient balance to perform the operation. InsufficientBalance = 4, + /// Insufficient allowance to perform the operation. InsufficientAllowance = 5, + /// The contract is currently paused. ContractPaused = 6, /// Reentrant call detected. Reentrant = 7,