use zera_proto::zera_txn::{
CoinDenomination, ContractFees, ContractType, ExpenseRatio, Governance, KeyValuePair,
MaxSupplyRelease, PreMintWallet, RestrictedKey, TokenCompliance,
};
use crate::crypto::address::generate_address_from_public_key;
use crate::error::{Result, ZeraError};
use crate::types::RpcConfig;
use crate::utils::validation::is_valid_contract_id;
#[derive(Debug, Clone, PartialEq)]
pub struct CreateContractOptions {
pub contract_version: u64,
pub symbol: String,
pub name: String,
pub contract_type: ContractType,
pub contract_id: String,
pub public_key_base58_identifier: String,
pub private_key_base58: String,
pub governance: Option<Governance>,
pub restricted_keys: Vec<RestrictedKey>,
pub max_supply: Option<String>,
pub contract_fees: Option<ContractFees>,
pub premint_wallets: Vec<PreMintWallet>,
pub coin_denomination: CoinDenomination,
pub custom_parameters: Vec<KeyValuePair>,
pub expense_ratio: Vec<ExpenseRatio>,
pub update_contract_fees: Option<bool>,
pub update_expense_ratio: Option<bool>,
pub quash_threshold: Option<u32>,
pub token_compliance: Vec<TokenCompliance>,
pub kyc_status: Option<bool>,
pub immutable_kyc_status: Option<bool>,
pub max_supply_release: Vec<MaxSupplyRelease>,
pub fee_id: Option<String>,
pub fee_amount_parts: Option<String>,
pub memo: Option<String>,
pub grpc_config: Option<RpcConfig>,
pub nonce: Option<u64>,
}
pub type BuildContractOptions = CreateContractOptions;
#[derive(Debug, Clone, PartialEq)]
pub struct UpdateContractOptions {
pub contract_id: String,
pub contract_version: u64,
pub public_key_base58_identifier: String,
pub private_key_base58: String,
pub name: Option<String>,
pub governance: Option<Governance>,
pub restricted_keys: Vec<RestrictedKey>,
pub contract_fees: Option<ContractFees>,
pub custom_parameters: Vec<KeyValuePair>,
pub expense_ratio: Vec<ExpenseRatio>,
pub token_compliance: Vec<TokenCompliance>,
pub kyc_status: Option<bool>,
pub immutable_kyc_status: Option<bool>,
pub quash_threshold: Option<u32>,
pub fee_id: Option<String>,
pub fee_amount_parts: Option<String>,
pub memo: Option<String>,
pub grpc_config: Option<RpcConfig>,
pub nonce: Option<u64>,
}
pub fn validate_create_contract_options(
contract_id: &str,
symbol: &str,
name: &str,
contract_version: u64,
contract_type: Option<ContractType>,
) -> Result<()> {
if !is_valid_contract_id(contract_id) {
return Err(ZeraError::Validation(
"ContractId must be provided and follow the format $[letters]+[4 digits] (e.g., $ZRA+0000)"
.to_string(),
));
}
if symbol.trim().is_empty() {
return Err(ZeraError::Validation(
"Symbol must be provided and non-empty".to_string(),
));
}
if name.trim().is_empty() {
return Err(ZeraError::Validation(
"Name must be provided and non-empty".to_string(),
));
}
if contract_type.is_none() {
return Err(ZeraError::Validation(
"Contract type must be provided (TOKEN=0, NFT=1, SBT=2)".to_string(),
));
}
let _ = contract_version;
Ok(())
}
pub fn validate_update_contract_options(contract_id: &str, contract_version: u64) -> Result<()> {
if !is_valid_contract_id(contract_id) {
return Err(ZeraError::Validation(
"ContractId must be provided and follow the format $[letters]+[4 digits] (e.g., $ZRA+0000)"
.to_string(),
));
}
if contract_version < 1 {
return Err(ZeraError::Validation(
"Contract version must be at least 1 for updates".to_string(),
));
}
Ok(())
}
pub fn validate_key_pair(
public_key_base58_identifier: &str,
private_key_base58: &str,
) -> Result<()> {
if public_key_base58_identifier.is_empty() {
return Err(ZeraError::Validation(
"Public key identifier is required".to_string(),
));
}
if private_key_base58.is_empty() {
return Err(ZeraError::Validation("Private key is required".to_string()));
}
generate_address_from_public_key(public_key_base58_identifier).map_err(|error| {
ZeraError::Validation(format!("Invalid public key identifier: {error}"))
})?;
Ok(())
}
pub fn validate_premint_wallets(
contract_type: ContractType,
premint_wallets: &[PreMintWallet],
) -> Result<()> {
if !premint_wallets.is_empty() && contract_type != ContractType::Token {
let type_name = match contract_type {
ContractType::Nft => "NFT",
ContractType::Sbt => "SBT",
ContractType::Token => "TOKEN",
};
return Err(ZeraError::Validation(format!(
"Premint wallets can only be used with TOKEN contract type. Current contract type is {type_name} ({}). Please remove premintWallets or change contract type to TOKEN.",
contract_type as i32
)));
}
Ok(())
}