ton_lib 0.0.38

A collection of types and utilities for interacting with the TON network
use crate::block_tlb::TVMStack;
use crate::contracts::client::contract_client_cache::ContractClientCache;
use crate::emulators::emul_bc_config::EmulBCConfig;
use crate::emulators::tvm::tvm_c7::TVMEmulatorC7;
use crate::emulators::tvm::tvm_emulator::TVMEmulator;
use crate::emulators::tvm::tvm_response::TVMGetMethodSuccess;
use crate::error::TLError;
use crate::libs_dict::LibsDict;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::sync::OnceCell;
use ton_lib_core::cell::{TonCell, TonCellRef, TonCellUtils, TonHash};
use ton_lib_core::error::TLCoreError;
use ton_lib_core::traits::contract_provider::{ContractProvider, ContractState};
use ton_lib_core::traits::tlb::TLB;
use ton_lib_core::types::{TonAddress, TxIdLTHash};

#[derive(Clone, Copy, Debug)]
pub struct ContractClientConfig {
    pub refresh_loop_idle_on_error: Duration,
    pub cache_capacity: u64,
    pub cache_ttl: Duration,
}

impl ContractClientConfig {
    pub fn new_no_cache(idle_on_error: Duration) -> Self {
        ContractClientConfig {
            refresh_loop_idle_on_error: idle_on_error,
            cache_capacity: 0,
            cache_ttl: Duration::from_secs(0),
        }
    }
}

#[derive(Clone)]
pub struct ContractClient(Arc<Inner>);

impl ContractClient {
    pub fn new(config: ContractClientConfig, data_provider: impl ContractProvider) -> Result<Self, TLError> {
        let provider = Arc::new(data_provider);
        let inner = Inner {
            provider: provider.clone(),
            cache: ContractClientCache::new(config, provider.clone())?,
            bc_config: OnceCell::new(),
        };
        Ok(ContractClient(Arc::new(inner)))
    }

    pub async fn get_contract(
        &self,
        address: &TonAddress,
        tx_id: Option<&TxIdLTHash>,
    ) -> Result<Arc<ContractState>, TLError> {
        self.0.cache.get_or_load_contract(address, tx_id).await
    }

    pub async fn emulate_get_method(
        &self,
        state: &ContractState,
        method_id: i32,
        stack_boc: Option<&[u8]>,
    ) -> Result<TVMGetMethodSuccess, TLError> {
        let code_boc = match &state.code_boc {
            Some(boc) => boc,
            None => {
                let err_msg = format!("code is None at state: {state:?}");
                return Err(TLCoreError::ContractError(err_msg).into());
            }
        };

        let data_boc = state.data_boc.as_deref().unwrap_or(&[]);

        let c7 = TVMEmulatorC7 {
            address: state.address.clone(),
            unix_time: SystemTime::now().duration_since(UNIX_EPOCH).map_err(TLCoreError::from)?.as_secs() as u32,
            balance: state.balance as u64,
            rand_seed: TonHash::ZERO,
            config: self.get_bc_config().await?.clone(),
        };

        let mut emulator = TVMEmulator::new(code_boc, data_boc, &c7)?;

        let code_cell = TonCell::from_boc(code_boc)?;
        let data_cell = TonCell::from_boc(data_boc)?;
        let lib_ids = TonCellUtils::extract_lib_ids([&code_cell, &data_cell])?;
        let libs_rsp = self
            .0
            .provider
            .load_libs(lib_ids.into_iter().collect(), state.mc_seqno)
            .await?
            .into_iter()
            .map(|(_, lib)| TonCellRef::from_boc(&lib))
            .collect::<Result<Vec<_>, _>>()?;

        if !libs_rsp.is_empty() {
            emulator.set_libs(&LibsDict::new(libs_rsp)?.to_boc()?)?;
        }
        let stack = stack_boc.unwrap_or(TVMStack::EMPTY_BOC);
        emulator.run_get_method(method_id, stack)
    }

    pub fn cache_stats(&self) -> HashMap<String, usize> { self.0.cache.cache_stats() }

    async fn get_bc_config(&self) -> Result<&EmulBCConfig, TLError> {
        self.0
            .bc_config
            .get_or_try_init(|| async {
                let config = self.0.provider.load_bc_config(None).await?;
                EmulBCConfig::from_boc(&config)
            })
            .await
    }
}

struct Inner {
    provider: Arc<dyn ContractProvider>,
    cache: Arc<ContractClientCache>,
    bc_config: OnceCell<EmulBCConfig>,
}