use algonaut_abi::{
abi_interactions::AbiReturnType,
abi_type::{AbiType, AbiValue},
};
use algonaut_core::{AppId, TransactionId};
use algonaut_model::algod::PendingTransactionResponse;
use crate::{Error, simulate::SimulateResponse};
const ABI_RETURN_HASH: [u8; 4] = [0x15, 0x1f, 0x7c, 0x75];
#[derive(Debug, Clone)]
pub struct AbiMethodResult {
pub transaction_id: TransactionId,
pub transaction_info: PendingTransactionResponse,
pub return_value: Result<AbiMethodReturnValue, AbiReturnDecodeError>,
}
#[derive(Debug, Clone)]
pub struct AbiReturnDecodeError(pub String);
#[derive(Debug, Clone)]
pub enum AbiMethodReturnValue {
Some(AbiValue),
Void,
}
#[derive(Debug, Clone)]
pub struct ExecuteOutcome {
pub confirmed_round: Option<u64>,
pub transaction_ids: Vec<TransactionId>,
pub method_results: Vec<AbiMethodResult>,
pub created_app_id: Option<AppId>,
pub created_app_ids: Vec<(usize, AppId)>,
}
impl ExecuteOutcome {
pub fn created_app_id_at(&self, index: usize) -> Option<AppId> {
self.created_app_ids
.iter()
.find(|(i, _)| *i == index)
.map(|(_, app_id)| *app_id)
}
}
#[derive(Debug, Clone)]
pub struct SimulateOutcome {
pub transaction_ids: Vec<TransactionId>,
pub method_results: Vec<AbiMethodResult>,
pub simulate_response: SimulateResponse,
}
pub(super) fn get_return_value_with_return_type(
pending_tx: &PendingTransactionResponse,
transaction_id: &TransactionId, return_type: AbiReturnType,
) -> Result<AbiMethodResult, Error> {
let return_value = match return_type {
AbiReturnType::Some(return_type) => {
get_return_value_with_abi_type(pending_tx, &return_type)?
}
AbiReturnType::Void => Ok(AbiMethodReturnValue::Void),
};
Ok(AbiMethodResult {
transaction_id: transaction_id.to_owned(),
transaction_info: pending_tx.clone(),
return_value,
})
}
fn get_return_value_with_abi_type(
pending_tx: &PendingTransactionResponse,
abi_type: &AbiType,
) -> Result<Result<AbiMethodReturnValue, AbiReturnDecodeError>, Error> {
let logs = pending_tx.logs.as_deref().ok_or(Error::MissingReturnLog)?;
let ret_line = logs.last().ok_or(Error::MissingReturnLog)?;
let decoded_ret_line = &ret_line.0;
if !decoded_ret_line.starts_with(&ABI_RETURN_HASH) {
return Err(Error::MissingReturnLog);
}
let abi_encoded = &decoded_ret_line[ABI_RETURN_HASH.len()..];
Ok(match abi_type.decode(abi_encoded) {
Ok(decoded) => Ok(AbiMethodReturnValue::Some(decoded)),
Err(e) => Err(AbiReturnDecodeError(format!("{e:?}"))),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn created_app_id_at_finds_the_creating_index() {
let outcome = ExecuteOutcome {
confirmed_round: Some(42),
transaction_ids: vec![],
method_results: vec![],
created_app_id: None,
created_app_ids: vec![(2, AppId(777)), (4, AppId(888))],
};
assert_eq!(outcome.created_app_id_at(2), Some(AppId(777)));
assert_eq!(outcome.created_app_id_at(4), Some(AppId(888)));
assert_eq!(outcome.created_app_id_at(0), None);
assert_eq!(outcome.created_app_id_at(3), None);
}
}