Skip to content

Commit 77695bb

Browse files
authored
Merge pull request #106 from Itodo-S/feat/contract-upgradeability
feat(contracts): add contract upgradeability
2 parents 1579f99 + 123f3b2 commit 77695bb

1 file changed

Lines changed: 56 additions & 1 deletion

File tree

contracts/src/lib.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![no_std]
22

3-
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, Env, String, Vec};
3+
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, BytesN, Env, String, Vec};
44

55
#[contracttype]
66
#[derive(Clone, Debug, PartialEq)]
@@ -394,6 +394,33 @@ impl AgenticPayContract {
394394
.get(&DataKey::ProjectCount)
395395
.unwrap_or(0)
396396
}
397+
398+
/// Upgrade the contract WASM code. Admin-only.
399+
///
400+
/// Uses Soroban's built-in upgrade mechanism which replaces the contract
401+
/// bytecode while preserving all persistent and instance storage. This
402+
/// allows the contract to be upgraded without redeploying or migrating data.
403+
///
404+
/// # Arguments
405+
/// * `admin` - Must match the stored admin address
406+
/// * `new_wasm_hash` - SHA-256 hash of the new WASM binary (uploaded via `soroban contract install`)
407+
pub fn upgrade(env: Env, admin: Address, new_wasm_hash: BytesN<32>) {
408+
admin.require_auth();
409+
410+
let stored_admin: Address = env
411+
.storage()
412+
.instance()
413+
.get(&DataKey::Admin)
414+
.expect("Not initialized");
415+
assert!(admin == stored_admin, "Only admin can upgrade");
416+
417+
env.deployer().update_current_contract_wasm(new_wasm_hash);
418+
}
419+
420+
/// Return the contract version for tracking upgrades.
421+
pub fn version(_env: Env) -> u32 {
422+
1
423+
}
397424
}
398425

399426
#[cfg(test)]
@@ -689,4 +716,32 @@ mod test {
689716
assert_eq!(id, 3);
690717
assert_eq!(client.get_project_count(), 3);
691718
}
719+
720+
#[test]
721+
fn test_version_returns_current() {
722+
let env = Env::default();
723+
let contract_id = env.register_contract(None, AgenticPayContract);
724+
let client = AgenticPayContractClient::new(&env, &contract_id);
725+
726+
assert_eq!(client.version(), 1);
727+
}
728+
729+
#[test]
730+
#[should_panic(expected = "Only admin can upgrade")]
731+
fn test_upgrade_rejects_non_admin() {
732+
let env = Env::default();
733+
env.mock_all_auths();
734+
735+
let contract_id = env.register_contract(None, AgenticPayContract);
736+
let client = AgenticPayContractClient::new(&env, &contract_id);
737+
738+
let admin = Address::generate(&env);
739+
let non_admin = Address::generate(&env);
740+
741+
client.initialize(&admin);
742+
743+
// Non-admin attempting upgrade should panic
744+
let fake_hash = BytesN::from_array(&env, &[0u8; 32]);
745+
client.upgrade(&non_admin, &fake_hash);
746+
}
692747
}

0 commit comments

Comments
 (0)