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}