diff --git a/contracts/smartdeploy/src/error.rs b/contracts/smartdeploy/src/error.rs index b358021..9c05e3e 100644 --- a/contracts/smartdeploy/src/error.rs +++ b/contracts/smartdeploy/src/error.rs @@ -22,4 +22,10 @@ pub enum Error { /// Failed to initialize contract InitFailed = 7, + + /// Failed to redeploy a deployed contract with no coreriff macro + RedeployDeployedFailed = 8, + + /// Contract doesn't have owner, impossible to perform the operation + NoOwnerSet = 9, } diff --git a/contracts/smartdeploy/src/lib.rs b/contracts/smartdeploy/src/lib.rs index a44675d..1827b2d 100644 --- a/contracts/smartdeploy/src/lib.rs +++ b/contracts/smartdeploy/src/lib.rs @@ -2,7 +2,7 @@ use loam_sdk::{soroban_contract, soroban_sdk}; use loam_sdk_core_riff::{owner::Owner, CoreRiff}; use registry::{ - contract::ContractRegistry, wasm::WasmRegistry, Deployable, DevDeployable, Publishable, + contract::ContractRegistry, wasm::WasmRegistry, Deployable, DevDeployable, Publishable, Claimable, }; pub mod error; @@ -24,6 +24,10 @@ impl Deployable for Contract { type Impl = ContractRegistry; } +impl Claimable for Contract { + type Impl = ContractRegistry; +} + impl DevDeployable for Contract { type Impl = ContractRegistry; } diff --git a/contracts/smartdeploy/src/registry.rs b/contracts/smartdeploy/src/registry.rs index 3c77679..bc29fcf 100644 --- a/contracts/smartdeploy/src/registry.rs +++ b/contracts/smartdeploy/src/registry.rs @@ -52,9 +52,6 @@ pub trait IsPublishable { #[riff] pub trait IsDeployable { - /// Claim a contract id of an already deployed contract - fn claim_deployed_contract(&mut self, deployed_name: soroban_sdk::String, id: soroban_sdk::Address) -> Result<(), Error>; - /// Deploys a new published contract returning the deployed contract's id. /// If no salt provided it will use the current sequence number. fn deploy( @@ -81,6 +78,31 @@ pub trait IsDeployable { ) -> Result, Error>; } +#[riff] +pub trait IsClaimable { + /// Claim a contract id of an already deployed contract + fn claim_already_deployed_contract( + &mut self, + deployed_name: soroban_sdk::String, + id: soroban_sdk::Address, + owner: soroban_sdk::Address, + ) -> Result<(), Error>; + + /// Get the owner of a claimed deployed contract + fn get_claimed_owner( + &self, + deployed_name: soroban_sdk::String + ) -> Result, Error>; + + /// Redeploy a claimed deployed contract to a new wasm. Defaults: use redeploy from coreriff + fn redeploy_claimed_contract( + &self, + binary_name: Option, + version: Option, + deployed_name: soroban_sdk::String, + redeploy_fn: Option<(soroban_sdk::Symbol, soroban_sdk::Vec)>, + ) -> Result<(), Error>; +} #[riff] pub trait IsDevDeployable { diff --git a/contracts/smartdeploy/src/registry/contract.rs b/contracts/smartdeploy/src/registry/contract.rs index 61aff27..0fc056a 100644 --- a/contracts/smartdeploy/src/registry/contract.rs +++ b/contracts/smartdeploy/src/registry/contract.rs @@ -9,11 +9,10 @@ use crate::{ util::{hash_string, MAX_BUMP}, version::Version, Contract, + WasmRegistry, }; -use crate::WasmRegistry; - -use super::{IsDeployable, IsDevDeployable}; +use super::{IsClaimable, IsDeployable, IsDevDeployable}; loam_sdk::import_contract!(core_riff); @@ -32,9 +31,29 @@ pub struct DeployEventData { deployer: Address, contract_id: Address, } +#[contracttype(export = false)] +pub struct ContractRegistry(pub Map); #[contracttype(export = false)] -pub struct ContractRegistry(pub Map); +#[derive(Clone)] +pub enum ContractState { + ContractId(Address), + Owner(Address, Address) +} + +impl ContractState { + pub fn contract_id(&self) -> &Address { + match self { + Self::ContractId(id) | Self::Owner(id, _) => id, + } + } + pub fn owner(&self) -> Option<&Address> { + match self { + Self::Owner(_, owner) => Some(owner), + Self::ContractId(_) => None + } + } +} impl Default for ContractRegistry { fn default() -> Self { @@ -59,13 +78,6 @@ impl Lazy for ContractRegistry { } impl IsDeployable for ContractRegistry { - fn claim_deployed_contract(&mut self, deployed_name: String, id: Address) -> Result<(), Error> { - if self.0.contains_key(deployed_name.clone()) { - return Err(Error::AlreadyClaimed); - } - self.0.set(deployed_name, id); - Ok(()) - } fn deploy( &mut self, contract_name: String, @@ -86,7 +98,7 @@ impl IsDeployable for ContractRegistry { if let Some((init_fn, args)) = init { let _ = env().invoke_contract::(&address, &init_fn, args); } - self.0.set(deployed_name.clone(), address.clone()); + self.0.set(deployed_name.clone(), ContractState::ContractId(address.clone())); // Publish a deploy event let version = version.map_or_else( @@ -112,6 +124,7 @@ impl IsDeployable for ContractRegistry { self.0 .get(deployed_name) .ok_or(Error::NoSuchContractDeployed) + .map(|contract| contract.contract_id().clone()) } fn list_deployed_contracts( @@ -126,12 +139,62 @@ impl IsDeployable for ContractRegistry { .take(limit.unwrap_or_else(|| self.0.len()) as usize); let mut res = vec![env()]; for item in items { - res.push_back(item); + res.push_back((item.0, item.1.contract_id().clone())); } Ok(res) } } +impl IsClaimable for ContractRegistry { + fn claim_already_deployed_contract( + &mut self, + deployed_name: soroban_sdk::String, + id: soroban_sdk::Address, + owner: soroban_sdk::Address, + ) -> Result<(), Error> { + owner.require_auth(); + if self.0.contains_key(deployed_name.clone()) { + return Err(Error::AlreadyClaimed); + } + self.0.set(deployed_name, ContractState::Owner(id, owner)); + Ok(()) + } + + fn get_claimed_owner( + &self, + deployed_name: soroban_sdk::String + ) -> Result, Error> { + self + .0 + .get(deployed_name) + .ok_or(Error::NoSuchContractDeployed) + .map(|contract| contract.owner().cloned()) + } + + fn redeploy_claimed_contract( + &self, + binary_name: Option, + version: Option, + deployed_name: soroban_sdk::String, + redeploy_fn: Option<(soroban_sdk::Symbol, soroban_sdk::Vec)>, + ) -> Result<(), Error> { + self + .get_claimed_owner(deployed_name.clone())? + .ok_or(Error::NoOwnerSet)? + .require_auth(); + let contract_id = self.fetch_contract_id(deployed_name)?; + if let Some(binary_name) = binary_name { + let hash = Contract::fetch_hash(binary_name, version)?; + env().deployer().update_current_contract_wasm(hash); + } else if let Some((fn_name, args)) = redeploy_fn { + let _ = env().invoke_contract::(&contract_id, &fn_name, args); + } else { + return Err(Error::RedeployDeployedFailed); + } + Ok(()) + } +} + fn deploy_and_init( owner: &Address, salt: BytesN<32>, @@ -157,14 +220,15 @@ impl IsDevDeployable for ContractRegistry { wasm: soroban_sdk::Bytes, ) -> Result { let wasm_hash = env().deployer().upload_contract_wasm(wasm); - if let Some(address) = self.0.get(name.clone()) { - let contract = core_riff::Client::new(env(), &address); + if let Some(contract_state) = self.0.get(name.clone()) { + let address = contract_state.contract_id(); + let contract = core_riff::Client::new(env(), address); contract.redeploy(&wasm_hash); - return Ok(address); + return Ok(address.clone()); } let salt = hash_string(&name); let id = deploy_and_init(&owner, salt, wasm_hash)?; - self.0.set(name, id.clone()); + self.0.set(name, ContractState::ContractId(id.clone())); Ok(id) } } diff --git a/deploy.sh b/deploy.sh index 98d870e..c202d8a 100755 --- a/deploy.sh +++ b/deploy.sh @@ -47,6 +47,7 @@ echo $ID if test "$FILE_HASH" = ""; then just publish smartdeploy just claim_self + just set_owner default fi if test "$SOROBAN_NETWORK" = "testnet"; then diff --git a/justfile b/justfile index 5e92aa9..9597a16 100644 --- a/justfile +++ b/justfile @@ -75,8 +75,11 @@ setup_default: @./deploy.sh [private] -@claim_self: - just smartdeploy claim_deployed_contract --deployed_name smartdeploy --id {{ id }} +@claim_self owner='default': + just smartdeploy claim_already_deployed_contract --deployed_name smartdeploy --id {{ id }} --owner {{owner}} + +@set_owner owner: + @just smartdeploy_raw -- owner_set --new_owner {{ owner }} [private] @install_self: