use zera_proto::zera_txn::{Parameters, SmartContractExecuteTxn};
use crate::contract::shared::validate_key_pair;
use crate::crypto::address::generate_address_from_public_key;
use crate::error::{Result, ZeraError};
use crate::fees::{FeeConfigHelper, UniversalFeeCalculator};
use crate::grpc::{submit_transaction, UnaryTransport, ValidatorApiClient};
use crate::sign::sign_with_key;
use crate::tx::{
build_standard_base_txn, get_address_and_nonce_with_client, BuildStandardBaseTxnParams,
};
use crate::types::RpcConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParameterType {
Bytes,
Uint32,
Uint64,
String,
}
impl ParameterType {
pub fn as_str(self) -> &'static str {
match self {
Self::Bytes => "bytes",
Self::Uint32 => "uint32",
Self::Uint64 => "uint64",
Self::String => "string",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExecuteParameter {
pub parameter_type: String,
pub value: Vec<u8>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct BuildSmartContractExecuteOptions {
pub memo: Option<String>,
pub grpc_config: Option<RpcConfig>,
pub gas_fee_in_usd: Option<f64>,
pub overestimate_percent: Option<f64>,
pub nonce: Option<u64>,
pub fee_id: Option<String>,
pub fee_amount_parts: Option<String>,
}
pub type CreateSmartContractExecuteOptions = BuildSmartContractExecuteOptions;
pub async fn build_smart_contract_execute_txn(
smart_contract_name: &str,
instance: u32,
function_name: &str,
parameters: &[ExecuteParameter],
public_key_base58_identifier: &str,
options: BuildSmartContractExecuteOptions,
) -> Result<SmartContractExecuteTxn> {
let client = ValidatorApiClient::new(options.grpc_config.clone().unwrap_or_default())?;
build_smart_contract_execute_txn_with_client(
smart_contract_name,
instance,
function_name,
parameters,
public_key_base58_identifier,
options,
&client,
)
.await
}
pub async fn build_smart_contract_execute_txn_with_client<T>(
smart_contract_name: &str,
instance: u32,
function_name: &str,
parameters: &[ExecuteParameter],
public_key_base58_identifier: &str,
options: BuildSmartContractExecuteOptions,
client: &ValidatorApiClient<T>,
) -> Result<SmartContractExecuteTxn>
where
T: UnaryTransport,
{
if smart_contract_name.is_empty() {
return Err(ZeraError::Validation(
"smartContractName is required".to_string(),
));
}
if function_name.is_empty() {
return Err(ZeraError::Validation(
"functionName is required".to_string(),
));
}
if public_key_base58_identifier.is_empty() {
return Err(ZeraError::Validation(
"publicKeyBase58Identifier is required".to_string(),
));
}
generate_address_from_public_key(public_key_base58_identifier)?;
let nonce = if let Some(nonce) = options.nonce {
nonce
} else {
get_address_and_nonce_with_client(public_key_base58_identifier, client)
.await?
.1
};
let base = build_standard_base_txn(BuildStandardBaseTxnParams {
public_key_id: public_key_base58_identifier.to_string(),
fee_id: options.fee_id.clone(),
fee_amount_parts: options.fee_amount_parts.clone(),
nonce,
memo: options.memo.clone(),
})?;
let mut execute_txn = SmartContractExecuteTxn {
base: Some(base),
smart_contract_name: smart_contract_name.to_string(),
instance,
function: function_name.to_string(),
parameters: parameters
.iter()
.map(|parameter| Parameters {
value: parameter.value.clone(),
r#type: parameter.parameter_type.clone(),
})
.collect(),
};
execute_txn = UniversalFeeCalculator::calculate_fee_with_client(
FeeConfigHelper {
contract_id: None,
proto_object: execute_txn,
token_info_map: std::collections::HashMap::new(),
base_fee_id: options.fee_id.clone(),
base_fee: options.fee_amount_parts.clone(),
contract_fee_id: None,
contract_fee: None,
interface_fee_id: None,
interface_fee: None,
interface_address: None,
overestimate_percent: options.overestimate_percent,
gas_fee_in_usd: options.gas_fee_in_usd,
grpc_config: None,
needs_initialization: None,
},
client,
)
.await?;
Ok(execute_txn)
}
pub async fn create_smart_contract_execute_txn(
smart_contract_name: &str,
instance: u32,
function_name: &str,
parameters: &[ExecuteParameter],
public_key_base58_identifier: &str,
private_key_base58: &str,
options: CreateSmartContractExecuteOptions,
) -> Result<SmartContractExecuteTxn> {
if private_key_base58.is_empty() {
return Err(ZeraError::Validation(
"privateKeyBase58 is required".to_string(),
));
}
validate_key_pair(public_key_base58_identifier, private_key_base58)?;
let mut execute_txn = build_smart_contract_execute_txn(
smart_contract_name,
instance,
function_name,
parameters,
public_key_base58_identifier,
options,
)
.await?;
sign_with_key(
&mut execute_txn,
private_key_base58,
public_key_base58_identifier,
)?;
Ok(execute_txn)
}
pub async fn send_smart_contract_execute_txn(
txn: &SmartContractExecuteTxn,
grpc_config: Option<RpcConfig>,
) -> Result<String> {
submit_transaction(txn, grpc_config.unwrap_or_default()).await
}