From 41324b2e1c4b7b754a4bf2ce098f3b09ea7d92c8 Mon Sep 17 00:00:00 2001 From: Nasr Date: Mon, 14 Jul 2025 12:28:08 +0800 Subject: [PATCH] feat: transactions query --- src/c/mod.rs | 26 +++++++++- src/c/types.rs | 106 ++++++++++++++++++++++++++++++++++++++++ src/wasm/mod.rs | 22 ++++++++- src/wasm/types.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 271 insertions(+), 3 deletions(-) diff --git a/src/c/mod.rs b/src/c/mod.rs index 6eaae75..af19eba 100644 --- a/src/c/mod.rs +++ b/src/c/mod.rs @@ -57,7 +57,9 @@ use types::{ }; use url::Url; -use crate::c::types::{ControllerQuery, TokenBalanceQuery, TokenQuery}; +use crate::c::types::{ + ControllerQuery, TokenBalanceQuery, TokenQuery, Transaction, TransactionQuery, +}; use crate::constants; use crate::types::{ Account, ControllerAccount, Provider, RegisterSessionResponse, RegisteredAccount, @@ -816,6 +818,28 @@ pub unsafe extern "C" fn client_metadata(client: *mut ToriiClient) -> Result Result> { + let query = query.into(); + let transactions_future = unsafe { (*client).inner.transactions(query) }; + + match RUNTIME.block_on(transactions_future) { + Ok(transactions) => Result::Ok(transactions.into()), + Err(e) => Result::Err(e.into()), + } +} + /// Subscribes to entity state updates /// /// # Parameters diff --git a/src/c/types.rs b/src/c/types.rs index 69a1722..98b0424 100644 --- a/src/c/types.rs +++ b/src/c/types.rs @@ -522,6 +522,112 @@ impl From for torii_proto::TokenBalanceQuery { } } +#[derive(Clone, Debug)] +#[repr(C)] +pub struct TransactionQuery { + pub transaction_hashes: CArray, + pub caller_addresses: CArray, + pub contract_addresses: CArray, + pub entrypoints: CArray<*const c_char>, + pub model_selectors: CArray, + pub from_block: COption, + pub to_block: COption, + pub pagination: Pagination, +} + +impl From for torii_proto::TransactionQuery { + fn from(val: TransactionQuery) -> Self { + let entrypoints: Vec<*const c_char> = val.entrypoints.into(); + let entrypoints = entrypoints + .into_iter() + .map(|e| unsafe { CStr::from_ptr(e).to_string_lossy().to_string() }) + .collect::>(); + + torii_proto::TransactionQuery { + pagination: val.pagination.into(), + transaction_hashes: val.transaction_hashes.into(), + caller_addresses: val.caller_addresses.into(), + contract_addresses: val.contract_addresses.into(), + entrypoints, + model_selectors: val.model_selectors.into(), + from_block: val.from_block.into(), + to_block: val.to_block.into(), + } + } +} + +#[derive(Clone, Debug)] +#[repr(C)] +pub struct Transaction { + pub transaction_hash: FieldElement, + pub sender_address: FieldElement, + pub calldata: CArray, + pub max_fee: FieldElement, + pub signature: CArray, + pub nonce: FieldElement, + pub block_number: u64, + pub transaction_type: *const c_char, + pub block_timestamp: u64, + pub calls: CArray, + pub unique_models: CArray, +} + +impl From for Transaction { + fn from(val: torii_proto::Transaction) -> Self { + Transaction { + transaction_hash: val.transaction_hash.into(), + sender_address: val.sender_address.into(), + calldata: val.calldata.into(), + max_fee: val.max_fee.into(), + signature: val.signature.into(), + nonce: val.nonce.into(), + block_number: val.block_number, + transaction_type: CString::new(val.transaction_type).unwrap().into_raw(), + block_timestamp: val.block_timestamp.timestamp() as u64, + calls: val.calls.into(), + unique_models: val.unique_models.into(), + } + } +} + +#[derive(Clone, Debug)] +#[repr(C)] +pub enum CallType { + Execute, + ExecuteFromOutside, +} + +impl From for CallType { + fn from(val: torii_proto::CallType) -> Self { + match val { + torii_proto::CallType::Execute => CallType::Execute, + torii_proto::CallType::ExecuteFromOutside => CallType::ExecuteFromOutside, + } + } +} + +#[derive(Clone, Debug)] +#[repr(C)] +pub struct TransactionCall { + pub contract_address: FieldElement, + pub entrypoint: *const c_char, + pub calldata: CArray, + pub call_type: CallType, + pub caller_address: FieldElement, +} + +impl From for TransactionCall { + fn from(val: torii_proto::TransactionCall) -> Self { + TransactionCall { + contract_address: val.contract_address.into(), + entrypoint: CString::new(val.entrypoint).unwrap().into_raw(), + calldata: val.calldata.into(), + call_type: val.call_type.into(), + caller_address: val.caller_address.into(), + } + } +} + #[derive(Clone, Debug)] #[repr(C)] pub struct Query { diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs index dfe2a32..614d585 100644 --- a/src/wasm/mod.rs +++ b/src/wasm/mod.rs @@ -34,14 +34,14 @@ use wasm_bindgen::prelude::*; use crate::constants; use crate::types::{Account, Provider, Subscription, ToriiClient}; use crate::utils::watch_tx; -use crate::wasm::types::{ControllerQuery, TokenBalanceQuery, TokenQuery}; +use crate::wasm::types::{ControllerQuery, TokenBalanceQuery, TokenQuery, TransactionQuery}; mod types; use types::{ BlockId, Call, Calls, Clause, ClientConfig, Controller, Controllers, Entities, Entity, IndexerUpdate, KeysClause, KeysClauses, Message, Model, Page, Query, Signature, Token, - TokenBalance, TokenBalances, TokenCollections, Tokens, WasmU256, + TokenBalance, TokenBalances, TokenCollections, Tokens, Transaction, Transactions, WasmU256, }; const JSON_COMPAT_SERIALIZER: serde_wasm_bindgen::Serializer = @@ -726,6 +726,24 @@ impl ToriiClient { Ok(Controllers(controllers.into())) } + /// Gets transactions matching the given query + /// + /// # Parameters + /// * `query` - Query parameters + /// + /// # Returns + /// Result containing transactions or error + #[wasm_bindgen(js_name = getTransactions)] + pub async fn get_transactions(&self, query: TransactionQuery) -> Result { + let query = query.into(); + let transactions = self + .inner + .transactions(query) + .await + .map_err(|e| JsValue::from(format!("failed to get transactions: {e}")))?; + Ok(Transactions(transactions.into())) + } + /// Gets token information for the given contract addresses /// /// # Parameters diff --git a/src/wasm/types.rs b/src/wasm/types.rs index e7141e1..0f67529 100644 --- a/src/wasm/types.rs +++ b/src/wasm/types.rs @@ -155,6 +155,126 @@ impl From for TokenBalance { } } +#[derive(Tsify, Serialize, Deserialize, Debug)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct TransactionQuery { + pub transaction_hashes: Vec, + pub caller_addresses: Vec, + pub contract_addresses: Vec, + pub entrypoints: Vec, + pub model_selectors: Vec, + pub from_block: Option, + pub to_block: Option, + pub pagination: Pagination, +} + +impl From for torii_proto::TransactionQuery { + fn from(val: TransactionQuery) -> Self { + torii_proto::TransactionQuery { + pagination: val.pagination.into(), + transaction_hashes: val + .transaction_hashes + .into_iter() + .map(|h| Felt::from_str(h.as_str()).unwrap()) + .collect(), + caller_addresses: val + .caller_addresses + .into_iter() + .map(|h| Felt::from_str(h.as_str()).unwrap()) + .collect(), + contract_addresses: val + .contract_addresses + .into_iter() + .map(|h| Felt::from_str(h.as_str()).unwrap()) + .collect(), + entrypoints: val.entrypoints, + model_selectors: val + .model_selectors + .into_iter() + .map(|h| Felt::from_str(h.as_str()).unwrap()) + .collect(), + from_block: val.from_block.into(), + to_block: val.to_block.into(), + } + } +} + +#[derive(Tsify, Serialize, Deserialize, Debug)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct Transactions(pub Page); + +#[derive(Tsify, Serialize, Deserialize, Debug)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct Transaction { + pub transaction_hash: String, + pub sender_address: String, + pub calldata: Vec, + pub max_fee: String, + pub signature: Vec, + pub nonce: String, + pub block_number: u64, + pub transaction_type: String, + pub block_timestamp: u64, + pub calls: Vec, + pub unique_models: Vec, +} + +impl From for Transaction { + fn from(val: torii_proto::Transaction) -> Self { + Transaction { + transaction_hash: format!("{:#x}", val.transaction_hash), + sender_address: format!("{:#x}", val.sender_address), + calldata: val.calldata.into_iter().map(|c| format!("{:#x}", c)).collect(), + max_fee: format!("{:#x}", val.max_fee), + signature: val.signature.into_iter().map(|s| format!("{:#x}", s)).collect(), + nonce: format!("{:#x}", val.nonce), + block_number: val.block_number, + transaction_type: val.transaction_type, + block_timestamp: val.block_timestamp.timestamp() as u64, + calls: val.calls.into_iter().map(|c| c.into()).collect(), + unique_models: val.unique_models.into_iter().map(|m| format!("{:#x}", m)).collect(), + } + } +} + +#[derive(Tsify, Serialize, Deserialize, Debug)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub enum CallType { + Execute, + ExecuteFromOutside, +} + +impl From for CallType { + fn from(val: torii_proto::CallType) -> Self { + match val { + torii_proto::CallType::Execute => CallType::Execute, + torii_proto::CallType::ExecuteFromOutside => CallType::ExecuteFromOutside, + } + } +} + +#[derive(Tsify, Serialize, Deserialize, Debug)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct TransactionCall { + pub contract_address: String, + pub entrypoint: String, + pub calldata: Vec, + pub call_type: CallType, + pub caller_address: String, +} + +impl From for TransactionCall { + fn from(val: torii_proto::TransactionCall) -> Self { + TransactionCall { + contract_address: format!("{:#x}", val.contract_address), + entrypoint: val.entrypoint, + calldata: val.calldata.into_iter().map(|c| format!("{:#x}", c)).collect(), + call_type: val.call_type.into(), + caller_address: format!("{:#x}", val.caller_address), + } + } +} + #[derive(Tsify, Serialize, Deserialize, Debug)] #[tsify(into_wasm_abi, from_wasm_abi)] pub struct ControllerQuery {