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
26 changes: 25 additions & 1 deletion contracts/token/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Structured event emission for all token contract operations.
//! Events are emitted to the ledger for indexing by off-chain services.

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

/// Emitted when the token contract is initialized.
pub fn emit_initialized(env: &Env, admin: &Address, decimals: u32, name: &String, symbol: &String) {
Expand Down Expand Up @@ -90,3 +90,27 @@ pub fn emit_unpaused(env: &Env, admin: &Address) {
env.events()
.publish((symbol_short!("unpause"),), (admin.clone(),));
}

/// 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()),
);
}
39 changes: 38 additions & 1 deletion contracts/token/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod events;
mod test;

use soroban_sdk::token::TokenInterface;
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String};
use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, String};

/// Storage keys for the token contract state.
#[derive(Clone)]
Expand Down Expand Up @@ -214,10 +214,47 @@ impl BcForgeToken {
events::emit_unpaused(&env, &admin);
}

/// Upgrades the contract to a new WASM hash. Admin-only.
pub fn upgrade(env: Env, new_wasm_hash: BytesN<32>) {
let admin = Self::read_admin(&env);
admin.require_auth();

env.deployer().update_current_contract_wasm(new_wasm_hash.clone());
events::emit_upgrade(&env, &admin, &new_wasm_hash);
}

/// Returns the contract version.
pub fn version(env: Env) -> String {
String::from_str(&env, "1.0.0")
}

/// Updates the token name. Admin-only.
pub fn update_name(env: Env, new_name: String) {
let admin = Self::read_admin(&env);
admin.require_auth();

let old_name = env.storage()
.instance()
.get(&DataKey::Name)
.unwrap_or_else(|| String::from_str(&env, "bc-forge"));

env.storage().instance().set(&DataKey::Name, &new_name);
events::emit_update_name(&env, &admin, &old_name, &new_name);
}

/// Updates the token symbol. Admin-only.
pub fn update_symbol(env: Env, new_symbol: String) {
let admin = Self::read_admin(&env);
admin.require_auth();

let old_symbol = env.storage()
.instance()
.get(&DataKey::Symbol)
.unwrap_or_else(|| String::from_str(&env, "SFG"));

env.storage().instance().set(&DataKey::Symbol, &new_symbol);
events::emit_update_symbol(&env, &admin, &old_symbol, &new_symbol);
}
}

// ─────────────────────────────────────────────────────────────────────────────
Expand Down
37 changes: 37 additions & 0 deletions sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
stringToScVal,
u32ToScVal,
scValToNative,
hashToScVal,
} from './utils';

// ─── Types ───────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -283,6 +284,42 @@ export class bcForgeClient {
return this.invokeContract('unpause', [], source);
}

/**
* Upgrades the contract to a new WASM hash. Admin-only.
*
* @param newWasmHash - 32-byte hex string or Buffer of the new WASM hash
* @param source - Admin keypair
*/
async upgrade(newWasmHash: string | Buffer, source: Keypair): Promise<TransactionResult> {
return this.invokeContract('upgrade', [
hashToScVal(newWasmHash),
], source);
}

/**
* Update the token name. Admin-only.
*
* @param newName - The new token name
* @param source - Admin keypair
*/
async updateName(newName: string, source: Keypair): Promise<TransactionResult> {
return this.invokeContract('update_name', [
stringToScVal(newName),
], source);
}

/**
* Update the token symbol. Admin-only.
*
* @param newSymbol - The new token symbol
* @param source - Admin keypair
*/
async updateSymbol(newSymbol: string, source: Keypair): Promise<TransactionResult> {
return this.invokeContract('update_symbol', [
stringToScVal(newSymbol),
], source);
}

// ─── Internal Helpers ────────────────────────────────────────────────────

/**
Expand Down
9 changes: 9 additions & 0 deletions sdk/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,12 @@ export function u32ToScVal(value: number): xdr.ScVal {
export function scValToNative(scVal: xdr.ScVal): any {
return sdkScValToNative(scVal);
}

/**
* Converts a 32-byte hex string or Buffer to an ScVal.
*/
export function hashToScVal(hash: string | Buffer): xdr.ScVal {
const buf = typeof hash === 'string' ? Buffer.from(hash, 'hex') : hash;
if (buf.length !== 32) throw new Error('Hash must be exactly 32 bytes');
return xdr.ScVal.scvBytes(buf);
}
Loading