Skip to main content

zera_sdk/contract/
shared.rs

1use zera_proto::zera_txn::{
2    CoinDenomination, ContractFees, ContractType, ExpenseRatio, Governance, KeyValuePair,
3    MaxSupplyRelease, PreMintWallet, RestrictedKey, TokenCompliance,
4};
5
6use crate::crypto::address::generate_address_from_public_key;
7use crate::error::{Result, ZeraError};
8use crate::types::RpcConfig;
9use crate::utils::validation::is_valid_contract_id;
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct CreateContractOptions {
13    pub contract_version: u64,
14    pub symbol: String,
15    pub name: String,
16    pub contract_type: ContractType,
17    pub contract_id: String,
18    pub public_key_base58_identifier: String,
19    pub private_key_base58: String,
20    pub governance: Option<Governance>,
21    pub restricted_keys: Vec<RestrictedKey>,
22    pub max_supply: Option<String>,
23    pub contract_fees: Option<ContractFees>,
24    pub premint_wallets: Vec<PreMintWallet>,
25    pub coin_denomination: CoinDenomination,
26    pub custom_parameters: Vec<KeyValuePair>,
27    pub expense_ratio: Vec<ExpenseRatio>,
28    pub update_contract_fees: Option<bool>,
29    pub update_expense_ratio: Option<bool>,
30    pub quash_threshold: Option<u32>,
31    pub token_compliance: Vec<TokenCompliance>,
32    pub kyc_status: Option<bool>,
33    pub immutable_kyc_status: Option<bool>,
34    pub max_supply_release: Vec<MaxSupplyRelease>,
35    pub fee_id: Option<String>,
36    pub fee_amount_parts: Option<String>,
37    pub memo: Option<String>,
38    pub grpc_config: Option<RpcConfig>,
39    pub nonce: Option<u64>,
40}
41
42pub type BuildContractOptions = CreateContractOptions;
43
44#[derive(Debug, Clone, PartialEq)]
45pub struct UpdateContractOptions {
46    pub contract_id: String,
47    pub contract_version: u64,
48    pub public_key_base58_identifier: String,
49    pub private_key_base58: String,
50    pub name: Option<String>,
51    pub governance: Option<Governance>,
52    pub restricted_keys: Vec<RestrictedKey>,
53    pub contract_fees: Option<ContractFees>,
54    pub custom_parameters: Vec<KeyValuePair>,
55    pub expense_ratio: Vec<ExpenseRatio>,
56    pub token_compliance: Vec<TokenCompliance>,
57    pub kyc_status: Option<bool>,
58    pub immutable_kyc_status: Option<bool>,
59    pub quash_threshold: Option<u32>,
60    pub fee_id: Option<String>,
61    pub fee_amount_parts: Option<String>,
62    pub memo: Option<String>,
63    pub grpc_config: Option<RpcConfig>,
64    pub nonce: Option<u64>,
65}
66
67pub fn validate_create_contract_options(
68    contract_id: &str,
69    symbol: &str,
70    name: &str,
71    contract_version: u64,
72    contract_type: Option<ContractType>,
73) -> Result<()> {
74    if !is_valid_contract_id(contract_id) {
75        return Err(ZeraError::Validation(
76            "ContractId must be provided and follow the format $[letters]+[4 digits] (e.g., $ZRA+0000)"
77                .to_string(),
78        ));
79    }
80    if symbol.trim().is_empty() {
81        return Err(ZeraError::Validation(
82            "Symbol must be provided and non-empty".to_string(),
83        ));
84    }
85    if name.trim().is_empty() {
86        return Err(ZeraError::Validation(
87            "Name must be provided and non-empty".to_string(),
88        ));
89    }
90    if contract_type.is_none() {
91        return Err(ZeraError::Validation(
92            "Contract type must be provided (TOKEN=0, NFT=1, SBT=2)".to_string(),
93        ));
94    }
95    let _ = contract_version;
96    Ok(())
97}
98
99pub fn validate_update_contract_options(contract_id: &str, contract_version: u64) -> Result<()> {
100    if !is_valid_contract_id(contract_id) {
101        return Err(ZeraError::Validation(
102            "ContractId must be provided and follow the format $[letters]+[4 digits] (e.g., $ZRA+0000)"
103                .to_string(),
104        ));
105    }
106    if contract_version < 1 {
107        return Err(ZeraError::Validation(
108            "Contract version must be at least 1 for updates".to_string(),
109        ));
110    }
111    Ok(())
112}
113
114pub fn validate_key_pair(
115    public_key_base58_identifier: &str,
116    private_key_base58: &str,
117) -> Result<()> {
118    if public_key_base58_identifier.is_empty() {
119        return Err(ZeraError::Validation(
120            "Public key identifier is required".to_string(),
121        ));
122    }
123    if private_key_base58.is_empty() {
124        return Err(ZeraError::Validation("Private key is required".to_string()));
125    }
126    generate_address_from_public_key(public_key_base58_identifier).map_err(|error| {
127        ZeraError::Validation(format!("Invalid public key identifier: {error}"))
128    })?;
129    Ok(())
130}
131
132pub fn validate_premint_wallets(
133    contract_type: ContractType,
134    premint_wallets: &[PreMintWallet],
135) -> Result<()> {
136    if !premint_wallets.is_empty() && contract_type != ContractType::Token {
137        let type_name = match contract_type {
138            ContractType::Nft => "NFT",
139            ContractType::Sbt => "SBT",
140            ContractType::Token => "TOKEN",
141        };
142        return Err(ZeraError::Validation(format!(
143            "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.",
144            contract_type as i32
145        )));
146    }
147    Ok(())
148}