Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 16 additions & 44 deletions contracts/admin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,16 @@ use soroban_sdk::{contracttype, vec, Address, Env, String, Vec};
pub enum AdminKey {
Admin,
Role(Role, Address),
/// The pool of administrator addresses for multi-sig.
AdminPool,
/// Minimum signatures required for multi-sig actions.
Threshold,
/// Active proposals: proposal_id -> Proposal.
Proposal(u64),
/// Counter for generating unique proposal IDs.
ProposalIdCounter,
}

/// Enumeration of available roles.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[contracttype]
pub enum Role {
/// Global administrator with full control.
Admin,
/// Account authorized to mint tokens.
Minter,
}

Expand Down Expand Up @@ -73,36 +66,26 @@ pub fn revoke_role(env: &Env, role: Role, address: &Address) {
}

pub fn has_role(env: &Env, role: Role, address: &Address) -> bool {
if env
.storage()
.persistent()
.has(&AdminKey::Role(Role::Admin, address.clone()))
{
return true;
}
env.storage()
.persistent()
.has(&AdminKey::Role(role, address.clone()))
.has(&AdminKey::Role(Role::Admin, address.clone()))
|| env
.storage()
.persistent()
.has(&AdminKey::Role(role, address.clone()))
}

// ─── Guards ──────────────────────────────────────────────────────────────────

/// Requires that the stored admin has authorized the current invocation.
pub fn require_admin(env: &Env) {
let admin = get_admin(env);
admin.require_auth();
get_admin(env).require_auth();
}

/// Requires that the specified address has the given role and has authorized the invocation.
pub fn require_role(env: &Env, role: Role, address: &Address) {
if !has_role(env, role, address) {
panic!("unauthorized: missing role");
}
address.require_auth();
}

// ─── Multi-Sig Primitives ───────────────────────────────────────────────────

pub fn set_admin_pool(env: &Env, pool: Vec<Address>, threshold: u32) {
if threshold == 0 || threshold > pool.len() {
panic!("invalid threshold for admin pool");
Expand All @@ -114,28 +97,19 @@ pub fn set_admin_pool(env: &Env, pool: Vec<Address>, threshold: u32) {
}

pub fn get_admin_pool(env: &Env) -> Vec<Address> {
env.storage()
.instance()
.get(&AdminKey::AdminPool)
.unwrap_or_else(|| {
if has_admin(env) {
vec![env, get_admin(env)]
} else {
vec![env]
}
})
env.storage().instance().get(&AdminKey::AdminPool).unwrap_or_else(|| {
if has_admin(env) {
vec![env, get_admin(env)]
} else {
vec![env]
}
})
}

pub fn get_threshold(env: &Env) -> u32 {
env.storage()
.instance()
.get(&AdminKey::Threshold)
.unwrap_or(1)
env.storage().instance().get(&AdminKey::Threshold).unwrap_or(1)
}

// ─── Proposals ──────────────────────────────────────────────────────────────

/// Creates a new proposal for an administrative action.
pub fn create_proposal(env: &Env, creator: Address, description: String) -> u64 {
creator.require_auth();
let pool = get_admin_pool(env);
Expand All @@ -147,19 +121,17 @@ pub fn create_proposal(env: &Env, creator: Address, description: String) -> u64
.storage()
.instance()
.get(&AdminKey::ProposalIdCounter)
.unwrap_or(0);
.unwrap_or(0u64);
env.storage()
.instance()
.set(&AdminKey::ProposalIdCounter, &(id + 1));

let proposal = Proposal {
creator: creator.clone(),
action_type,
description,
approvals: vec![env, creator],
executed: false,
};

env.storage()
.instance()
.set(&AdminKey::Proposal(id), &proposal);
Expand Down Expand Up @@ -209,7 +181,7 @@ pub fn mark_executed(env: &Env, proposal_id: u64) {
.expect("proposal not found");

if proposal.executed {
panic!("already executed");
panic!("proposal already executed");
}
if !is_proposal_ready(env, proposal_id) {
panic!("threshold not met");
Expand Down
90 changes: 4 additions & 86 deletions contracts/token/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
//! # bc-forge Token Events
//!
//! Structured event emission for all token contract operations.
//! Events are emitted to the ledger for indexing by off-chain services.
//! Structured event emission for the token contract.

use soroban_sdk::{symbol_short, Address, BytesN, Env, String};
use soroban_sdk::{symbol_short, Address, Env, String};

/// Emitted when the token contract is initialized.
pub fn emit_initialized(env: &Env, admin: &Address, decimals: u32, name: &String, symbol: &String) {
env.events().publish(
(symbol_short!("init"),),
(admin.clone(), decimals, name.clone(), symbol.clone()),
);
}

/// Emitted when tokens are minted.
pub fn emit_mint(
env: &Env,
admin: &Address,
Expand All @@ -28,21 +23,18 @@ pub fn emit_mint(
);
}

/// Emitted when tokens are burned.
pub fn emit_burn(env: &Env, from: &Address, amount: i128, new_balance: i128, new_supply: i128) {
env.events().publish(
(symbol_short!("burn"),),
(from.clone(), amount, new_balance, new_supply),
);
}

/// Emitted on a standard transfer.
pub fn emit_transfer(env: &Env, from: &Address, to: &Address, amount: i128) {
env.events()
.publish((symbol_short!("xfer"),), (from.clone(), to.clone(), amount));
}

/// Emitted on a delegated transfer (transfer_from).
pub fn emit_transfer_from(
env: &Env,
spender: &Address,
Expand All @@ -63,100 +55,26 @@ pub fn emit_transfer_from(
);
}

/// Emitted when an allowance is approved.
pub fn emit_approve(env: &Env, from: &Address, spender: &Address, amount: i128) {
pub fn emit_approve(env: &Env, from: &Address, spender: &Address, amount: i128, expiration: u32) {
env.events().publish(
(symbol_short!("approve"),),
(from.clone(), spender.clone(), amount),
(from.clone(), spender.clone(), amount, expiration),
);
}

/// Emitted when contract ownership is transferred.
pub fn emit_ownership_transferred(env: &Env, old_admin: &Address, new_admin: &Address) {
env.events().publish(
(symbol_short!("own_xfer"),),
(old_admin.clone(), new_admin.clone()),
);
}

/// Emitted when a new admin is proposed (two-step transfer).
pub fn emit_ownership_proposed(env: &Env, old_admin: &Address, pending_admin: &Address) {
env.events().publish(
(symbol_short!("own_prop"),),
(old_admin.clone(), pending_admin.clone()),
);
}

/// Emitted when pending admin accepts ownership.
pub fn emit_ownership_accepted(env: &Env, old_admin: &Address, new_admin: &Address) {
env.events().publish(
(symbol_short!("own_acc"),),
(old_admin.clone(), new_admin.clone()),
);
}

/// Emitted when ownership transfer is cancelled.
pub fn emit_ownership_cancelled(env: &Env, admin: &Address, cancelled_admin: &Address) {
env.events().publish(
(symbol_short!("own_can"),),
(admin.clone(), cancelled_admin.clone()),
);
}

/// Emitted when the contract is paused.
pub fn emit_paused(env: &Env, admin: &Address) {
env.events()
.publish((symbol_short!("paused"),), (admin.clone(),));
}

/// Emitted when the contract is unpaused.
pub fn emit_unpaused(env: &Env, admin: &Address) {
env.events()
.publish((symbol_short!("unpause"),), (admin.clone(),));
}

/// Emitted when tokens are clawed back.
pub fn emit_clawback(env: &Env, admin: &Address, from: &Address, to: &Address, amount: i128) {
env.events().publish(
(symbol_short!("clawback"),),
(admin.clone(), from.clone(), to.clone(), amount),
);
}

/// Emitted when tokens are locked.
pub fn emit_locked(env: &Env, user: &Address, amount: i128, unlock_time: u64) {
env.events().publish(
(symbol_short!("lock"),),
(user.clone(), amount, unlock_time),
);
}

/// Emitted when locked tokens are withdrawn.
pub fn emit_withdraw_locked(env: &Env, user: &Address, amount: i128) {
env.events()
.publish((symbol_short!("unlock"),), (user.clone(), amount));
}

/// Emitted when the contract is upgraded.
pub fn emit_upgrade(env: &Env, admin: &Address, new_wasm_hash: &BytesN<32>) {
env.events().publish(
(symbol_short!("upgrade"),),
(admin.clone(), new_wasm_hash.clone()),
);
}

/// Emitted when the token name is updated.
pub fn emit_update_name(env: &Env, admin: &Address, old_name: &String, new_name: &String) {
env.events().publish(
(symbol_short!("upd_name"),),
(admin.clone(), old_name.clone(), new_name.clone()),
);
}

/// Emitted when the token symbol is updated.
pub fn emit_update_symbol(env: &Env, admin: &Address, old_symbol: &String, new_symbol: &String) {
env.events().publish(
(symbol_short!("upd_sym"),),
(admin.clone(), old_symbol.clone(), new_symbol.clone()),
);
}
Loading
Loading