use std::str::FromStr;
use anyhow::ensure;
use move_core_types::{
account_address::AccountAddress,
annotated_value::{MoveDatatypeLayout, MoveValue},
ident_str,
identifier::{IdentStr, Identifier},
language_storage::StructTag,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::{Bytes, serde_as};
use crate::{
IOTA_SYSTEM_ADDRESS,
base_types::{IotaAddress, ObjectID, TransactionDigest},
error::{IotaError, IotaResult},
iota_serde::{BigInt, Readable},
object::bounded_visitor::BoundedVisitor,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventEnvelope {
pub timestamp: u64,
pub tx_digest: TransactionDigest,
pub event_num: u64,
pub event: Event,
pub parsed_json: Value,
}
#[serde_as]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)]
#[serde(rename_all = "camelCase")]
pub struct EventID {
pub tx_digest: TransactionDigest,
#[schemars(with = "BigInt<u64>")]
#[serde_as(as = "Readable<BigInt<u64>, _>")]
pub event_seq: u64,
}
impl From<(TransactionDigest, u64)> for EventID {
fn from((tx_digest_num, event_seq_number): (TransactionDigest, u64)) -> Self {
Self {
tx_digest: tx_digest_num as TransactionDigest,
event_seq: event_seq_number,
}
}
}
impl From<EventID> for String {
fn from(id: EventID) -> Self {
format!("{:?}:{}", id.tx_digest, id.event_seq)
}
}
impl TryFrom<String> for EventID {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
let values = value.split(':').collect::<Vec<_>>();
ensure!(values.len() == 2, "Malformed EventID : {value}");
Ok((
TransactionDigest::from_str(values[0])?,
u64::from_str(values[1])?,
)
.into())
}
}
impl EventEnvelope {
pub fn new(
timestamp: u64,
tx_digest: TransactionDigest,
event_num: u64,
event: Event,
move_struct_json_value: Value,
) -> Self {
Self {
timestamp,
tx_digest,
event_num,
event,
parsed_json: move_struct_json_value,
}
}
}
#[serde_as]
#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize, Hash)]
pub struct Event {
pub package_id: ObjectID,
pub transaction_module: Identifier,
pub sender: IotaAddress,
pub type_: StructTag,
#[serde_as(as = "Bytes")]
pub contents: Vec<u8>,
}
impl Event {
pub fn new(
package_id: &AccountAddress,
module: &IdentStr,
sender: IotaAddress,
type_: StructTag,
contents: Vec<u8>,
) -> Self {
Self {
package_id: ObjectID::from(*package_id),
transaction_module: Identifier::from(module),
sender,
type_,
contents,
}
}
pub fn move_event_to_move_value(
contents: &[u8],
layout: MoveDatatypeLayout,
) -> IotaResult<MoveValue> {
BoundedVisitor::deserialize_value(contents, &layout.into_layout()).map_err(|e| {
IotaError::ObjectSerialization {
error: e.to_string(),
}
})
}
pub fn is_system_epoch_info_event(&self) -> bool {
self.type_.address == IOTA_SYSTEM_ADDRESS
&& self.type_.module.as_ident_str() == ident_str!("iota_system_state_inner")
&& self.type_.name.as_ident_str() == ident_str!("SystemEpochInfoEventV1")
}
}
impl Event {
pub fn random_for_testing() -> Self {
Self {
package_id: ObjectID::random(),
transaction_module: Identifier::new("test").unwrap(),
sender: AccountAddress::random().into(),
type_: StructTag {
address: AccountAddress::random(),
module: Identifier::new("test").unwrap(),
name: Identifier::new("test").unwrap(),
type_params: vec![],
},
contents: vec![],
}
}
}
#[derive(Deserialize)]
pub struct SystemEpochInfoEventV1 {
pub epoch: u64,
pub protocol_version: u64,
pub reference_gas_price: u64,
pub total_stake: u64,
pub storage_charge: u64,
pub storage_rebate: u64,
pub storage_fund_balance: u64,
pub total_gas_fees: u64,
pub total_stake_rewards_distributed: u64,
pub burnt_tokens_amount: u64,
pub minted_tokens_amount: u64,
}