From b75c82c11bdd2324b8010b0ae9deb0ef14557755 Mon Sep 17 00:00:00 2001 From: Sakariyah Abdulhazeem Date: Sun, 27 Sep 2026 12:02:54 +0100 Subject: [PATCH 1/3] feat: initialize creator keys from factory config --- creator-keys-factory/Cargo.toml | 1 + creator-keys-factory/src/lib.rs | 100 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/creator-keys-factory/Cargo.toml b/creator-keys-factory/Cargo.toml index 3c0b7c52..23409dd1 100644 --- a/creator-keys-factory/Cargo.toml +++ b/creator-keys-factory/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] soroban-sdk = { workspace = true } +creator-keys = { path = "../creator-keys" } [dev-dependencies] soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/creator-keys-factory/src/lib.rs b/creator-keys-factory/src/lib.rs index f2b779a1..5c47c5b0 100644 --- a/creator-keys-factory/src/lib.rs +++ b/creator-keys-factory/src/lib.rs @@ -2,6 +2,9 @@ //! Minimal factory that deploys `creator-keys` contract instances and keeps a //! registry of the deployed addresses. +use creator_keys::{ + CreatorKeysContractClient, CurvePreset, RegisterCreatorParams, +}; use soroban_sdk::{ contract, contracterror, contractimpl, contracttype, symbol_short, Address, BytesN, Env, Symbol, Vec, @@ -16,6 +19,25 @@ pub enum FactoryError { AlreadyInitialised = 1, NotInitialised = 2, Unauthorized = 3, + InvalidConfig = 4, + InitialisationFailed = 5, +} + +/// Configuration applied atomically to a newly deployed creator key. +/// +/// The factory keeps deployment and creator registration in one transaction so +/// callers never receive an uninitialised key address. `creator` must authorize +/// the call; an admin may still use the legacy `deploy_key` entrypoint for +/// deployments that are initialized by a separate transaction. +#[derive(Clone, Debug, Eq, PartialEq)] +#[contracttype] +pub struct KeyDeploymentConfig { + pub creator: Address, + pub handle: soroban_sdk::String, + pub max_supply: Option, + pub max_keys_per_wallet: Option, + /// Curve preset: 0 = flat, 1 = linear, 2 = quadratic. + pub curve_preset: u32, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -140,6 +162,84 @@ impl CreatorKeysFactory { Ok(key) } + /// Deploy and initialize a creator key in one transaction. + pub fn deploy_key_with_config( + env: Env, + caller: Address, + salt: BytesN<32>, + config: KeyDeploymentConfig, + ) -> Result { + caller.require_auth(); + if caller != config.creator { + return Err(FactoryError::Unauthorized); + } + if config.handle.len() == 0 || config.handle.len() > 64 { + return Err(FactoryError::InvalidConfig); + } + if config.max_supply == Some(0) || config.max_keys_per_wallet == Some(0) { + return Err(FactoryError::InvalidConfig); + } + if config.curve_preset > 2 { + return Err(FactoryError::InvalidConfig); + } + + let allowed = env + .storage() + .instance() + .get(&DataKey::Allowed(caller.clone())) + .unwrap_or(false); + if caller != read_admin(&env)? && !allowed { + return Err(FactoryError::Unauthorized); + } + let wasm_hash: BytesN<32> = env + .storage() + .instance() + .get(&DataKey::WasmHash) + .ok_or(FactoryError::NotInitialised)?; + let key = env + .deployer() + .with_current_contract(salt) + .deploy_v2(wasm_hash, ()); + + let key_client = CreatorKeysContractClient::new(&env, &key); + let curve_preset = match config.curve_preset { + 0 => CurvePreset::Flat, + 1 => CurvePreset::Linear, + _ => CurvePreset::Quadratic, + }; + key_client + .try_register_creator( + &RegisterCreatorParams { + creator: config.creator.clone(), + handle: config.handle, + }, + &None, + &config.max_supply, + &config.max_keys_per_wallet, + &Some(curve_preset), + &None, + &None, + ) + .map_err(|_| FactoryError::InitialisationFailed)? + .map_err(|_| FactoryError::InitialisationFailed)?; + + let mut registry = Self::get_registry(env.clone()); + registry.push_back(key.clone()); + env.storage().instance().set(&DataKey::Registry, ®istry); + let mut creator_keys = Self::get_keys_by_creator(env.clone(), config.creator.clone()); + creator_keys.push_back(key.clone()); + env.storage().instance().set( + &DataKey::CreatorKeys(config.creator.clone()), + &creator_keys, + ); + write_registry_count(&env, registry.len()); + env.events().publish( + (KEY_DEPLOYED_EVENT_NAME, config.creator.clone()), + KeyDeployedEvent { key: key.clone(), creator: config.creator }, + ); + Ok(key) + } + /// Removes a key from the factory registry when the deployed contract is deprecated. /// Only the creator of that key or the admin may do so. pub fn remove_key( From d8b1ddb482736c4bb88e0377a0e87e99f837a8b7 Mon Sep 17 00:00:00 2001 From: Sakariyah Abdulhazeem Date: Sun, 27 Sep 2026 12:39:29 +0100 Subject: [PATCH 2/3] fix: format creator key factory --- creator-keys-factory/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/creator-keys-factory/src/lib.rs b/creator-keys-factory/src/lib.rs index 5c47c5b0..ab8569fd 100644 --- a/creator-keys-factory/src/lib.rs +++ b/creator-keys-factory/src/lib.rs @@ -2,9 +2,7 @@ //! Minimal factory that deploys `creator-keys` contract instances and keeps a //! registry of the deployed addresses. -use creator_keys::{ - CreatorKeysContractClient, CurvePreset, RegisterCreatorParams, -}; +use creator_keys::{CreatorKeysContractClient, CurvePreset, RegisterCreatorParams}; use soroban_sdk::{ contract, contracterror, contractimpl, contracttype, symbol_short, Address, BytesN, Env, Symbol, Vec, @@ -228,14 +226,16 @@ impl CreatorKeysFactory { env.storage().instance().set(&DataKey::Registry, ®istry); let mut creator_keys = Self::get_keys_by_creator(env.clone(), config.creator.clone()); creator_keys.push_back(key.clone()); - env.storage().instance().set( - &DataKey::CreatorKeys(config.creator.clone()), - &creator_keys, - ); + env.storage() + .instance() + .set(&DataKey::CreatorKeys(config.creator.clone()), &creator_keys); write_registry_count(&env, registry.len()); env.events().publish( (KEY_DEPLOYED_EVENT_NAME, config.creator.clone()), - KeyDeployedEvent { key: key.clone(), creator: config.creator }, + KeyDeployedEvent { + key: key.clone(), + creator: config.creator, + }, ); Ok(key) } From 0f019685b077acc6e5cfae0ac34ae81ba201a1dd Mon Sep 17 00:00:00 2001 From: Sakariyah Abdulhazeem Date: Sun, 27 Sep 2026 13:48:44 +0100 Subject: [PATCH 3/3] fix: satisfy clippy handle validation --- creator-keys-factory/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/creator-keys-factory/src/lib.rs b/creator-keys-factory/src/lib.rs index ab8569fd..0ca78e80 100644 --- a/creator-keys-factory/src/lib.rs +++ b/creator-keys-factory/src/lib.rs @@ -171,7 +171,7 @@ impl CreatorKeysFactory { if caller != config.creator { return Err(FactoryError::Unauthorized); } - if config.handle.len() == 0 || config.handle.len() > 64 { + if config.handle.is_empty() || config.handle.len() > 64 { return Err(FactoryError::InvalidConfig); } if config.max_supply == Some(0) || config.max_keys_per_wallet == Some(0) {