use super::utils::*;
use crate::{
helper_fn::inner_get_agent_app_entries_activity,
rhai_engine::RhaiEngine,
types::{
entries::{
parked_link::{ParkedLink, ParkedLinkType},
DataBlob,
},
Ledger, UnitMap,
},
};
use hdi::prelude::{debug, trace, wasm_error, Action, ActionHash, ExternResult, Timestamp};
use rhai::{Array, Dynamic, EvalAltResult, Map, Position};
use serde_json::Value;
use std::time::Duration;
use zfuel::fuel::ZFuel;
pub fn get_spend_links_author(spend_link_hash: String) -> Result<Dynamic, Box<EvalAltResult>> {
let spend_link_hash = ActionHash::try_from(spend_link_hash).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse ActionHash: {e}").into(),
Position::NONE,
))
})?;
let a = ParkedLink::must_get(&spend_link_hash).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get ParkedLink: {e}").into(),
Position::NONE,
))
})?;
Ok(Dynamic::from(a.creator.to_string()))
}
pub fn check_cool_down_period(
most_recent_spend_link_hash: String,
agreement_id: String,
executed_timestamp: String,
cool_down_period: String,
withdrawing_unit_index: String,
) -> Result<Dynamic, Box<EvalAltResult>> {
let most_recent_spend_link_hash =
ActionHash::try_from(most_recent_spend_link_hash).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse ActionHash: {e}").into(),
Position::NONE,
))
})?;
let agreement_id = ActionHash::try_from(agreement_id).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse ActionHash: {e}").into(),
Position::NONE,
))
})?;
let current_link = ParkedLink::must_get(&most_recent_spend_link_hash).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get ParkedLink: {e}").into(),
Position::NONE,
))
})?;
let executed_timestamp = Timestamp::try_from(executed_timestamp).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse Timestamp: {e}").into(),
Position::NONE,
))
})?;
let until_time = (executed_timestamp
- Duration::from_secs(cool_down_period.parse::<u64>().map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse cool_down_period: {e}").into(),
Position::NONE,
))
})?))
.expect("Failed to subtract seconds from timestamp");
let author = current_link.creator;
let (agents_chain_entry_records, _agent_activity) = inner_get_agent_app_entries_activity(
author.to_owned(),
most_recent_spend_link_hash.to_owned(),
)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
e.to_string().into(),
Position::NONE,
))
})?;
let filtered_agents_chain_entry_record_before_timestamp = agents_chain_entry_records
.clone()
.into_iter()
.filter(|r| r.action().timestamp() < until_time)
.collect::<Vec<_>>();
let ledger_at_time =
Ledger::calculate_from(&filtered_agents_chain_entry_record_before_timestamp);
let all_links: Vec<ParkedLink> = agents_chain_entry_records
.iter()
.filter(|s| s.action().timestamp() > until_time)
.filter_map(|r| r.try_into().ok())
.filter(|s: &ParkedLink| s.ea_id == agreement_id)
.collect();
let total_spend_amounts = all_links.iter().filter_map(|s| s.amount()).collect();
let total_spends = UnitMap::sum_vec(total_spend_amounts).unwrap_or_default();
let ledger_balance_for_unit_6_hours_ago =
ledger_at_time.balance.get_safe(&withdrawing_unit_index);
let total_spend_amounts_for_unit_6_hours_ago = total_spends.get_safe(&withdrawing_unit_index);
let difference = (ledger_balance_for_unit_6_hours_ago
- total_spend_amounts_for_unit_6_hours_ago)
.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
e.to_string().into(),
Position::NONE,
))
})?;
if difference < ZFuel::zero() {
return Ok(Dynamic::from(false));
}
Ok(Dynamic::from(true))
}
fn get_parked_link_from_source(allocation: Map) -> ExternResult<(ParkedLink, ActionHash)> {
let source = allocation["source"].to_string();
let source_action = ActionHash::try_from(source)
.map_err(|e| wasm_error!("Failed to parse ActionHash: {}", e))?;
let parked_link = ParkedLink::must_get(&source_action)?;
Ok((parked_link, source_action))
}
pub fn acceding_sort_allocation(allocation: Array) -> Result<Array, Box<EvalAltResult>> {
let parked_links: Vec<(ParkedLink, ActionHash)> = allocation
.into_iter()
.filter_map(|a| {
a.try_cast::<Map>()
.and_then(|map| get_parked_link_from_source(map).ok())
})
.collect();
let mut sorted_parked_links = parked_links;
sorted_parked_links.sort_by_key(|(a, _)| a.timestamp);
let sorted_parked_links_map: Vec<Map> = sorted_parked_links
.into_iter()
.map(|(a, id)| {
let mut map = Map::new();
let amount: Map = if let Some(amount) = a.amount() {
amount.to_map()
} else {
UnitMap::new().to_map()
};
map.insert("amount".into(), Dynamic::from(amount));
map.insert("source".into(), Dynamic::from(id.to_string()));
map.insert("spender".into(), Dynamic::from(a.creator.to_string()));
map
})
.collect();
Ok(sorted_parked_links_map
.into_iter()
.map(Dynamic::from)
.collect())
}
pub fn get_data_blob(hash: String) -> Result<Dynamic, Box<EvalAltResult>> {
let blob_hash_id = ActionHash::try_from(hash).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse ActionHash: {e}").into(),
Position::NONE,
))
})?;
let blob = DataBlob::must_get(&blob_hash_id).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to get DataBlob: {e}").into(),
Position::NONE,
))
})?;
let blob_bytes = (blob.0).data;
let value: Value = rmp_serde::from_slice(&blob_bytes).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse DataBlob: {blob_bytes:?} Error: {e} End").into(),
Position::NONE,
))
})?;
let dynamic = RhaiEngine::json_to_rhai_dynamic(&value);
debug!("value: {:?}", dynamic);
Ok(dynamic)
}
pub fn parse_record_to_parked_amount_and_source(
record: Map,
) -> Result<Dynamic, Box<EvalAltResult>> {
let record = try_into_record(record)?;
let record_id = record.action_address();
let action = record.action();
let create_link = match action {
Action::CreateLink(create_link) => create_link,
_ => {
return Err(Box::new(EvalAltResult::ErrorRuntime(
"Expected CreateLink action".into(),
Position::NONE,
)))
}
};
let tag = ParkedLinkType::from_create_link(create_link).map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Failed to parse ParkedData: {e}").into(),
Position::NONE,
))
})?;
if let ParkedLinkType::ParkedSpendBalance(parked_spend_tag) = tag {
trace!("parked_spend_tag: {:?}", parked_spend_tag);
let mut map = Map::new();
map.insert(
"amount".into(),
Dynamic::from(parked_spend_tag.amount.to_map()),
);
map.insert("source".into(), Dynamic::from(record_id.to_string()));
trace!("parse_record_to_parked_amount_and_source: {:?}", map);
Ok(Dynamic::from(map))
} else if let ParkedLinkType::ParkedSpendCredit(parked_spend_tag) = tag {
trace!("parked_spend_credit_tag: {:?}", parked_spend_tag);
let mut map = Map::new();
map.insert(
"amount".into(),
Dynamic::from(parked_spend_tag.amount.to_map()),
);
map.insert("source".into(), Dynamic::from(record_id.to_string()));
trace!("parse_record_to_parked_amount_and_source: {:?}", map);
Ok(Dynamic::from(map))
} else {
Err(Box::new(EvalAltResult::ErrorRuntime(
"Expected ParkedSpendData".into(),
Position::NONE,
)))
}
}