pub mod ast;
pub mod canonical_log;
pub mod compiler;
pub mod debugger;
pub mod event_type_helpers;
pub mod identifiers;
mod idl_models;
pub mod metrics_context;
mod packed_row;
pub mod program_sdk;
pub mod proto_router;
pub mod public_artifacts;
pub mod python;
pub mod resolvers;
pub mod runtime_resolvers;
pub mod runtime_resolvers_factory;
pub mod rust;
mod rust_doc;
pub mod scheduler;
pub mod slot_hash_cache;
pub mod snapshot;
pub mod spec_trait;
mod stack_types;
pub mod transaction_metadata;
pub mod typescript;
pub mod typescript_instructions;
pub mod versioned;
pub mod vm;
pub mod vm_metrics;
pub use slot_hash_cache::{get_slot_hash, record_slot_hash};
pub use canonical_log::{CanonicalLog, LogLevel};
pub use debugger::{VmDebugEvent, VmDebugger, VmLookupHop};
pub use metrics_context::{FieldAccessor, FieldRef, MetricsContext};
pub use resolvers::{
InstructionContext, KeyResolution, ResolveContext, ReverseLookupUpdater, TokenMetadata,
};
pub use runtime_resolvers::{
InProcessResolver, ResolverApplyFuture, ResolverBatchFuture, ResolverBatchResult,
RuntimeResolver, RuntimeResolverBatchRequest, RuntimeResolverBatchResponse,
RuntimeResolverRequest, RuntimeResolverResponse, SharedRuntimeResolver,
};
pub use transaction_metadata::{
SolanaTransactionConfig, SolanaTransactionMetadata, SolanaTransactionVersion,
SOLANA_TRANSACTION_METADATA_KEY,
};
pub use typescript::{write_typescript_to_file, TypeScriptCompiler, TypeScriptConfig};
pub use vm::{
CapacityWarning, CleanupResult, DirtyTracker, FieldChange, PendingAccountUpdate,
PendingQueueStats, QueuedAccountUpdate, ResolverRequest, ResolverTarget, ScheduledCallback,
StateTableConfig, UpdateContext, VmMemoryStats,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Mutation {
pub export: String,
pub key: Value,
pub patch: Value,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub append: Vec<String>,
}
pub const WHOLE_ENTITY_MARKER: &str = "__arete_whole_entity";
pub const ENTITY_DELETE_MARKER: &str = "__arete_entity_delete";
pub const ACCOUNT_POSITION_MARKER: &str = "__arete_account_position";
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AccountPosition {
#[serde(with = "decimal_u64")]
pub slot: u64,
#[serde(with = "decimal_u64")]
pub write_version: u64,
}
impl AccountPosition {
pub const fn new(slot: u64, write_version: u64) -> Self {
Self {
slot,
write_version,
}
}
}
mod decimal_u64 {
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&value.to_string())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
value.parse().map_err(serde::de::Error::custom)
}
}
const CREATED_MARK: &str = "created";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WholeEntity {
Created,
Resent,
}
impl Mutation {
pub fn delete(export: impl Into<String>, key: Value) -> Self {
Self {
export: export.into(),
key,
patch: serde_json::json!({ ENTITY_DELETE_MARKER: true }),
append: Vec::new(),
}
}
pub fn is_delete(&self) -> bool {
self.patch.get(ENTITY_DELETE_MARKER) == Some(&Value::Bool(true))
}
pub fn mark_account_position(&mut self, position: AccountPosition) {
if let Value::Object(fields) = &mut self.patch {
fields.insert(
ACCOUNT_POSITION_MARKER.to_string(),
serde_json::to_value(position).expect("AccountPosition is serializable"),
);
}
}
pub fn take_account_position(&mut self) -> Result<Option<AccountPosition>, String> {
let Value::Object(fields) = &mut self.patch else {
return Ok(None);
};
let Some(value) = fields.remove(ACCOUNT_POSITION_MARKER) else {
return Ok(None);
};
serde_json::from_value(value)
.map(Some)
.map_err(|error| format!("invalid {ACCOUNT_POSITION_MARKER}: {error}"))
}
pub fn mark_whole_entity(&mut self) {
self.set_whole_entity_mark(Value::Bool(true));
}
pub fn mark_created(&mut self) {
self.set_whole_entity_mark(Value::String(CREATED_MARK.to_string()));
}
fn set_whole_entity_mark(&mut self, mark: Value) {
if let Value::Object(fields) = &mut self.patch {
fields.insert(WHOLE_ENTITY_MARKER.to_string(), mark);
}
}
pub fn whole_entity_mark(&self) -> Option<WholeEntity> {
Self::read_whole_entity_mark(self.patch.get(WHOLE_ENTITY_MARKER)?)
}
pub fn is_whole_entity(&self) -> bool {
self.whole_entity_mark().is_some()
}
pub fn take_whole_entity_mark(&mut self) -> Option<WholeEntity> {
match &mut self.patch {
Value::Object(fields) => {
Self::read_whole_entity_mark(&fields.remove(WHOLE_ENTITY_MARKER)?)
}
_ => None,
}
}
fn read_whole_entity_mark(mark: &Value) -> Option<WholeEntity> {
match mark {
Value::Bool(true) => Some(WholeEntity::Resent),
Value::String(kind) if kind == CREATED_MARK => Some(WholeEntity::Created),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventWrapper<T = Value> {
pub timestamp: i64,
pub data: T,
#[serde(skip_serializing_if = "Option::is_none")]
pub slot: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signature: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_index: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ix_path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CaptureWrapper<T = Value> {
pub timestamp: i64,
pub account_address: String,
pub data: T,
#[serde(skip_serializing_if = "Option::is_none")]
pub slot: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signature: Option<String>,
}
#[cfg(test)]
mod mutation_metadata_tests {
use super::*;
use serde_json::json;
#[test]
fn account_position_is_exact_reserved_metadata() {
let mut mutation = Mutation {
export: "Account".into(),
key: json!("address"),
patch: json!({"balance": 1}),
append: vec![],
};
let position = AccountPosition::new(9_007_199_254_740_993, u64::MAX);
mutation.mark_account_position(position);
assert_eq!(
mutation.patch[ACCOUNT_POSITION_MARKER],
json!({
"slot": "9007199254740993",
"writeVersion": "18446744073709551615"
})
);
assert_eq!(mutation.take_account_position().unwrap(), Some(position));
assert!(mutation.patch.get(ACCOUNT_POSITION_MARKER).is_none());
assert_eq!(mutation.patch["balance"], 1);
}
}