From 22eaae6b0d131be8620f56e43924fb6bfdd5ade4 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Thu, 23 Oct 2025 13:21:04 +0200 Subject: [PATCH 01/48] Remove non v1 api endpoints. --- src/main.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7ea6c14..e9be986 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1265,22 +1265,17 @@ async fn main() { let app = Router::new() .route("/", get(alive)) .route("/versions", get(versions)) - .route("/v1/info", get(info)) .route("/info", get(info)) + .route("/v1/info", get(info)) .route("/v1/eth/proof/{block_hash}", get(get_eth_proof)) - .route("/eth/proof/{block_hash}", get(get_eth_proof)) .route("/v1/eth/head", get(get_eth_head)) - .route("/eth/head", get(get_eth_head_legacy)) .route("/v1/avl/head", get(get_avl_head)) - .route("/avl/head", get(get_avl_head)) .route( "/v1/avl/proof/{block_hash}/{message_id}", get(get_avl_proof), ) .route("/v1/transactions", get(transactions)) .route("/transactions", get(transactions)) - .route("/avl/proof/{block_hash}/{message_id}", get(get_avl_proof)) - .route("/beacon/slot/{slot_number}", get(get_beacon_slot)) .route("/v1/head/{chain_id}", get(get_head)) .route("/v1/proof/{chain_id}", get(get_proof)) .layer(TraceLayer::new_for_http()) From d66cc211aa66729a0bec59cb5269979af0bb0089 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Thu, 23 Oct 2025 14:14:32 +0200 Subject: [PATCH 02/48] Remove succinct api and move models. --- .env.example | 2 +- README.md | 2 +- src/main.rs | 489 +++++++------------------------------------------- src/models.rs | 282 ++++++++++++++++++++++++++++- 4 files changed, 344 insertions(+), 431 deletions(-) diff --git a/.env.example b/.env.example index 866019c..2fc964d 100755 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ AVAIL_CLIENT_URL= -SUCCINCT_URL= +MERKLE_PROOF_SERVICE_URL= AVAIL_CHAIN_NAME= CONTRACT_CHAIN_ID= VECTORX_CONTRACT_ADDRESS= diff --git a/README.md b/README.md index 8ab723b..990ae63 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Bridge API -The bridge API is a REST API for fetching proofs from Avail's Kate RPC and Succinct API to submit on Ethereum or +The bridge API is a REST API for fetching proofs from Avail's Kate RPC and Merkle proof service API to submit on Ethereum or any off-chain proof verification. ## Deploying the bridge API diff --git a/src/main.rs b/src/main.rs index e9be986..886c4aa 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,13 @@ mod models; mod schema; -use crate::models::{AvailSend, EthereumSend, StatusEnum}; +use crate::models::*; + use crate::schema::avail_sends::dsl::avail_sends; use crate::schema::ethereum_sends::dsl::ethereum_sends; use alloy::primitives::{Address, B256, U256, hex}; use alloy::providers::ProviderBuilder; -use alloy::sol; use anyhow::{Context, Result, anyhow}; -use avail_core::data_proof::AddressedMessage; use axum::body::{Body, to_bytes}; use axum::response::Response; use axum::{ @@ -25,7 +24,7 @@ use diesel::{ ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, SelectableHelper, r2d2, r2d2::ConnectionManager, }; -use http::{HeaderMap, HeaderName, HeaderValue, Method}; +use http::Method; use jsonrpsee::{ core::ClientError, core::client::ClientT, @@ -34,7 +33,7 @@ use jsonrpsee::{ }; use lazy_static::lazy_static; use reqwest::Client; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; use serde_with::serde_as; use sha3::{Digest, Keccak256}; @@ -58,309 +57,28 @@ use tracing_subscriber::prelude::*; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; -sol!( - #[allow(missing_docs)] - #[sol(rpc)] - SP1Vector, - "src/abi/SP1Vector.json" -); - -#[derive(Debug, Deserialize)] -struct Root { - data: Data, -} - -#[derive(Debug, Deserialize)] -struct Data { - message: Message, -} - -#[derive(Debug, Deserialize)] -struct Message { - slot: String, - body: MessageBody, -} - -#[derive(Debug, Deserialize)] -struct MessageBody { - execution_payload: ExecutionPayload, -} - -#[derive(Debug, Deserialize)] -struct ExecutionPayload { - block_number: String, - block_hash: String, -} - -struct ErrorResponse { - pub error: anyhow::Error, - pub headers_keypairs: Vec<(String, String)>, - pub status_code: Option, -} - -impl ErrorResponse { - pub fn new(error: anyhow::Error) -> Self { - Self { - error, - headers_keypairs: vec![], - status_code: None, - } - } - pub fn with_status(error: anyhow::Error, status_code: StatusCode) -> Self { - Self { - error, - headers_keypairs: vec![], - status_code: Some(status_code), - } - } - - pub fn with_status_and_headers( - error: anyhow::Error, - status_code: StatusCode, - headers: &[(&str, &str)], - ) -> Self { - let h = headers - .iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect::>(); - Self { - error, - headers_keypairs: h, - status_code: Some(status_code), - } - } -} - -// Tell axum how to convert `AppError` into a response. -impl IntoResponse for ErrorResponse { - fn into_response(self) -> Response { - let status = self - .status_code - .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR); - let mut headermap = HeaderMap::new(); - for (k, v) in self.headers_keypairs { - headermap.insert( - HeaderName::try_from(k).unwrap(), - HeaderValue::try_from(v).unwrap(), - ); - } - let json_resp = Json(json!({"error" : format!("{:#}", self.error)})); - - (status, headermap, json_resp).into_response() - } -} - -// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into -// `Result<_, AppError>`. That way you don't need to do that manually. -impl From for ErrorResponse -where - E: Into, -{ - fn from(err: E) -> Self { - Self { - error: err.into(), - headers_keypairs: vec![], - status_code: None, - } - } -} - #[derive(Debug)] -struct AppState { - avail_client: HttpClient, - ethereum_client: HttpClient, - request_client: Client, - succinct_base_url: String, - beaconchain_base_url: String, - avail_chain_name: String, - contract_chain_id: String, - contract_address: String, - bridge_contract_address: String, - eth_head_cache_maxage: u16, - avl_head_cache_maxage: u16, - head_cache_maxage: u16, - avl_proof_cache_maxage: u32, - eth_proof_cache_maxage: u32, - proof_cache_maxage: u32, - eth_proof_failure_cache_maxage: u32, - slot_mapping_cache_maxage: u32, - transactions_cache_maxage: u32, - connection_pool: r2d2::Pool>, - chains: HashMap, -} - -#[derive(Debug)] -struct Chain { - rpc_url: String, - contract_address: Address, -} - -#[derive(Deserialize)] -struct IndexStruct { - index: u32, -} - -#[derive(Deserialize)] -struct ProofQueryStruct { - index: u32, - block_hash: B256, -} - -#[derive(Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct KateQueryDataProofResponse { - data_proof: DataProof, - #[serde(skip_serializing_if = "Option::is_none")] - message: Option, -} - -#[derive(Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct DataProof { - roots: Roots, - proof: Vec, - leaf_index: u32, - leaf: B256, -} - -#[derive(Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct Roots { - data_root: B256, - blob_root: B256, - bridge_root: B256, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct AccountStorageProofResponse { - account_proof: Vec, - storage_proof: Vec, -} - -#[derive(Deserialize)] -struct StorageProof { - proof: Vec, -} - -#[derive(Deserialize)] -struct SuccinctAPIResponse { - data: Option, - error: Option, - success: Option, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct SlotMappingResponse { - block_hash: String, - block_number: String, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct SuccinctAPIData { - range_hash: B256, - data_commitment: B256, - merkle_branch: Vec, - index: u16, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct AggregatedResponse { - data_root_proof: Vec, - leaf_proof: Vec, - range_hash: B256, - data_root_index: u16, - leaf: B256, - leaf_index: u32, - data_root: B256, - blob_root: B256, - bridge_root: B256, - data_root_commitment: B256, - block_hash: B256, - message: Option, -} - -impl AggregatedResponse { - pub fn new( - range_data: SuccinctAPIData, - data_proof_res: KateQueryDataProofResponse, - hash: B256, - ) -> Self { - AggregatedResponse { - data_root_proof: range_data.merkle_branch, - leaf_proof: data_proof_res.data_proof.proof, - range_hash: range_data.range_hash, - data_root_index: range_data.index, - leaf: data_proof_res.data_proof.leaf, - leaf_index: data_proof_res.data_proof.leaf_index, - data_root: data_proof_res.data_proof.roots.data_root, - blob_root: data_proof_res.data_proof.roots.blob_root, - bridge_root: data_proof_res.data_proof.roots.bridge_root, - data_root_commitment: range_data.data_commitment, - block_hash: hash, - message: data_proof_res.message, - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct EthProofResponse { - account_proof: Vec, - storage_proof: Vec, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct HeadResponseV2 { - pub slot: u64, - pub block_number: u64, - pub block_hash: B256, - pub timestamp: u64, - pub timestamp_diff: u64, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct HeadResponseLegacy { - pub slot: u64, - pub timestamp: u64, - pub timestamp_diff: u64, -} - -#[derive(Serialize, Deserialize)] -struct ChainHeadResponse { - pub head: u32, -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct RangeBlocks { - start: u32, - end: u32, -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct RangeBlocksAPIResponse { - data: RangeBlocks, -} - -#[derive(Debug, Deserialize)] -pub struct HeaderBlockNumber { - #[serde(deserialize_with = "hex_to_u32")] - pub number: u32, -} - -fn hex_to_u32<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let s: &str = Deserialize::deserialize(deserializer)?; - u32::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) +pub struct AppState { + pub avail_client: HttpClient, + pub ethereum_client: HttpClient, + pub request_client: Client, + pub merkle_proof_service_base_url: String, + pub beaconchain_base_url: String, + pub avail_chain_name: String, + pub contract_chain_id: String, + pub contract_address: String, + pub bridge_contract_address: String, + pub eth_head_cache_maxage: u16, + pub avl_head_cache_maxage: u16, + pub head_cache_maxage: u16, + pub avl_proof_cache_maxage: u32, + pub eth_proof_cache_maxage: u32, + pub proof_cache_maxage: u32, + pub eth_proof_failure_cache_maxage: u32, + pub slot_mapping_cache_maxage: u32, + pub transactions_cache_maxage: u32, + pub connection_pool: r2d2::Pool>, + pub chains: HashMap, } async fn alive() -> Result, StatusCode> { @@ -565,21 +283,22 @@ async fn get_eth_proof( let eth_proof_failure_cache_maxage = state.eth_proof_failure_cache_maxage; let url = format!( "{}?chainName={}&contractChainId={}&contractAddress={}&blockHash={}", - state.succinct_base_url, + state.merkle_proof_service_base_url, state.avail_chain_name, state.contract_chain_id, state.contract_address, block_hash ); - let succinct_response_fut = tokio::spawn(async move { - let succinct_response = state.request_client.get(url).send().await; - match succinct_response { - Ok(resp) => resp.json::().await, + let mekrle_proof_response_fut = tokio::spawn(async move { + let merkle_proof_response = state.request_client.get(url).send().await; + match merkle_proof_response { + Ok(resp) => resp.json::().await, Err(err) => Err(err), } }); - let (data_proof, succinct_response) = join!(data_proof_response_fut, succinct_response_fut); + let (data_proof, merkle_proof_response) = + join!(data_proof_response_fut, mekrle_proof_response_fut); let data_proof_res: KateQueryDataProofResponse = data_proof .map_err(|e| { tracing::error!("❌ Failed to fetch the kate query data. Error: {e:#}"); @@ -598,7 +317,7 @@ async fn get_eth_proof( ) })?; - let succinct_data = succinct_response + let merkle_proof_data = merkle_proof_response .map_err(|e| { tracing::error!("❌ Failed to get the merkle proof data. Error: {e:#}"); ErrorResponse::with_status_and_headers( @@ -615,25 +334,22 @@ async fn get_eth_proof( &[("Cache-Control", "public, max-age=60, must-revalidate")], ) })?; - let succinct_data = match succinct_data { - SuccinctAPIResponse { + let merkle_data = match merkle_proof_data { + MekrleProofAPIResponse { data: Some(data), .. } => data, - SuccinctAPIResponse { + MekrleProofAPIResponse { success: Some(false), error: Some(data), .. } => { if data.contains("not in the range of blocks") { tracing::warn!( - "⏳ Succinct VectorX contract not updated yet! Response: {}", + "⏳ Merkle proof VectorX contract not updated yet! Response: {}", data ); } else { - tracing::error!( - "❌ Succinct API returned unsuccessfully. Response: {}", - data - ); + tracing::error!("❌ Merkle API returned unsuccessfully. Response: {}", data); } return Err(ErrorResponse::with_status_and_headers( @@ -647,9 +363,9 @@ async fn get_eth_proof( } _ => { - tracing::error!("❌ Succinct API returned no data"); + tracing::error!("❌ Merkle proof API returned no data"); return Err(ErrorResponse::with_status_and_headers( - anyhow!("Succinct API returned no data"), + anyhow!("Merkle proof API returned no data"), StatusCode::NOT_FOUND, &[("Cache-Control", "public, max-age=60, must-revalidate")], )); @@ -663,16 +379,16 @@ async fn get_eth_proof( format!("public, max-age={}, immutable", eth_proof_cache_maxage), )], Json(json!(AggregatedResponse { - data_root_proof: succinct_data.merkle_branch, + data_root_proof: merkle_data.merkle_branch, leaf_proof: data_proof_res.data_proof.proof, - range_hash: succinct_data.range_hash, - data_root_index: succinct_data.index, + range_hash: merkle_data.range_hash, + data_root_index: merkle_data.index, leaf: data_proof_res.data_proof.leaf, leaf_index: data_proof_res.data_proof.leaf_index, data_root: data_proof_res.data_proof.roots.data_root, blob_root: data_proof_res.data_proof.roots.blob_root, bridge_root: data_proof_res.data_proof.roots.bridge_root, - data_root_commitment: succinct_data.data_commitment, + data_root_commitment: merkle_data.data_commitment, block_hash, message: data_proof_res.message })), @@ -739,59 +455,6 @@ async fn get_avl_proof( .into_response()) } -/// Creates a request to the beaconcha service for mapping slot to the block number. -#[inline(always)] -async fn get_beacon_slot( - Path(slot): Path, - State(state): State>, -) -> Result { - let resp = state - .request_client - .get(format!( - "{}/eth/v2/beacon/blocks/{}", - state.beaconchain_base_url, slot - )) - .send() - .await - .map_err(|e| { - tracing::error!("❌ Cannot get beacon API data: {e:#}"); - ErrorResponse::with_status_and_headers( - e.into(), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; - - let response_data = resp.json::().await.map_err(|e| { - tracing::error!("❌ Cannot get beacon API response data: {e:#}"); - ErrorResponse::with_status_and_headers( - e.into(), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; - Ok(( - StatusCode::OK, - [( - "Cache-Control", - format!( - "public, max-age={}, immutable", - state.slot_mapping_cache_maxage - ), - )], - Json(json!(SlotMappingResponse { - block_number: response_data - .data - .message - .body - .execution_payload - .block_number, - block_hash: response_data.data.message.body.execution_payload.block_hash - })), - ) - .into_response()) -} - /// get_eth_head returns Ethereum head with the latest slot/block that is stored and a time. #[inline(always)] async fn get_eth_head( @@ -827,39 +490,6 @@ async fn get_eth_head( .into_response()) } -/// get_eth_head returns Ethereum head with the latest slot/block that is stored and a time. -#[inline(always)] -async fn get_eth_head_legacy( - State(state): State>, -) -> Result { - let slot_block_head = SLOT_BLOCK_HEAD.read().await; - let (slot, _block, _hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { - ErrorResponse::with_status_and_headers( - anyhow!("Not found"), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; - - let now = Utc::now().timestamp() as u64; - Ok(( - StatusCode::OK, - [( - "Cache-Control", - format!( - "public, max-age={}, must-revalidate", - state.eth_head_cache_maxage - ), - )], - Json(json!(HeadResponseLegacy { - slot: *slot, - timestamp: *timestamp, - timestamp_diff: now - *timestamp - })), - ) - .into_response()) -} - /// get_avl_head returns start and end blocks which the contract has commitments #[inline(always)] async fn get_avl_head( @@ -867,7 +497,10 @@ async fn get_avl_head( ) -> Result { let url = format!( "{}/{}/?contractChainId={}&contractAddress={}", - state.succinct_base_url, "range", state.contract_chain_id, state.contract_address + state.merkle_proof_service_base_url, + "range", + state.contract_chain_id, + state.contract_address ); let response = state.request_client.get(url).send().await.map_err(|e| { tracing::error!("❌ Cannot parse range blocks: {e:#}"); @@ -1040,21 +673,21 @@ async fn get_proof( &[("Cache-Control", "max-age=60, must-revalidate")], ) })? { - Ok(SuccinctAPIResponse { + Ok(MekrleProofAPIResponse { data: Some(data), .. }) => data, - Ok(SuccinctAPIResponse { + Ok(MekrleProofAPIResponse { success: Some(false), error: Some(data), .. }) => { if data.contains("not in the range of blocks") { - tracing::warn!( - "Succinct VectorX contract not updated yet! Response: {}", + tracing::warn!("VectorX contract not updated yet! Response: {}", data); + } else { + tracing::error!( + "Merkle proof API returned unsuccessfully. Response: {}", data ); - } else { - tracing::error!("Succinct API returned unsuccessfully. Response: {}", data); } return Err(ErrorResponse::with_status_and_headers( anyhow!("error: {data}"), @@ -1063,7 +696,7 @@ async fn get_proof( )); } Err(err) => { - tracing::error!("Cannot get succinct api response {:?}", err); + tracing::error!("Cannot get merkle proof api response {:?}", err); return Err(ErrorResponse::with_status_and_headers( anyhow!("error: {err}"), StatusCode::INTERNAL_SERVER_ERROR, @@ -1071,9 +704,9 @@ async fn get_proof( )); } _ => { - tracing::error!("Succinct API returned no data"); + tracing::error!("Merkle proof API returned no data"); return Err(ErrorResponse::with_status_and_headers( - anyhow!("Succinct API returned no data"), + anyhow!("Merkle proof API returned no data"), StatusCode::INTERNAL_SERVER_ERROR, &[("Cache-Control", "max-age=60, must-revalidate")], )); @@ -1113,10 +746,10 @@ fn spawn_kate_proof( fn spawn_merkle_proof_range_fetch( state: Arc, block_hash: B256, -) -> JoinHandle> { +) -> JoinHandle> { let url = format!( "{}?chainName={}&contractChainId={}&contractAddress={}&blockHash={}", - state.succinct_base_url, + state.merkle_proof_service_base_url, state.avail_chain_name, state.contract_chain_id, state.contract_address, @@ -1125,7 +758,7 @@ fn spawn_merkle_proof_range_fetch( tokio::spawn(async move { let res = state.request_client.get(url).send().await; match res { - Ok(resp) => resp.json::().await, + Ok(resp) => resp.json::().await, Err(e) => Err(e), } }) @@ -1210,7 +843,7 @@ async fn main() { ) .unwrap(), request_client: Client::builder().brotli(true).build().unwrap(), - succinct_base_url: env::var("SUCCINCT_URL") + merkle_proof_service_base_url: env::var("MERKLE_PROOF_SERVICE_URL") .unwrap_or("https://beaconapi.succinct.xyz/api/integrations/vectorx".to_owned()), beaconchain_base_url: env::var("BEACONCHAIN_URL") .unwrap_or("https://sepolia.beaconcha.in/api/v1/slot".to_owned()), diff --git a/src/models.rs b/src/models.rs index 67ebf4d..b71b491 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,4 +1,9 @@ use crate::schema::sql_types::Status; +use alloy::primitives::{Address, B256}; +use alloy::sol; +use avail_core::data_proof::AddressedMessage; +use axum::Json; +use axum::response::{IntoResponse, Response}; use chrono::NaiveDateTime; use diesel::pg::{Pg, PgValue}; use diesel::serialize::{IsNull, Output}; @@ -8,11 +13,286 @@ use diesel::{ serialize::ToSql, *, }; +use http::{HeaderMap, HeaderName, HeaderValue, StatusCode}; use jsonrpsee::core::Serialize; -use serde::Deserialize; +use serde::{Deserialize, Deserializer}; +use serde_json::json; use serde_with::serde_as; use std::io::Write; +sol!( + #[allow(missing_docs)] + #[sol(rpc)] + SP1Vector, + "src/abi/SP1Vector.json" +); + +#[derive(Debug, Deserialize)] +pub struct Root { + pub data: Data, +} + +#[derive(Debug, Deserialize)] +pub struct Data { + pub message: Message, +} + +#[derive(Debug, Deserialize)] +pub struct Message { + pub slot: String, + pub body: MessageBody, +} + +#[derive(Debug, Deserialize)] +pub struct MessageBody { + pub execution_payload: ExecutionPayload, +} + +#[derive(Debug, Deserialize)] +pub struct ExecutionPayload { + pub block_number: String, + pub block_hash: String, +} + +pub struct ErrorResponse { + pub error: anyhow::Error, + pub headers_keypairs: Vec<(String, String)>, + pub status_code: Option, +} + +impl ErrorResponse { + pub fn new(error: anyhow::Error) -> Self { + Self { + error, + headers_keypairs: vec![], + status_code: None, + } + } + pub fn with_status(error: anyhow::Error, status_code: StatusCode) -> Self { + Self { + error, + headers_keypairs: vec![], + status_code: Some(status_code), + } + } + + pub fn with_status_and_headers( + error: anyhow::Error, + status_code: StatusCode, + headers: &[(&str, &str)], + ) -> Self { + let h = headers + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect::>(); + Self { + error, + headers_keypairs: h, + status_code: Some(status_code), + } + } +} + +// Tell axum how to convert `AppError` into a response. +impl IntoResponse for ErrorResponse { + fn into_response(self) -> Response { + let status = self + .status_code + .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR); + let mut headermap = HeaderMap::new(); + for (k, v) in self.headers_keypairs { + headermap.insert( + HeaderName::try_from(k).unwrap(), + HeaderValue::try_from(v).unwrap(), + ); + } + let json_resp = Json(json!({"error" : format!("{:#}", self.error)})); + + (status, headermap, json_resp).into_response() + } +} + +// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into +// `Result<_, AppError>`. That way you don't need to do that manually. +impl From for ErrorResponse +where + E: Into, +{ + fn from(err: E) -> Self { + Self { + error: err.into(), + headers_keypairs: vec![], + status_code: None, + } + } +} + +#[derive(Debug)] +pub struct Chain { + pub rpc_url: String, + pub contract_address: Address, +} + +#[derive(Deserialize)] +pub struct IndexStruct { + pub index: u32, +} + +#[derive(Deserialize)] +pub struct ProofQueryStruct { + pub index: u32, + pub block_hash: B256, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct KateQueryDataProofResponse { + pub data_proof: DataProof, + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DataProof { + pub roots: Roots, + pub proof: Vec, + pub leaf_index: u32, + pub leaf: B256, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Roots { + pub data_root: B256, + pub blob_root: B256, + pub bridge_root: B256, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AccountStorageProofResponse { + pub account_proof: Vec, + pub storage_proof: Vec, +} + +#[derive(Deserialize)] +pub struct StorageProof { + pub proof: Vec, +} + +#[derive(Deserialize)] +pub struct MekrleProofAPIResponse { + pub data: Option, + pub error: Option, + pub success: Option, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SlotMappingResponse { + pub block_hash: String, + pub block_number: String, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MerkleProofData { + pub range_hash: B256, + pub data_commitment: B256, + pub merkle_branch: Vec, + pub index: u16, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AggregatedResponse { + pub data_root_proof: Vec, + pub leaf_proof: Vec, + pub range_hash: B256, + pub data_root_index: u16, + pub leaf: B256, + pub leaf_index: u32, + pub data_root: B256, + pub blob_root: B256, + pub bridge_root: B256, + pub data_root_commitment: B256, + pub block_hash: B256, + pub message: Option, +} + +impl AggregatedResponse { + pub fn new( + range_data: MerkleProofData, + data_proof_res: KateQueryDataProofResponse, + hash: B256, + ) -> Self { + AggregatedResponse { + data_root_proof: range_data.merkle_branch, + leaf_proof: data_proof_res.data_proof.proof, + range_hash: range_data.range_hash, + data_root_index: range_data.index, + leaf: data_proof_res.data_proof.leaf, + leaf_index: data_proof_res.data_proof.leaf_index, + data_root: data_proof_res.data_proof.roots.data_root, + blob_root: data_proof_res.data_proof.roots.blob_root, + bridge_root: data_proof_res.data_proof.roots.bridge_root, + data_root_commitment: range_data.data_commitment, + block_hash: hash, + message: data_proof_res.message, + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct EthProofResponse { + pub account_proof: Vec, + pub storage_proof: Vec, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HeadResponseV2 { + pub slot: u64, + pub block_number: u64, + pub block_hash: B256, + pub timestamp: u64, + pub timestamp_diff: u64, +} + +#[derive(Serialize, Deserialize)] +pub struct ChainHeadResponse { + pub head: u32, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RangeBlocks { + start: u32, + end: u32, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RangeBlocksAPIResponse { + data: RangeBlocks, +} + +#[derive(Debug, Deserialize)] +pub struct HeaderBlockNumber { + #[serde(deserialize_with = "hex_to_u32")] + pub number: u32, +} + +fn hex_to_u32<'de, D>(deserializer: D) -> anyhow::Result +where + D: Deserializer<'de>, +{ + let s: &str = Deserialize::deserialize(deserializer)?; + u32::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) +} + #[derive(Debug, Clone, PartialEq, FromSqlRow, AsExpression, Eq)] #[diesel(sql_type = Status)] #[derive(Serialize, Deserialize)] From 38e283e27d57fef5f68beb3c974f11e12ded100a Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 24 Oct 2025 11:52:47 +0200 Subject: [PATCH 03/48] Update deps. --- Cargo.lock | 421 ++++++++++++++++++++++++++++++++++++++--------------- Cargo.toml | 9 +- 2 files changed, 304 insertions(+), 126 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 766d0d8..ab79b45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,9 +102,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6f38130b8716f18c69cede2b8ebe6cf70038a3d97740907bb0637941f759be" +checksum = "ae62e633fa48b4190af5e841eb05179841bb8b713945103291e2c0867037c0d1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -120,6 +120,7 @@ dependencies = [ "alloy-signer-local", "alloy-transport", "alloy-transport-http", + "alloy-trie", ] [[package]] @@ -135,15 +136,16 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7329eb72d95576dfb8813175bcf671198fb24266b0b3e520052a513e30c284df" +checksum = "b9b151e38e42f1586a01369ec52a6934702731d07e8509a7307331b09f6c46dc" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-serde", "alloy-trie", + "alloy-tx-macros", "auto_impl", "c-kzg", "derive_more 2.0.1", @@ -153,15 +155,16 @@ dependencies = [ "rand 0.8.5", "secp256k1 0.30.0", "serde", + "serde_json", "serde_with", "thiserror 2.0.11", ] [[package]] name = "alloy-consensus-any" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31b286aeef04a32720c10defd21c3aa6c626154ac442b55f6d472caeb1c6741" +checksum = "6e2d5e8668ef6215efdb7dcca6f22277b4e483a5650e05f5de22b2350971f4b8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -173,9 +176,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1658352ca9425d7b5bbb3ae364bc276ab18d4afae06f5faf00377b6964fdf68" +checksum = "630288cf4f3a34a8c6bc75c03dce1dbd47833138f65f37d53a1661eafc96b83f" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -189,14 +192,15 @@ dependencies = [ "alloy-transport", "futures", "futures-util", + "serde_json", "thiserror 2.0.11", ] [[package]] name = "alloy-core" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c5a28f166629752f2e7246b813cdea3243cca59aab2d4264b1fd68392c10eb" +checksum = "5ca96214615ec8cf3fa2a54b32f486eb49100ca7fe7eb0b8c1137cd316e7250a" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -207,9 +211,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cc14d832bc3331ca22a1c7819de1ede99f58f61a7d123952af7dde8de124a6" +checksum = "3fdff496dd4e98a81f4861e66f7eaf5f2488971848bb42d9c892f871730245c8" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -259,9 +263,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa190bfa5340aee544ac831114876fa73bc8da487095b49a5ea153a6a4656ea" +checksum = "e5434834adaf64fa20a6fb90877bc1d33214c41b055cc49f82189c98614368cc" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -274,27 +278,30 @@ dependencies = [ "derive_more 2.0.1", "either", "serde", + "serde_with", "sha2 0.10.8", + "thiserror 2.0.11", ] [[package]] name = "alloy-genesis" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b81b2dfd278d58af8bfde8753fa4685407ba8fbad8bc88a2bb0e065eed48478" +checksum = "919a8471cfbed7bcd8cf1197a57dda583ce0e10c6385f6ff4e8b41304b223392" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-serde", "alloy-trie", "serde", + "serde_with", ] [[package]] name = "alloy-json-abi" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ccaa79753d7bf15f06399ea76922afbfaf8d18bebed9e8fc452984b4a90dcc9" +checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -304,12 +311,13 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ab2dba5dca01ad4281b4d4726a18e2012a20e3950bfc2a90c5376840555366" +checksum = "d7c69f6c9c68a1287c9d5ff903d0010726934de0dac10989be37b75a29190d55" dependencies = [ "alloy-primitives", "alloy-sol-types", + "http", "serde", "serde_json", "thiserror 2.0.11", @@ -318,9 +326,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ed07e76fbc72790a911ea100cdfbe85b1f12a097c91b948042e854959d140e" +checksum = "8eaf2ae05219e73e0979cb2cf55612aafbab191d130f203079805eaf881cca58" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -344,9 +352,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05aa52713c376f797b3c7077708585f22a5c3053a7b1b2b355ea98edeb2052d" +checksum = "e58f4f345cef483eab7374f2b6056973c7419ffe8ad35e994b7a7f5d8e0c7ba4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -357,17 +365,17 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c35fc4b03ace65001676358ffbbaefe2a2b27ee50fe777c345082c7c888be8" +checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more 2.0.1", - "foldhash", - "hashbrown 0.15.2", + "foldhash 0.2.0", + "hashbrown 0.16.0", "indexmap 2.7.0", "itoa", "k256", @@ -384,9 +392,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a3f7a59c276c6e410267e77a166f9297dbe74e4605f1abf625e29d85c53144" +checksum = "de2597751539b1cc8fe4204e5325f9a9ed83fcacfb212018dfcfa7877e76de21" dependencies = [ "alloy-chains", "alloy-consensus", @@ -445,15 +453,14 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f185483536cbcbf55971077140e03548dad4f3a4ddb35044bcdc01b8f02ce1" +checksum = "edf8eb8be597cfa8c312934d2566ec4516f066d69164f9212d7a148979fdcfd8" dependencies = [ "alloy-json-rpc", "alloy-primitives", "alloy-transport", "alloy-transport-http", - "async-stream", "futures", "pin-project", "reqwest", @@ -463,16 +470,15 @@ dependencies = [ "tokio-stream", "tower", "tracing", - "tracing-futures", "url", "wasmtimer", ] [[package]] name = "alloy-rpc-types" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "347dfd77ba4d74886dba9e2872ff64fb246001b08868d27baec94e7248503e08" +checksum = "339af7336571dd39ae3a15bde08ae6a647e62f75350bd415832640268af92c06" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -482,9 +488,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67971a228100ac65bd86e90439028853435f21796330ef08f00a70a918a84126" +checksum = "fbde0801a32d21c5f111f037bee7e22874836fba7add34ed4a6919932dd7cf23" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -493,9 +499,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d9b4293dfd4721781d33ee40de060376932d4a55d421cf6618ad66ff97cc52" +checksum = "361cd87ead4ba7659bda8127902eda92d17fa7ceb18aba1676f7be10f7222487" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -508,14 +514,15 @@ dependencies = [ "itertools 0.13.0", "serde", "serde_json", + "serde_with", "thiserror 2.0.11", ] [[package]] name = "alloy-serde" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7d927aa39ca51545ae4c9cf4bdb2cbc1f6b46ab4b54afc3ed9255f93eedbce" +checksum = "64600fc6c312b7e0ba76f73a381059af044f4f21f43e07f51f1fa76c868fe302" dependencies = [ "alloy-primitives", "serde", @@ -524,9 +531,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c63771b50008d2b079187e9e74a08235ab16ecaf4609b4eb895e2890a3bcd465" +checksum = "5772858492b26f780468ae693405f895d6a27dea6e3eab2c36b6217de47c2647" dependencies = [ "alloy-primitives", "async-trait", @@ -539,9 +546,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db906294ee7876bd332cd760f460d30de183554434e07fc19d7d54e16a7aeaf0" +checksum = "f4195b803d0a992d8dbaab2ca1986fc86533d4bc80967c0cce7668b26ad99ef9" dependencies = [ "alloy-consensus", "alloy-network", @@ -555,9 +562,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8612e0658964d616344f199ab251a49d48113992d81b92dab93ed855faa66383" +checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -569,9 +576,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a384edac7283bc4c010a355fb648082860c04b826bb7a814c45263c8f304c74" +checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -588,9 +595,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd588c2d516da7deb421b8c166dc60b7ae31bca5beea29ab6621fcfa53d6ca5" +checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ "alloy-json-abi", "const-hex", @@ -606,9 +613,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86ddeb70792c7ceaad23e57d52250107ebbb86733e52f4a25d8dc1abc931837" +checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" dependencies = [ "serde", "winnow 0.7.10", @@ -616,9 +623,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584cb97bfc5746cb9dcc4def77da11694b5d6d7339be91b7480a6a68dc129387" +checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -628,12 +635,13 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9b645fe4f4e6582cfbb4a8d20cedcf5aa23548e92eacbdacac6278b425e023" +checksum = "025a940182bddaeb594c26fe3728525ae262d0806fe6a4befdf5d7bc13d54bce" dependencies = [ "alloy-json-rpc", "alloy-primitives", + "auto_impl", "base64 0.22.1", "derive_more 2.0.1", "futures", @@ -651,9 +659,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.7" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee18869ecabe658ff6316e7db7c25d958c7d10f0a1723c2f7447d4f402920b66" +checksum = "e3b5064d1e1e1aabc918b5954e7fb8154c39e77ec6903a581b973198b26628fa" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -666,9 +674,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "983d99aa81f586cef9dae38443245e585840fcf0fc58b09aee0b1f27aed1d500" +checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -680,6 +688,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-tx-macros" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e52276fdb553d3c11563afad2898f4085165e4093604afe3d78b69afbf408f" +dependencies = [ + "alloy-primitives", + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1383,11 +1404,12 @@ dependencies = [ [[package]] name = "backon" -version = "0.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e5b65cc81d81fbb8488f36458ab4771be35a722967bbc959df28b47397e3ff" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ "fastrand", + "gloo-timers", "tokio", ] @@ -1628,7 +1650,6 @@ dependencies = [ "backon", "chrono", "diesel", - "diesel-enum", "dotenvy", "http", "jsonrpsee", @@ -1749,6 +1770,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.39" @@ -2043,8 +2070,18 @@ version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.10", + "darling_macro 0.20.10", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -2061,13 +2098,39 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "serde", + "strsim", + "syn 2.0.101", +] + [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core", + "darling_core 0.20.10", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", "quote", "syn 2.0.101", ] @@ -2206,17 +2269,6 @@ dependencies = [ "r2d2", ] -[[package]] -name = "diesel-enum" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4b8470493d7120fd53f7d7db6ad167c2bc06fdf8abb3cef7e7669eb1347b415" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "diesel_derives" version = "2.2.3" @@ -2326,7 +2378,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" dependencies = [ - "darling", + "darling 0.20.10", "either", "heck", "proc-macro2", @@ -2671,6 +2723,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2924,8 +2982,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -2935,9 +2995,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -2973,6 +3035,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "group" version = "0.13.0" @@ -3054,7 +3128,16 @@ checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.4", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "foldhash 0.2.0", "serde", ] @@ -3216,6 +3299,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots 1.0.3", ] [[package]] @@ -3613,9 +3697,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fba77a59c4c644fd48732367624d1bcf6f409f9c9a286fbc71d2f1fc0b2ea16" +checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -3626,9 +3710,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693c93cbb7db25f4108ed121304b671a36002c2db67dff2ee4391a688c738547" +checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" dependencies = [ "async-trait", "bytes", @@ -3651,9 +3735,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6962d2bd295f75e97dd328891e58fce166894b974c1f7ce2e7597f02eeceb791" +checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" dependencies = [ "base64 0.22.1", "http-body", @@ -3674,9 +3758,9 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa4f5daed39f982a1bb9d15449a28347490ad42b212f8eaa2a2a344a0dce9e9" +checksum = "2da3f8ab5ce1bb124b6d082e62dffe997578ceaf0aeb9f3174a214589dc00f07" dependencies = [ "heck", "proc-macro-crate", @@ -3687,9 +3771,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66df7256371c45621b3b7d2fb23aea923d577616b9c0e9c0b950a6ea5c2be0ca" +checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" dependencies = [ "http", "serde", @@ -3842,6 +3926,12 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "mach" version = "0.3.2" @@ -4115,13 +4205,14 @@ dependencies = [ [[package]] name = "nybbles" -version = "0.3.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +checksum = "2c4b5ecbd0beec843101bffe848217f770e8b8da81d8355b7d6e226f2199b3dc" dependencies = [ "alloy-rlp", - "const-hex", + "cfg-if", "proptest", + "ruint", "serde", "smallvec", ] @@ -4622,6 +4713,61 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.11", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.1", + "ring 0.17.8", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.11", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.38" @@ -4852,7 +4998,10 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -4860,6 +5009,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-rustls", "tokio-util", "tower", "tower-service", @@ -4867,6 +5017,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.26.11", "windows-registry", ] @@ -4923,13 +5074,14 @@ dependencies = [ [[package]] name = "ruint" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", "ark-ff 0.4.2", + "ark-ff 0.5.0", "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", @@ -4943,7 +5095,7 @@ dependencies = [ "rand 0.9.1", "rlp", "ruint-macro", - "serde", + "serde_core", "valuable", "zeroize", ] @@ -5059,6 +5211,7 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ + "web-time", "zeroize", ] @@ -5338,10 +5491,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -5354,11 +5508,20 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -5367,14 +5530,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -5432,7 +5596,7 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ - "darling", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.101", @@ -6573,9 +6737,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d879005cc1b5ba4e18665be9e9501d9da3a9b95f625497c4cb7ee082b532e" +checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" dependencies = [ "paste", "proc-macro2", @@ -6948,9 +7112,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.5.2" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "async-compression", "bitflags 2.8.0", @@ -6958,7 +7122,6 @@ dependencies = [ "futures-core", "http", "http-body", - "http-body-util", "pin-project-lite", "tokio", "tokio-util", @@ -7012,18 +7175,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "futures", - "futures-task", - "pin-project", - "tracing", -] - [[package]] name = "tracing-log" version = "0.1.4" @@ -7570,6 +7721,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-root-certs" version = "0.26.11" @@ -7588,6 +7749,24 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.3", +] + +[[package]] +name = "webpki-roots" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 017aa5b..d4dd96b 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,27 +10,26 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -alloy = { version = "1", features = ["serde", "json"] } +alloy = { version = "1.0.27", features = ["serde", "json"] } axum = { version = "0.8", features = ["http2", "macros", "tracing"] } dotenvy = "0.15" -jsonrpsee = { version = "0.25", features = ["http-client", "macros", "async-client"] } +jsonrpsee = { version = "0.26.0", features = ["http-client", "macros", "async-client"] } reqwest = { version = "0.12", features = ["json", "brotli"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } sha3 = "0.10" tokio = { version = "1.35", features = ["macros", "rt-multi-thread", "parking_lot"] } -tower-http = { version = "0.5", features = ["trace", "compression-br", "cors"] } +tower-http = { version = "0.6.6", features = ["trace", "compression-br", "cors"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } sp-io = "34.0" sp-core = "34.0" avail-core = { git = "https://github.com/availproject/avail-core", tag = "core-node-3"} http = "1.1" -backon = { version = "0.5", features = ["tokio-sleep"] } +backon = { version = "1.5.2", features = ["tokio-sleep"] } anyhow = "1" lazy_static = "1.5" diesel = { version = "2.2", features = ["postgres", "chrono", "r2d2"] } -diesel-enum = "0.2.1" serde_with = "3.11.0" chrono = { version = "0.4", features = ["serde"] } From 2c77fe56547e5ec941dda915bdcbb90cfd352064 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 24 Oct 2025 12:04:24 +0200 Subject: [PATCH 04/48] Move skipped models. --- src/main.rs | 80 -------------------------------------------------- src/models.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 80 deletions(-) diff --git a/src/main.rs b/src/main.rs index 886c4aa..d41f7b5 100755 --- a/src/main.rs +++ b/src/main.rs @@ -100,86 +100,6 @@ async fn versions(State(_state): State>) -> Result, St Ok(Json(json!(["v1"]))) } -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TransactionQueryParams { - eth_address: Option, - avail_address: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde_as] -#[serde(rename_all = "camelCase")] -pub struct TransactionData { - pub message_id: i64, - pub status: StatusEnum, - pub source_transaction_hash: String, - pub source_block_number: i64, - pub source_block_hash: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub source_transaction_index: Option, - #[serde_as(as = "TimestampSeconds")] - pub source_timestamp: NaiveDateTime, - pub token_id: String, - pub destination_block_number: Option, - pub destination_block_hash: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub destination_transaction_index: Option, - #[serde_as(as = "Option")] - pub destination_timestamp: Option, - pub depositor_address: String, - pub receiver_address: String, - pub amount: String, -} - -#[derive(Debug, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionResult { - pub avail_send: Vec, - pub eth_send: Vec, -} - -fn map_ethereum_send_to_transaction_result(send: EthereumSend) -> TransactionData { - TransactionData { - message_id: send.message_id, - status: send.status, - source_transaction_hash: send.source_transaction_hash, - source_block_number: send.source_block_number, - source_block_hash: send.source_block_hash, - source_transaction_index: None, - source_timestamp: send.source_timestamp, - token_id: send.token_id, - destination_block_number: send.destination_block_number, - destination_block_hash: send.destination_block_hash, - destination_transaction_index: send.destination_transaction_index, - destination_timestamp: send.destination_timestamp, - depositor_address: send.depositor_address, - receiver_address: send.receiver_address, - amount: send.amount, - } -} - -// Function to map AvailSend to TransactionResult -fn map_avail_send_to_transaction_result(send: AvailSend) -> TransactionData { - TransactionData { - message_id: send.message_id, - status: send.status, - source_transaction_hash: send.source_transaction_hash, - source_block_number: send.source_block_number, - source_block_hash: send.source_block_hash, - source_transaction_index: Some(send.source_transaction_index), - source_timestamp: send.source_timestamp, - token_id: send.token_id, - destination_block_number: send.destination_block_number, - destination_block_hash: send.destination_block_hash, - destination_transaction_index: None, - destination_timestamp: send.destination_timestamp, - depositor_address: send.depositor_address, - receiver_address: send.receiver_address, - amount: send.amount, - } -} - #[inline(always)] async fn transactions( Query(address_query): Query, diff --git a/src/models.rs b/src/models.rs index b71b491..0359ddb 100644 --- a/src/models.rs +++ b/src/models.rs @@ -18,6 +18,7 @@ use jsonrpsee::core::Serialize; use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::serde_as; +use sp_core::{H160, H256}; use std::io::Write; sol!( @@ -380,3 +381,83 @@ pub struct EthereumSend { pub receiver_address: String, pub amount: String, } + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionQueryParams { + pub eth_address: Option, + pub avail_address: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde_as] +#[serde(rename_all = "camelCase")] +pub struct TransactionData { + pub message_id: i64, + pub status: StatusEnum, + pub source_transaction_hash: String, + pub source_block_number: i64, + pub source_block_hash: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub source_transaction_index: Option, + #[serde_as(as = "TimestampSeconds")] + pub source_timestamp: NaiveDateTime, + pub token_id: String, + pub destination_block_number: Option, + pub destination_block_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub destination_transaction_index: Option, + #[serde_as(as = "Option")] + pub destination_timestamp: Option, + pub depositor_address: String, + pub receiver_address: String, + pub amount: String, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionResult { + pub avail_send: Vec, + pub eth_send: Vec, +} + +pub fn map_ethereum_send_to_transaction_result(send: EthereumSend) -> TransactionData { + TransactionData { + message_id: send.message_id, + status: send.status, + source_transaction_hash: send.source_transaction_hash, + source_block_number: send.source_block_number, + source_block_hash: send.source_block_hash, + source_transaction_index: None, + source_timestamp: send.source_timestamp, + token_id: send.token_id, + destination_block_number: send.destination_block_number, + destination_block_hash: send.destination_block_hash, + destination_transaction_index: send.destination_transaction_index, + destination_timestamp: send.destination_timestamp, + depositor_address: send.depositor_address, + receiver_address: send.receiver_address, + amount: send.amount, + } +} + +// Function to map AvailSend to TransactionResult +pub fn map_avail_send_to_transaction_result(send: AvailSend) -> TransactionData { + TransactionData { + message_id: send.message_id, + status: send.status, + source_transaction_hash: send.source_transaction_hash, + source_block_number: send.source_block_number, + source_block_hash: send.source_block_hash, + source_transaction_index: Some(send.source_transaction_index), + source_timestamp: send.source_timestamp, + token_id: send.token_id, + destination_block_number: send.destination_block_number, + destination_block_hash: send.destination_block_hash, + destination_transaction_index: None, + destination_timestamp: send.destination_timestamp, + depositor_address: send.depositor_address, + receiver_address: send.receiver_address, + amount: send.amount, + } +} From 91ee37ece399cc95dae8b30737a7780b191d85cc Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 28 Oct 2025 12:40:39 +0100 Subject: [PATCH 05/48] Add eth fetch tx. --- src/main.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index d41f7b5..6d76cb3 100755 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,13 @@ use crate::models::*; use crate::schema::avail_sends::dsl::avail_sends; use crate::schema::ethereum_sends::dsl::ethereum_sends; +use alloy::consensus::transaction::TxHashable; use alloy::primitives::{Address, B256, U256, hex}; use alloy::providers::ProviderBuilder; use anyhow::{Context, Result, anyhow}; use axum::body::{Body, to_bytes}; use axum::response::Response; +use axum::routing::post; use axum::{ Router, extract::{Json, Path, Query, State}, @@ -40,7 +42,6 @@ use sha3::{Digest, Keccak256}; use sp_core::{Decode, H160, H256}; use sp_io::hashing::twox_128; use std::collections::HashMap; -use std::sync::Arc; use std::{env, process, time::Duration}; #[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; @@ -53,6 +54,8 @@ use tower_http::{ }; use tracing_subscriber::prelude::*; +use alloy::rpc::types::Transaction; +use std::sync::{Arc, Mutex}; #[cfg(not(target_env = "msvc"))] #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; @@ -85,6 +88,39 @@ async fn alive() -> Result, StatusCode> { Ok(Json(json!({ "name": "Avail Bridge API" }))) } +#[inline(always)] +async fn transaction( + State(state): State>, + Path(hash): Path, +) -> Result { + + // fetch details of the eth transaction + let resp: Result = state + .ethereum_client + .request("eth_getTransactionByHash", rpc_params![hash]) + .await; + + let tx: Transaction = resp.map_err(|e| { + tracing::error!("❌ Cannot get transaction: {e:#}"); + if e.to_string().ends_with("status code: 429") { + ErrorResponse::with_status_and_headers( + e.into(), + StatusCode::TOO_MANY_REQUESTS, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + ) + } else { + ErrorResponse::with_status_and_headers( + e.into(), + StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + ) + } + })?; + + + Ok(Json(json!({ "transaction": tx }))) +} + #[inline(always)] async fn info(State(state): State>) -> Result, StatusCode> { Ok(Json(json!({ @@ -818,19 +854,19 @@ async fn main() { let app = Router::new() .route("/", get(alive)) .route("/versions", get(versions)) - .route("/info", get(info)) .route("/v1/info", get(info)) - .route("/v1/eth/proof/{block_hash}", get(get_eth_proof)) - .route("/v1/eth/head", get(get_eth_head)) - .route("/v1/avl/head", get(get_avl_head)) + .route("/v2/transaction/{txHash}", post(transaction)) + .route("/v1/eth/proof/{block_hash}", get(get_eth_proof)) // get proof from avail for ethereum + .route("/v1/eth/head", get(get_eth_head)) // fetch head form eth contract + .route("/v1/avl/head", get(get_avl_head)) // fetch head form avail pallet .route( "/v1/avl/proof/{block_hash}/{message_id}", - get(get_avl_proof), + get(get_avl_proof), // get proof from ethereum for avail ) - .route("/v1/transactions", get(transactions)) + .route("/v1/transactions", get(transactions)) // fetch all transaction .route("/transactions", get(transactions)) - .route("/v1/head/{chain_id}", get(get_head)) - .route("/v1/proof/{chain_id}", get(get_proof)) + .route("/v1/head/{chain_id}", get(get_head)) // get head based on chain + .route("/v1/proof/{chain_id}", get(get_proof)) // get proof for avail based on chain .layer(TraceLayer::new_for_http()) .layer(CompressionLayer::new()) .layer( From 76b4cfe60762d73a32ea1bd533d19084b9b036ff Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 29 Oct 2025 15:45:23 +0100 Subject: [PATCH 06/48] Add endpoint to insert tx. --- Cargo.lock | 562 +++++++++++++++++++++++++++++++++++++------- Cargo.toml | 4 +- docker-compose.yaml | 17 ++ src/main.rs | 226 ++++++++++-------- src/models.rs | 109 ++++----- src/schema.rs | 79 ------- 6 files changed, 678 insertions(+), 319 deletions(-) create mode 100644 docker-compose.yaml delete mode 100644 src/schema.rs diff --git a/Cargo.lock b/Cargo.lock index ab79b45..8b7cd0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1285,6 +1285,15 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1564,6 +1573,9 @@ name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +dependencies = [ + "serde", +] [[package]] name = "bitvec" @@ -1649,7 +1661,6 @@ dependencies = [ "axum", "backon", "chrono", - "diesel", "dotenvy", "http", "jsonrpsee", @@ -1661,6 +1672,7 @@ dependencies = [ "sha3", "sp-core 34.0.0", "sp-io 34.0.0", + "sqlx", "tikv-jemallocator", "tokio", "tower-http", @@ -1822,6 +1834,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-hex" version = "1.14.0" @@ -1969,6 +1990,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -2156,6 +2186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -2254,43 +2285,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "diesel" -version = "2.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf1bedf64cdb9643204a36dd15b19a6ce8e7aa7f7b105868e9f1fad5ffa7d12" -dependencies = [ - "bitflags 2.8.0", - "byteorder", - "chrono", - "diesel_derives", - "itoa", - "pq-sys", - "r2d2", -] - -[[package]] -name = "diesel_derives" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" -dependencies = [ - "diesel_table_macro_syntax", - "dsl_auto_type", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" -dependencies = [ - "syn 2.0.101", -] - [[package]] name = "digest" version = "0.9.0" @@ -2372,20 +2366,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dsl_auto_type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" -dependencies = [ - "darling 0.20.10", - "either", - "heck", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "dunce" version = "1.0.5" @@ -2579,6 +2559,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "ethabi-decode" version = "2.0.0" @@ -2611,6 +2602,17 @@ dependencies = [ "uint 0.10.0", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "expander" version = "2.2.1" @@ -2711,6 +2713,17 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2905,6 +2918,17 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -3141,6 +3165,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "heck" version = "0.5.0" @@ -3177,6 +3210,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -3217,6 +3259,15 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "http" version = "1.2.0" @@ -3819,6 +3870,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -3832,6 +3886,17 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags 2.8.0", + "libc", + "redox_syscall", +] + [[package]] name = "libsecp256k1" version = "0.7.1" @@ -3880,6 +3945,16 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -4024,6 +4099,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.7.4" @@ -4138,6 +4223,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -4163,6 +4265,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -4346,6 +4459,12 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -4405,6 +4524,15 @@ dependencies = [ "password-hash", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -4454,6 +4582,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -4559,15 +4698,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "pq-sys" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cc05d7ea95200187117196eee9edd0644424911821aeb28a18ce60ea0b8793" -dependencies = [ - "vcpkg", -] - [[package]] name = "prettyplease" version = "0.2.29" @@ -4783,17 +4913,6 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] - [[package]] name = "radium" version = "0.7.0" @@ -4899,9 +5018,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags 2.8.0", ] @@ -5072,6 +5191,26 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "ruint" version = "1.17.0" @@ -5321,15 +5460,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot", -] - [[package]] name = "schnellru" version = "0.2.4" @@ -5612,6 +5742,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.9.9" @@ -6615,6 +6756,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -6626,6 +6770,200 @@ dependencies = [ "der", ] +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64 0.22.1", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.2", + "hashlink", + "indexmap 2.7.0", + "log", + "memchr", + "once_cell", + "percent-encoding", + "rustls", + "serde", + "serde_json", + "sha2 0.10.8", + "smallvec", + "thiserror 2.0.11", + "tokio", + "tokio-stream", + "tracing", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.101", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.8", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.101", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.8.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest 0.10.7", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac 0.12.1", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde", + "sha1", + "sha2 0.10.8", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.8.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac 0.12.1", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde", + "serde_json", + "sha2 0.10.8", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.11", + "tracing", + "url", +] + [[package]] name = "ss58-registry" version = "1.51.0" @@ -6653,6 +6991,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" @@ -7349,6 +7698,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.14" @@ -7364,6 +7719,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -7484,6 +7845,12 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -7767,6 +8134,16 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -7813,6 +8190,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-registry" version = "0.4.0" @@ -7830,7 +8213,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.1", ] [[package]] @@ -7839,7 +8222,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-link", + "windows-link 0.1.1", ] [[package]] @@ -7878,6 +8261,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.42.2" diff --git a/Cargo.toml b/Cargo.toml index d4dd96b..2121527 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,10 +29,10 @@ http = "1.1" backon = { version = "1.5.2", features = ["tokio-sleep"] } anyhow = "1" lazy_static = "1.5" -diesel = { version = "2.2", features = ["postgres", "chrono", "r2d2"] } serde_with = "3.11.0" chrono = { version = "0.4", features = ["serde"] } - +sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-rustls", "macros", "chrono"] } +#alloy-rpc-types ="1.0.27" [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = "0.6" diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..472cf3d --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,17 @@ +version: '3' + +services: + db: + image: postgres:latest + container_name: bridge-api + environment: + POSTGRES_USER: avail + POSTGRES_PASSWORD: avail + POSTGRES_DB: ui-indexer + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/src/main.rs b/src/main.rs index 6d76cb3..c67820c 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,7 @@ mod models; -mod schema; - use crate::models::*; -use crate::schema::avail_sends::dsl::avail_sends; -use crate::schema::ethereum_sends::dsl::ethereum_sends; -use alloy::consensus::transaction::TxHashable; +use alloy::consensus::transaction::{Recovered, TxHashable}; use alloy::primitives::{Address, B256, U256, hex}; use alloy::providers::ProviderBuilder; use anyhow::{Context, Result, anyhow}; @@ -22,10 +18,7 @@ use axum::{ use backon::ExponentialBuilder; use backon::Retryable; use chrono::{NaiveDateTime, Utc}; -use diesel::{ - ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, SelectableHelper, r2d2, - r2d2::ConnectionManager, -}; + use http::Method; use jsonrpsee::{ core::ClientError, @@ -54,8 +47,23 @@ use tower_http::{ }; use tracing_subscriber::prelude::*; +use crate::models::StatusEnum::{InProgress, Initialized}; +use alloy::consensus::TxEnvelope; +use alloy::core::sol; +use alloy::hex::{ToHex, ToHexExt}; +use alloy::network::TransactionResponse; use alloy::rpc::types::Transaction; +use sqlx::{FromRow, PgPool, Pool, Postgres, Row, query}; use std::sync::{Arc, Mutex}; +use tracing::log::__private_api::log; +use tracing::log::info; +use tracing::warn; +// sol! { +// contract AvailBridge { +// function sendAVAIL(bytes32 recipient,uint256 amount) +// } +// } + #[cfg(not(target_env = "msvc"))] #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; @@ -80,7 +88,7 @@ pub struct AppState { pub eth_proof_failure_cache_maxage: u32, pub slot_mapping_cache_maxage: u32, pub transactions_cache_maxage: u32, - pub connection_pool: r2d2::Pool>, + pub db: PgPool, pub chains: HashMap, } @@ -93,14 +101,13 @@ async fn transaction( State(state): State>, Path(hash): Path, ) -> Result { - // fetch details of the eth transaction - let resp: Result = state + let resp: Result = state .ethereum_client .request("eth_getTransactionByHash", rpc_params![hash]) .await; - let tx: Transaction = resp.map_err(|e| { + let tx: TransactionRpc = resp.map_err(|e| { tracing::error!("❌ Cannot get transaction: {e:#}"); if e.to_string().ends_with("status code: 429") { ErrorResponse::with_status_and_headers( @@ -117,8 +124,30 @@ async fn transaction( } })?; + let recipient = &tx.input[10..74]; + let amount = &tx.input[74..]; + + query("INSERT INTO ethereum_sends (message_id, status, source_transaction_hash, source_block_number, source_block_hash, + source_timestamp, depositor_address, receiver_address, amount) VALUES( + $1, $2, $3, $4, $5, $6, $7, $8, $9)") + .bind(1) + .bind(Initialized) + .bind(tx.hash) + .bind(tx.block_number as i64) + .bind(tx.block_hash) + .bind(Utc::now()) + .bind(tx.from) + .bind(format!("0x{}",recipient)) + .bind(amount) + .execute(&state.db) + .await + .map_err(|e|{ + warn!("Cannot insert tx {}", e); + return anyhow!("Cannot insert tx"); + } + )?; - Ok(Json(json!({ "transaction": tx }))) + Ok(()) } #[inline(always)] @@ -141,81 +170,83 @@ async fn transactions( Query(address_query): Query, State(state): State>, ) -> Result { - if address_query.eth_address.is_none() && address_query.avail_address.is_none() { - tracing::error!("Query params not provided."); - return Err(ErrorResponse::with_status_and_headers( - anyhow!("Invalid request: Query params not provided"), - StatusCode::BAD_REQUEST, - &[("Cache-Control", "max-age=60, must-revalidate")], - )); - } + // if address_query.eth_address.is_none() && address_query.avail_address.is_none() { + // tracing::error!("Query params not provided."); + // return Err(ErrorResponse::with_status_and_headers( + // anyhow!("Invalid request: Query params not provided"), + // StatusCode::BAD_REQUEST, + // &[("Cache-Control", "max-age=60, must-revalidate")], + // )); + // } + // + // let cloned_state = state.clone(); + // let mut conn = cloned_state + // .connection_pool + // .get_timeout(Duration::from_secs(1)) + // .expect("Get connection pool"); + // + // // Initialize the result variables + // let mut transaction_results: TransactionResult = TransactionResult::default(); + // + // // Return the combined results + // if let Some(eth_address) = address_query.eth_address { + // let ethereum_sends_results = ethereum_sends + // .select(EthereumSend::as_select()) + // .filter(schema::ethereum_sends::depositor_address.eq(format!("{:?}", eth_address))) + // .order_by(schema::ethereum_sends::source_timestamp.desc()) + // .limit(500) + // .load::(&mut conn); + // + // transaction_results.eth_send = ethereum_sends_results + // .map_err(|e| { + // tracing::error!("Cannot get ethereum send transactions:: {e:#}"); + // ErrorResponse::with_status_and_headers( + // e.into(), + // StatusCode::INTERNAL_SERVER_ERROR, + // &[("Cache-Control", "public, max-age=60, must-revalidate")], + // ) + // })? + // .into_iter() + // .map(map_ethereum_send_to_transaction_result) + // .collect(); + // } + // + // if let Some(avail_address) = address_query.avail_address { + // let avail_sends_results = avail_sends + // .select(AvailSend::as_select()) + // .filter(schema::avail_sends::depositor_address.eq(format!("{:?}", avail_address))) + // .order_by(schema::avail_sends::source_timestamp.desc()) + // .limit(500) + // .load::(&mut conn); + // + // transaction_results.avail_send = avail_sends_results + // .map_err(|e| { + // tracing::error!("Cannot get avail send transactions: {e:#}"); + // ErrorResponse::with_status_and_headers( + // e.into(), + // StatusCode::INTERNAL_SERVER_ERROR, + // &[("Cache-Control", "public, max-age=60, must-revalidate")], + // ) + // })? + // .into_iter() + // .map(map_avail_send_to_transaction_result) + // .collect(); + // } + // + // Ok(( + // StatusCode::OK, + // [( + // "Cache-Control", + // format!( + // "public, max-age={}, immutable", + // state.transactions_cache_maxage + // ), + // )], + // Json(json!(transaction_results)), + // ) + // .into_response()) - let cloned_state = state.clone(); - let mut conn = cloned_state - .connection_pool - .get_timeout(Duration::from_secs(1)) - .expect("Get connection pool"); - - // Initialize the result variables - let mut transaction_results: TransactionResult = TransactionResult::default(); - - // Return the combined results - if let Some(eth_address) = address_query.eth_address { - let ethereum_sends_results = ethereum_sends - .select(EthereumSend::as_select()) - .filter(schema::ethereum_sends::depositor_address.eq(format!("{:?}", eth_address))) - .order_by(schema::ethereum_sends::source_timestamp.desc()) - .limit(500) - .load::(&mut conn); - - transaction_results.eth_send = ethereum_sends_results - .map_err(|e| { - tracing::error!("Cannot get ethereum send transactions:: {e:#}"); - ErrorResponse::with_status_and_headers( - e.into(), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })? - .into_iter() - .map(map_ethereum_send_to_transaction_result) - .collect(); - } - - if let Some(avail_address) = address_query.avail_address { - let avail_sends_results = avail_sends - .select(AvailSend::as_select()) - .filter(schema::avail_sends::depositor_address.eq(format!("{:?}", avail_address))) - .order_by(schema::avail_sends::source_timestamp.desc()) - .limit(500) - .load::(&mut conn); - - transaction_results.avail_send = avail_sends_results - .map_err(|e| { - tracing::error!("Cannot get avail send transactions: {e:#}"); - ErrorResponse::with_status_and_headers( - e.into(), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })? - .into_iter() - .map(map_avail_send_to_transaction_result) - .collect(); - } - - Ok(( - StatusCode::OK, - [( - "Cache-Control", - format!( - "public, max-age={}, immutable", - state.transactions_cache_maxage - ), - )], - Json(json!(transaction_results)), - ) - .into_response()) + Ok(()) } #[inline(always)] @@ -756,14 +787,19 @@ async fn main() { // Connection pool let connections_string = format!( "postgresql://{}:{}@{}/{}", - env::var("PG_USERNAME").unwrap_or("myuser".to_owned()), - env::var("PG_PASSWORD").unwrap_or("mypassword".to_owned()), + env::var("PG_USERNAME").unwrap_or("avail".to_owned()), + env::var("PG_PASSWORD").unwrap_or("avail".to_owned()), env::var("POSTGRES_URL").unwrap_or("localhost:5432".to_owned()), - env::var("POSTGRES_DB").unwrap_or("bridge-ui-indexer".to_owned()), + env::var("POSTGRES_DB").unwrap_or("ui-indexer".to_owned()), ); - let connection_pool = r2d2::Pool::builder() - .build(ConnectionManager::::new(connections_string)) - .expect("Failed to create pool."); + + info!("Connecting to {}", connections_string); + + let db = PgPool::connect(&connections_string) + .await + .context("Cannot get connection pool") + .unwrap(); + const SUPPORTED_CHAIN_IDS: [u64; 7] = [1, 123, 32657, 84532, 11155111, 17000, 421614]; // loop through expected_chain_ids and store the chain information, if value is missing, skip chain_id let chains = SUPPORTED_CHAIN_IDS @@ -847,7 +883,7 @@ async fn main() { transactions_mapping_response.parse::().ok() }) .unwrap_or(60), - connection_pool, + db, chains, }); diff --git a/src/models.rs b/src/models.rs index 0359ddb..596edb5 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,18 +1,10 @@ -use crate::schema::sql_types::Status; use alloy::primitives::{Address, B256}; use alloy::sol; use avail_core::data_proof::AddressedMessage; use axum::Json; use axum::response::{IntoResponse, Response}; use chrono::NaiveDateTime; -use diesel::pg::{Pg, PgValue}; -use diesel::serialize::{IsNull, Output}; -use diesel::{ - deserialize::{self, FromSql}, - expression::AsExpression, - serialize::ToSql, - *, -}; + use http::{HeaderMap, HeaderName, HeaderValue, StatusCode}; use jsonrpsee::core::Serialize; use serde::{Deserialize, Deserializer}; @@ -294,49 +286,46 @@ where u32::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) } -#[derive(Debug, Clone, PartialEq, FromSqlRow, AsExpression, Eq)] -#[diesel(sql_type = Status)] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Serialize, Deserialize, sqlx::Type)] +#[sqlx(type_name = "status")] pub enum StatusEnum { + #[sqlx(rename = "INITIALIZED")] + Initialized, + #[sqlx(rename = "IN_PROGRESS")] InProgress, + #[sqlx(rename = "CLAIM_PENDING")] ClaimPending, + #[sqlx(rename = "BRIDGED")] Bridged, } -impl ToSql for StatusEnum { - fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { - match *self { - StatusEnum::InProgress => out.write_all(b"IN_PROGRESS")?, - StatusEnum::ClaimPending => out.write_all(b"CLAIM_PENDING")?, - StatusEnum::Bridged => out.write_all(b"BRIDGED")?, - } - Ok(IsNull::No) - } -} -impl FromSql for StatusEnum { - fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { - match bytes.as_bytes() { - b"IN_PROGRESS" => Ok(StatusEnum::InProgress), - b"CLAIM_PENDING" => Ok(StatusEnum::ClaimPending), - b"BRIDGED" => Ok(StatusEnum::Bridged), - _ => Err(format!( - "Unrecognized enum variant {}", - std::str::from_utf8(bytes.as_bytes()).unwrap() - ) - .as_str() - .into()), - } - } -} +// impl ToSql for StatusEnum { +// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { +// match *self { +// StatusEnum::InProgress => out.write_all(b"IN_PROGRESS")?, +// StatusEnum::ClaimPending => out.write_all(b"CLAIM_PENDING")?, +// StatusEnum::Bridged => out.write_all(b"BRIDGED")?, +// } +// Ok(IsNull::No) +// } +// } +// +// impl FromSql for StatusEnum { +// fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { +// match bytes.as_bytes() { +// b"IN_PROGRESS" => Ok(StatusEnum::InProgress), +// b"CLAIM_PENDING" => Ok(StatusEnum::ClaimPending), +// b"BRIDGED" => Ok(StatusEnum::Bridged), +// _ => Err(format!( +// "Unrecognized enum variant {}", +// std::str::from_utf8(bytes.as_bytes()).unwrap() +// ) +// .as_str() +// .into()), +// } +// } +// } -#[derive(Queryable, Selectable, Insertable, Identifiable, Serialize)] -#[serde(rename_all = "camelCase")] -#[diesel(table_name = crate::schema::avail_sends)] -#[diesel(primary_key(message_id))] -#[diesel(check_for_backend(diesel::pg::Pg))] -#[derive(Clone, Debug)] -#[serde_as] pub struct AvailSend { pub message_id: i64, pub status: StatusEnum, @@ -344,38 +333,27 @@ pub struct AvailSend { pub source_block_number: i64, pub source_block_hash: String, pub source_transaction_index: i64, - #[serde_as(as = "TimestampSeconds")] pub source_timestamp: NaiveDateTime, pub token_id: String, pub destination_block_number: Option, pub destination_block_hash: Option, - #[serde_as(as = "Option")] pub destination_timestamp: Option, pub depositor_address: String, pub receiver_address: String, pub amount: String, } -#[derive(Queryable, Selectable, Insertable, Identifiable, Serialize)] -#[serde(rename_all = "camelCase")] -#[diesel(table_name = crate::schema::ethereum_sends)] -#[diesel(primary_key(message_id))] -#[diesel(check_for_backend(diesel::pg::Pg))] -#[derive(Clone, Debug)] -#[serde_as] pub struct EthereumSend { pub message_id: i64, pub status: StatusEnum, pub source_transaction_hash: String, pub source_block_number: i64, pub source_block_hash: String, - #[serde_as(as = "TimestampSeconds")] pub source_timestamp: NaiveDateTime, pub token_id: String, pub destination_block_number: Option, pub destination_block_hash: Option, pub destination_transaction_index: Option, - #[serde_as(as = "Option")] pub destination_timestamp: Option, pub depositor_address: String, pub receiver_address: String, @@ -400,14 +378,12 @@ pub struct TransactionData { pub source_block_hash: String, #[serde(skip_serializing_if = "Option::is_none")] pub source_transaction_index: Option, - #[serde_as(as = "TimestampSeconds")] pub source_timestamp: NaiveDateTime, pub token_id: String, pub destination_block_number: Option, pub destination_block_hash: Option, #[serde(skip_serializing_if = "Option::is_none")] pub destination_transaction_index: Option, - #[serde_as(as = "Option")] pub destination_timestamp: Option, pub depositor_address: String, pub receiver_address: String, @@ -461,3 +437,20 @@ pub fn map_avail_send_to_transaction_result(send: AvailSend) -> TransactionData amount: send.amount, } } + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionRpc { + pub from: String, + pub to: String, + pub input: String, + pub value: String, + #[serde(deserialize_with = "hex_to_u32")] + pub nonce: u32, + pub block_hash: String, + #[serde(deserialize_with = "hex_to_u32")] + pub block_number: u32, + #[serde(deserialize_with = "hex_to_u32")] + pub transaction_index: u32, + pub hash: String, +} diff --git a/src/schema.rs b/src/schema.rs deleted file mode 100644 index 54dec35..0000000 --- a/src/schema.rs +++ /dev/null @@ -1,79 +0,0 @@ -// @generated automatically by Diesel CLI. - -pub mod sql_types { - #[derive(diesel::query_builder::QueryId, Clone, diesel::sql_types::SqlType)] - #[diesel(postgres_type(name = "claim_type"))] - pub struct ClaimType; - - #[derive(diesel::query_builder::QueryId, Clone, diesel::sql_types::SqlType)] - #[diesel(postgres_type(name = "status"))] - pub struct Status; -} - -diesel::table! { - use diesel::sql_types::*; - use super::sql_types::Status; - use super::sql_types::ClaimType; - - avail_sends (message_id) { - message_id -> Int8, - status -> Status, - #[max_length = 66] - source_transaction_hash -> Varchar, - source_block_number -> Int8, - #[max_length = 66] - source_block_hash -> Varchar, - source_transaction_index -> Int8, - source_timestamp -> Timestamp, - #[max_length = 66] - token_id -> Varchar, - #[max_length = 66] - destination_transaction_hash -> Nullable, - destination_block_number -> Nullable, - #[max_length = 66] - destination_block_hash -> Nullable, - destination_timestamp -> Nullable, - #[max_length = 66] - depositor_address -> Varchar, - #[max_length = 22] - receiver_address -> Varchar, - #[max_length = 255] - amount -> Varchar, - claim_type -> ClaimType, - } -} - -diesel::table! { - use diesel::sql_types::*; - use super::sql_types::Status; - use super::sql_types::ClaimType; - - ethereum_sends (message_id) { - message_id -> Int8, - status -> Status, - #[max_length = 66] - source_transaction_hash -> Varchar, - source_block_number -> Int8, - #[max_length = 66] - source_block_hash -> Varchar, - source_timestamp -> Timestamp, - #[max_length = 66] - token_id -> Varchar, - #[max_length = 66] - destination_transaction_hash -> Nullable, - destination_block_number -> Nullable, - #[max_length = 66] - destination_block_hash -> Nullable, - destination_transaction_index-> Nullable, - destination_timestamp -> Nullable, - #[max_length = 66] - depositor_address -> Varchar, - #[max_length = 66] - receiver_address -> Varchar, - #[max_length = 255] - amount -> Varchar, - claim_type -> ClaimType, - } -} - -diesel::allow_tables_to_appear_in_same_query!(avail_sends, ethereum_sends,); From e82067b7817e63829c5c3e0d47ee4ba146c0e859 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Thu, 30 Oct 2025 11:47:43 +0100 Subject: [PATCH 07/48] Fetch tx receipt and get message id. --- src/main.rs | 65 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/src/main.rs b/src/main.rs index c67820c..58fdced 100755 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ mod models; use crate::models::*; use alloy::consensus::transaction::{Recovered, TxHashable}; -use alloy::primitives::{Address, B256, U256, hex}; +use alloy::primitives::{Address, B256, Log, LogData, U256, hex}; use alloy::providers::ProviderBuilder; use anyhow::{Context, Result, anyhow}; use axum::body::{Body, to_bytes}; @@ -50,14 +50,16 @@ use tracing_subscriber::prelude::*; use crate::models::StatusEnum::{InProgress, Initialized}; use alloy::consensus::TxEnvelope; use alloy::core::sol; -use alloy::hex::{ToHex, ToHexExt}; +use alloy::hex::{FromHex, ToHex, ToHexExt}; use alloy::network::TransactionResponse; -use alloy::rpc::types::Transaction; +use alloy::rpc::types::{Transaction, TransactionReceipt}; use sqlx::{FromRow, PgPool, Pool, Postgres, Row, query}; +use std::str::FromStr; use std::sync::{Arc, Mutex}; use tracing::log::__private_api::log; use tracing::log::info; use tracing::warn; + // sol! { // contract AvailBridge { // function sendAVAIL(bytes32 recipient,uint256 amount) @@ -101,37 +103,56 @@ async fn transaction( State(state): State>, Path(hash): Path, ) -> Result { - // fetch details of the eth transaction - let resp: Result = state + let tx: TransactionRpc = state .ethereum_client .request("eth_getTransactionByHash", rpc_params![hash]) - .await; - - let tx: TransactionRpc = resp.map_err(|e| { - tracing::error!("❌ Cannot get transaction: {e:#}"); - if e.to_string().ends_with("status code: 429") { - ErrorResponse::with_status_and_headers( - e.into(), - StatusCode::TOO_MANY_REQUESTS, - &[("Cache-Control", "public, max-age=60, must-revalidate")], + .await + .map_err(|e| { + tracing::error!("Cannot get transaction: {e:#}"); + ErrorResponse::with_status( + anyhow!("Cannot get transaction"), + StatusCode::INTERNAL_SERVER_ERROR, ) - } else { - ErrorResponse::with_status_and_headers( - e.into(), + })?; + + let receipt: TransactionReceipt = state + .ethereum_client + .request("eth_getTransactionReceipt", rpc_params![hash]) + .await + .map_err(|e| { + tracing::error!("Cannot get transaction receipt: {e:#}"); + ErrorResponse::with_status( + anyhow!("Cannot get transaction receipt"), StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], ) - } - })?; + })?; + + let target_topic = + B256::from_str("0x06fd209663be9278f96bc53dfbf6cf3cdcf2172c38b5de30abff93ba443d653a")?; + + let log_data: LogData = receipt + .inner + .logs() + .iter() + .find(|log| log.topics().contains(&target_topic)) + .map(|log| log.data()) + .unwrap() + .clone(); + + let event_data = U256::from_be_slice(&log_data.data); + // Extract the least-significant 64 bits/should match the message size + let message_id = event_data.as_limbs()[0] as i64; + // recipient must be from a function call data recipient+amount + // sendAVAIL(bytes32 recipient,uint256 amount) let recipient = &tx.input[10..74]; let amount = &tx.input[74..]; query("INSERT INTO ethereum_sends (message_id, status, source_transaction_hash, source_block_number, source_block_hash, source_timestamp, depositor_address, receiver_address, amount) VALUES( $1, $2, $3, $4, $5, $6, $7, $8, $9)") - .bind(1) - .bind(Initialized) + .bind(message_id) + .bind(Initialized) .bind(tx.hash) .bind(tx.block_number as i64) .bind(tx.block_hash) From b4207d3eb8db265814f9bc3adf9b972ca97c4881 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 31 Oct 2025 10:35:43 +0100 Subject: [PATCH 08/48] Decode tx events. --- src/main.rs | 87 ++++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/src/main.rs b/src/main.rs index 58fdced..b022814 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ mod models; +use crate::AvailBridge::{AvailBridgeCalls, AvailBridgeEvents, MessageSent}; use crate::models::*; use alloy::consensus::transaction::{Recovered, TxHashable}; use alloy::primitives::{Address, B256, Log, LogData, U256, hex}; use alloy::providers::ProviderBuilder; +use alloy::sol_types::{SolCall, SolEventInterface, SolInterface}; use anyhow::{Context, Result, anyhow}; use axum::body::{Body, to_bytes}; use axum::response::Response; @@ -18,7 +20,14 @@ use axum::{ use backon::ExponentialBuilder; use backon::Retryable; use chrono::{NaiveDateTime, Utc}; +use sp_core::hexdisplay::AsBytesRef; +use crate::models::StatusEnum::{InProgress, Initialized}; +use alloy::consensus::TxEnvelope; +use alloy::core::sol; +use alloy::hex::{FromHex, ToHex, ToHexExt}; +use alloy::network::TransactionResponse; +use alloy::rpc::types::{Transaction, TransactionReceipt}; use http::Method; use jsonrpsee::{ core::ClientError, @@ -34,7 +43,10 @@ use serde_with::serde_as; use sha3::{Digest, Keccak256}; use sp_core::{Decode, H160, H256}; use sp_io::hashing::twox_128; +use sqlx::{FromRow, PgPool, Pool, Postgres, Row, query}; use std::collections::HashMap; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; use std::{env, process, time::Duration}; #[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; @@ -45,26 +57,18 @@ use tower_http::{ cors::{Any, CorsLayer}, trace::TraceLayer, }; -use tracing_subscriber::prelude::*; - -use crate::models::StatusEnum::{InProgress, Initialized}; -use alloy::consensus::TxEnvelope; -use alloy::core::sol; -use alloy::hex::{FromHex, ToHex, ToHexExt}; -use alloy::network::TransactionResponse; -use alloy::rpc::types::{Transaction, TransactionReceipt}; -use sqlx::{FromRow, PgPool, Pool, Postgres, Row, query}; -use std::str::FromStr; -use std::sync::{Arc, Mutex}; use tracing::log::__private_api::log; use tracing::log::info; use tracing::warn; +use tracing_subscriber::prelude::*; -// sol! { -// contract AvailBridge { -// function sendAVAIL(bytes32 recipient,uint256 amount) -// } -// } +sol! { + #[derive(Debug)] + contract AvailBridge { + function sendAVAIL(bytes32 recipient, uint256 amount) external; + event MessageSent(address indexed from, bytes32 indexed to, uint256 messageId); + } +} #[cfg(not(target_env = "msvc"))] #[global_allocator] @@ -127,45 +131,46 @@ async fn transaction( ) })?; + let AvailBridgeCalls::sendAVAIL(call) = + AvailBridge::AvailBridgeCalls::abi_decode(hex::decode(tx.input)?.as_bytes_ref())?; + + let recipient = call.recipient; + let amount = call.amount; + let target_topic = B256::from_str("0x06fd209663be9278f96bc53dfbf6cf3cdcf2172c38b5de30abff93ba443d653a")?; - let log_data: LogData = receipt + let log = receipt .inner .logs() .iter() .find(|log| log.topics().contains(&target_topic)) - .map(|log| log.data()) - .unwrap() - .clone(); - - let event_data = U256::from_be_slice(&log_data.data); - // Extract the least-significant 64 bits/should match the message size - let message_id = event_data.as_limbs()[0] as i64; + .ok_or_else(|| anyhow!("Cannot find transaction log"))? + .clone() + .into(); - // recipient must be from a function call data recipient+amount - // sendAVAIL(bytes32 recipient,uint256 amount) - let recipient = &tx.input[10..74]; - let amount = &tx.input[74..]; + let decoded = AvailBridgeEvents::decode_log(&log)?; + let AvailBridgeEvents::MessageSent(call) = decoded.data; + let message_id = call.messageId; query("INSERT INTO ethereum_sends (message_id, status, source_transaction_hash, source_block_number, source_block_hash, source_timestamp, depositor_address, receiver_address, amount) VALUES( $1, $2, $3, $4, $5, $6, $7, $8, $9)") .bind(message_id) .bind(Initialized) - .bind(tx.hash) - .bind(tx.block_number as i64) - .bind(tx.block_hash) - .bind(Utc::now()) - .bind(tx.from) - .bind(format!("0x{}",recipient)) - .bind(amount) - .execute(&state.db) - .await - .map_err(|e|{ - warn!("Cannot insert tx {}", e); - return anyhow!("Cannot insert tx"); - } + .bind(tx.hash) + .bind(tx.block_number as i64) + .bind(tx.block_hash) + .bind(Utc::now()) + .bind(tx.from) + .bind(format!("0x{}", recipient)) + .bind(amount) + .execute(&state.db) + .await + .map_err(|e| { + warn!("Cannot insert tx {}", e); + return anyhow!("Cannot insert tx"); + } )?; Ok(()) From 1eb602bb9439ec73cff614a7fc264695822fe349 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 31 Oct 2025 10:55:46 +0100 Subject: [PATCH 09/48] Fix inputs. --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index b022814..98f1512 100755 --- a/src/main.rs +++ b/src/main.rs @@ -151,7 +151,7 @@ async fn transaction( let decoded = AvailBridgeEvents::decode_log(&log)?; let AvailBridgeEvents::MessageSent(call) = decoded.data; - let message_id = call.messageId; + let message_id: i64 = call.messageId.try_into()?; query("INSERT INTO ethereum_sends (message_id, status, source_transaction_hash, source_block_number, source_block_hash, source_timestamp, depositor_address, receiver_address, amount) VALUES( @@ -163,8 +163,8 @@ async fn transaction( .bind(tx.block_hash) .bind(Utc::now()) .bind(tx.from) - .bind(format!("0x{}", recipient)) - .bind(amount) + .bind(*recipient) + .bind(format!("{:x}", amount)) .execute(&state.db) .await .map_err(|e| { From 163fdd5393406fe3b5577d5654ef5ef0162bd93d Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 3 Dec 2025 14:25:22 +0100 Subject: [PATCH 10/48] Transaction response WIP. --- Cargo.lock | 31 ++++++ Cargo.toml | 5 +- src/main.rs | 255 +++++++++++++++++++++++++++++++------------------- src/models.rs | 137 +++++++++++++-------------- 4 files changed, 257 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b7cd0f..0d03b2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1484,6 +1484,20 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bigdecimal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560f42649de9fa436b73517378a147ec21f6c997a546581df4b4b31677828934" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "binary-merkle-tree" version = "13.0.0" @@ -1660,6 +1674,7 @@ dependencies = [ "avail-core", "axum", "backon", + "bigdecimal", "chrono", "dotenvy", "http", @@ -1678,6 +1693,7 @@ dependencies = [ "tower-http", "tracing", "tracing-subscriber 0.3.19", + "uuid", ] [[package]] @@ -6790,6 +6806,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ "base64 0.22.1", + "bigdecimal", "bytes", "chrono", "crc", @@ -6866,6 +6883,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", + "bigdecimal", "bitflags 2.8.0", "byteorder", "bytes", @@ -6909,6 +6927,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", + "bigdecimal", "bitflags 2.8.0", "byteorder", "chrono", @@ -6926,6 +6945,7 @@ dependencies = [ "log", "md-5", "memchr", + "num-bigint", "once_cell", "rand 0.8.5", "serde", @@ -7760,6 +7780,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2121527..073c538 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,11 @@ anyhow = "1" lazy_static = "1.5" serde_with = "3.11.0" chrono = { version = "0.4", features = ["serde"] } -sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-rustls", "macros", "chrono"] } +sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-rustls", "macros", "chrono", "bigdecimal"] } #alloy-rpc-types ="1.0.27" +bigdecimal = { version = "0.4", features = ["serde"] } +uuid = { version = "1.18.0", features = ["v4"] } + [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = "0.6" diff --git a/src/main.rs b/src/main.rs index 98f1512..f04a135 100755 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,7 @@ use alloy::core::sol; use alloy::hex::{FromHex, ToHex, ToHexExt}; use alloy::network::TransactionResponse; use alloy::rpc::types::{Transaction, TransactionReceipt}; +use bigdecimal::BigDecimal; use http::Method; use jsonrpsee::{ core::ClientError, @@ -61,6 +62,7 @@ use tracing::log::__private_api::log; use tracing::log::info; use tracing::warn; use tracing_subscriber::prelude::*; +use uuid::Uuid; sol! { #[derive(Debug)] @@ -134,8 +136,16 @@ async fn transaction( let AvailBridgeCalls::sendAVAIL(call) = AvailBridge::AvailBridgeCalls::abi_decode(hex::decode(tx.input)?.as_bytes_ref())?; - let recipient = call.recipient; + let recipient = format!("0x{}", hex::encode(*call.recipient)); let amount = call.amount; + let a = format!("{:x}", amount); + let av = u128::from_str_radix(&a, 16).map_err(|e| { + tracing::error!("Cannot parse amount: {e:#}"); + ErrorResponse::with_status( + anyhow!("Cannot fetch valid transaction data"), + StatusCode::INTERNAL_SERVER_ERROR, + ) + })?; let target_topic = B256::from_str("0x06fd209663be9278f96bc53dfbf6cf3cdcf2172c38b5de30abff93ba443d653a")?; @@ -153,25 +163,34 @@ async fn transaction( let AvailBridgeEvents::MessageSent(call) = decoded.data; let message_id: i64 = call.messageId.try_into()?; - query("INSERT INTO ethereum_sends (message_id, status, source_transaction_hash, source_block_number, source_block_hash, - source_timestamp, depositor_address, receiver_address, amount) VALUES( - $1, $2, $3, $4, $5, $6, $7, $8, $9)") - .bind(message_id) - .bind(Initialized) - .bind(tx.hash) - .bind(tx.block_number as i64) - .bind(tx.block_hash) - .bind(Utc::now()) - .bind(tx.from) - .bind(*recipient) - .bind(format!("{:x}", amount)) - .execute(&state.db) - .await - .map_err(|e| { - warn!("Cannot insert tx {}", e); - return anyhow!("Cannot insert tx"); - } - )?; + query( + "INSERT INTO event ( + + id, + message_id, + event_type, + status, + sender, + receiver, + amount, + block_hash + ) VALUES( + $1, $2, $3, $4, $5, $6, $7, $8)", + ) + .bind(H256::random().to_string()) + .bind(message_id) + .bind("MessageSent") + .bind("Initialized") + .bind(tx.from) + .bind(recipient) + .bind(BigDecimal::from(av)) + .bind(tx.block_hash) + .execute(&state.db) + .await + .map_err(|e| { + warn!("Cannot insert tx {}", e); + return anyhow!("Cannot insert tx"); + })?; Ok(()) } @@ -196,85 +215,129 @@ async fn transactions( Query(address_query): Query, State(state): State>, ) -> Result { - // if address_query.eth_address.is_none() && address_query.avail_address.is_none() { - // tracing::error!("Query params not provided."); - // return Err(ErrorResponse::with_status_and_headers( - // anyhow!("Invalid request: Query params not provided"), - // StatusCode::BAD_REQUEST, - // &[("Cache-Control", "max-age=60, must-revalidate")], - // )); - // } - // - // let cloned_state = state.clone(); - // let mut conn = cloned_state - // .connection_pool - // .get_timeout(Duration::from_secs(1)) - // .expect("Get connection pool"); - // - // // Initialize the result variables - // let mut transaction_results: TransactionResult = TransactionResult::default(); - // - // // Return the combined results - // if let Some(eth_address) = address_query.eth_address { - // let ethereum_sends_results = ethereum_sends - // .select(EthereumSend::as_select()) - // .filter(schema::ethereum_sends::depositor_address.eq(format!("{:?}", eth_address))) - // .order_by(schema::ethereum_sends::source_timestamp.desc()) - // .limit(500) - // .load::(&mut conn); - // - // transaction_results.eth_send = ethereum_sends_results - // .map_err(|e| { - // tracing::error!("Cannot get ethereum send transactions:: {e:#}"); - // ErrorResponse::with_status_and_headers( - // e.into(), - // StatusCode::INTERNAL_SERVER_ERROR, - // &[("Cache-Control", "public, max-age=60, must-revalidate")], - // ) - // })? - // .into_iter() - // .map(map_ethereum_send_to_transaction_result) - // .collect(); - // } - // - // if let Some(avail_address) = address_query.avail_address { - // let avail_sends_results = avail_sends - // .select(AvailSend::as_select()) - // .filter(schema::avail_sends::depositor_address.eq(format!("{:?}", avail_address))) - // .order_by(schema::avail_sends::source_timestamp.desc()) - // .limit(500) - // .load::(&mut conn); - // - // transaction_results.avail_send = avail_sends_results - // .map_err(|e| { - // tracing::error!("Cannot get avail send transactions: {e:#}"); - // ErrorResponse::with_status_and_headers( - // e.into(), - // StatusCode::INTERNAL_SERVER_ERROR, - // &[("Cache-Control", "public, max-age=60, must-revalidate")], - // ) - // })? - // .into_iter() - // .map(map_avail_send_to_transaction_result) - // .collect(); - // } - // - // Ok(( - // StatusCode::OK, - // [( - // "Cache-Control", - // format!( - // "public, max-age={}, immutable", - // state.transactions_cache_maxage - // ), - // )], - // Json(json!(transaction_results)), - // ) - // .into_response()) + if address_query.eth_address.is_none() && address_query.avail_address.is_none() { + tracing::error!("Query params not provided."); + return Err(ErrorResponse::with_status_and_headers( + anyhow!("Invalid request: Query params not provided"), + StatusCode::BAD_REQUEST, + &[("Cache-Control", "max-age=60, must-revalidate")], + )); + } - Ok(()) + let mut transaction_results: TransactionResult = TransactionResult::default(); + + if let Some(eth_address) = address_query.eth_address { + let address: String = format!("{:?}", &address_query.eth_address.unwrap()); + + + let eth_send = sqlx::query_as!( + TransactionData, + r#" + SELECT + es.message_id , + es.sender , + es.receiver, + es.amount + -- es.event_type , + -- es.proof , + -- es.block_hash + + FROM event es + -- JOIN avail_sends de + -- ON de.message_id = es.message_id + WHERE es.sender = $1 + ORDER BY es.message_id ASC + "#, + address, + ) + .fetch_all(&state.db) + .await?; + + transaction_results.eth_send = eth_send; + } + + Ok(Json(json!(transaction_results))) } +// if address_query.eth_address.is_none() && address_query.avail_address.is_none() { +// tracing::error!("Query params not provided."); +// return Err(ErrorResponse::with_status_and_headers( +// anyhow!("Invalid request: Query params not provided"), +// StatusCode::BAD_REQUEST, +// &[("Cache-Control", "max-age=60, must-revalidate")], +// )); +// } +// +// let cloned_state = state.clone(); +// let mut conn = cloned_state +// .connection_pool +// .get_timeout(Duration::from_secs(1)) +// .expect("Get connection pool"); +// +// // Initialize the result variables +// let mut transaction_results: TransactionResult = TransactionResult::default(); +// +// // Return the combined results +// if let Some(eth_address) = address_query.eth_address { +// let ethereum_sends_results = ethereum_sends +// .select(EthereumSend::as_select()) +// .filter(schema::ethereum_sends::depositor_address.eq(format!("{:?}", eth_address))) +// .order_by(schema::ethereum_sends::source_timestamp.desc()) +// .limit(500) +// .load::(&mut conn); +// +// transaction_results.eth_send = ethereum_sends_results +// .map_err(|e| { +// tracing::error!("Cannot get ethereum send transactions:: {e:#}"); +// ErrorResponse::with_status_and_headers( +// e.into(), +// StatusCode::INTERNAL_SERVER_ERROR, +// &[("Cache-Control", "public, max-age=60, must-revalidate")], +// ) +// })? +// .into_iter() +// .map(map_ethereum_send_to_transaction_result) +// .collect(); +// } +// +// if let Some(avail_address) = address_query.avail_address { +// let avail_sends_results = avail_sends +// .select(AvailSend::as_select()) +// .filter(schema::avail_sends::depositor_address.eq(format!("{:?}", avail_address))) +// .order_by(schema::avail_sends::source_timestamp.desc()) +// .limit(500) +// .load::(&mut conn); +// +// transaction_results.avail_send = avail_sends_results +// .map_err(|e| { +// tracing::error!("Cannot get avail send transactions: {e:#}"); +// ErrorResponse::with_status_and_headers( +// e.into(), +// StatusCode::INTERNAL_SERVER_ERROR, +// &[("Cache-Control", "public, max-age=60, must-revalidate")], +// ) +// })? +// .into_iter() +// .map(map_avail_send_to_transaction_result) +// .collect(); +// } +// +// Ok(( +// StatusCode::OK, +// [( +// "Cache-Control", +// format!( +// "public, max-age={}, immutable", +// state.transactions_cache_maxage +// ), +// )], +// Json(json!(transaction_results)), +// ) +// .into_response()) + +// Ok(()) +// } + #[inline(always)] async fn get_eth_proof( Path(block_hash): Path, diff --git a/src/models.rs b/src/models.rs index 596edb5..69c051a 100644 --- a/src/models.rs +++ b/src/models.rs @@ -11,6 +11,8 @@ use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::serde_as; use sp_core::{H160, H256}; +use sqlx::Type; +use sqlx::types::{BigDecimal, JsonValue}; use std::io::Write; sol!( @@ -286,8 +288,8 @@ where u32::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) } -#[derive(Debug, Serialize, Deserialize, sqlx::Type)] -#[sqlx(type_name = "status")] +#[derive(Debug, Serialize, Deserialize, Type)] +#[sqlx(type_name = "status", rename_all = "SCREAMING_SNAKE_CASE")] pub enum StatusEnum { #[sqlx(rename = "INITIALIZED")] Initialized, @@ -339,27 +341,27 @@ pub struct AvailSend { pub destination_block_hash: Option, pub destination_timestamp: Option, pub depositor_address: String, - pub receiver_address: String, - pub amount: String, -} -pub struct EthereumSend { - pub message_id: i64, - pub status: StatusEnum, - pub source_transaction_hash: String, - pub source_block_number: i64, - pub source_block_hash: String, - pub source_timestamp: NaiveDateTime, - pub token_id: String, - pub destination_block_number: Option, - pub destination_block_hash: Option, - pub destination_transaction_index: Option, - pub destination_timestamp: Option, - pub depositor_address: String, - pub receiver_address: String, pub amount: String, } +// pub struct EthereumSend { +// pub message_id: i64, +// pub status: StatusEnum, +// pub source_transaction_hash: String, +// pub source_block_number: i64, +// pub source_block_hash: String, +// pub source_timestamp: NaiveDateTime, +// pub token_id: String, +// pub destination_block_number: Option, +// pub destination_block_hash: Option, +// pub destination_transaction_index: Option, +// pub destination_timestamp: Option, +// pub depositor_address: String, +// pub receiver_address: String, +// pub amount: String, +// } + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionQueryParams { @@ -371,23 +373,10 @@ pub struct TransactionQueryParams { #[serde_as] #[serde(rename_all = "camelCase")] pub struct TransactionData { - pub message_id: i64, - pub status: StatusEnum, - pub source_transaction_hash: String, - pub source_block_number: i64, - pub source_block_hash: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub source_transaction_index: Option, - pub source_timestamp: NaiveDateTime, - pub token_id: String, - pub destination_block_number: Option, - pub destination_block_hash: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub destination_transaction_index: Option, - pub destination_timestamp: Option, - pub depositor_address: String, - pub receiver_address: String, - pub amount: String, + pub message_id: BigDecimal, + pub sender: String, + pub receiver: String, + pub amount: BigDecimal, } #[derive(Debug, Default, Serialize, Deserialize)] @@ -397,46 +386,46 @@ pub struct TransactionResult { pub eth_send: Vec, } -pub fn map_ethereum_send_to_transaction_result(send: EthereumSend) -> TransactionData { - TransactionData { - message_id: send.message_id, - status: send.status, - source_transaction_hash: send.source_transaction_hash, - source_block_number: send.source_block_number, - source_block_hash: send.source_block_hash, - source_transaction_index: None, - source_timestamp: send.source_timestamp, - token_id: send.token_id, - destination_block_number: send.destination_block_number, - destination_block_hash: send.destination_block_hash, - destination_transaction_index: send.destination_transaction_index, - destination_timestamp: send.destination_timestamp, - depositor_address: send.depositor_address, - receiver_address: send.receiver_address, - amount: send.amount, - } -} +// pub fn map_ethereum_send_to_transaction_result(send: EthereumSend) -> TransactionData { +// TransactionData { +// message_id: send.message_id, +// status: send.status, +// source_transaction_hash: send.source_transaction_hash, +// source_block_number: send.source_block_number, +// source_block_hash: send.source_block_hash, +// source_transaction_index: None, +// source_timestamp: send.source_timestamp, +// token_id: send.token_id, +// destination_block_number: send.destination_block_number, +// destination_block_hash: send.destination_block_hash, +// destination_transaction_index: send.destination_transaction_index, +// destination_timestamp: send.destination_timestamp, +// depositor_address: send.depositor_address, +// receiver_address: send.receiver_address, +// amount: send.amount, +// } +// } // Function to map AvailSend to TransactionResult -pub fn map_avail_send_to_transaction_result(send: AvailSend) -> TransactionData { - TransactionData { - message_id: send.message_id, - status: send.status, - source_transaction_hash: send.source_transaction_hash, - source_block_number: send.source_block_number, - source_block_hash: send.source_block_hash, - source_transaction_index: Some(send.source_transaction_index), - source_timestamp: send.source_timestamp, - token_id: send.token_id, - destination_block_number: send.destination_block_number, - destination_block_hash: send.destination_block_hash, - destination_transaction_index: None, - destination_timestamp: send.destination_timestamp, - depositor_address: send.depositor_address, - receiver_address: send.receiver_address, - amount: send.amount, - } -} +// pub fn map_avail_send_to_transaction_result(send: AvailSend) -> TransactionData { +// TransactionData { +// message_id: send.message_id, +// status: send.status, +// source_transaction_hash: send.source_transaction_hash, +// source_block_number: send.source_block_number, +// source_block_hash: send.source_block_hash, +// source_transaction_index: Some(send.source_transaction_index), +// source_timestamp: send.source_timestamp, +// token_id: send.token_id, +// destination_block_number: send.destination_block_number, +// destination_block_hash: send.destination_block_hash, +// destination_transaction_index: None, +// destination_timestamp: send.destination_timestamp, +// depositor_address: send.depositor_address, +// receiver_address: send.receiver_address, +// amount: send.amount, +// } +// } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] From 458a7a6b8aca285e4630aaff261c0a038d45634e Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 9 Dec 2025 11:31:34 +0100 Subject: [PATCH 11/48] Transactions db query wip. --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 53 ++++++++++++++++++++++----------------------------- src/models.rs | 14 ++++++++------ 4 files changed, 33 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d03b2f..f704800 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1680,6 +1680,7 @@ dependencies = [ "http", "jsonrpsee", "lazy_static", + "log", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 073c538..069319c 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-rustls", "mac #alloy-rpc-types ="1.0.27" bigdecimal = { version = "0.4", features = ["serde"] } uuid = { version = "1.18.0", features = ["v4"] } +log = "0.4.25" [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = "0.6" diff --git a/src/main.rs b/src/main.rs index f04a135..a69123a 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,10 @@ mod models; -use crate::AvailBridge::{AvailBridgeCalls, AvailBridgeEvents, MessageSent}; +use crate::AvailBridge::{AvailBridgeCalls, AvailBridgeEvents}; use crate::models::*; -use alloy::consensus::transaction::{Recovered, TxHashable}; -use alloy::primitives::{Address, B256, Log, LogData, U256, hex}; +use alloy::primitives::{Address, B256, U256, hex}; use alloy::providers::ProviderBuilder; -use alloy::sol_types::{SolCall, SolEventInterface, SolInterface}; +use alloy::sol_types::{SolEventInterface, SolInterface}; use anyhow::{Context, Result, anyhow}; use axum::body::{Body, to_bytes}; use axum::response::Response; @@ -19,15 +18,11 @@ use axum::{ }; use backon::ExponentialBuilder; use backon::Retryable; -use chrono::{NaiveDateTime, Utc}; +use chrono::{Utc}; use sp_core::hexdisplay::AsBytesRef; -use crate::models::StatusEnum::{InProgress, Initialized}; -use alloy::consensus::TxEnvelope; use alloy::core::sol; -use alloy::hex::{FromHex, ToHex, ToHexExt}; -use alloy::network::TransactionResponse; -use alloy::rpc::types::{Transaction, TransactionReceipt}; +use alloy::rpc::types::{TransactionReceipt}; use bigdecimal::BigDecimal; use http::Method; use jsonrpsee::{ @@ -38,16 +33,14 @@ use jsonrpsee::{ }; use lazy_static::lazy_static; use reqwest::Client; -use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; -use serde_with::serde_as; use sha3::{Digest, Keccak256}; -use sp_core::{Decode, H160, H256}; +use sp_core::{Decode}; use sp_io::hashing::twox_128; -use sqlx::{FromRow, PgPool, Pool, Postgres, Row, query}; +use sqlx::{PgPool, query}; use std::collections::HashMap; use std::str::FromStr; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc}; use std::{env, process, time::Duration}; #[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; @@ -58,11 +51,8 @@ use tower_http::{ cors::{Any, CorsLayer}, trace::TraceLayer, }; -use tracing::log::__private_api::log; -use tracing::log::info; -use tracing::warn; +use tracing::{info, warn}; use tracing_subscriber::prelude::*; -use uuid::Uuid; sol! { #[derive(Debug)] @@ -164,9 +154,7 @@ async fn transaction( let message_id: i64 = call.messageId.try_into()?; query( - "INSERT INTO event ( - - id, + "INSERT INTO bridge_event ( message_id, event_type, status, @@ -175,9 +163,8 @@ async fn transaction( amount, block_hash ) VALUES( - $1, $2, $3, $4, $5, $6, $7, $8)", + $1, $2, $3, $4, $5, $6, $7)", ) - .bind(H256::random().to_string()) .bind(message_id) .bind("MessageSent") .bind("Initialized") @@ -215,6 +202,9 @@ async fn transactions( Query(address_query): Query, State(state): State>, ) -> Result { + + info!("Transaction query: {:?}", address_query); + if address_query.eth_address.is_none() && address_query.avail_address.is_none() { tracing::error!("Query params not provided."); return Err(ErrorResponse::with_status_and_headers( @@ -234,21 +224,24 @@ async fn transactions( TransactionData, r#" SELECT - es.message_id , - es.sender , + es.message_id, + es.sender, es.receiver, - es.amount - -- es.event_type , + es.amount, + es.event_type, + es.status, -- es.proof , - -- es.block_hash + es.block_hash - FROM event es + FROM bridge_event es -- JOIN avail_sends de -- ON de.message_id = es.message_id WHERE es.sender = $1 + AND es.event_type = $2 ORDER BY es.message_id ASC "#, address, + "MessageSent" ) .fetch_all(&state.db) .await?; diff --git a/src/models.rs b/src/models.rs index 69c051a..60bf87d 100644 --- a/src/models.rs +++ b/src/models.rs @@ -12,8 +12,7 @@ use serde_json::json; use serde_with::serde_as; use sp_core::{H160, H256}; use sqlx::Type; -use sqlx::types::{BigDecimal, JsonValue}; -use std::io::Write; +use sqlx::types::{BigDecimal}; sol!( #[allow(missing_docs)] @@ -291,13 +290,13 @@ where #[derive(Debug, Serialize, Deserialize, Type)] #[sqlx(type_name = "status", rename_all = "SCREAMING_SNAKE_CASE")] pub enum StatusEnum { - #[sqlx(rename = "INITIALIZED")] + #[sqlx(rename = "initiated")] Initialized, - #[sqlx(rename = "IN_PROGRESS")] + #[sqlx(rename = "in_progress")] InProgress, - #[sqlx(rename = "CLAIM_PENDING")] + #[sqlx(rename = "claim_ready")] ClaimPending, - #[sqlx(rename = "BRIDGED")] + #[sqlx(rename = "bridged")] Bridged, } @@ -376,7 +375,10 @@ pub struct TransactionData { pub message_id: BigDecimal, pub sender: String, pub receiver: String, + pub block_hash: Option, pub amount: BigDecimal, + pub status: String, + pub event_type: String, } #[derive(Debug, Default, Serialize, Deserialize)] From e35bd7e46c38cf25d4e4872eaabc57e767d6bfa7 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 9 Dec 2025 15:05:43 +0100 Subject: [PATCH 12/48] Models wip for eth. --- src/main.rs | 1 - src/models.rs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index a69123a..cfe7a7a 100755 --- a/src/main.rs +++ b/src/main.rs @@ -228,7 +228,6 @@ async fn transactions( es.sender, es.receiver, es.amount, - es.event_type, es.status, -- es.proof , es.block_hash diff --git a/src/models.rs b/src/models.rs index 60bf87d..dce0a22 100644 --- a/src/models.rs +++ b/src/models.rs @@ -288,7 +288,7 @@ where } #[derive(Debug, Serialize, Deserialize, Type)] -#[sqlx(type_name = "status", rename_all = "SCREAMING_SNAKE_CASE")] +#[sqlx(type_name = "status")] pub enum StatusEnum { #[sqlx(rename = "initiated")] Initialized, @@ -378,7 +378,6 @@ pub struct TransactionData { pub block_hash: Option, pub amount: BigDecimal, pub status: String, - pub event_type: String, } #[derive(Debug, Default, Serialize, Deserialize)] From 1ff933354d2adcc713348573b89c5833227e1215 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 10 Dec 2025 15:32:27 +0100 Subject: [PATCH 13/48] Add time estimate. --- src/main.rs | 69 +++++++++++++++++++++++++++++++++++++++++++-------- src/models.rs | 14 +++++++++++ 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index cfe7a7a..5203392 100755 --- a/src/main.rs +++ b/src/main.rs @@ -18,11 +18,11 @@ use axum::{ }; use backon::ExponentialBuilder; use backon::Retryable; -use chrono::{Utc}; +use chrono::{DateTime, TimeZone, Utc}; use sp_core::hexdisplay::AsBytesRef; use alloy::core::sol; -use alloy::rpc::types::{TransactionReceipt}; +use alloy::rpc::types::TransactionReceipt; use bigdecimal::BigDecimal; use http::Method; use jsonrpsee::{ @@ -35,12 +35,12 @@ use lazy_static::lazy_static; use reqwest::Client; use serde_json::{Value, json}; use sha3::{Digest, Keccak256}; -use sp_core::{Decode}; +use sp_core::Decode; use sp_io::hashing::twox_128; use sqlx::{PgPool, query}; use std::collections::HashMap; use std::str::FromStr; -use std::sync::{Arc}; +use std::sync::Arc; use std::{env, process, time::Duration}; #[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; @@ -202,7 +202,6 @@ async fn transactions( Query(address_query): Query, State(state): State>, ) -> Result { - info!("Transaction query: {:?}", address_query); if address_query.eth_address.is_none() && address_query.avail_address.is_none() { @@ -219,9 +218,8 @@ async fn transactions( if let Some(eth_address) = address_query.eth_address { let address: String = format!("{:?}", &address_query.eth_address.unwrap()); - - let eth_send = sqlx::query_as!( - TransactionData, + let rows: Vec = sqlx::query_as!( + TransactionRow, r#" SELECT es.message_id, @@ -231,7 +229,6 @@ async fn transactions( es.status, -- es.proof , es.block_hash - FROM bridge_event es -- JOIN avail_sends de -- ON de.message_id = es.message_id @@ -242,8 +239,39 @@ async fn transactions( address, "MessageSent" ) - .fetch_all(&state.db) - .await?; + .fetch_all(&state.db) + .await?; + + let slot_block_head = SLOT_BLOCK_HEAD.read().await; + let (slot, block, hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { + ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) + })?; + + info!("Timestamp: {}", timestamp); + + let claim_estimate = time_until_next_update(*timestamp).await; + let mut eth_send: Vec = Vec::new(); + + // TODO stats + for r in rows { + let mut estimate = None; + if r.status == "In_progress" { + estimate = Some(claim_estimate.as_secs()); + } else if r.status == "Initialized" { + estimate = Some(60*60); + } + + let tx = TransactionData { + message_id: r.message_id, + sender: r.sender, + receiver: r.receiver, + block_hash: r.block_hash, + amount: r.amount, + status: r.status, + claim_estimate: estimate, + }; + eth_send.push(tx); + } transaction_results.eth_send = eth_send; } @@ -251,6 +279,22 @@ async fn transactions( Ok(Json(json!(transaction_results))) } +async fn time_until_next_update(timestamp_s: u64) -> Duration { + let now_ms = Utc::now().timestamp_millis() as u64; + + let last_update_ms = Duration::from_secs(timestamp_s).as_millis() as u64; + + // elapsed time in milliseconds + let elapsed_ms = now_ms.saturating_sub(last_update_ms); + + let update_frequency_ms: u64 = 60 * 60 * 1000; // 1 hour in ms + + // remaining time, 0 if already past the interval + let remaining_ms = update_frequency_ms.saturating_sub(elapsed_ms); + + Duration::from_millis(remaining_ms) +} + // if address_query.eth_address.is_none() && address_query.avail_address.is_none() { // tracing::error!("Query params not provided."); // return Err(ErrorResponse::with_status_and_headers( @@ -999,6 +1043,7 @@ async fn main() { .await .unwrap(); + tracing::info!("Starting head tracking task?????????"); tokio::spawn(async move { tracing::info!("Starting head tracking task"); if let Err(e) = track_slot_avail_task(shared_state.clone()).await { @@ -1109,6 +1154,8 @@ async fn track_slot_avail_task(state: Arc) -> Result<()> { let mut slot_block_head = SLOT_BLOCK_HEAD.write().await; tracing::info!("Beacon mapping: {slot}:{bl}"); *slot_block_head = Some((slot, bl, hash, timestamp)); + info!("Timestamp form task: {:?}", timestamp); + drop(slot_block_head); tokio::time::sleep(Duration::from_secs(60 * 5)).await; diff --git a/src/models.rs b/src/models.rs index dce0a22..3e78a6a 100644 --- a/src/models.rs +++ b/src/models.rs @@ -368,6 +368,18 @@ pub struct TransactionQueryParams { pub avail_address: Option, } +#[derive(Debug, Serialize, Deserialize)] +#[serde_as] +#[serde(rename_all = "camelCase")] +pub struct TransactionRow { + pub message_id: BigDecimal, + pub sender: String, + pub receiver: String, + pub block_hash: Option, + pub amount: BigDecimal, + pub status: String, +} + #[derive(Debug, Serialize, Deserialize)] #[serde_as] #[serde(rename_all = "camelCase")] @@ -378,6 +390,8 @@ pub struct TransactionData { pub block_hash: Option, pub amount: BigDecimal, pub status: String, + #[serde(default)] + pub claim_estimate: Option, } #[derive(Debug, Default, Serialize, Deserialize)] From 01dc75f56190c27238115a775598ea086e671a7a Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Thu, 11 Dec 2025 13:29:11 +0100 Subject: [PATCH 14/48] wip query --- src/main.rs | 55 ++++++++++++++++++++++++++++----------------------- src/models.rs | 12 ++++++----- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5203392..e89324b 100755 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use axum::{ }; use backon::ExponentialBuilder; use backon::Retryable; -use chrono::{DateTime, TimeZone, Utc}; +use chrono::Utc; use sp_core::hexdisplay::AsBytesRef; use alloy::core::sol; @@ -218,27 +218,34 @@ async fn transactions( if let Some(eth_address) = address_query.eth_address { let address: String = format!("{:?}", &address_query.eth_address.unwrap()); - let rows: Vec = sqlx::query_as!( - TransactionRow, + let rows: Vec = sqlx::query_as::<_, TransactionRow>( r#" - SELECT - es.message_id, - es.sender, - es.receiver, - es.amount, - es.status, - -- es.proof , - es.block_hash - FROM bridge_event es - -- JOIN avail_sends de - -- ON de.message_id = es.message_id - WHERE es.sender = $1 + SELECT + es.message_id, + es.sender, + es.receiver, + es.amount, + es.source_block_hash, + es.source_transaction_hash, + COALESCE( + CASE + WHEN es.status = 'initiated' THEN 'initiated' -- needed? + WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress' + WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged' + ELSE es.status + END, + 'in_progress' + ) AS final_status + FROM bridge_event es + LEFT JOIN public.avail_execute_table AS aet + ON es.message_id = aet.message_id + WHERE es.sender = $1 AND es.event_type = $2 - ORDER BY es.message_id ASC - "#, - address, - "MessageSent" + ORDER BY es.message_id ASC limit 1000 + "#, ) + .bind(address) + .bind("MessageSent") .fetch_all(&state.db) .await?; @@ -255,19 +262,18 @@ async fn transactions( // TODO stats for r in rows { let mut estimate = None; - if r.status == "In_progress" { + if r.final_status != "bridged" { estimate = Some(claim_estimate.as_secs()); - } else if r.status == "Initialized" { - estimate = Some(60*60); } let tx = TransactionData { message_id: r.message_id, sender: r.sender, receiver: r.receiver, - block_hash: r.block_hash, + source_block_hash: r.source_block_hash, + source_transaction_hash: r.source_transaction_hash, amount: r.amount, - status: r.status, + status: r.final_status, claim_estimate: estimate, }; eth_send.push(tx); @@ -1043,7 +1049,6 @@ async fn main() { .await .unwrap(); - tracing::info!("Starting head tracking task?????????"); tokio::spawn(async move { tracing::info!("Starting head tracking task"); if let Err(e) = track_slot_avail_task(shared_state.clone()).await { diff --git a/src/models.rs b/src/models.rs index 3e78a6a..a303cbf 100644 --- a/src/models.rs +++ b/src/models.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::serde_as; use sp_core::{H160, H256}; -use sqlx::Type; +use sqlx::{FromRow, Type}; use sqlx::types::{BigDecimal}; sol!( @@ -368,16 +368,17 @@ pub struct TransactionQueryParams { pub avail_address: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, FromRow)] #[serde_as] #[serde(rename_all = "camelCase")] pub struct TransactionRow { pub message_id: BigDecimal, pub sender: String, pub receiver: String, - pub block_hash: Option, + pub source_block_hash: String, + pub source_transaction_hash: String, pub amount: BigDecimal, - pub status: String, + pub final_status: String, } #[derive(Debug, Serialize, Deserialize)] @@ -387,7 +388,8 @@ pub struct TransactionData { pub message_id: BigDecimal, pub sender: String, pub receiver: String, - pub block_hash: Option, + pub source_block_hash: String, + pub source_transaction_hash: String, pub amount: BigDecimal, pub status: String, #[serde(default)] From 20a16ed9bd2222845384fdb48c79e5d122459846 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Thu, 11 Dec 2025 15:39:36 +0100 Subject: [PATCH 15/48] Add avail send query. --- src/main.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/models.rs | 4 ++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index e89324b..8946d0c 100755 --- a/src/main.rs +++ b/src/main.rs @@ -227,6 +227,7 @@ async fn transactions( es.amount, es.source_block_hash, es.source_transaction_hash, + ai.block_height, COALESCE( CASE WHEN es.status = 'initiated' THEN 'initiated' -- needed? @@ -236,9 +237,11 @@ async fn transactions( END, 'in_progress' ) AS final_status + FROM bridge_event es LEFT JOIN public.avail_execute_table AS aet ON es.message_id = aet.message_id + INNER JOIN public.avail_indexer ai on ai.id = aet.id WHERE es.sender = $1 AND es.event_type = $2 ORDER BY es.message_id ASC limit 1000 @@ -275,6 +278,7 @@ async fn transactions( amount: r.amount, status: r.final_status, claim_estimate: estimate, + destination_block_number: r.block_height }; eth_send.push(tx); } @@ -282,9 +286,90 @@ async fn transactions( transaction_results.eth_send = eth_send; } + + if let Some(avail_address) = address_query.avail_address { + let rows: Vec = sqlx::query_as::<_, TransactionRow>( + r#" + SELECT + ai1.id as message_id, + ai1.signature_address, + es.to, + es.amount, + ai1.ext_hash, + ai1.ext_hash, + ai1.block_height, + COALESCE( + CASE + WHEN aet.message_id IS NULL THEN 'in_progress' + WHEN aet.message_id IS NOT NULL THEN 'bridged' + END, + 'in_progress' + ) AS final_status + FROM avail_send_message_table es + INNER JOIN public.avail_indexer AS ai1 + ON ai1.id = es.id + INNER JOIN public.bridge_event AS aet + ON es.id = aet.message_id + LEFT JOIN public.avail_indexer AS ai2 + ON ai2.id = aet.message_id + WHERE ai1.signature_address = $1 + AND aet.event_type = $3 + + AND es.type = $2 + ORDER BY es.id ASC + LIMIT 1000; + "#, + ) + .bind(avail_address) + .bind("FungibleToken") + .bind("MessageReceived") + .fetch_all(&state.db) + .await?; + + let slot_block_head = SLOT_BLOCK_HEAD.read().await; + let (slot, block, hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { + ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) + })?; + + info!("Timestamp: {}", timestamp); + + let claim_estimate = time_until_next_update(*timestamp).await; + let mut avail_send: Vec = Vec::new(); + + // TODO stats + for r in rows { + let mut estimate = None; + if r.final_status != "bridged" { + estimate = Some(claim_estimate.as_secs()); + } + + let tx = TransactionData { + message_id: r.message_id, + sender: r.sender, + receiver: r.receiver, + source_block_hash: r.source_block_hash, + source_transaction_hash: r.source_transaction_hash, + amount: r.amount, + status: r.final_status, + claim_estimate: estimate, + destination_block_number: r.block_height + }; + avail_send.push(tx); + } + + transaction_results.avail_send = avail_send; + } + + + Ok(Json(json!(transaction_results))) } + + + + + async fn time_until_next_update(timestamp_s: u64) -> Duration { let now_ms = Utc::now().timestamp_millis() as u64; diff --git a/src/models.rs b/src/models.rs index a303cbf..8b3cccc 100644 --- a/src/models.rs +++ b/src/models.rs @@ -365,7 +365,7 @@ pub struct AvailSend { #[serde(rename_all = "camelCase")] pub struct TransactionQueryParams { pub eth_address: Option, - pub avail_address: Option, + pub avail_address: Option, } #[derive(Debug, Serialize, Deserialize, FromRow)] @@ -379,6 +379,7 @@ pub struct TransactionRow { pub source_transaction_hash: String, pub amount: BigDecimal, pub final_status: String, + pub block_height: i32, } #[derive(Debug, Serialize, Deserialize)] @@ -394,6 +395,7 @@ pub struct TransactionData { pub status: String, #[serde(default)] pub claim_estimate: Option, + pub destination_block_number: i32, } #[derive(Debug, Default, Serialize, Deserialize)] From a09ca8673b4331f882abb9e80d8eeea06801a906 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 12 Dec 2025 15:33:07 +0100 Subject: [PATCH 16/48] Add time estimate on avail transaction claim. --- src/main.rs | 116 ++++++++++++++++++++++++++++++++++++-------------- src/models.rs | 16 +++---- 2 files changed, 92 insertions(+), 40 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8946d0c..3de77dd 100755 --- a/src/main.rs +++ b/src/main.rs @@ -213,6 +213,13 @@ async fn transactions( )); } + let avail_finalized_block: i32 = state + .avail_client + .request("chain_getFinalizedHead", rpc_params![]) + .await + .context("finalized head").unwrap_or(0); + + let mut transaction_results: TransactionResult = TransactionResult::default(); if let Some(eth_address) = address_query.eth_address { @@ -230,7 +237,6 @@ async fn transactions( ai.block_height, COALESCE( CASE - WHEN es.status = 'initiated' THEN 'initiated' -- needed? WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress' WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged' ELSE es.status @@ -257,15 +263,20 @@ async fn transactions( ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) })?; - info!("Timestamp: {}", timestamp); - let claim_estimate = time_until_next_update(*timestamp).await; let mut eth_send: Vec = Vec::new(); - // TODO stats - for r in rows { + for mut r in rows { let mut estimate = None; - if r.final_status != "bridged" { + + if r.final_status != "bridged" + && r.final_status != "in_progress" + && r.block_height <= *block as i32 + { + r.final_status = "claim_ready".to_string(); + } + + if r.final_status != "bridged" && r.final_status != "claim_ready" { estimate = Some(claim_estimate.as_secs()); } @@ -278,7 +289,7 @@ async fn transactions( amount: r.amount, status: r.final_status, claim_estimate: estimate, - destination_block_number: r.block_height + destination_block_number: r.block_height, }; eth_send.push(tx); } @@ -286,21 +297,19 @@ async fn transactions( transaction_results.eth_send = eth_send; } - if let Some(avail_address) = address_query.avail_address { let rows: Vec = sqlx::query_as::<_, TransactionRow>( r#" SELECT ai1.id as message_id, - ai1.signature_address, - es.to, + ai1.signature_address as sender, + es.to as receiver, es.amount, - ai1.ext_hash, - ai1.ext_hash, + ai1.block_hash as source_block_hash, + ai1.ext_hash as source_transaction_hash, ai1.block_height, COALESCE( CASE - WHEN aet.message_id IS NULL THEN 'in_progress' WHEN aet.message_id IS NOT NULL THEN 'bridged' END, 'in_progress' @@ -308,41 +317,70 @@ async fn transactions( FROM avail_send_message_table es INNER JOIN public.avail_indexer AS ai1 ON ai1.id = es.id - INNER JOIN public.bridge_event AS aet + LEFT JOIN public.bridge_event AS aet ON es.id = aet.message_id LEFT JOIN public.avail_indexer AS ai2 ON ai2.id = aet.message_id WHERE ai1.signature_address = $1 AND aet.event_type = $3 - + AND ai1.ext_success = $4 AND es.type = $2 ORDER BY es.id ASC LIMIT 1000; "#, ) - .bind(avail_address) - .bind("FungibleToken") - .bind("MessageReceived") - .fetch_all(&state.db) - .await?; + .bind(avail_address) + .bind("FungibleToken") + .bind("MessageReceived") + .bind(true) + .fetch_all(&state.db) + .await?; - let slot_block_head = SLOT_BLOCK_HEAD.read().await; - let (slot, block, hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { - ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) + + + let url = format!( + "{}/{}?contractChainId={}&contractAddress={}", + state.merkle_proof_service_base_url, + "range", + state.contract_chain_id, + state.contract_address + ); + + info!("url {}", url); + + let response = state.request_client.get(url).send().await.map_err(|e| { + tracing::error!("❌ Cannot parse range blocks: {e:#}"); + ErrorResponse::with_status_and_headers( + anyhow!("{e:#}"), + StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + ) })?; - info!("Timestamp: {}", timestamp); + let range_blocks = response + .json::() + .await + .map_err(|e| { + tracing::error!("❌ Cannot parse range blocks: {e:#}"); + ErrorResponse::with_status_and_headers( + anyhow!("{e:#}"), + StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + ) + })?; - let claim_estimate = time_until_next_update(*timestamp).await; + + let claim_estimate = remaining_time_seconds(avail_finalized_block as u64,range_blocks.data.end as u64,360,20); let mut avail_send: Vec = Vec::new(); - // TODO stats for r in rows { let mut estimate = None; if r.final_status != "bridged" { estimate = Some(claim_estimate.as_secs()); } - + if r.final_status != "bridged" && r.final_status != "claim_ready" { + estimate = Some(claim_estimate.as_secs()); + } let tx = TransactionData { message_id: r.message_id, sender: r.sender, @@ -352,7 +390,7 @@ async fn transactions( amount: r.amount, status: r.final_status, claim_estimate: estimate, - destination_block_number: r.block_height + destination_block_number: r.block_height, }; avail_send.push(tx); } @@ -360,14 +398,28 @@ async fn transactions( transaction_results.avail_send = avail_send; } - - Ok(Json(json!(transaction_results))) } +fn remaining_time_seconds( + current_block: u64, + last_updated_block: u64, + blocks_per_update: u64, + block_time_seconds: u64, +) -> Duration { + let blocks_since_update = current_block.saturating_sub(last_updated_block); + let blocks_remaining = blocks_per_update.saturating_sub(blocks_since_update); - + Duration::from_secs(blocks_remaining * block_time_seconds) +} +#[test] +fn test() { + let d = remaining_time_seconds(350, 50, 360, 20); + assert_eq!(1200, d.as_secs()); + let d = remaining_time_seconds(100, 50, 360, 20); + assert_eq!(6200, d.as_secs()); +} async fn time_until_next_update(timestamp_s: u64) -> Duration { @@ -885,7 +937,7 @@ async fn get_proof( .. }) => { if data.contains("not in the range of blocks") { - tracing::warn!("VectorX contract not updated yet! Response: {}", data); + warn!("VectorX contract not updated yet! Response: {}", data); } else { tracing::error!( "Merkle proof API returned unsuccessfully. Response: {}", diff --git a/src/models.rs b/src/models.rs index 8b3cccc..d81b2a3 100644 --- a/src/models.rs +++ b/src/models.rs @@ -262,15 +262,15 @@ pub struct ChainHeadResponse { #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -struct RangeBlocks { - start: u32, - end: u32, +pub struct RangeBlocks { + pub start: u32, + pub end: u32, } #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RangeBlocksAPIResponse { - data: RangeBlocks, + pub data: RangeBlocks, } #[derive(Debug, Deserialize)] @@ -372,12 +372,12 @@ pub struct TransactionQueryParams { #[serde_as] #[serde(rename_all = "camelCase")] pub struct TransactionRow { - pub message_id: BigDecimal, + pub message_id: i64, pub sender: String, pub receiver: String, pub source_block_hash: String, pub source_transaction_hash: String, - pub amount: BigDecimal, + pub amount: String, pub final_status: String, pub block_height: i32, } @@ -386,12 +386,12 @@ pub struct TransactionRow { #[serde_as] #[serde(rename_all = "camelCase")] pub struct TransactionData { - pub message_id: BigDecimal, + pub message_id: i64, pub sender: String, pub receiver: String, pub source_block_hash: String, pub source_transaction_hash: String, - pub amount: BigDecimal, + pub amount: String, pub status: String, #[serde(default)] pub claim_estimate: Option, From f58cc96f3436ce8a999ff4144c3f775e54a8709e Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Mon, 15 Dec 2025 11:48:05 +0100 Subject: [PATCH 17/48] Tidy query. --- src/main.rs | 174 ++++++++++++++--------------------------- src/models.rs | 27 ++++++- src/query_avail_tx.sql | 27 +++++++ src/query_eth_tx.sql | 24 ++++++ 4 files changed, 134 insertions(+), 118 deletions(-) create mode 100644 src/query_avail_tx.sql create mode 100644 src/query_eth_tx.sql diff --git a/src/main.rs b/src/main.rs index 3de77dd..9557ca2 100755 --- a/src/main.rs +++ b/src/main.rs @@ -204,6 +204,7 @@ async fn transactions( ) -> Result { info!("Transaction query: {:?}", address_query); + // check provided params if address_query.eth_address.is_none() && address_query.avail_address.is_none() { tracing::error!("Query params not provided."); return Err(ErrorResponse::with_status_and_headers( @@ -213,50 +214,25 @@ async fn transactions( )); } - let avail_finalized_block: i32 = state + let avail_finalized_block: u32 = state .avail_client .request("chain_getFinalizedHead", rpc_params![]) .await - .context("finalized head").unwrap_or(0); - + .context("finalized head") + .unwrap_or(0); let mut transaction_results: TransactionResult = TransactionResult::default(); if let Some(eth_address) = address_query.eth_address { let address: String = format!("{:?}", &address_query.eth_address.unwrap()); - let rows: Vec = sqlx::query_as::<_, TransactionRow>( - r#" - SELECT - es.message_id, - es.sender, - es.receiver, - es.amount, - es.source_block_hash, - es.source_transaction_hash, - ai.block_height, - COALESCE( - CASE - WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress' - WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged' - ELSE es.status - END, - 'in_progress' - ) AS final_status - - FROM bridge_event es - LEFT JOIN public.avail_execute_table AS aet - ON es.message_id = aet.message_id - INNER JOIN public.avail_indexer ai on ai.id = aet.id - WHERE es.sender = $1 - AND es.event_type = $2 - ORDER BY es.message_id ASC limit 1000 - "#, - ) - .bind(address) - .bind("MessageSent") - .fetch_all(&state.db) - .await?; + // fetch sendAvail for ethereum and join with execute on avail + let rows: Vec = + sqlx::query_as::<_, TransactionRow>(include_str!("query_eth_tx.sql")) + .bind(address) + .bind("MessageSent") + .fetch_all(&state.db) + .await?; let slot_block_head = SLOT_BLOCK_HEAD.read().await; let (slot, block, hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { @@ -268,29 +244,25 @@ async fn transactions( for mut r in rows { let mut estimate = None; - if r.final_status != "bridged" - && r.final_status != "in_progress" && r.block_height <= *block as i32 { r.final_status = "claim_ready".to_string(); - } - - if r.final_status != "bridged" && r.final_status != "claim_ready" { + } else if r.final_status != "bridged" && r.final_status != "claim_ready" { estimate = Some(claim_estimate.as_secs()); } - let tx = TransactionData { - message_id: r.message_id, - sender: r.sender, - receiver: r.receiver, - source_block_hash: r.source_block_hash, - source_transaction_hash: r.source_transaction_hash, - amount: r.amount, - status: r.final_status, - claim_estimate: estimate, - destination_block_number: r.block_height, - }; + let tx = TransactionData::new( + r.message_id, + r.sender, + r.receiver, + r.source_block_hash, + r.source_transaction_hash, + r.amount, + r.final_status, + estimate, + r.block_height, + ); eth_send.push(tx); } @@ -298,56 +270,24 @@ async fn transactions( } if let Some(avail_address) = address_query.avail_address { - let rows: Vec = sqlx::query_as::<_, TransactionRow>( - r#" - SELECT - ai1.id as message_id, - ai1.signature_address as sender, - es.to as receiver, - es.amount, - ai1.block_hash as source_block_hash, - ai1.ext_hash as source_transaction_hash, - ai1.block_height, - COALESCE( - CASE - WHEN aet.message_id IS NOT NULL THEN 'bridged' - END, - 'in_progress' - ) AS final_status - FROM avail_send_message_table es - INNER JOIN public.avail_indexer AS ai1 - ON ai1.id = es.id - LEFT JOIN public.bridge_event AS aet - ON es.id = aet.message_id - LEFT JOIN public.avail_indexer AS ai2 - ON ai2.id = aet.message_id - WHERE ai1.signature_address = $1 - AND aet.event_type = $3 - AND ai1.ext_success = $4 - AND es.type = $2 - ORDER BY es.id ASC - LIMIT 1000; - "#, - ) - .bind(avail_address) - .bind("FungibleToken") - .bind("MessageReceived") - .bind(true) - .fetch_all(&state.db) - .await?; - - + let rows: Vec = + sqlx::query_as::<_, TransactionRow>(include_str!("query_avail_tx.sql")) + .bind(avail_address) + .bind("FungibleToken") + .bind("MessageReceived") + .bind(true) + .fetch_all(&state.db) + .await?; let url = format!( - "{}/{}?contractChainId={}&contractAddress={}", + "{}/api/{}?chainName={}&contractChainId={}&contractAddress={}", state.merkle_proof_service_base_url, "range", + state.avail_chain_name, state.contract_chain_id, state.contract_address ); - info!("url {}", url); - let response = state.request_client.get(url).send().await.map_err(|e| { tracing::error!("❌ Cannot parse range blocks: {e:#}"); ErrorResponse::with_status_and_headers( @@ -369,29 +309,30 @@ async fn transactions( ) })?; - - let claim_estimate = remaining_time_seconds(avail_finalized_block as u64,range_blocks.data.end as u64,360,20); + let claim_estimate = + remaining_time_seconds(avail_finalized_block, range_blocks.data.end, 380, 20); let mut avail_send: Vec = Vec::new(); - for r in rows { + for mut r in rows { let mut estimate = None; - if r.final_status != "bridged" { - estimate = Some(claim_estimate.as_secs()); - } - if r.final_status != "bridged" && r.final_status != "claim_ready" { + + if r.final_status == "in_progress" && r.block_height < range_blocks.data.end as i32 { + r.final_status = "claim_ready".to_string(); + } else if r.final_status != "claim_ready" || r.final_status != "bridged" { estimate = Some(claim_estimate.as_secs()); } - let tx = TransactionData { - message_id: r.message_id, - sender: r.sender, - receiver: r.receiver, - source_block_hash: r.source_block_hash, - source_transaction_hash: r.source_transaction_hash, - amount: r.amount, - status: r.final_status, - claim_estimate: estimate, - destination_block_number: r.block_height, - }; + + let tx = TransactionData::new( + r.message_id, + r.sender, + r.receiver, + r.source_block_hash, + r.source_transaction_hash, + r.amount, + r.final_status, + estimate, + r.block_height, + ); avail_send.push(tx); } @@ -402,16 +343,16 @@ async fn transactions( } fn remaining_time_seconds( - current_block: u64, - last_updated_block: u64, - blocks_per_update: u64, - block_time_seconds: u64, + current_block: u32, + last_updated_block: u32, + blocks_per_update: u32, + block_time_seconds: u32, ) -> Duration { let blocks_since_update = current_block.saturating_sub(last_updated_block); let blocks_remaining = blocks_per_update.saturating_sub(blocks_since_update); - Duration::from_secs(blocks_remaining * block_time_seconds) + Duration::from_secs((blocks_remaining * block_time_seconds) as u64) } #[test] fn test() { @@ -421,7 +362,6 @@ fn test() { assert_eq!(6200, d.as_secs()); } - async fn time_until_next_update(timestamp_s: u64) -> Duration { let now_ms = Utc::now().timestamp_millis() as u64; diff --git a/src/models.rs b/src/models.rs index d81b2a3..44ffb8f 100644 --- a/src/models.rs +++ b/src/models.rs @@ -11,8 +11,8 @@ use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::serde_as; use sp_core::{H160, H256}; +use sqlx::types::BigDecimal; use sqlx::{FromRow, Type}; -use sqlx::types::{BigDecimal}; sol!( #[allow(missing_docs)] @@ -398,6 +398,31 @@ pub struct TransactionData { pub destination_block_number: i32, } +impl TransactionData { + pub fn new( + message_id: i64, + sender: String, + receiver: String, + source_block_hash: String, + source_transaction_hash: String, + amount: String, + status: String, + claim_estimate: Option, + destination_block_number: i32, + ) -> Self { + Self { + message_id, + sender, + receiver, + source_block_hash, + source_transaction_hash, + amount, + status, + claim_estimate, + destination_block_number, + } + } +} #[derive(Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionResult { diff --git a/src/query_avail_tx.sql b/src/query_avail_tx.sql new file mode 100644 index 0000000..3a3a378 --- /dev/null +++ b/src/query_avail_tx.sql @@ -0,0 +1,27 @@ +SELECT + ai1.id as message_id, + ai1.signature_address as sender, + es.to as receiver, + es.amount, + ai1.block_hash as source_block_hash, + ai1.ext_hash as source_transaction_hash, + ai1.block_height, + COALESCE( + CASE + WHEN aet.message_id IS NOT NULL THEN 'bridged' + END, + 'in_progress' + ) AS final_status +FROM avail_send_message_table es + INNER JOIN public.avail_indexer AS ai1 + ON ai1.id = es.id + LEFT JOIN public.bridge_event AS aet + ON es.id = aet.message_id + LEFT JOIN public.avail_indexer AS ai2 + ON ai2.id = aet.message_id +WHERE ai1.signature_address = $1 + AND aet.event_type = $3 + AND ai1.ext_success = $4 + AND es.type = $2 +ORDER BY es.id ASC +LIMIT 1000; \ No newline at end of file diff --git a/src/query_eth_tx.sql b/src/query_eth_tx.sql new file mode 100644 index 0000000..4040636 --- /dev/null +++ b/src/query_eth_tx.sql @@ -0,0 +1,24 @@ +SELECT + es.message_id, + es.sender, + es.receiver, + es.amount, + es.source_block_hash, + es.source_transaction_hash, + ai.block_height, + COALESCE( + CASE + WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress' + WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged' + ELSE es.status + END, + 'in_progress' + ) AS final_status + +FROM bridge_event es + LEFT JOIN public.avail_execute_table AS aet + ON es.message_id = aet.message_id + INNER JOIN public.avail_indexer ai on ai.id = aet.id +WHERE es.sender = $1 + AND es.event_type = $2 +ORDER BY es.message_id ASC limit 1000 \ No newline at end of file From 7e032f2ebcab6f0fd49d0553f6d7c5f4e5e8baed Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Mon, 15 Dec 2025 12:10:20 +0100 Subject: [PATCH 18/48] Tidy. --- src/main.rs | 19 +++---- src/models.rs | 117 +++-------------------------------------- src/query_avail_tx.sql | 6 +-- src/query_eth_tx.sql | 8 +-- 4 files changed, 23 insertions(+), 127 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9557ca2..bbb63c0 100755 --- a/src/main.rs +++ b/src/main.rs @@ -224,18 +224,15 @@ async fn transactions( let mut transaction_results: TransactionResult = TransactionResult::default(); if let Some(eth_address) = address_query.eth_address { - let address: String = format!("{:?}", &address_query.eth_address.unwrap()); - - // fetch sendAvail for ethereum and join with execute on avail let rows: Vec = sqlx::query_as::<_, TransactionRow>(include_str!("query_eth_tx.sql")) - .bind(address) + .bind(format!("{:?}", eth_address)) .bind("MessageSent") .fetch_all(&state.db) .await?; let slot_block_head = SLOT_BLOCK_HEAD.read().await; - let (slot, block, hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { + let (_slot, block, _hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) })?; @@ -244,11 +241,11 @@ async fn transactions( for mut r in rows { let mut estimate = None; - if r.final_status != "bridged" + if r.final_status != BridgeStatusEnum::Bridged && r.block_height <= *block as i32 { - r.final_status = "claim_ready".to_string(); - } else if r.final_status != "bridged" && r.final_status != "claim_ready" { + r.final_status = BridgeStatusEnum::ClaimReady + } else if r.final_status != BridgeStatusEnum::Bridged && r.final_status != BridgeStatusEnum::ClaimReady { estimate = Some(claim_estimate.as_secs()); } @@ -316,9 +313,9 @@ async fn transactions( for mut r in rows { let mut estimate = None; - if r.final_status == "in_progress" && r.block_height < range_blocks.data.end as i32 { - r.final_status = "claim_ready".to_string(); - } else if r.final_status != "claim_ready" || r.final_status != "bridged" { + if r.final_status == BridgeStatusEnum::InProgress && r.block_height < range_blocks.data.end as i32 { + r.final_status = BridgeStatusEnum::ClaimReady; + } else if r.final_status != BridgeStatusEnum::ClaimReady || r.final_status != BridgeStatusEnum::Bridged { estimate = Some(claim_estimate.as_secs()); } diff --git a/src/models.rs b/src/models.rs index 44ffb8f..cec81ae 100644 --- a/src/models.rs +++ b/src/models.rs @@ -10,7 +10,7 @@ use jsonrpsee::core::Serialize; use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::serde_as; -use sp_core::{H160, H256}; +use sp_core::{H160}; use sqlx::types::BigDecimal; use sqlx::{FromRow, Type}; @@ -287,80 +287,20 @@ where u32::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) } -#[derive(Debug, Serialize, Deserialize, Type)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] #[sqlx(type_name = "status")] -pub enum StatusEnum { +#[derive(sqlx::Type)] +pub enum BridgeStatusEnum { #[sqlx(rename = "initiated")] Initialized, #[sqlx(rename = "in_progress")] InProgress, #[sqlx(rename = "claim_ready")] - ClaimPending, + ClaimReady, #[sqlx(rename = "bridged")] Bridged, } -// impl ToSql for StatusEnum { -// fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { -// match *self { -// StatusEnum::InProgress => out.write_all(b"IN_PROGRESS")?, -// StatusEnum::ClaimPending => out.write_all(b"CLAIM_PENDING")?, -// StatusEnum::Bridged => out.write_all(b"BRIDGED")?, -// } -// Ok(IsNull::No) -// } -// } -// -// impl FromSql for StatusEnum { -// fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { -// match bytes.as_bytes() { -// b"IN_PROGRESS" => Ok(StatusEnum::InProgress), -// b"CLAIM_PENDING" => Ok(StatusEnum::ClaimPending), -// b"BRIDGED" => Ok(StatusEnum::Bridged), -// _ => Err(format!( -// "Unrecognized enum variant {}", -// std::str::from_utf8(bytes.as_bytes()).unwrap() -// ) -// .as_str() -// .into()), -// } -// } -// } - -pub struct AvailSend { - pub message_id: i64, - pub status: StatusEnum, - pub source_transaction_hash: String, - pub source_block_number: i64, - pub source_block_hash: String, - pub source_transaction_index: i64, - pub source_timestamp: NaiveDateTime, - pub token_id: String, - pub destination_block_number: Option, - pub destination_block_hash: Option, - pub destination_timestamp: Option, - pub depositor_address: String, - - pub amount: String, -} - -// pub struct EthereumSend { -// pub message_id: i64, -// pub status: StatusEnum, -// pub source_transaction_hash: String, -// pub source_block_number: i64, -// pub source_block_hash: String, -// pub source_timestamp: NaiveDateTime, -// pub token_id: String, -// pub destination_block_number: Option, -// pub destination_block_hash: Option, -// pub destination_transaction_index: Option, -// pub destination_timestamp: Option, -// pub depositor_address: String, -// pub receiver_address: String, -// pub amount: String, -// } - #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionQueryParams { @@ -378,7 +318,7 @@ pub struct TransactionRow { pub source_block_hash: String, pub source_transaction_hash: String, pub amount: String, - pub final_status: String, + pub final_status: BridgeStatusEnum, pub block_height: i32, } @@ -392,7 +332,7 @@ pub struct TransactionData { pub source_block_hash: String, pub source_transaction_hash: String, pub amount: String, - pub status: String, + pub status: BridgeStatusEnum, #[serde(default)] pub claim_estimate: Option, pub destination_block_number: i32, @@ -406,7 +346,7 @@ impl TransactionData { source_block_hash: String, source_transaction_hash: String, amount: String, - status: String, + status: BridgeStatusEnum, claim_estimate: Option, destination_block_number: i32, ) -> Self { @@ -430,47 +370,6 @@ pub struct TransactionResult { pub eth_send: Vec, } -// pub fn map_ethereum_send_to_transaction_result(send: EthereumSend) -> TransactionData { -// TransactionData { -// message_id: send.message_id, -// status: send.status, -// source_transaction_hash: send.source_transaction_hash, -// source_block_number: send.source_block_number, -// source_block_hash: send.source_block_hash, -// source_transaction_index: None, -// source_timestamp: send.source_timestamp, -// token_id: send.token_id, -// destination_block_number: send.destination_block_number, -// destination_block_hash: send.destination_block_hash, -// destination_transaction_index: send.destination_transaction_index, -// destination_timestamp: send.destination_timestamp, -// depositor_address: send.depositor_address, -// receiver_address: send.receiver_address, -// amount: send.amount, -// } -// } - -// Function to map AvailSend to TransactionResult -// pub fn map_avail_send_to_transaction_result(send: AvailSend) -> TransactionData { -// TransactionData { -// message_id: send.message_id, -// status: send.status, -// source_transaction_hash: send.source_transaction_hash, -// source_block_number: send.source_block_number, -// source_block_hash: send.source_block_hash, -// source_transaction_index: Some(send.source_transaction_index), -// source_timestamp: send.source_timestamp, -// token_id: send.token_id, -// destination_block_number: send.destination_block_number, -// destination_block_hash: send.destination_block_hash, -// destination_transaction_index: None, -// destination_timestamp: send.destination_timestamp, -// depositor_address: send.depositor_address, -// receiver_address: send.receiver_address, -// amount: send.amount, -// } -// } - #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionRpc { diff --git a/src/query_avail_tx.sql b/src/query_avail_tx.sql index 3a3a378..2588e25 100644 --- a/src/query_avail_tx.sql +++ b/src/query_avail_tx.sql @@ -8,9 +8,9 @@ SELECT ai1.block_height, COALESCE( CASE - WHEN aet.message_id IS NOT NULL THEN 'bridged' + WHEN aet.message_id IS NOT NULL THEN 'bridged'::status END, - 'in_progress' + 'in_progress'::status ) AS final_status FROM avail_send_message_table es INNER JOIN public.avail_indexer AS ai1 @@ -23,5 +23,5 @@ WHERE ai1.signature_address = $1 AND aet.event_type = $3 AND ai1.ext_success = $4 AND es.type = $2 -ORDER BY es.id ASC +ORDER BY es.id DESC LIMIT 1000; \ No newline at end of file diff --git a/src/query_eth_tx.sql b/src/query_eth_tx.sql index 4040636..332f50e 100644 --- a/src/query_eth_tx.sql +++ b/src/query_eth_tx.sql @@ -8,11 +8,11 @@ SELECT ai.block_height, COALESCE( CASE - WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress' - WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged' + WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status + WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status ELSE es.status END, - 'in_progress' + 'in_progress'::status ) AS final_status FROM bridge_event es @@ -21,4 +21,4 @@ FROM bridge_event es INNER JOIN public.avail_indexer ai on ai.id = aet.id WHERE es.sender = $1 AND es.event_type = $2 -ORDER BY es.message_id ASC limit 1000 \ No newline at end of file +ORDER BY es.message_id DESC limit 1000 \ No newline at end of file From 2ef1a8b18773f9481ccb63f6dd7bce0f746c5687 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Mon, 15 Dec 2025 15:12:14 +0100 Subject: [PATCH 19/48] Tidy query. --- .env.example | 4 + src/main.rs | 186 ++++++++++++++--------------------------- src/models.rs | 28 ++++--- src/query_avail_tx.sql | 1 + src/query_eth_tx.sql | 4 +- 5 files changed, 87 insertions(+), 136 deletions(-) diff --git a/.env.example b/.env.example index 2fc964d..e196c61 100755 --- a/.env.example +++ b/.env.example @@ -23,3 +23,7 @@ POSTGRES_URL= PG_USERNAME= PG_PASSWORD= POSTGRES_DB= + +HELIOS_UPDATE_FREQUENCY= + +VECTOR_UPDATE_FREQUENCY= \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index bbb63c0..4aa4606 100755 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use axum::{ }; use backon::ExponentialBuilder; use backon::Retryable; -use chrono::Utc; +use chrono::{Utc}; use sp_core::hexdisplay::AsBytesRef; use alloy::core::sol; @@ -39,6 +39,7 @@ use sp_core::Decode; use sp_io::hashing::twox_128; use sqlx::{PgPool, query}; use std::collections::HashMap; +use std::ops::Sub; use std::str::FromStr; use std::sync::Arc; use std::{env, process, time::Duration}; @@ -53,6 +54,7 @@ use tower_http::{ }; use tracing::{info, warn}; use tracing_subscriber::prelude::*; +use crate::models::TxDirection::{AvailEth, EthAvail}; sol! { #[derive(Debug)] @@ -88,6 +90,8 @@ pub struct AppState { pub transactions_cache_maxage: u32, pub db: PgPool, pub chains: HashMap, + pub helios_update_frequency: u32, + pub vector_update_frequency: u32, } async fn alive() -> Result, StatusCode> { @@ -161,9 +165,10 @@ async fn transaction( sender, receiver, amount, - block_hash + source_block_hash, + source_transaction_hash ) VALUES( - $1, $2, $3, $4, $5, $6, $7)", + $1, $2, $3, $4, $5, $6, $7, $8)", ) .bind(message_id) .bind("MessageSent") @@ -172,6 +177,7 @@ async fn transaction( .bind(recipient) .bind(BigDecimal::from(av)) .bind(tx.block_hash) + .bind(tx.hash) .execute(&state.db) .await .map_err(|e| { @@ -221,7 +227,7 @@ async fn transactions( .context("finalized head") .unwrap_or(0); - let mut transaction_results: TransactionResult = TransactionResult::default(); + let mut transaction_results: Vec = vec![]; if let Some(eth_address) = address_query.eth_address { let rows: Vec = @@ -236,20 +242,20 @@ async fn transactions( ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) })?; - let claim_estimate = time_until_next_update(*timestamp).await; - let mut eth_send: Vec = Vec::new(); + let claim_estimate = time_until_next_helios_update(*timestamp, state.helios_update_frequency); for mut r in rows { let mut estimate = None; - if r.final_status != BridgeStatusEnum::Bridged - && r.block_height <= *block as i32 - { + if r.final_status != BridgeStatusEnum::Bridged && r.block_height <= *block as i32 { r.final_status = BridgeStatusEnum::ClaimReady - } else if r.final_status != BridgeStatusEnum::Bridged && r.final_status != BridgeStatusEnum::ClaimReady { + } else if r.final_status != BridgeStatusEnum::Bridged + && r.final_status != BridgeStatusEnum::ClaimReady + { estimate = Some(claim_estimate.as_secs()); } let tx = TransactionData::new( + EthAvail, r.message_id, r.sender, r.receiver, @@ -259,11 +265,10 @@ async fn transactions( r.final_status, estimate, r.block_height, + None ); - eth_send.push(tx); + transaction_results.push(tx); } - - transaction_results.eth_send = eth_send; } if let Some(avail_address) = address_query.avail_address { @@ -306,20 +311,29 @@ async fn transactions( ) })?; - let claim_estimate = - remaining_time_seconds(avail_finalized_block, range_blocks.data.end, 380, 20); - let mut avail_send: Vec = Vec::new(); + let claim_estimate = remaining_time_seconds( + avail_finalized_block, + range_blocks.data.end, + state.vector_update_frequency, + 20, + ); + let avail_send: Vec = Vec::new(); for mut r in rows { let mut estimate = None; - if r.final_status == BridgeStatusEnum::InProgress && r.block_height < range_blocks.data.end as i32 { + if r.final_status == BridgeStatusEnum::InProgress + && r.block_height < range_blocks.data.end as i32 + { r.final_status = BridgeStatusEnum::ClaimReady; - } else if r.final_status != BridgeStatusEnum::ClaimReady || r.final_status != BridgeStatusEnum::Bridged { + } else if r.final_status == BridgeStatusEnum::Initialized || + r.final_status == BridgeStatusEnum::InProgress + { estimate = Some(claim_estimate.as_secs()); } let tx = TransactionData::new( + AvailEth, r.message_id, r.sender, r.receiver, @@ -329,11 +343,11 @@ async fn transactions( r.final_status, estimate, r.block_height, + r.ext_index, ); - avail_send.push(tx); - } - transaction_results.avail_send = avail_send; + transaction_results.push(tx); + } } Ok(Json(json!(transaction_results))) @@ -346,114 +360,19 @@ fn remaining_time_seconds( block_time_seconds: u32, ) -> Duration { let blocks_since_update = current_block.saturating_sub(last_updated_block); - let blocks_remaining = blocks_per_update.saturating_sub(blocks_since_update); Duration::from_secs((blocks_remaining * block_time_seconds) as u64) } -#[test] -fn test() { - let d = remaining_time_seconds(350, 50, 360, 20); - assert_eq!(1200, d.as_secs()); - let d = remaining_time_seconds(100, 50, 360, 20); - assert_eq!(6200, d.as_secs()); -} - -async fn time_until_next_update(timestamp_s: u64) -> Duration { - let now_ms = Utc::now().timestamp_millis() as u64; - - let last_update_ms = Duration::from_secs(timestamp_s).as_millis() as u64; - // elapsed time in milliseconds - let elapsed_ms = now_ms.saturating_sub(last_update_ms); - - let update_frequency_ms: u64 = 60 * 60 * 1000; // 1 hour in ms - - // remaining time, 0 if already past the interval - let remaining_ms = update_frequency_ms.saturating_sub(elapsed_ms); - - Duration::from_millis(remaining_ms) +fn time_until_next_helios_update(timestamp_ms: u64, heliso_update_frequency: u32) -> Duration { + let now = Utc::now().timestamp() as u64; + let time_since_update = now - timestamp_ms; + let update_frequency = Duration::from_secs(heliso_update_frequency as u64).as_secs(); + let remaining = update_frequency.saturating_sub(time_since_update); + Duration::from_secs(remaining) } -// if address_query.eth_address.is_none() && address_query.avail_address.is_none() { -// tracing::error!("Query params not provided."); -// return Err(ErrorResponse::with_status_and_headers( -// anyhow!("Invalid request: Query params not provided"), -// StatusCode::BAD_REQUEST, -// &[("Cache-Control", "max-age=60, must-revalidate")], -// )); -// } -// -// let cloned_state = state.clone(); -// let mut conn = cloned_state -// .connection_pool -// .get_timeout(Duration::from_secs(1)) -// .expect("Get connection pool"); -// -// // Initialize the result variables -// let mut transaction_results: TransactionResult = TransactionResult::default(); -// -// // Return the combined results -// if let Some(eth_address) = address_query.eth_address { -// let ethereum_sends_results = ethereum_sends -// .select(EthereumSend::as_select()) -// .filter(schema::ethereum_sends::depositor_address.eq(format!("{:?}", eth_address))) -// .order_by(schema::ethereum_sends::source_timestamp.desc()) -// .limit(500) -// .load::(&mut conn); -// -// transaction_results.eth_send = ethereum_sends_results -// .map_err(|e| { -// tracing::error!("Cannot get ethereum send transactions:: {e:#}"); -// ErrorResponse::with_status_and_headers( -// e.into(), -// StatusCode::INTERNAL_SERVER_ERROR, -// &[("Cache-Control", "public, max-age=60, must-revalidate")], -// ) -// })? -// .into_iter() -// .map(map_ethereum_send_to_transaction_result) -// .collect(); -// } -// -// if let Some(avail_address) = address_query.avail_address { -// let avail_sends_results = avail_sends -// .select(AvailSend::as_select()) -// .filter(schema::avail_sends::depositor_address.eq(format!("{:?}", avail_address))) -// .order_by(schema::avail_sends::source_timestamp.desc()) -// .limit(500) -// .load::(&mut conn); -// -// transaction_results.avail_send = avail_sends_results -// .map_err(|e| { -// tracing::error!("Cannot get avail send transactions: {e:#}"); -// ErrorResponse::with_status_and_headers( -// e.into(), -// StatusCode::INTERNAL_SERVER_ERROR, -// &[("Cache-Control", "public, max-age=60, must-revalidate")], -// ) -// })? -// .into_iter() -// .map(map_avail_send_to_transaction_result) -// .collect(); -// } -// -// Ok(( -// StatusCode::OK, -// [( -// "Cache-Control", -// format!( -// "public, max-age={}, immutable", -// state.transactions_cache_maxage -// ), -// )], -// Json(json!(transaction_results)), -// ) -// .into_response()) - -// Ok(()) -// } - #[inline(always)] async fn get_eth_proof( Path(block_hash): Path, @@ -1090,6 +1009,14 @@ async fn main() { .unwrap_or(60), db, chains, + helios_update_frequency: env::var("HELIOS_UPDATE_FREQUENCY") + .ok() + .and_then(|helios_update_frequency| helios_update_frequency.parse::().ok()) + .unwrap_or(3600), + vector_update_frequency: env::var("VECTOR_UPDATE_FREQUENCY") + .ok() + .and_then(|vector_update_frequency| vector_update_frequency.parse::().ok()) + .unwrap_or(360), }); let app = Router::new() @@ -1253,3 +1180,18 @@ async fn track_slot_avail_task(state: Arc) -> Result<()> { Ok(()) } + +#[test] +fn test_remaining_time_for_vector_update() { + let d = remaining_time_seconds(350, 50, 360, 20); + assert_eq!(1200, d.as_secs()); + let d = remaining_time_seconds(100, 50, 360, 20); + assert_eq!(6200, d.as_secs()); +} + +#[test] +fn test_remaining_time_for_helios_update() { + let update = Utc::now().timestamp() - 1200; + let remaining = time_until_next_helios_update(update as u64); + assert_eq!(2400, remaining.as_secs()); +} diff --git a/src/models.rs b/src/models.rs index cec81ae..c41bc20 100644 --- a/src/models.rs +++ b/src/models.rs @@ -3,7 +3,6 @@ use alloy::sol; use avail_core::data_proof::AddressedMessage; use axum::Json; use axum::response::{IntoResponse, Response}; -use chrono::NaiveDateTime; use http::{HeaderMap, HeaderName, HeaderValue, StatusCode}; use jsonrpsee::core::Serialize; @@ -11,8 +10,7 @@ use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::serde_as; use sp_core::{H160}; -use sqlx::types::BigDecimal; -use sqlx::{FromRow, Type}; +use sqlx::{FromRow}; sol!( #[allow(missing_docs)] @@ -182,13 +180,6 @@ pub struct MekrleProofAPIResponse { pub success: Option, } -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct SlotMappingResponse { - pub block_hash: String, - pub block_number: String, -} - #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct MerkleProofData { @@ -288,8 +279,8 @@ where } #[derive(Debug, PartialEq, Serialize, Deserialize)] -#[sqlx(type_name = "status")] #[derive(sqlx::Type)] +#[sqlx(type_name = "status")] pub enum BridgeStatusEnum { #[sqlx(rename = "initiated")] Initialized, @@ -320,12 +311,21 @@ pub struct TransactionRow { pub amount: String, pub final_status: BridgeStatusEnum, pub block_height: i32, + pub ext_index: Option, +} + + +#[derive(Debug, Serialize, Deserialize)] +pub enum TxDirection { + AvailEth, + EthAvail } #[derive(Debug, Serialize, Deserialize)] #[serde_as] #[serde(rename_all = "camelCase")] pub struct TransactionData { + pub direction: TxDirection, pub message_id: i64, pub sender: String, pub receiver: String, @@ -333,13 +333,14 @@ pub struct TransactionData { pub source_transaction_hash: String, pub amount: String, pub status: BridgeStatusEnum, - #[serde(default)] pub claim_estimate: Option, pub destination_block_number: i32, + pub tx_index: Option, } impl TransactionData { pub fn new( + direction: TxDirection, message_id: i64, sender: String, receiver: String, @@ -349,8 +350,10 @@ impl TransactionData { status: BridgeStatusEnum, claim_estimate: Option, destination_block_number: i32, + tx_index: Option, ) -> Self { Self { + direction, message_id, sender, receiver, @@ -360,6 +363,7 @@ impl TransactionData { status, claim_estimate, destination_block_number, + tx_index, } } } diff --git a/src/query_avail_tx.sql b/src/query_avail_tx.sql index 2588e25..8a6ca2e 100644 --- a/src/query_avail_tx.sql +++ b/src/query_avail_tx.sql @@ -6,6 +6,7 @@ SELECT ai1.block_hash as source_block_hash, ai1.ext_hash as source_transaction_hash, ai1.block_height, + ai1.ext_index, COALESCE( CASE WHEN aet.message_id IS NOT NULL THEN 'bridged'::status diff --git a/src/query_eth_tx.sql b/src/query_eth_tx.sql index 332f50e..74b907c 100644 --- a/src/query_eth_tx.sql +++ b/src/query_eth_tx.sql @@ -13,8 +13,8 @@ SELECT ELSE es.status END, 'in_progress'::status - ) AS final_status - + ) AS final_status, + NULL::integer AS ext_index FROM bridge_event es LEFT JOIN public.avail_execute_table AS aet ON es.message_id = aet.message_id From 8d6d46cfe3eca15b66351f1fd88d8a76f0d4ffc9 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Mon, 15 Dec 2025 15:18:08 +0100 Subject: [PATCH 20/48] Skip null fields. --- src/models.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/models.rs b/src/models.rs index c41bc20..16b6770 100644 --- a/src/models.rs +++ b/src/models.rs @@ -333,8 +333,10 @@ pub struct TransactionData { pub source_transaction_hash: String, pub amount: String, pub status: BridgeStatusEnum, + #[serde(skip_serializing_if = "Option::is_none")] pub claim_estimate: Option, pub destination_block_number: i32, + #[serde(skip_serializing_if = "Option::is_none")] pub tx_index: Option, } From b2c212f60da874bc26eecbb2040ebb16c1bf7155 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Mon, 15 Dec 2025 15:26:35 +0100 Subject: [PATCH 21/48] Fix tx insert. --- src/main.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4aa4606..f5ea560 100755 --- a/src/main.rs +++ b/src/main.rs @@ -18,9 +18,10 @@ use axum::{ }; use backon::ExponentialBuilder; use backon::Retryable; -use chrono::{Utc}; +use chrono::Utc; use sp_core::hexdisplay::AsBytesRef; +use crate::models::TxDirection::{AvailEth, EthAvail}; use alloy::core::sol; use alloy::rpc::types::TransactionReceipt; use bigdecimal::BigDecimal; @@ -54,7 +55,6 @@ use tower_http::{ }; use tracing::{info, warn}; use tracing_subscriber::prelude::*; -use crate::models::TxDirection::{AvailEth, EthAvail}; sol! { #[derive(Debug)] @@ -166,17 +166,19 @@ async fn transaction( receiver, amount, source_block_hash, + block_number, source_transaction_hash ) VALUES( - $1, $2, $3, $4, $5, $6, $7, $8)", + $1, $2, $3, $4, $5, $6, $7, $8, $9)", ) .bind(message_id) .bind("MessageSent") - .bind("Initialized") + .bind(BridgeStatusEnum::Initialized) .bind(tx.from) .bind(recipient) .bind(BigDecimal::from(av)) .bind(tx.block_hash) + .bind(tx.block_number as i32) .bind(tx.hash) .execute(&state.db) .await @@ -242,7 +244,8 @@ async fn transactions( ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) })?; - let claim_estimate = time_until_next_helios_update(*timestamp, state.helios_update_frequency); + let claim_estimate = + time_until_next_helios_update(*timestamp, state.helios_update_frequency); for mut r in rows { let mut estimate = None; @@ -265,7 +268,7 @@ async fn transactions( r.final_status, estimate, r.block_height, - None + None, ); transaction_results.push(tx); } @@ -326,8 +329,8 @@ async fn transactions( && r.block_height < range_blocks.data.end as i32 { r.final_status = BridgeStatusEnum::ClaimReady; - } else if r.final_status == BridgeStatusEnum::Initialized || - r.final_status == BridgeStatusEnum::InProgress + } else if r.final_status == BridgeStatusEnum::Initialized + || r.final_status == BridgeStatusEnum::InProgress { estimate = Some(claim_estimate.as_secs()); } From a72ea674abd1ace2b5bda5d18268ff5655353980 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 16 Dec 2025 15:09:42 +0100 Subject: [PATCH 22/48] Add doc. --- README.md | 81 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 990ae63..7e764b9 100755 --- a/README.md +++ b/README.md @@ -302,54 +302,40 @@ RUSTFLAGS="-C target-cpu=native" cargo run --profile maxperf * Request - `GET /v1/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d` + `GET /v2/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d` ```bash - # curl /v1/transactions?ethAddress=ðAddress= - curl localhost:8080/v1/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d + # curl /v2/transactions?ethAddress=ðAddress= + curl localhost:8080/v2/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d ``` * Response ```json - { - "availSend": [ - { - "amount": "10000000000000000", - "depositorAddress": "0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7a", - "destinationBlockHash": null, - "destinationBlockNumber": null, - "destinationTimestamp": null, - "messageId": 106695577567233, - "receiverAddress": "0x8d31529525f23b14767d4dde78567ca083d3d56f", - "sourceBlockHash": "0x9cd91b1bd5497d96a98cf20e1563df29d85b8444d906a64a40980629e1288d70", - "sourceBlockNumber": 24842, - "sourceTimestamp": "2024-04-23T08:15:40", - "sourceTransactionHash": "0x9cd91b1bd5497d96a98cf20e1563df29d85b8444d906a64a40980629e1288d70", - "sourceTransactionIndex": 1, - "status": "claimPending", - "tokenId": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - ], - "ethSend": [ - { - "amount": "10000000000000000", - "depositorAddress": "0x48e7e157cf873c15a5a6734ea37c000e1cb2383d", - "destinationBlockHash": "0xb52bdaf649c84fdccdf2e18aac13e18b0a67ceca11e23bfb94e6476cab23eb38", - "destinationBlockNumber": 22650, - "destinationTimestamp": "2024-04-22T20:03:40", - "destinationTransactionIndex": 1, - "messageId": 311, - "receiverAddress": "0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7a", - "sourceBlockHash": "0x463b0853f65f7a80c4c8ca188c37eb9589fb42cfe327bc1736ce02245bc045e8", - "sourceBlockNumber": 5755158, - "sourceTimestamp": "2024-04-22T19:40:24", - "sourceTransactionHash": "0xcf7945f68d7544a81eb8c32a794e97e229da3535aebd5488a4404f0a1093f29c", - "status": "bridged", - "tokenId": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - ] + [ + { + "amount": "230000000000000000000000", + "destinationBlockNumber": 1815039, + "direction": "EthAvail", + "messageId": 1717, + "receiver": "0xc2b6ddd8382bcb813753562adb3d30cda40369750401b195dbabc6ac9bce620c", + "sender": "0xc1b2aff52877b4a23422f554f3d240be50ec80cf", + "sourceBlockHash": "0x8de5e002e1508780075a27c94fd4ef802899fb8adf0fb6a91c054e20a7ba41fd", + "sourceTransactionHash": "0x8900d0483699fde57a13451d86130b3632946a0883936f9fb831914f18f643fb", + "status": "Bridged" + }, + { + "amount": "112659800000000000000000", + "destinationBlockNumber": 1814712, + "direction": "EthAvail", + "messageId": 1714, + "receiver": "0xc2b6ddd8382bcb813753562adb3d30cda40369750401b195dbabc6ac9bce620c", + "sender": "0xc1b2aff52877b4a23422f554f3d240be50ec80cf", + "sourceBlockHash": "0xd3b45a1add7316c98d5bb7386d350a785b71f1db7c980018546b54de3172c5b7", + "sourceTransactionHash": "0xa4060c27091859b64b71e4f479830b95c6948d29e189a1af6b1b016db0f48be9", + "status": "Bridged" } + ] ``` ### Map slot to Ethereum block number (Deprecated) @@ -374,6 +360,21 @@ RUSTFLAGS="-C target-cpu=native" cargo run --profile maxperf } ``` + +### Initiate transaction + +* To mark transaction as initiated: + + * Request + + `POST /v2/transaction/:tx_hash` + + ```bash + # curl /v2/transaction/ + curl -X POST \ + http://localhost:8080/v2/transaction/0x03c6dbd3c24c3f85e05be26d79f2f676f7c7ef4709 + ``` + ### Examples of using bridge api * We have prepared a set of examples written in Rust and Typescript to help you understand how to use bridge api. You can explore these examples by visiting our [code examples repository](https://github.com/availproject/avail-bridge-examples). From 55f874d63b4e24c0e4ff021bed8a1da3cddcb29e Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 16 Dec 2025 15:15:05 +0100 Subject: [PATCH 23/48] Rename status. --- src/main.rs | 4 ++-- src/models.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index f5ea560..7d6717b 100755 --- a/src/main.rs +++ b/src/main.rs @@ -173,7 +173,7 @@ async fn transaction( ) .bind(message_id) .bind("MessageSent") - .bind(BridgeStatusEnum::Initialized) + .bind(BridgeStatusEnum::Initiated) .bind(tx.from) .bind(recipient) .bind(BigDecimal::from(av)) @@ -329,7 +329,7 @@ async fn transactions( && r.block_height < range_blocks.data.end as i32 { r.final_status = BridgeStatusEnum::ClaimReady; - } else if r.final_status == BridgeStatusEnum::Initialized + } else if r.final_status == BridgeStatusEnum::Initiated || r.final_status == BridgeStatusEnum::InProgress { estimate = Some(claim_estimate.as_secs()); diff --git a/src/models.rs b/src/models.rs index 16b6770..54013be 100644 --- a/src/models.rs +++ b/src/models.rs @@ -283,7 +283,7 @@ where #[sqlx(type_name = "status")] pub enum BridgeStatusEnum { #[sqlx(rename = "initiated")] - Initialized, + Initiated, #[sqlx(rename = "in_progress")] InProgress, #[sqlx(rename = "claim_ready")] From 28406191c9aefe86dd8c1222c0a5462a1f1d3b21 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 16 Dec 2025 17:39:00 +0100 Subject: [PATCH 24/48] Tidy query. --- {src => sql}/query_avail_tx.sql | 16 +++---- {src => sql}/query_eth_tx.sql | 2 +- src/main.rs | 82 +++++++++++++++++---------------- 3 files changed, 51 insertions(+), 49 deletions(-) rename {src => sql}/query_avail_tx.sql (56%) rename {src => sql}/query_eth_tx.sql (90%) diff --git a/src/query_avail_tx.sql b/sql/query_avail_tx.sql similarity index 56% rename from src/query_avail_tx.sql rename to sql/query_avail_tx.sql index 8a6ca2e..0980507 100644 --- a/src/query_avail_tx.sql +++ b/sql/query_avail_tx.sql @@ -1,10 +1,10 @@ SELECT - ai1.id as message_id, - ai1.signature_address as sender, - es.to as receiver, - es.amount, - ai1.block_hash as source_block_hash, - ai1.ext_hash as source_transaction_hash, + ai1.id as message_id, + COALESCE(ai1.signature_address, '') AS "sender!", + es.to AS "receiver!", + COALESCE(es.amount, '0')::text AS "amount!", + ai1.block_hash as source_block_hash, + ai1.ext_hash as source_transaction_hash, ai1.block_height, ai1.ext_index, COALESCE( @@ -12,14 +12,12 @@ SELECT WHEN aet.message_id IS NOT NULL THEN 'bridged'::status END, 'in_progress'::status - ) AS final_status + ) ::status AS "final_status!: BridgeStatusEnum" FROM avail_send_message_table es INNER JOIN public.avail_indexer AS ai1 ON ai1.id = es.id LEFT JOIN public.bridge_event AS aet ON es.id = aet.message_id - LEFT JOIN public.avail_indexer AS ai2 - ON ai2.id = aet.message_id WHERE ai1.signature_address = $1 AND aet.event_type = $3 AND ai1.ext_success = $4 diff --git a/src/query_eth_tx.sql b/sql/query_eth_tx.sql similarity index 90% rename from src/query_eth_tx.sql rename to sql/query_eth_tx.sql index 74b907c..d4dedff 100644 --- a/src/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -13,7 +13,7 @@ SELECT ELSE es.status END, 'in_progress'::status - ) AS final_status, + ) ::status AS "final_status!: BridgeStatusEnum", NULL::integer AS ext_index FROM bridge_event es LEFT JOIN public.avail_execute_table AS aet diff --git a/src/main.rs b/src/main.rs index 7d6717b..0f0579b 100755 --- a/src/main.rs +++ b/src/main.rs @@ -38,9 +38,8 @@ use serde_json::{Value, json}; use sha3::{Digest, Keccak256}; use sp_core::Decode; use sp_io::hashing::twox_128; -use sqlx::{PgPool, query}; +use sqlx::PgPool; use std::collections::HashMap; -use std::ops::Sub; use std::str::FromStr; use std::sync::Arc; use std::{env, process, time::Duration}; @@ -157,34 +156,35 @@ async fn transaction( let AvailBridgeEvents::MessageSent(call) = decoded.data; let message_id: i64 = call.messageId.try_into()?; - query( - "INSERT INTO bridge_event ( - message_id, - event_type, - status, - sender, - receiver, - amount, - source_block_hash, - block_number, - source_transaction_hash - ) VALUES( - $1, $2, $3, $4, $5, $6, $7, $8, $9)", + sqlx::query!( + r#" + INSERT INTO bridge_event ( + message_id, + event_type, + status, + sender, + receiver, + amount, + source_block_hash, + block_number, + source_transaction_hash + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + "#, + message_id, + "MessageSent", + BridgeStatusEnum::Initiated as BridgeStatusEnum, // cast if needed + tx.from, + recipient, + BigDecimal::from(av).to_string(), + tx.block_hash, + tx.block_number as i32, + tx.hash ) - .bind(message_id) - .bind("MessageSent") - .bind(BridgeStatusEnum::Initiated) - .bind(tx.from) - .bind(recipient) - .bind(BigDecimal::from(av)) - .bind(tx.block_hash) - .bind(tx.block_number as i32) - .bind(tx.hash) .execute(&state.db) .await .map_err(|e| { warn!("Cannot insert tx {}", e); - return anyhow!("Cannot insert tx"); + anyhow!("Cannot insert tx") })?; Ok(()) @@ -232,12 +232,14 @@ async fn transactions( let mut transaction_results: Vec = vec![]; if let Some(eth_address) = address_query.eth_address { - let rows: Vec = - sqlx::query_as::<_, TransactionRow>(include_str!("query_eth_tx.sql")) - .bind(format!("{:?}", eth_address)) - .bind("MessageSent") - .fetch_all(&state.db) - .await?; + let rows: Vec = sqlx::query_file_as!( + TransactionRow, + "sql/query_eth_tx.sql", + format!("{:?}", eth_address), + "MessageSent" + ) + .fetch_all(&state.db) + .await?; let slot_block_head = SLOT_BLOCK_HEAD.read().await; let (_slot, block, _hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { @@ -275,14 +277,16 @@ async fn transactions( } if let Some(avail_address) = address_query.avail_address { - let rows: Vec = - sqlx::query_as::<_, TransactionRow>(include_str!("query_avail_tx.sql")) - .bind(avail_address) - .bind("FungibleToken") - .bind("MessageReceived") - .bind(true) - .fetch_all(&state.db) - .await?; + let rows: Vec = sqlx::query_file_as!( + TransactionRow, + "sql/query_avail_tx.sql", + avail_address, + "FungibleToken", + "MessageReceived", + true + ) + .fetch_all(&state.db) + .await?; let url = format!( "{}/api/{}?chainName={}&contractChainId={}&contractAddress={}", From d18ba7224feca62843c79b3076e448b7cbf54519 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 17 Dec 2025 10:00:31 +0100 Subject: [PATCH 25/48] Add .sqlx --- ...5441ee2881e6491fc3af0ad94b7dbad358604.json | 35 ++++++++ ...65cab902fc76df32b349bea5b6435594725a6.json | 84 ++++++++++++++++++ ...0549967c51ba7a87d54bcb2a15d7897302609.json | 86 +++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 .sqlx/query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json create mode 100644 .sqlx/query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json create mode 100644 .sqlx/query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json diff --git a/.sqlx/query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json b/.sqlx/query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json new file mode 100644 index 0000000..fda1b79 --- /dev/null +++ b/.sqlx/query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO bridge_event (\n message_id,\n event_type,\n status,\n sender,\n receiver,\n amount,\n source_block_hash,\n block_number,\n source_transaction_hash\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text", + { + "Custom": { + "name": "status", + "kind": { + "Enum": [ + "bridged", + "in_progress", + "claim_ready", + "initialized", + "initiated" + ] + } + } + }, + "Text", + "Text", + "Text", + "Text", + "Int4", + "Text" + ] + }, + "nullable": [] + }, + "hash": "27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604" +} diff --git a/.sqlx/query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json b/.sqlx/query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json new file mode 100644 index 0000000..8e9af9e --- /dev/null +++ b/.sqlx/query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json @@ -0,0 +1,84 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n es.message_id,\n es.sender,\n es.receiver,\n es.amount,\n es.source_block_hash,\n es.source_transaction_hash,\n ai.block_height,\n COALESCE(\n CASE\n WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE es.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\",\n NULL::integer AS ext_index\nFROM bridge_event es\n LEFT JOIN public.avail_execute_table AS aet\n ON es.message_id = aet.message_id\n INNER JOIN public.avail_indexer ai on ai.id = aet.id\nWHERE es.sender = $1\n AND es.event_type = $2\nORDER BY es.message_id DESC limit 1000", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "message_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "sender", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "receiver", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "amount", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "source_block_hash", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "source_transaction_hash", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "block_height", + "type_info": "Int4" + }, + { + "ordinal": 7, + "name": "final_status!: BridgeStatusEnum", + "type_info": { + "Custom": { + "name": "status", + "kind": { + "Enum": [ + "bridged", + "in_progress", + "claim_ready", + "initialized", + "initiated" + ] + } + } + } + }, + { + "ordinal": 8, + "name": "ext_index", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + null, + null + ] + }, + "hash": "3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6" +} diff --git a/.sqlx/query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json b/.sqlx/query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json new file mode 100644 index 0000000..295b00c --- /dev/null +++ b/.sqlx/query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json @@ -0,0 +1,86 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n ai1.id as message_id,\n COALESCE(ai1.signature_address, '') AS \"sender!\",\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai1.block_hash as source_block_hash,\n ai1.ext_hash as source_transaction_hash,\n ai1.block_height,\n ai1.ext_index,\n COALESCE(\n CASE\n WHEN aet.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai1\n ON ai1.id = es.id\n LEFT JOIN public.bridge_event AS aet\n ON es.id = aet.message_id\nWHERE ai1.signature_address = $1\n AND aet.event_type = $3\n AND ai1.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "message_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "sender!", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "receiver!", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "amount!", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "source_block_hash", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "source_transaction_hash", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "block_height", + "type_info": "Int4" + }, + { + "ordinal": 7, + "name": "ext_index", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "final_status!: BridgeStatusEnum", + "type_info": { + "Custom": { + "name": "status", + "kind": { + "Enum": [ + "bridged", + "in_progress", + "claim_ready", + "initialized", + "initiated" + ] + } + } + } + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [ + false, + null, + false, + null, + false, + false, + false, + false, + null + ] + }, + "hash": "db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609" +} From e1a93dc0301e0700f49865588e528614dc1048c2 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 17 Dec 2025 10:53:53 +0100 Subject: [PATCH 26/48] Tidy tx data. --- src/main.rs | 150 +++++++++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0f0579b..540bc28 100755 --- a/src/main.rs +++ b/src/main.rs @@ -222,17 +222,10 @@ async fn transactions( )); } - let avail_finalized_block: u32 = state - .avail_client - .request("chain_getFinalizedHead", rpc_params![]) - .await - .context("finalized head") - .unwrap_or(0); - - let mut transaction_results: Vec = vec![]; + let mut transaction_data_results: Vec = vec![]; if let Some(eth_address) = address_query.eth_address { - let rows: Vec = sqlx::query_file_as!( + let transactions: Vec = sqlx::query_file_as!( TransactionRow, "sql/query_eth_tx.sql", format!("{:?}", eth_address), @@ -242,42 +235,42 @@ async fn transactions( .await?; let slot_block_head = SLOT_BLOCK_HEAD.read().await; - let (_slot, block, _hash, timestamp) = slot_block_head.as_ref().ok_or_else(|| { + let (_slot, last_eth_block, _hash, helios_update_timestamp) = slot_block_head.as_ref().ok_or_else(|| { ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) })?; let claim_estimate = - time_until_next_helios_update(*timestamp, state.helios_update_frequency); + time_until_next_helios_update(*helios_update_timestamp, state.helios_update_frequency); - for mut r in rows { + for mut tx in transactions { let mut estimate = None; - if r.final_status != BridgeStatusEnum::Bridged && r.block_height <= *block as i32 { - r.final_status = BridgeStatusEnum::ClaimReady - } else if r.final_status != BridgeStatusEnum::Bridged - && r.final_status != BridgeStatusEnum::ClaimReady + if tx.final_status != BridgeStatusEnum::Bridged && tx.block_height <= (*last_eth_block) as i32 { // safe cast + tx.final_status = BridgeStatusEnum::ClaimReady + } else if tx.final_status != BridgeStatusEnum::Bridged + && tx.final_status != BridgeStatusEnum::ClaimReady { estimate = Some(claim_estimate.as_secs()); } - let tx = TransactionData::new( + let tx_data = TransactionData::new( EthAvail, - r.message_id, - r.sender, - r.receiver, - r.source_block_hash, - r.source_transaction_hash, - r.amount, - r.final_status, + tx.message_id, + tx.sender, + tx.receiver, + tx.source_block_hash, + tx.source_transaction_hash, + tx.amount, + tx.final_status, estimate, - r.block_height, + tx.block_height, None, ); - transaction_results.push(tx); + transaction_data_results.push(tx_data); } } if let Some(avail_address) = address_query.avail_address { - let rows: Vec = sqlx::query_file_as!( + let transactions: Vec = sqlx::query_file_as!( TransactionRow, "sql/query_avail_tx.sql", avail_address, @@ -288,35 +281,14 @@ async fn transactions( .fetch_all(&state.db) .await?; - let url = format!( - "{}/api/{}?chainName={}&contractChainId={}&contractAddress={}", - state.merkle_proof_service_base_url, - "range", - state.avail_chain_name, - state.contract_chain_id, - state.contract_address - ); - - let response = state.request_client.get(url).send().await.map_err(|e| { - tracing::error!("❌ Cannot parse range blocks: {e:#}"); - ErrorResponse::with_status_and_headers( - anyhow!("{e:#}"), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; + let range_blocks = fetch_range_blocks(&state).await?; - let range_blocks = response - .json::() + let avail_finalized_block: u32 = state + .avail_client + .request("chain_getFinalizedHead", rpc_params![]) .await - .map_err(|e| { - tracing::error!("❌ Cannot parse range blocks: {e:#}"); - ErrorResponse::with_status_and_headers( - anyhow!("{e:#}"), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; + .context("finalized head") + .unwrap_or(0); let claim_estimate = remaining_time_seconds( avail_finalized_block, @@ -324,40 +296,72 @@ async fn transactions( state.vector_update_frequency, 20, ); - let avail_send: Vec = Vec::new(); - for mut r in rows { + for mut tx in transactions { let mut estimate = None; - if r.final_status == BridgeStatusEnum::InProgress - && r.block_height < range_blocks.data.end as i32 + if tx.final_status == BridgeStatusEnum::InProgress + && tx.block_height < range_blocks.data.end as i32 { - r.final_status = BridgeStatusEnum::ClaimReady; - } else if r.final_status == BridgeStatusEnum::Initiated - || r.final_status == BridgeStatusEnum::InProgress + tx.final_status = BridgeStatusEnum::ClaimReady; + } else if tx.final_status == BridgeStatusEnum::Initiated + || tx.final_status == BridgeStatusEnum::InProgress { estimate = Some(claim_estimate.as_secs()); } - let tx = TransactionData::new( + let tx_data = TransactionData::new( AvailEth, - r.message_id, - r.sender, - r.receiver, - r.source_block_hash, - r.source_transaction_hash, - r.amount, - r.final_status, + tx.message_id, + tx.sender, + tx.receiver, + tx.source_block_hash, + tx.source_transaction_hash, + tx.amount, + tx.final_status, estimate, - r.block_height, - r.ext_index, + tx.block_height, + tx.ext_index, ); - transaction_results.push(tx); + transaction_data_results.push(tx_data); } } - Ok(Json(json!(transaction_results))) + Ok(Json(json!(transaction_data_results))) +} + +async fn fetch_range_blocks(state: &Arc) -> Result { + let block_range_url = format!( + "{}/api/{}?chainName={}&contractChainId={}&contractAddress={}", + state.merkle_proof_service_base_url, + "range", + state.avail_chain_name, + state.contract_chain_id, + state.contract_address + ); + + let response = state.request_client.get(block_range_url).send().await.map_err(|e| { + tracing::error!("Cannot parse range blocks: {e:#}"); + ErrorResponse::with_status_and_headers( + anyhow!("{e:#}"), + StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + ) + })?; + + let range_blocks = response + .json::() + .await + .map_err(|e| { + tracing::error!("Cannot parse range blocks: {e:#}"); + ErrorResponse::with_status_and_headers( + anyhow!("{e:#}"), + StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + ) + })?; + Ok(range_blocks) } fn remaining_time_seconds( From fbdf21d458acebff309950fc024bf1999b961c9d Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 17 Dec 2025 14:20:52 +0100 Subject: [PATCH 27/48] Extract insert query. --- ...d99e7065eefe46d695b8479b7bd5a2c0b399.json} | 4 +- ...b575cfe288f5155df959ccde95012ab67038.json} | 4 +- ...9c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json} | 4 +- sql/insert_tx.sql | 11 ++ sql/query_avail_tx.sql | 34 ++--- sql/query_eth_tx.sql | 28 ++--- src/main.rs | 116 +++++++++--------- src/models.rs | 6 - 8 files changed, 104 insertions(+), 103 deletions(-) rename .sqlx/{query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json => query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json} (58%) rename .sqlx/{query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json => query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json} (73%) rename .sqlx/{query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json => query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json} (60%) create mode 100644 sql/insert_tx.sql diff --git a/.sqlx/query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json b/.sqlx/query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json similarity index 58% rename from .sqlx/query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json rename to .sqlx/query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json index 295b00c..c0aacd5 100644 --- a/.sqlx/query-db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609.json +++ b/.sqlx/query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n ai1.id as message_id,\n COALESCE(ai1.signature_address, '') AS \"sender!\",\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai1.block_hash as source_block_hash,\n ai1.ext_hash as source_transaction_hash,\n ai1.block_height,\n ai1.ext_index,\n COALESCE(\n CASE\n WHEN aet.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai1\n ON ai1.id = es.id\n LEFT JOIN public.bridge_event AS aet\n ON es.id = aet.message_id\nWHERE ai1.signature_address = $1\n AND aet.event_type = $3\n AND ai1.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", + "query": "SELECT\n ai.id as message_id,\n COALESCE(ai.signature_address, '') AS \"sender!\",\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash as source_block_hash,\n ai.ext_hash as source_transaction_hash,\n ai.block_height,\n ai.ext_index,\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND be.event_type = $3\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", "describe": { "columns": [ { @@ -82,5 +82,5 @@ null ] }, - "hash": "db37570b43a1f74e8c61573aedc0549967c51ba7a87d54bcb2a15d7897302609" + "hash": "1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399" } diff --git a/.sqlx/query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json b/.sqlx/query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json similarity index 73% rename from .sqlx/query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json rename to .sqlx/query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json index 8e9af9e..ad81c00 100644 --- a/.sqlx/query-3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6.json +++ b/.sqlx/query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n es.message_id,\n es.sender,\n es.receiver,\n es.amount,\n es.source_block_hash,\n es.source_transaction_hash,\n ai.block_height,\n COALESCE(\n CASE\n WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE es.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\",\n NULL::integer AS ext_index\nFROM bridge_event es\n LEFT JOIN public.avail_execute_table AS aet\n ON es.message_id = aet.message_id\n INNER JOIN public.avail_indexer ai on ai.id = aet.id\nWHERE es.sender = $1\n AND es.event_type = $2\nORDER BY es.message_id DESC limit 1000", + "query": "SELECT\n be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n ai.block_height,\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\",\n NULL::integer AS ext_index\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n INNER JOIN public.avail_indexer ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\nORDER BY be.message_id DESC limit 1000", "describe": { "columns": [ { @@ -80,5 +80,5 @@ null ] }, - "hash": "3ed137e1d484a819494dbe3b18865cab902fc76df32b349bea5b6435594725a6" + "hash": "b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038" } diff --git a/.sqlx/query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json b/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json similarity index 60% rename from .sqlx/query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json rename to .sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json index fda1b79..e668387 100644 --- a/.sqlx/query-27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604.json +++ b/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO bridge_event (\n message_id,\n event_type,\n status,\n sender,\n receiver,\n amount,\n source_block_hash,\n block_number,\n source_transaction_hash\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ", + "query": "INSERT INTO bridge_event (\n message_id,\n event_type,\n status,\n sender,\n receiver,\n amount,\n source_block_hash,\n block_number,\n source_transaction_hash\n) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", "describe": { "columns": [], "parameters": { @@ -31,5 +31,5 @@ }, "nullable": [] }, - "hash": "27b3205a1fd69c6ad7f1ae684855441ee2881e6491fc3af0ad94b7dbad358604" + "hash": "e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5" } diff --git a/sql/insert_tx.sql b/sql/insert_tx.sql new file mode 100644 index 0000000..50ca572 --- /dev/null +++ b/sql/insert_tx.sql @@ -0,0 +1,11 @@ +INSERT INTO bridge_event ( + message_id, + event_type, + status, + sender, + receiver, + amount, + source_block_hash, + block_number, + source_transaction_hash +) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) \ No newline at end of file diff --git a/sql/query_avail_tx.sql b/sql/query_avail_tx.sql index 0980507..18c1e2f 100644 --- a/sql/query_avail_tx.sql +++ b/sql/query_avail_tx.sql @@ -1,26 +1,26 @@ SELECT - ai1.id as message_id, - COALESCE(ai1.signature_address, '') AS "sender!", - es.to AS "receiver!", - COALESCE(es.amount, '0')::text AS "amount!", - ai1.block_hash as source_block_hash, - ai1.ext_hash as source_transaction_hash, - ai1.block_height, - ai1.ext_index, + ai.id as message_id, + COALESCE(ai.signature_address, '') AS "sender!", + es.to AS "receiver!", + COALESCE(es.amount, '0')::text AS "amount!", + ai.block_hash as source_block_hash, + ai.ext_hash as source_transaction_hash, + ai.block_height, + ai.ext_index, COALESCE( CASE - WHEN aet.message_id IS NOT NULL THEN 'bridged'::status + WHEN be.message_id IS NOT NULL THEN 'bridged'::status END, 'in_progress'::status - ) ::status AS "final_status!: BridgeStatusEnum" + ) ::status AS "final_status!: BridgeStatusEnum" FROM avail_send_message_table es - INNER JOIN public.avail_indexer AS ai1 - ON ai1.id = es.id - LEFT JOIN public.bridge_event AS aet - ON es.id = aet.message_id -WHERE ai1.signature_address = $1 - AND aet.event_type = $3 - AND ai1.ext_success = $4 + INNER JOIN public.avail_indexer AS ai + ON ai.id = es.id + LEFT JOIN public.bridge_event AS be + ON es.id = be.message_id +WHERE ai.signature_address = $1 + AND be.event_type = $3 + AND ai.ext_success = $4 AND es.type = $2 ORDER BY es.id DESC LIMIT 1000; \ No newline at end of file diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index d4dedff..dfd94cc 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -1,24 +1,24 @@ SELECT - es.message_id, - es.sender, - es.receiver, - es.amount, - es.source_block_hash, - es.source_transaction_hash, + be.message_id, + be.sender, + be.receiver, + be.amount, + be.source_block_hash, + be.source_transaction_hash, ai.block_height, COALESCE( CASE - WHEN es.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status - WHEN es.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status - ELSE es.status + WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status + WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status + ELSE be.status END, 'in_progress'::status ) ::status AS "final_status!: BridgeStatusEnum", NULL::integer AS ext_index -FROM bridge_event es +FROM bridge_event be LEFT JOIN public.avail_execute_table AS aet - ON es.message_id = aet.message_id + ON be.message_id = aet.message_id INNER JOIN public.avail_indexer ai on ai.id = aet.id -WHERE es.sender = $1 - AND es.event_type = $2 -ORDER BY es.message_id DESC limit 1000 \ No newline at end of file +WHERE be.sender = $1 + AND be.event_type = $2 +ORDER BY be.message_id DESC limit 1000 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 540bc28..f08d2cc 100755 --- a/src/main.rs +++ b/src/main.rs @@ -156,20 +156,8 @@ async fn transaction( let AvailBridgeEvents::MessageSent(call) = decoded.data; let message_id: i64 = call.messageId.try_into()?; - sqlx::query!( - r#" - INSERT INTO bridge_event ( - message_id, - event_type, - status, - sender, - receiver, - amount, - source_block_hash, - block_number, - source_transaction_hash - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - "#, + sqlx::query_file!( + "sql/insert_tx.sql", message_id, "MessageSent", BridgeStatusEnum::Initiated as BridgeStatusEnum, // cast if needed @@ -235,16 +223,20 @@ async fn transactions( .await?; let slot_block_head = SLOT_BLOCK_HEAD.read().await; - let (_slot, last_eth_block, _hash, helios_update_timestamp) = slot_block_head.as_ref().ok_or_else(|| { - ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) - })?; + let (_slot, last_eth_block, _hash, helios_update_timestamp) = + slot_block_head.as_ref().ok_or_else(|| { + ErrorResponse::with_status(anyhow!("Not found"), StatusCode::INTERNAL_SERVER_ERROR) + })?; let claim_estimate = time_until_next_helios_update(*helios_update_timestamp, state.helios_update_frequency); for mut tx in transactions { let mut estimate = None; - if tx.final_status != BridgeStatusEnum::Bridged && tx.block_height <= (*last_eth_block) as i32 { // safe cast + if tx.final_status != BridgeStatusEnum::Bridged + && tx.block_height <= (*last_eth_block) as i32 + { + // safe cast tx.final_status = BridgeStatusEnum::ClaimReady } else if tx.final_status != BridgeStatusEnum::Bridged && tx.final_status != BridgeStatusEnum::ClaimReady @@ -252,7 +244,7 @@ async fn transactions( estimate = Some(claim_estimate.as_secs()); } - let tx_data = TransactionData::new( + transaction_data_results.push(TransactionData::new( EthAvail, tx.message_id, tx.sender, @@ -264,8 +256,7 @@ async fn transactions( estimate, tx.block_height, None, - ); - transaction_data_results.push(tx_data); + )); } } @@ -290,7 +281,7 @@ async fn transactions( .context("finalized head") .unwrap_or(0); - let claim_estimate = remaining_time_seconds( + let claim_estimate = time_until_next_vector_update( avail_finalized_block, range_blocks.data.end, state.vector_update_frequency, @@ -310,7 +301,7 @@ async fn transactions( estimate = Some(claim_estimate.as_secs()); } - let tx_data = TransactionData::new( + transaction_data_results.push(TransactionData::new( AvailEth, tx.message_id, tx.sender, @@ -322,16 +313,16 @@ async fn transactions( estimate, tx.block_height, tx.ext_index, - ); - - transaction_data_results.push(tx_data); + )); } } Ok(Json(json!(transaction_data_results))) } -async fn fetch_range_blocks(state: &Arc) -> Result { +async fn fetch_range_blocks( + state: &Arc, +) -> Result { let block_range_url = format!( "{}/api/{}?chainName={}&contractChainId={}&contractAddress={}", state.merkle_proof_service_base_url, @@ -341,14 +332,19 @@ async fn fetch_range_blocks(state: &Arc) -> Result() @@ -364,26 +360,6 @@ async fn fetch_range_blocks(state: &Arc) -> Result Duration { - let blocks_since_update = current_block.saturating_sub(last_updated_block); - let blocks_remaining = blocks_per_update.saturating_sub(blocks_since_update); - - Duration::from_secs((blocks_remaining * block_time_seconds) as u64) -} - -fn time_until_next_helios_update(timestamp_ms: u64, heliso_update_frequency: u32) -> Duration { - let now = Utc::now().timestamp() as u64; - let time_since_update = now - timestamp_ms; - let update_frequency = Duration::from_secs(heliso_update_frequency as u64).as_secs(); - let remaining = update_frequency.saturating_sub(time_since_update); - Duration::from_secs(remaining) -} - #[inline(always)] async fn get_eth_proof( Path(block_hash): Path, @@ -1192,17 +1168,37 @@ async fn track_slot_avail_task(state: Arc) -> Result<()> { Ok(()) } +fn time_until_next_vector_update( + current_block: u32, + last_updated_block: u32, + blocks_per_update: u32, + block_time_seconds: u32, +) -> Duration { + let blocks_since_update = current_block.saturating_sub(last_updated_block); + let blocks_remaining = blocks_per_update.saturating_sub(blocks_since_update); + + Duration::from_secs((blocks_remaining * block_time_seconds) as u64) +} + +fn time_until_next_helios_update(timestamp_ms: u64, helios_update_frequency: u32) -> Duration { + let now = Utc::now().timestamp() as u64; + let time_since_update = now - timestamp_ms; + let update_frequency = Duration::from_secs(helios_update_frequency as u64).as_secs(); + let remaining = update_frequency.saturating_sub(time_since_update); + Duration::from_secs(remaining) +} + #[test] fn test_remaining_time_for_vector_update() { - let d = remaining_time_seconds(350, 50, 360, 20); - assert_eq!(1200, d.as_secs()); - let d = remaining_time_seconds(100, 50, 360, 20); - assert_eq!(6200, d.as_secs()); + let duration = time_until_next_vector_update(350, 50, 360, 20); + assert_eq!(1200, duration.as_secs()); + let duration = time_until_next_vector_update(100, 50, 360, 20); + assert_eq!(6200, duration.as_secs()); } #[test] fn test_remaining_time_for_helios_update() { let update = Utc::now().timestamp() - 1200; - let remaining = time_until_next_helios_update(update as u64); + let remaining = time_until_next_helios_update(update as u64, 3600); assert_eq!(2400, remaining.as_secs()); } diff --git a/src/models.rs b/src/models.rs index 54013be..226675c 100644 --- a/src/models.rs +++ b/src/models.rs @@ -369,12 +369,6 @@ impl TransactionData { } } } -#[derive(Debug, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionResult { - pub avail_send: Vec, - pub eth_send: Vec, -} #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] From d021fa23c17280484648d8ec6b1b85be8785a746 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 17 Dec 2025 15:22:29 +0100 Subject: [PATCH 28/48] fmt. --- src/models.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/models.rs b/src/models.rs index 226675c..f7cbcd4 100644 --- a/src/models.rs +++ b/src/models.rs @@ -9,8 +9,8 @@ use jsonrpsee::core::Serialize; use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::serde_as; -use sp_core::{H160}; -use sqlx::{FromRow}; +use sp_core::H160; +use sqlx::FromRow; sol!( #[allow(missing_docs)] @@ -278,8 +278,7 @@ where u32::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) } -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[derive(sqlx::Type)] +#[derive(Debug, PartialEq, Serialize, Deserialize, sqlx::Type)] #[sqlx(type_name = "status")] pub enum BridgeStatusEnum { #[sqlx(rename = "initiated")] @@ -314,11 +313,10 @@ pub struct TransactionRow { pub ext_index: Option, } - #[derive(Debug, Serialize, Deserialize)] pub enum TxDirection { AvailEth, - EthAvail + EthAvail, } #[derive(Debug, Serialize, Deserialize)] From b4c80f6da3c4d2ea50026e74c29a27f8d37d819b Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 17 Dec 2025 15:28:58 +0100 Subject: [PATCH 29/48] Update deps. --- Cargo.lock | 221 +++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 4 +- 2 files changed, 139 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f704800..5a9ade9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,9 +102,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae62e633fa48b4190af5e841eb05179841bb8b713945103291e2c0867037c0d1" +checksum = "f07655fedc35188f3c50ff8fc6ee45703ae14ef1bc7ae7d80e23a747012184e3" dependencies = [ "alloy-consensus", "alloy-contract", @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b151e38e42f1586a01369ec52a6934702731d07e8509a7307331b09f6c46dc" +checksum = "2e318e25fb719e747a7e8db1654170fc185024f3ed5b10f86c08d448a912f6e2" dependencies = [ "alloy-eips", "alloy-primitives", @@ -147,6 +147,7 @@ dependencies = [ "alloy-trie", "alloy-tx-macros", "auto_impl", + "borsh", "c-kzg", "derive_more 2.0.1", "either", @@ -162,9 +163,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2d5e8668ef6215efdb7dcca6f22277b4e483a5650e05f5de22b2350971f4b8" +checksum = "364380a845193a317bcb7a5398fc86cdb66c47ebe010771dde05f6869bf9e64a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -176,9 +177,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630288cf4f3a34a8c6bc75c03dce1dbd47833138f65f37d53a1661eafc96b83f" +checksum = "08d39c80ffc806f27a76ed42f3351a455f3dc4f81d6ff92c8aad2cf36b7d3a34" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -240,32 +241,34 @@ dependencies = [ [[package]] name = "alloy-eip2930" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" +checksum = "9441120fa82df73e8959ae0e4ab8ade03de2aaae61be313fbf5746277847ce25" dependencies = [ "alloy-primitives", "alloy-rlp", + "borsh", "serde", ] [[package]] name = "alloy-eip7702" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4769c6ffddca380b0070d71c8b7f30bed375543fe76bb2f74ec0acf4b7cd16" +checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" dependencies = [ "alloy-primitives", "alloy-rlp", + "borsh", "serde", "thiserror 2.0.11", ] [[package]] name = "alloy-eips" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5434834adaf64fa20a6fb90877bc1d33214c41b055cc49f82189c98614368cc" +checksum = "a4c4d7c5839d9f3a467900c625416b24328450c65702eb3d8caff8813e4d1d33" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -274,6 +277,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "auto_impl", + "borsh", "c-kzg", "derive_more 2.0.1", "either", @@ -285,14 +289,15 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919a8471cfbed7bcd8cf1197a57dda583ce0e10c6385f6ff4e8b41304b223392" +checksum = "1ba4b1be0988c11f0095a2380aa596e35533276b8fa6c9e06961bbfe0aebcac5" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-serde", "alloy-trie", + "borsh", "serde", "serde_with", ] @@ -311,9 +316,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c69f6c9c68a1287c9d5ff903d0010726934de0dac10989be37b75a29190d55" +checksum = "f72cf87cda808e593381fb9f005ffa4d2475552b7a6c5ac33d087bf77d82abd0" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -326,9 +331,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf2ae05219e73e0979cb2cf55612aafbab191d130f203079805eaf881cca58" +checksum = "12aeb37b6f2e61b93b1c3d34d01ee720207c76fe447e2a2c217e433ac75b17f5" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -352,9 +357,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58f4f345cef483eab7374f2b6056973c7419ffe8ad35e994b7a7f5d8e0c7ba4" +checksum = "abd29ace62872083e30929cd9b282d82723196d196db589f3ceda67edcc05552" dependencies = [ "alloy-consensus", "alloy-eips", @@ -392,9 +397,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2597751539b1cc8fe4204e5325f9a9ed83fcacfb212018dfcfa7877e76de21" +checksum = "9b710636d7126e08003b8217e24c09f0cca0b46d62f650a841736891b1ed1fc1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -453,9 +458,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edf8eb8be597cfa8c312934d2566ec4516f066d69164f9212d7a148979fdcfd8" +checksum = "d0882e72d2c1c0c79dcf4ab60a67472d3f009a949f774d4c17d0bdb669cfde05" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -476,9 +481,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339af7336571dd39ae3a15bde08ae6a647e62f75350bd415832640268af92c06" +checksum = "39cf1398cb33aacb139a960fa3d8cf8b1202079f320e77e952a0b95967bf7a9f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -488,9 +493,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbde0801a32d21c5f111f037bee7e22874836fba7add34ed4a6919932dd7cf23" +checksum = "6a63fb40ed24e4c92505f488f9dd256e2afaed17faa1b7a221086ebba74f4122" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -499,9 +504,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361cd87ead4ba7659bda8127902eda92d17fa7ceb18aba1676f7be10f7222487" +checksum = "9eae0c7c40da20684548cbc8577b6b7447f7bf4ddbac363df95e3da220e41e72" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -520,9 +525,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64600fc6c312b7e0ba76f73a381059af044f4f21f43e07f51f1fa76c868fe302" +checksum = "c0df1987ed0ff2d0159d76b52e7ddfc4e4fbddacc54d2fbee765e0d14d7c01b5" dependencies = [ "alloy-primitives", "serde", @@ -531,9 +536,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5772858492b26f780468ae693405f895d6a27dea6e3eab2c36b6217de47c2647" +checksum = "6ff69deedee7232d7ce5330259025b868c5e6a52fa8dffda2c861fb3a5889b24" dependencies = [ "alloy-primitives", "async-trait", @@ -546,9 +551,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4195b803d0a992d8dbaab2ca1986fc86533d4bc80967c0cce7668b26ad99ef9" +checksum = "72cfe0be3ec5a8c1a46b2e5a7047ed41121d360d97f4405bb7c1c784880c86cb" dependencies = [ "alloy-consensus", "alloy-network", @@ -635,12 +640,11 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025a940182bddaeb594c26fe3728525ae262d0806fe6a4befdf5d7bc13d54bce" +checksum = "be98b07210d24acf5b793c99b759e9a696e4a2e67593aec0487ae3b3e1a2478c" dependencies = [ "alloy-json-rpc", - "alloy-primitives", "auto_impl", "base64 0.22.1", "derive_more 2.0.1", @@ -659,9 +663,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5064d1e1e1aabc918b5954e7fb8154c39e77ec6903a581b973198b26628fa" +checksum = "4198a1ee82e562cab85e7f3d5921aab725d9bd154b6ad5017f82df1695877c97" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -690,11 +694,10 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.41" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e52276fdb553d3c11563afad2898f4085165e4093604afe3d78b69afbf408f" +checksum = "333544408503f42d7d3792bfc0f7218b643d968a03d2c0ed383ae558fb4a76d0" dependencies = [ - "alloy-primitives", "darling 0.21.3", "proc-macro2", "quote", @@ -1653,6 +1656,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "bounded-collections" version = "0.2.2" @@ -3333,13 +3359,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", @@ -3347,6 +3374,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -3388,21 +3416,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.1", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -3698,6 +3733,16 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itertools" version = "0.10.5" @@ -3893,9 +3938,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libm" @@ -4873,7 +4918,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.6.1", "thiserror 2.0.11", "tokio", "tracing", @@ -4910,9 +4955,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.1", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5108,16 +5153,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ - "async-compression", "base64 0.22.1", "bytes", "encoding_rs", "futures-core", - "futures-util", "h2", "http", "http-body", @@ -5126,35 +5169,30 @@ dependencies = [ "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", - "tokio-util", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.26.11", - "windows-registry", + "webpki-roots 1.0.3", ] [[package]] @@ -5352,15 +5390,6 @@ dependencies = [ "security-framework 3.2.0", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -5872,6 +5901,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "sp-api" version = "26.0.0" @@ -7369,7 +7408,7 @@ dependencies = [ "mio", "parking_lot", "pin-project-lite", - "socket2", + "socket2 0.5.8", "tokio-macros", "windows-sys 0.52.0", ] @@ -7482,19 +7521,23 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", "bitflags 2.8.0", "bytes", "futures-core", + "futures-util", "http", "http-body", + "http-body-util", + "iri-string", "pin-project-lite", "tokio", "tokio-util", + "tower", "tower-layer", "tower-service", "tracing", @@ -8198,7 +8241,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -8236,7 +8279,7 @@ checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets 0.53.0", + "windows-targets 0.53.5", ] [[package]] @@ -8293,6 +8336,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -8350,10 +8402,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", diff --git a/Cargo.toml b/Cargo.toml index 069319c..4700059 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,11 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -alloy = { version = "1.0.27", features = ["serde", "json"] } +alloy = { version = "1.1.3", features = ["serde", "json"] } axum = { version = "0.8", features = ["http2", "macros", "tracing"] } dotenvy = "0.15" jsonrpsee = { version = "0.26.0", features = ["http-client", "macros", "async-client"] } -reqwest = { version = "0.12", features = ["json", "brotli"] } +reqwest = { version = "0.12.26", features = ["json", "brotli"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } sha3 = "0.10" From d47aa72340e2c1fb6eb4ed685d1cf2c494af72a7 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Thu, 18 Dec 2025 10:52:41 +0100 Subject: [PATCH 30/48] Update query. --- ...e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json} | 4 ++-- sql/query_eth_tx.sql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename .sqlx/{query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json => query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json} (89%) diff --git a/.sqlx/query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json b/.sqlx/query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json similarity index 89% rename from .sqlx/query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json rename to .sqlx/query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json index ad81c00..9c2bb51 100644 --- a/.sqlx/query-b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038.json +++ b/.sqlx/query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n ai.block_height,\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\",\n NULL::integer AS ext_index\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n INNER JOIN public.avail_indexer ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\nORDER BY be.message_id DESC limit 1000", + "query": "SELECT\n be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n ai.block_height,\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\",\n NULL::integer AS ext_index\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\nORDER BY be.message_id DESC limit 1000", "describe": { "columns": [ { @@ -80,5 +80,5 @@ null ] }, - "hash": "b72f35989278d4040cfefa81e06bb575cfe288f5155df959ccde95012ab67038" + "hash": "49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb" } diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index dfd94cc..d18b3b8 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -18,7 +18,7 @@ SELECT FROM bridge_event be LEFT JOIN public.avail_execute_table AS aet ON be.message_id = aet.message_id - INNER JOIN public.avail_indexer ai on ai.id = aet.id + LEFT JOIN public.avail_indexer ai on ai.id = aet.id WHERE be.sender = $1 AND be.event_type = $2 ORDER BY be.message_id DESC limit 1000 \ No newline at end of file From 8d50e177df03a8bb4127382380c237eb5ceb47f1 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Thu, 18 Dec 2025 13:27:06 +0100 Subject: [PATCH 31/48] Tidy query. --- ...7d99e7065eefe46d695b8479b7bd5a2c0b399.json | 86 ----------------- ...1de15e4e557164a8bcbb8c494b827118d8c3f.json | 92 +++++++++++++++++++ ...8e3a13aa8d7fbd0f47252e26752879ec8844.json} | 24 +++-- README.md | 54 +++++------ sql/query_avail_tx.sql | 30 +++--- sql/query_eth_tx.sql | 39 ++++---- src/main.rs | 24 +++-- src/models.rs | 52 ++++++++--- 8 files changed, 225 insertions(+), 176 deletions(-) delete mode 100644 .sqlx/query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json create mode 100644 .sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json rename .sqlx/{query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json => query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json} (51%) diff --git a/.sqlx/query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json b/.sqlx/query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json deleted file mode 100644 index c0aacd5..0000000 --- a/.sqlx/query-1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n ai.id as message_id,\n COALESCE(ai.signature_address, '') AS \"sender!\",\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash as source_block_hash,\n ai.ext_hash as source_transaction_hash,\n ai.block_height,\n ai.ext_index,\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND be.event_type = $3\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "message_id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "sender!", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "receiver!", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "amount!", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "source_block_hash", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "source_transaction_hash", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "block_height", - "type_info": "Int4" - }, - { - "ordinal": 7, - "name": "ext_index", - "type_info": "Int4" - }, - { - "ordinal": 8, - "name": "final_status!: BridgeStatusEnum", - "type_info": { - "Custom": { - "name": "status", - "kind": { - "Enum": [ - "bridged", - "in_progress", - "claim_ready", - "initialized", - "initiated" - ] - } - } - } - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Bool" - ] - }, - "nullable": [ - false, - null, - false, - null, - false, - false, - false, - false, - null - ] - }, - "hash": "1f462ee285c4a76eb6d8bcde2e47d99e7065eefe46d695b8479b7bd5a2c0b399" -} diff --git a/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json b/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json new file mode 100644 index 0000000..38b5bdc --- /dev/null +++ b/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json @@ -0,0 +1,92 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT ai.id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND be.event_type = $3\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "message_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "sender", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "receiver!", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "amount!", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "source_block_hash", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "source_transaction_hash", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "source_block_height", + "type_info": "Int4" + }, + { + "ordinal": 7, + "name": "source_tx_index", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "destination_tx_hash?: String", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "final_status!: BridgeStatusEnum", + "type_info": { + "Custom": { + "name": "status", + "kind": { + "Enum": [ + "bridged", + "in_progress", + "claim_ready", + "initialized", + "initiated" + ] + } + } + } + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [ + false, + false, + false, + null, + false, + false, + false, + false, + false, + null + ] + }, + "hash": "3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f" +} diff --git a/.sqlx/query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json b/.sqlx/query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json similarity index 51% rename from .sqlx/query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json rename to .sqlx/query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json index 9c2bb51..2e637b4 100644 --- a/.sqlx/query-49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb.json +++ b/.sqlx/query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n ai.block_height,\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\",\n NULL::integer AS ext_index\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\nORDER BY be.message_id DESC limit 1000", + "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\nORDER BY be.message_id DESC\nlimit 1000", "describe": { "columns": [ { @@ -35,11 +35,21 @@ }, { "ordinal": 6, - "name": "block_height", + "name": "source_block_height", "type_info": "Int4" }, { "ordinal": 7, + "name": "destination_block_height?: i32", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "destination_tx_index?: i32", + "type_info": "Int4" + }, + { + "ordinal": 9, "name": "final_status!: BridgeStatusEnum", "type_info": { "Custom": { @@ -55,11 +65,6 @@ } } } - }, - { - "ordinal": 8, - "name": "ext_index", - "type_info": "Int4" } ], "parameters": { @@ -76,9 +81,10 @@ false, false, false, - null, + false, + false, null ] }, - "hash": "49f3e13c0095e27ca3ef37a6a89ca6b97d97a17c1406fb65ba6abd94d76f0dfb" + "hash": "d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844" } diff --git a/README.md b/README.md index 7e764b9..d2167b1 100755 --- a/README.md +++ b/README.md @@ -309,34 +309,34 @@ RUSTFLAGS="-C target-cpu=native" cargo run --profile maxperf curl localhost:8080/v2/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d ``` - * Response + * Response - ```json - [ - { - "amount": "230000000000000000000000", - "destinationBlockNumber": 1815039, - "direction": "EthAvail", - "messageId": 1717, - "receiver": "0xc2b6ddd8382bcb813753562adb3d30cda40369750401b195dbabc6ac9bce620c", - "sender": "0xc1b2aff52877b4a23422f554f3d240be50ec80cf", - "sourceBlockHash": "0x8de5e002e1508780075a27c94fd4ef802899fb8adf0fb6a91c054e20a7ba41fd", - "sourceTransactionHash": "0x8900d0483699fde57a13451d86130b3632946a0883936f9fb831914f18f643fb", - "status": "Bridged" - }, - { - "amount": "112659800000000000000000", - "destinationBlockNumber": 1814712, - "direction": "EthAvail", - "messageId": 1714, - "receiver": "0xc2b6ddd8382bcb813753562adb3d30cda40369750401b195dbabc6ac9bce620c", - "sender": "0xc1b2aff52877b4a23422f554f3d240be50ec80cf", - "sourceBlockHash": "0xd3b45a1add7316c98d5bb7386d350a785b71f1db7c980018546b54de3172c5b7", - "sourceTransactionHash": "0xa4060c27091859b64b71e4f479830b95c6948d29e189a1af6b1b016db0f48be9", - "status": "Bridged" - } - ] - ``` + ```json + [ + { + "amount": "200000000000000000000000", + "claimEstimate": 3284, + "direction": "EthAvail", + "messageId": 1771, + "receiver": "0xc2b6ddd8382bcb813753562adb3d30cda40369750401b195dbabc6ac9bce620c", + "sender": "0xc1b2aff52877b4a23422f554f3d240be50ec80cf", + "sourceBlockHash": "0x1b18ad7d42e941b5df80585c2674aa9cd70cca6312dcca6374237b512e59ad7a", + "sourceTxHash": "0x3d4fa0e38bae95ad0a512def7c8d19174ecb943fe6a49fab55682ec6f89ab60a", + "status": "InProgress" + }, + { + "amount": "189766320000000000000000", + "claimEstimate": 3284, + "direction": "EthAvail", + "messageId": 1751, + "receiver": "0xc2b6ddd8382bcb813753562adb3d30cda40369750401b195dbabc6ac9bce620c", + "sender": "0xc1b2aff52877b4a23422f554f3d240be50ec80cf", + "sourceBlockHash": "0x207a0be28232e64fb984ac0ad1ea6e79cb32bbbf8217962277a4b2c9bc802997", + "sourceTxHash": "0xd060a6b7f8946bd0c8c8fb1bedf58f81a5745c250ef800e1bc18b00d57c79013", + "status": "InProgress" + } + ] + ``` ### Map slot to Ethereum block number (Deprecated) diff --git a/sql/query_avail_tx.sql b/sql/query_avail_tx.sql index 18c1e2f..8e25031 100644 --- a/sql/query_avail_tx.sql +++ b/sql/query_avail_tx.sql @@ -1,18 +1,18 @@ -SELECT - ai.id as message_id, - COALESCE(ai.signature_address, '') AS "sender!", - es.to AS "receiver!", - COALESCE(es.amount, '0')::text AS "amount!", - ai.block_hash as source_block_hash, - ai.ext_hash as source_transaction_hash, - ai.block_height, - ai.ext_index, - COALESCE( - CASE - WHEN be.message_id IS NOT NULL THEN 'bridged'::status - END, - 'in_progress'::status - ) ::status AS "final_status!: BridgeStatusEnum" +SELECT ai.id AS message_id, + ai.signature_address AS sender, + es.to AS "receiver!", + COALESCE(es.amount, '0')::text AS "amount!", + ai.block_hash AS source_block_hash, + ai.ext_hash AS source_transaction_hash, + ai.block_height AS source_block_height, + ai.ext_index AS source_tx_index, + be.source_transaction_hash AS "destination_tx_hash?: String", + COALESCE( + CASE + WHEN be.message_id IS NOT NULL THEN 'bridged'::status + END, + 'in_progress'::status + ) ::status AS "final_status!: BridgeStatusEnum" FROM avail_send_message_table es INNER JOIN public.avail_indexer AS ai ON ai.id = es.id diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index d18b3b8..60b770e 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -1,24 +1,25 @@ -SELECT - be.message_id, - be.sender, - be.receiver, - be.amount, - be.source_block_hash, - be.source_transaction_hash, - ai.block_height, - COALESCE( - CASE - WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status - WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status - ELSE be.status - END, - 'in_progress'::status - ) ::status AS "final_status!: BridgeStatusEnum", - NULL::integer AS ext_index +SELECT be.message_id, + be.sender, + be.receiver, + be.amount, + be.source_block_hash, + be.source_transaction_hash, + be.block_number as source_block_height, + ai.block_height as "destination_block_height?: i32", + ai.ext_index as "destination_tx_index?: i32", + COALESCE( + CASE + WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status + WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status + ELSE be.status + END, + 'in_progress'::status + ) ::status AS "final_status!: BridgeStatusEnum" FROM bridge_event be LEFT JOIN public.avail_execute_table AS aet ON be.message_id = aet.message_id - LEFT JOIN public.avail_indexer ai on ai.id = aet.id + LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id WHERE be.sender = $1 AND be.event_type = $2 -ORDER BY be.message_id DESC limit 1000 \ No newline at end of file +ORDER BY be.message_id DESC +limit 1000 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f08d2cc..8c36552 100755 --- a/src/main.rs +++ b/src/main.rs @@ -213,8 +213,8 @@ async fn transactions( let mut transaction_data_results: Vec = vec![]; if let Some(eth_address) = address_query.eth_address { - let transactions: Vec = sqlx::query_file_as!( - TransactionRow, + let transactions: Vec = sqlx::query_file_as!( + EthTransactionRow, "sql/query_eth_tx.sql", format!("{:?}", eth_address), "MessageSent" @@ -234,7 +234,7 @@ async fn transactions( for mut tx in transactions { let mut estimate = None; if tx.final_status != BridgeStatusEnum::Bridged - && tx.block_height <= (*last_eth_block) as i32 + && tx.source_block_height <= (*last_eth_block) as i32 { // safe cast tx.final_status = BridgeStatusEnum::ClaimReady @@ -254,15 +254,18 @@ async fn transactions( tx.amount, tx.final_status, estimate, - tx.block_height, + None, + None, + tx.destination_block_height, + tx.destination_tx_index, None, )); } } if let Some(avail_address) = address_query.avail_address { - let transactions: Vec = sqlx::query_file_as!( - TransactionRow, + let transactions: Vec = sqlx::query_file_as!( + AvailTransactionRow, "sql/query_avail_tx.sql", avail_address, "FungibleToken", @@ -292,7 +295,7 @@ async fn transactions( let mut estimate = None; if tx.final_status == BridgeStatusEnum::InProgress - && tx.block_height < range_blocks.data.end as i32 + && tx.source_block_height < range_blocks.data.end as i32 { tx.final_status = BridgeStatusEnum::ClaimReady; } else if tx.final_status == BridgeStatusEnum::Initiated @@ -311,8 +314,11 @@ async fn transactions( tx.amount, tx.final_status, estimate, - tx.block_height, - tx.ext_index, + Some(tx.source_tx_index), + Some(tx.source_block_height), + None, + None, + tx.destination_tx_hash, )); } } diff --git a/src/models.rs b/src/models.rs index f7cbcd4..0fad2a6 100644 --- a/src/models.rs +++ b/src/models.rs @@ -301,7 +301,7 @@ pub struct TransactionQueryParams { #[derive(Debug, Serialize, Deserialize, FromRow)] #[serde_as] #[serde(rename_all = "camelCase")] -pub struct TransactionRow { +pub struct EthTransactionRow { pub message_id: i64, pub sender: String, pub receiver: String, @@ -309,8 +309,25 @@ pub struct TransactionRow { pub source_transaction_hash: String, pub amount: String, pub final_status: BridgeStatusEnum, - pub block_height: i32, - pub ext_index: Option, + pub source_block_height: i32, + pub destination_block_height: Option, + pub destination_tx_index: Option, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +#[serde_as] +#[serde(rename_all = "camelCase")] +pub struct AvailTransactionRow { + pub message_id: i64, + pub sender: String, + pub receiver: String, + pub source_block_hash: String, + pub source_transaction_hash: String, + pub amount: String, + pub final_status: BridgeStatusEnum, + pub source_block_height: i32, + pub source_tx_index: i32, + pub destination_tx_hash: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -328,14 +345,21 @@ pub struct TransactionData { pub sender: String, pub receiver: String, pub source_block_hash: String, - pub source_transaction_hash: String, + pub source_tx_hash: String, pub amount: String, pub status: BridgeStatusEnum, #[serde(skip_serializing_if = "Option::is_none")] pub claim_estimate: Option, - pub destination_block_number: i32, #[serde(skip_serializing_if = "Option::is_none")] - pub tx_index: Option, + pub source_block_number: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub source_tx_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub destination_block_number: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub destination_tx_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub destination_tx_hash: Option, } impl TransactionData { @@ -345,12 +369,15 @@ impl TransactionData { sender: String, receiver: String, source_block_hash: String, - source_transaction_hash: String, + source_tx_hash: String, amount: String, status: BridgeStatusEnum, claim_estimate: Option, - destination_block_number: i32, - tx_index: Option, + source_block_number: Option, + source_tx_index: Option, + destination_block_number: Option, + destination_tx_index: Option, + destination_tx_hash: Option, ) -> Self { Self { direction, @@ -358,12 +385,15 @@ impl TransactionData { sender, receiver, source_block_hash, - source_transaction_hash, + source_tx_hash, amount, status, claim_estimate, + source_block_number, + source_tx_index, destination_block_number, - tx_index, + destination_tx_index, + destination_tx_hash, } } } From fced51d37a228dc1df3bfa5d7bffdaf8c81d56c2 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Mon, 22 Dec 2025 12:05:06 +0100 Subject: [PATCH 32/48] Make signature address optional. --- ...804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json | 2 +- src/main.rs | 2 +- src/models.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json b/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json index 38b5bdc..55d31f1 100644 --- a/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json +++ b/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json @@ -77,7 +77,7 @@ }, "nullable": [ false, - false, + true, false, null, false, diff --git a/src/main.rs b/src/main.rs index 8c36552..a0a61ec 100755 --- a/src/main.rs +++ b/src/main.rs @@ -307,7 +307,7 @@ async fn transactions( transaction_data_results.push(TransactionData::new( AvailEth, tx.message_id, - tx.sender, + tx.sender.unwrap_or("".to_string()), // cannot be empty because of where clause tx.receiver, tx.source_block_hash, tx.source_transaction_hash, diff --git a/src/models.rs b/src/models.rs index 0fad2a6..f0ed13b 100644 --- a/src/models.rs +++ b/src/models.rs @@ -319,7 +319,7 @@ pub struct EthTransactionRow { #[serde(rename_all = "camelCase")] pub struct AvailTransactionRow { pub message_id: i64, - pub sender: String, + pub sender: Option, pub receiver: String, pub source_block_hash: String, pub source_transaction_hash: String, From 7d8ebc16726dcae12bd27095db434b9e190bf046 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 26 Dec 2025 11:57:24 +0100 Subject: [PATCH 33/48] Match only successful ext on avail side. --- ...fdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json} | 4 ++-- sql/query_eth_tx.sql | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) rename .sqlx/{query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json => query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json} (92%) diff --git a/.sqlx/query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json b/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json similarity index 92% rename from .sqlx/query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json rename to .sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json index 2e637b4..45ca2df 100644 --- a/.sqlx/query-d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844.json +++ b/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\nORDER BY be.message_id DESC\nlimit 1000", + "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND ai.ext_success = true\nORDER BY be.message_id DESC\nlimit 1000", "describe": { "columns": [ { @@ -86,5 +86,5 @@ null ] }, - "hash": "d6489ae90b6ffe84e84308002c828e3a13aa8d7fbd0f47252e26752879ec8844" + "hash": "a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff" } diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index 60b770e..b21ee62 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -21,5 +21,6 @@ FROM bridge_event be LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id WHERE be.sender = $1 AND be.event_type = $2 + AND ai.ext_success = true ORDER BY be.message_id DESC limit 1000 \ No newline at end of file From 7a60536f76b797146f9c3344ec1bd626a801b952 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 26 Dec 2025 13:38:19 +0100 Subject: [PATCH 34/48] Change type to BigDecimal. --- ...d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json | 5 ++--- ...82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json | 7 +++---- ...1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json | 7 +++---- src/main.rs | 6 +++--- src/models.rs | 10 +++++----- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json b/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json index 55d31f1..2567a20 100644 --- a/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json +++ b/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json @@ -56,11 +56,10 @@ "name": "status", "kind": { "Enum": [ - "bridged", + "initiated", "in_progress", "claim_ready", - "initialized", - "initiated" + "bridged" ] } } diff --git a/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json b/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json index 45ca2df..b14a315 100644 --- a/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json +++ b/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json @@ -6,7 +6,7 @@ { "ordinal": 0, "name": "message_id", - "type_info": "Int8" + "type_info": "Numeric" }, { "ordinal": 1, @@ -56,11 +56,10 @@ "name": "status", "kind": { "Enum": [ - "bridged", + "initiated", "in_progress", "claim_ready", - "initialized", - "initiated" + "bridged" ] } } diff --git a/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json b/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json index e668387..41bc221 100644 --- a/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json +++ b/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json @@ -5,18 +5,17 @@ "columns": [], "parameters": { "Left": [ - "Int8", + "Numeric", "Text", { "Custom": { "name": "status", "kind": { "Enum": [ - "bridged", + "initiated", "in_progress", "claim_ready", - "initialized", - "initiated" + "bridged" ] } } diff --git a/src/main.rs b/src/main.rs index a0a61ec..13d111f 100755 --- a/src/main.rs +++ b/src/main.rs @@ -154,16 +154,16 @@ async fn transaction( let decoded = AvailBridgeEvents::decode_log(&log)?; let AvailBridgeEvents::MessageSent(call) = decoded.data; - let message_id: i64 = call.messageId.try_into()?; + let message_id: u64 = call.messageId.try_into()?; sqlx::query_file!( "sql/insert_tx.sql", - message_id, + BigDecimal::from(message_id), "MessageSent", BridgeStatusEnum::Initiated as BridgeStatusEnum, // cast if needed tx.from, recipient, - BigDecimal::from(av).to_string(), + av.to_string(), tx.block_hash, tx.block_number as i32, tx.hash diff --git a/src/models.rs b/src/models.rs index f0ed13b..8fcdc01 100644 --- a/src/models.rs +++ b/src/models.rs @@ -3,7 +3,7 @@ use alloy::sol; use avail_core::data_proof::AddressedMessage; use axum::Json; use axum::response::{IntoResponse, Response}; - +use bigdecimal::BigDecimal; use http::{HeaderMap, HeaderName, HeaderValue, StatusCode}; use jsonrpsee::core::Serialize; use serde::{Deserialize, Deserializer}; @@ -302,7 +302,7 @@ pub struct TransactionQueryParams { #[serde_as] #[serde(rename_all = "camelCase")] pub struct EthTransactionRow { - pub message_id: i64, + pub message_id: BigDecimal, pub sender: String, pub receiver: String, pub source_block_hash: String, @@ -318,7 +318,7 @@ pub struct EthTransactionRow { #[serde_as] #[serde(rename_all = "camelCase")] pub struct AvailTransactionRow { - pub message_id: i64, + pub message_id: BigDecimal, pub sender: Option, pub receiver: String, pub source_block_hash: String, @@ -341,7 +341,7 @@ pub enum TxDirection { #[serde(rename_all = "camelCase")] pub struct TransactionData { pub direction: TxDirection, - pub message_id: i64, + pub message_id: BigDecimal, pub sender: String, pub receiver: String, pub source_block_hash: String, @@ -365,7 +365,7 @@ pub struct TransactionData { impl TransactionData { pub fn new( direction: TxDirection, - message_id: i64, + message_id: BigDecimal, sender: String, receiver: String, source_block_hash: String, From 6d72332c14e3b57dddaac18f2b34423dee44723d Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 26 Dec 2025 13:57:53 +0100 Subject: [PATCH 35/48] fix ext success. --- ...965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json} | 4 ++-- sql/query_eth_tx.sql | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) rename .sqlx/{query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json => query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json} (62%) diff --git a/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json b/.sqlx/query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json similarity index 62% rename from .sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json rename to .sqlx/query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json index b14a315..4e12817 100644 --- a/.sqlx/query-a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff.json +++ b/.sqlx/query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND ai.ext_success = true\nORDER BY be.message_id DESC\nlimit 1000", + "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND ai.ext_success = true\n OR ai.ext_success is null\n\nORDER BY be.message_id DESC\nlimit 1000", "describe": { "columns": [ { @@ -85,5 +85,5 @@ null ] }, - "hash": "a50a2fb6422efdf7cb82668bdb1aa81abe0e32c5d1be6d76ef476b1162decbff" + "hash": "21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd" } diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index b21ee62..df086f6 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -4,9 +4,9 @@ SELECT be.message_id, be.amount, be.source_block_hash, be.source_transaction_hash, - be.block_number as source_block_height, - ai.block_height as "destination_block_height?: i32", - ai.ext_index as "destination_tx_index?: i32", + be.block_number as source_block_height, + ai.block_height as "destination_block_height?: i32", + ai.ext_index as "destination_tx_index?: i32", COALESCE( CASE WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status @@ -14,13 +14,15 @@ SELECT be.message_id, ELSE be.status END, 'in_progress'::status - ) ::status AS "final_status!: BridgeStatusEnum" + ) ::status AS "final_status!: BridgeStatusEnum" FROM bridge_event be LEFT JOIN public.avail_execute_table AS aet ON be.message_id = aet.message_id LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id WHERE be.sender = $1 - AND be.event_type = $2 - AND ai.ext_success = true + AND be.event_type = $2 + AND ai.ext_success = true + OR ai.ext_success is null + ORDER BY be.message_id DESC limit 1000 \ No newline at end of file From 721da0fe156fef92c7a25bcaa22ee3e2dffa68f1 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 26 Dec 2025 14:02:48 +0100 Subject: [PATCH 36/48] fix ext success or null. --- ...848af7c0f953e5581b2d3cdb7ac51c15451cd.json | 89 ------------------- sql/query_eth_tx.sql | 4 +- 2 files changed, 2 insertions(+), 91 deletions(-) delete mode 100644 .sqlx/query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json diff --git a/.sqlx/query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json b/.sqlx/query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json deleted file mode 100644 index 4e12817..0000000 --- a/.sqlx/query-21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND ai.ext_success = true\n OR ai.ext_success is null\n\nORDER BY be.message_id DESC\nlimit 1000", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "message_id", - "type_info": "Numeric" - }, - { - "ordinal": 1, - "name": "sender", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "receiver", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "amount", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "source_block_hash", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "source_transaction_hash", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "source_block_height", - "type_info": "Int4" - }, - { - "ordinal": 7, - "name": "destination_block_height?: i32", - "type_info": "Int4" - }, - { - "ordinal": 8, - "name": "destination_tx_index?: i32", - "type_info": "Int4" - }, - { - "ordinal": 9, - "name": "final_status!: BridgeStatusEnum", - "type_info": { - "Custom": { - "name": "status", - "kind": { - "Enum": [ - "initiated", - "in_progress", - "claim_ready", - "bridged" - ] - } - } - } - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - null - ] - }, - "hash": "21dca3256a7a79288113e7c965c848af7c0f953e5581b2d3cdb7ac51c15451cd" -} diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index df086f6..e6509c8 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -21,8 +21,8 @@ FROM bridge_event be LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id WHERE be.sender = $1 AND be.event_type = $2 - AND ai.ext_success = true - OR ai.ext_success is null + AND (ai.ext_success = true + OR ai.ext_success is null) ORDER BY be.message_id DESC limit 1000 \ No newline at end of file From a3ba8ebf479277130f4fcff45765762588041359 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 26 Dec 2025 14:05:54 +0100 Subject: [PATCH 37/48] add missing sqlx file. --- ...efb14fad2067e4368acc28d9ebed2fc5b5ded.json | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json diff --git a/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json b/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json new file mode 100644 index 0000000..dd9d40f --- /dev/null +++ b/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json @@ -0,0 +1,89 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND (ai.ext_success = true\n OR ai.ext_success is null)\n\nORDER BY be.message_id DESC\nlimit 1000", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "message_id", + "type_info": "Numeric" + }, + { + "ordinal": 1, + "name": "sender", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "receiver", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "amount", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "source_block_hash", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "source_transaction_hash", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "source_block_height", + "type_info": "Int4" + }, + { + "ordinal": 7, + "name": "destination_block_height?: i32", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "destination_tx_index?: i32", + "type_info": "Int4" + }, + { + "ordinal": 9, + "name": "final_status!: BridgeStatusEnum", + "type_info": { + "Custom": { + "name": "status", + "kind": { + "Enum": [ + "initiated", + "in_progress", + "claim_ready", + "bridged" + ] + } + } + } + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + null + ] + }, + "hash": "b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded" +} From 8d8363c2541118b2ae4ada99c9a40e3b20389480 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 26 Dec 2025 14:34:41 +0100 Subject: [PATCH 38/48] Fix avail query. --- ...1304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json} | 4 ++-- sql/query_avail_tx.sql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename .sqlx/{query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json => query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json} (91%) diff --git a/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json b/.sqlx/query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json similarity index 91% rename from .sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json rename to .sqlx/query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json index 2567a20..5c20fee 100644 --- a/.sqlx/query-3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f.json +++ b/.sqlx/query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT ai.id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND be.event_type = $3\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", + "query": "SELECT ai.id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND (be.event_type = $3 or be.event_type is null)\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", "describe": { "columns": [ { @@ -87,5 +87,5 @@ null ] }, - "hash": "3b851bd17804aa27c9d98b2955d1de15e4e557164a8bcbb8c494b827118d8c3f" + "hash": "5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4" } diff --git a/sql/query_avail_tx.sql b/sql/query_avail_tx.sql index 8e25031..5eb9019 100644 --- a/sql/query_avail_tx.sql +++ b/sql/query_avail_tx.sql @@ -19,7 +19,7 @@ FROM avail_send_message_table es LEFT JOIN public.bridge_event AS be ON es.id = be.message_id WHERE ai.signature_address = $1 - AND be.event_type = $3 + AND (be.event_type = $3 or be.event_type is null) AND ai.ext_success = $4 AND es.type = $2 ORDER BY es.id DESC From 74c4174401a26cbf44f16a848d72ad873d6758a2 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 30 Dec 2025 12:13:48 +0100 Subject: [PATCH 39/48] Update db mapping. --- ...e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json | 7 ++++--- ...f3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json} | 9 +++++---- ...4b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json | 7 ++++--- sql/query_avail_tx.sql | 2 +- src/main.rs | 5 ++--- 5 files changed, 16 insertions(+), 14 deletions(-) rename .sqlx/{query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json => query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json} (92%) diff --git a/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json b/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json index dd9d40f..9398ea8 100644 --- a/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json +++ b/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json @@ -6,7 +6,7 @@ { "ordinal": 0, "name": "message_id", - "type_info": "Numeric" + "type_info": "Int8" }, { "ordinal": 1, @@ -56,10 +56,11 @@ "name": "status", "kind": { "Enum": [ - "initiated", + "bridged", "in_progress", "claim_ready", - "bridged" + "initialized", + "initiated" ] } } diff --git a/.sqlx/query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json b/.sqlx/query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json similarity index 92% rename from .sqlx/query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json rename to .sqlx/query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json index 5c20fee..ca9afde 100644 --- a/.sqlx/query-5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4.json +++ b/.sqlx/query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT ai.id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND (be.event_type = $3 or be.event_type is null)\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", + "query": "SELECT be.message_id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND (be.event_type = $3 or be.event_type is null)\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", "describe": { "columns": [ { @@ -56,10 +56,11 @@ "name": "status", "kind": { "Enum": [ - "initiated", + "bridged", "in_progress", "claim_ready", - "bridged" + "initialized", + "initiated" ] } } @@ -87,5 +88,5 @@ null ] }, - "hash": "5c32bc668d421304f3d98a360cebb16504818380440854d3b881996fa71b42b4" + "hash": "d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e" } diff --git a/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json b/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json index 41bc221..e668387 100644 --- a/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json +++ b/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json @@ -5,17 +5,18 @@ "columns": [], "parameters": { "Left": [ - "Numeric", + "Int8", "Text", { "Custom": { "name": "status", "kind": { "Enum": [ - "initiated", + "bridged", "in_progress", "claim_ready", - "bridged" + "initialized", + "initiated" ] } } diff --git a/sql/query_avail_tx.sql b/sql/query_avail_tx.sql index 5eb9019..6b9343a 100644 --- a/sql/query_avail_tx.sql +++ b/sql/query_avail_tx.sql @@ -1,4 +1,4 @@ -SELECT ai.id AS message_id, +SELECT be.message_id AS message_id, ai.signature_address AS sender, es.to AS "receiver!", COALESCE(es.amount, '0')::text AS "amount!", diff --git a/src/main.rs b/src/main.rs index 13d111f..33aa84a 100755 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,6 @@ use sp_core::hexdisplay::AsBytesRef; use crate::models::TxDirection::{AvailEth, EthAvail}; use alloy::core::sol; use alloy::rpc::types::TransactionReceipt; -use bigdecimal::BigDecimal; use http::Method; use jsonrpsee::{ core::ClientError, @@ -154,11 +153,11 @@ async fn transaction( let decoded = AvailBridgeEvents::decode_log(&log)?; let AvailBridgeEvents::MessageSent(call) = decoded.data; - let message_id: u64 = call.messageId.try_into()?; + let message_id: i64 = call.messageId.try_into()?; sqlx::query_file!( "sql/insert_tx.sql", - BigDecimal::from(message_id), + message_id, "MessageSent", BridgeStatusEnum::Initiated as BridgeStatusEnum, // cast if needed tx.from, From 5e7b39c9a239d05faae63880c78b30099428ca4c Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 30 Dec 2025 13:20:52 +0100 Subject: [PATCH 40/48] Update query. --- ...f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json} | 7 ++++--- ...0787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json} | 4 ++-- sql/query_avail_tx.sql | 6 +++--- sql/query_eth_tx.sql | 2 +- src/main.rs | 7 ++++--- 5 files changed, 14 insertions(+), 12 deletions(-) rename .sqlx/{query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json => query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json} (91%) rename .sqlx/{query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json => query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json} (89%) diff --git a/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json b/.sqlx/query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json similarity index 91% rename from .sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json rename to .sqlx/query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json index 9398ea8..88a668e 100644 --- a/.sqlx/query-b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded.json +++ b/.sqlx/query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND (ai.ext_success = true\n OR ai.ext_success is null)\n\nORDER BY be.message_id DESC\nlimit 1000", + "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND (ai.ext_success = $3\n OR ai.ext_success is null)\n\nORDER BY be.message_id DESC\nlimit 1000", "describe": { "columns": [ { @@ -70,7 +70,8 @@ "parameters": { "Left": [ "Text", - "Text" + "Text", + "Bool" ] }, "nullable": [ @@ -86,5 +87,5 @@ null ] }, - "hash": "b5a64187798ec3a1e508a9bf292efb14fad2067e4368acc28d9ebed2fc5b5ded" + "hash": "045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f" } diff --git a/.sqlx/query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json b/.sqlx/query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json similarity index 89% rename from .sqlx/query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json rename to .sqlx/query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json index ca9afde..5b0354d 100644 --- a/.sqlx/query-d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e.json +++ b/.sqlx/query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT be.message_id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND (be.event_type = $3 or be.event_type is null)\n AND ai.ext_success = $4\n AND es.type = $2\nORDER BY es.id DESC\nLIMIT 1000;", + "query": "SELECT ai.id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND es.type = $2\n AND (be.event_type = $3 or be.event_type is null)\n AND ai.ext_success = $4\nORDER BY es.id DESC\nLIMIT 1000;\n", "describe": { "columns": [ { @@ -88,5 +88,5 @@ null ] }, - "hash": "d17ee610fd451e04cf3d45a65394a18edb4357092ce6a54fb4a068252864b04e" + "hash": "9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88" } diff --git a/sql/query_avail_tx.sql b/sql/query_avail_tx.sql index 6b9343a..441be09 100644 --- a/sql/query_avail_tx.sql +++ b/sql/query_avail_tx.sql @@ -1,4 +1,4 @@ -SELECT be.message_id AS message_id, +SELECT ai.id AS message_id, ai.signature_address AS sender, es.to AS "receiver!", COALESCE(es.amount, '0')::text AS "amount!", @@ -19,8 +19,8 @@ FROM avail_send_message_table es LEFT JOIN public.bridge_event AS be ON es.id = be.message_id WHERE ai.signature_address = $1 + AND es.type = $2 AND (be.event_type = $3 or be.event_type is null) AND ai.ext_success = $4 - AND es.type = $2 ORDER BY es.id DESC -LIMIT 1000; \ No newline at end of file +LIMIT 1000; diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index e6509c8..5511110 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -21,7 +21,7 @@ FROM bridge_event be LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id WHERE be.sender = $1 AND be.event_type = $2 - AND (ai.ext_success = true + AND (ai.ext_success = $3 OR ai.ext_success is null) ORDER BY be.message_id DESC diff --git a/src/main.rs b/src/main.rs index 33aa84a..9bea40e 100755 --- a/src/main.rs +++ b/src/main.rs @@ -139,14 +139,14 @@ async fn transaction( ) })?; - let target_topic = + let bridge_event_topic = B256::from_str("0x06fd209663be9278f96bc53dfbf6cf3cdcf2172c38b5de30abff93ba443d653a")?; let log = receipt .inner .logs() .iter() - .find(|log| log.topics().contains(&target_topic)) + .find(|log| log.topics().contains(&bridge_event_topic)) .ok_or_else(|| anyhow!("Cannot find transaction log"))? .clone() .into(); @@ -216,7 +216,8 @@ async fn transactions( EthTransactionRow, "sql/query_eth_tx.sql", format!("{:?}", eth_address), - "MessageSent" + "MessageSent", + true ) .fetch_all(&state.db) .await?; From 2c88e7b58a0b7d0c7a1b68b631755a01f087967e Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 30 Dec 2025 15:17:21 +0100 Subject: [PATCH 41/48] Move fn. --- src/main.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9bea40e..16fc0f6 100755 --- a/src/main.rs +++ b/src/main.rs @@ -96,6 +96,21 @@ async fn alive() -> Result, StatusCode> { Ok(Json(json!({ "name": "Avail Bridge API" }))) } +#[inline(always)] +async fn info(State(state): State>) -> Result, StatusCode> { + Ok(Json(json!({ + "vectorXContractAddress": state.contract_address, + "vectorXChainId": state.contract_chain_id, + "bridgeContractAddress" : state.bridge_contract_address, + "availChainName": state.avail_chain_name, + }))) +} + +#[inline(always)] +async fn versions(State(_state): State>) -> Result, StatusCode> { + Ok(Json(json!(["v1"]))) +} + #[inline(always)] async fn transaction( State(state): State>, @@ -177,21 +192,6 @@ async fn transaction( Ok(()) } -#[inline(always)] -async fn info(State(state): State>) -> Result, StatusCode> { - Ok(Json(json!({ - "vectorXContractAddress": state.contract_address, - "vectorXChainId": state.contract_chain_id, - "bridgeContractAddress" : state.bridge_contract_address, - "availChainName": state.avail_chain_name, - }))) -} - -#[inline(always)] -async fn versions(State(_state): State>) -> Result, StatusCode> { - Ok(Json(json!(["v1"]))) -} - #[inline(always)] async fn transactions( Query(address_query): Query, From f0a7db623c7a207ae0be256ef29c3e4848a37c67 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 30 Dec 2025 15:41:55 +0100 Subject: [PATCH 42/48] Add error rsp. --- src/main.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 16fc0f6..c7abcd5 100755 --- a/src/main.rs +++ b/src/main.rs @@ -122,9 +122,10 @@ async fn transaction( .await .map_err(|e| { tracing::error!("Cannot get transaction: {e:#}"); - ErrorResponse::with_status( + ErrorResponse::with_status_and_headers( anyhow!("Cannot get transaction"), StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], ) })?; @@ -134,9 +135,10 @@ async fn transaction( .await .map_err(|e| { tracing::error!("Cannot get transaction receipt: {e:#}"); - ErrorResponse::with_status( + ErrorResponse::with_status_and_headers( anyhow!("Cannot get transaction receipt"), StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], ) })?; @@ -148,9 +150,10 @@ async fn transaction( let a = format!("{:x}", amount); let av = u128::from_str_radix(&a, 16).map_err(|e| { tracing::error!("Cannot parse amount: {e:#}"); - ErrorResponse::with_status( - anyhow!("Cannot fetch valid transaction data"), + ErrorResponse::with_status_and_headers( + anyhow!("Cannot parse amount."), StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], ) })?; @@ -185,8 +188,12 @@ async fn transaction( .execute(&state.db) .await .map_err(|e| { - warn!("Cannot insert tx {}", e); - anyhow!("Cannot insert tx") + tracing::error!("Cannot insert tx: {e:#}"); + ErrorResponse::with_status_and_headers( + anyhow!("Cannot insert tx."), + StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + ) })?; Ok(()) From fd65d7847bf1dc03474d59aeaf23aaa261195153 Mon Sep 17 00:00:00 2001 From: Abheek Tripathy Date: Tue, 13 Jan 2026 09:09:00 +0530 Subject: [PATCH 43/48] fix: txn query api + remove insert txn endpoint --- sql/insert_tx.sql | 11 ----- src/main.rs | 101 ++-------------------------------------------- 2 files changed, 3 insertions(+), 109 deletions(-) delete mode 100644 sql/insert_tx.sql diff --git a/sql/insert_tx.sql b/sql/insert_tx.sql deleted file mode 100644 index 50ca572..0000000 --- a/sql/insert_tx.sql +++ /dev/null @@ -1,11 +0,0 @@ -INSERT INTO bridge_event ( - message_id, - event_type, - status, - sender, - receiver, - amount, - source_block_hash, - block_number, - source_transaction_hash -) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c7abcd5..78be144 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,11 @@ mod models; -use crate::AvailBridge::{AvailBridgeCalls, AvailBridgeEvents}; use crate::models::*; use alloy::primitives::{Address, B256, U256, hex}; use alloy::providers::ProviderBuilder; -use alloy::sol_types::{SolEventInterface, SolInterface}; use anyhow::{Context, Result, anyhow}; use axum::body::{Body, to_bytes}; use axum::response::Response; -use axum::routing::post; use axum::{ Router, extract::{Json, Path, Query, State}, @@ -19,11 +16,9 @@ use axum::{ use backon::ExponentialBuilder; use backon::Retryable; use chrono::Utc; -use sp_core::hexdisplay::AsBytesRef; use crate::models::TxDirection::{AvailEth, EthAvail}; use alloy::core::sol; -use alloy::rpc::types::TransactionReceipt; use http::Method; use jsonrpsee::{ core::ClientError, @@ -39,7 +34,6 @@ use sp_core::Decode; use sp_io::hashing::twox_128; use sqlx::PgPool; use std::collections::HashMap; -use std::str::FromStr; use std::sync::Arc; use std::{env, process, time::Duration}; #[cfg(not(target_env = "msvc"))] @@ -111,94 +105,6 @@ async fn versions(State(_state): State>) -> Result, St Ok(Json(json!(["v1"]))) } -#[inline(always)] -async fn transaction( - State(state): State>, - Path(hash): Path, -) -> Result { - let tx: TransactionRpc = state - .ethereum_client - .request("eth_getTransactionByHash", rpc_params![hash]) - .await - .map_err(|e| { - tracing::error!("Cannot get transaction: {e:#}"); - ErrorResponse::with_status_and_headers( - anyhow!("Cannot get transaction"), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; - - let receipt: TransactionReceipt = state - .ethereum_client - .request("eth_getTransactionReceipt", rpc_params![hash]) - .await - .map_err(|e| { - tracing::error!("Cannot get transaction receipt: {e:#}"); - ErrorResponse::with_status_and_headers( - anyhow!("Cannot get transaction receipt"), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; - - let AvailBridgeCalls::sendAVAIL(call) = - AvailBridge::AvailBridgeCalls::abi_decode(hex::decode(tx.input)?.as_bytes_ref())?; - - let recipient = format!("0x{}", hex::encode(*call.recipient)); - let amount = call.amount; - let a = format!("{:x}", amount); - let av = u128::from_str_radix(&a, 16).map_err(|e| { - tracing::error!("Cannot parse amount: {e:#}"); - ErrorResponse::with_status_and_headers( - anyhow!("Cannot parse amount."), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; - - let bridge_event_topic = - B256::from_str("0x06fd209663be9278f96bc53dfbf6cf3cdcf2172c38b5de30abff93ba443d653a")?; - - let log = receipt - .inner - .logs() - .iter() - .find(|log| log.topics().contains(&bridge_event_topic)) - .ok_or_else(|| anyhow!("Cannot find transaction log"))? - .clone() - .into(); - - let decoded = AvailBridgeEvents::decode_log(&log)?; - let AvailBridgeEvents::MessageSent(call) = decoded.data; - let message_id: i64 = call.messageId.try_into()?; - - sqlx::query_file!( - "sql/insert_tx.sql", - message_id, - "MessageSent", - BridgeStatusEnum::Initiated as BridgeStatusEnum, // cast if needed - tx.from, - recipient, - av.to_string(), - tx.block_hash, - tx.block_number as i32, - tx.hash - ) - .execute(&state.db) - .await - .map_err(|e| { - tracing::error!("Cannot insert tx: {e:#}"); - ErrorResponse::with_status_and_headers( - anyhow!("Cannot insert tx."), - StatusCode::INTERNAL_SERVER_ERROR, - &[("Cache-Control", "public, max-age=60, must-revalidate")], - ) - })?; - - Ok(()) -} - #[inline(always)] async fn transactions( Query(address_query): Query, @@ -261,7 +167,7 @@ async fn transactions( tx.amount, tx.final_status, estimate, - None, + Some(tx.source_block_height), None, tx.destination_block_height, tx.destination_tx_index, @@ -321,8 +227,8 @@ async fn transactions( tx.amount, tx.final_status, estimate, - Some(tx.source_tx_index), Some(tx.source_block_height), + Some(tx.source_tx_index), None, None, tx.destination_tx_hash, @@ -337,7 +243,7 @@ async fn fetch_range_blocks( state: &Arc, ) -> Result { let block_range_url = format!( - "{}/api/{}?chainName={}&contractChainId={}&contractAddress={}", + "{}/{}?chainName={}&contractChainId={}&contractAddress={}", state.merkle_proof_service_base_url, "range", state.avail_chain_name, @@ -1023,7 +929,6 @@ async fn main() { .route("/", get(alive)) .route("/versions", get(versions)) .route("/v1/info", get(info)) - .route("/v2/transaction/{txHash}", post(transaction)) .route("/v1/eth/proof/{block_hash}", get(get_eth_proof)) // get proof from avail for ethereum .route("/v1/eth/head", get(get_eth_head)) // fetch head form eth contract .route("/v1/avl/head", get(get_avl_head)) // fetch head form avail pallet From b47f3b9149819ccb167f1fca370fa3a334730bbd Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Mon, 12 Jan 2026 11:11:31 +0100 Subject: [PATCH 44/48] update insert query. --- ...bb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .sqlx/{query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json => query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json} (81%) diff --git a/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json b/.sqlx/query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json similarity index 81% rename from .sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json rename to .sqlx/query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json index e668387..a841dd3 100644 --- a/.sqlx/query-e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5.json +++ b/.sqlx/query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO bridge_event (\n message_id,\n event_type,\n status,\n sender,\n receiver,\n amount,\n source_block_hash,\n block_number,\n source_transaction_hash\n) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + "query": "INSERT INTO bridge_event (\n message_id,\n event_type,\n status,\n sender,\n receiver,\n amount,\n source_block_hash,\n block_number,\n source_transaction_hash\n) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\nON CONFLICT (message_id) DO NOTHING;", "describe": { "columns": [], "parameters": { @@ -31,5 +31,5 @@ }, "nullable": [] }, - "hash": "e3955d345fd82dc14b1793a2fea89c289cb8cf1e5b95c1e5f08a12f1e5d441b5" + "hash": "e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a" } From f90d69245dfa6f266454d25e56e93eed2d432313 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Tue, 13 Jan 2026 09:21:18 +0100 Subject: [PATCH 45/48] Update readme. --- ...b3fc0a75a0f9fd4c278080645d7e67d321f4a.json | 35 ------------------- README.md | 15 -------- src/models.rs | 17 --------- 3 files changed, 67 deletions(-) delete mode 100644 .sqlx/query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json diff --git a/.sqlx/query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json b/.sqlx/query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json deleted file mode 100644 index a841dd3..0000000 --- a/.sqlx/query-e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO bridge_event (\n message_id,\n event_type,\n status,\n sender,\n receiver,\n amount,\n source_block_hash,\n block_number,\n source_transaction_hash\n) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\nON CONFLICT (message_id) DO NOTHING;", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Text", - { - "Custom": { - "name": "status", - "kind": { - "Enum": [ - "bridged", - "in_progress", - "claim_ready", - "initialized", - "initiated" - ] - } - } - }, - "Text", - "Text", - "Text", - "Text", - "Int4", - "Text" - ] - }, - "nullable": [] - }, - "hash": "e0a8aca997debb235130c33f586b3fc0a75a0f9fd4c278080645d7e67d321f4a" -} diff --git a/README.md b/README.md index d2167b1..c40d62c 100755 --- a/README.md +++ b/README.md @@ -360,21 +360,6 @@ RUSTFLAGS="-C target-cpu=native" cargo run --profile maxperf } ``` - -### Initiate transaction - -* To mark transaction as initiated: - - * Request - - `POST /v2/transaction/:tx_hash` - - ```bash - # curl /v2/transaction/ - curl -X POST \ - http://localhost:8080/v2/transaction/0x03c6dbd3c24c3f85e05be26d79f2f676f7c7ef4709 - ``` - ### Examples of using bridge api * We have prepared a set of examples written in Rust and Typescript to help you understand how to use bridge api. You can explore these examples by visiting our [code examples repository](https://github.com/availproject/avail-bridge-examples). diff --git a/src/models.rs b/src/models.rs index 8fcdc01..9c39614 100644 --- a/src/models.rs +++ b/src/models.rs @@ -397,20 +397,3 @@ impl TransactionData { } } } - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionRpc { - pub from: String, - pub to: String, - pub input: String, - pub value: String, - #[serde(deserialize_with = "hex_to_u32")] - pub nonce: u32, - pub block_hash: String, - #[serde(deserialize_with = "hex_to_u32")] - pub block_number: u32, - #[serde(deserialize_with = "hex_to_u32")] - pub transaction_index: u32, - pub hash: String, -} From 6ef06a1b9f58b3675901785536615cd5bfb3f0ac Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Wed, 21 Jan 2026 10:49:19 +0100 Subject: [PATCH 46/48] Update error responses. --- src/main.rs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 78be144..7c4af7d 100755 --- a/src/main.rs +++ b/src/main.rs @@ -328,7 +328,7 @@ async fn get_eth_proof( .map_err(|e| { tracing::error!("❌ Failed to get the kate query data. Error: {e:#}"); ErrorResponse::with_status_and_headers( - anyhow::anyhow!("Something went wrong."), + anyhow::anyhow!("Failed to get the kate query data."), StatusCode::BAD_REQUEST, &[("Cache-Control", "public, max-age=60, must-revalidate")], ) @@ -338,7 +338,7 @@ async fn get_eth_proof( .map_err(|e| { tracing::error!("❌ Failed to get the merkle proof data. Error: {e:#}"); ErrorResponse::with_status_and_headers( - anyhow::anyhow!("Something went wrong."), + anyhow::anyhow!("Failed to get the merkle proof data."), StatusCode::INTERNAL_SERVER_ERROR, &[("Cache-Control", "public, max-age=60, must-revalidate")], ) @@ -361,22 +361,30 @@ async fn get_eth_proof( .. } => { if data.contains("not in the range of blocks") { - tracing::warn!( + warn!( "⏳ Merkle proof VectorX contract not updated yet! Response: {}", data ); + + return Err(ErrorResponse::with_status_and_headers( + anyhow!("VectorX contract not updated yet!"), + StatusCode::NOT_FOUND, + &[( + "Cache-Control", + &format!( + "public, max-age={eth_proof_failure_cache_maxage}, must-revalidate" + ), + )], + )); } else { tracing::error!("❌ Merkle API returned unsuccessfully. Response: {}", data); - } - return Err(ErrorResponse::with_status_and_headers( - anyhow!("{data}"), - StatusCode::NOT_FOUND, - &[( - "Cache-Control", - &format!("public, max-age={eth_proof_failure_cache_maxage}, must-revalidate"), - )], - )); + return Err(ErrorResponse::with_status_and_headers( + anyhow::anyhow!("Something went wrong."), + StatusCode::INTERNAL_SERVER_ERROR, + &[("Cache-Control", "public, max-age=60, must-revalidate")], + )); + } } _ => { From e7827cf34da8d2130e95b15a0868696d37e77383 Mon Sep 17 00:00:00 2001 From: Sasa Prsic Date: Fri, 23 Jan 2026 09:37:47 +0100 Subject: [PATCH 47/48] Update doc. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c40d62c..16f6ec6 100755 --- a/README.md +++ b/README.md @@ -302,11 +302,11 @@ RUSTFLAGS="-C target-cpu=native" cargo run --profile maxperf * Request - `GET /v2/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d` + `GET /v1/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d` ```bash - # curl /v2/transactions?ethAddress=ðAddress= - curl localhost:8080/v2/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d + # curl /v1/transactions?ethAddress=ðAddress= + curl localhost:8080/v1/transactions?availAddress=0x1a985fdff5f6eee4afce1dc0f367ab925cdca57e7e8585329830fc3ce6ef4e7aðAddress=0x48e7e157cf873c15a5a6734ea37c000e1cb2383d ``` * Response From 1c97af97d4ec92de3cc4c98574ea9cdc223aada9 Mon Sep 17 00:00:00 2001 From: Abheek Tripathy Date: Fri, 6 Feb 2026 06:28:14 +0530 Subject: [PATCH 48/48] add timestamp --- ...abdd8bac5de0a9ac71c248ac7c841edec225.json} | 19 ++++++++++++------- ...ecad9ad0c9bd398c13fdd879dced0dc81c43.json} | 17 +++++++++++------ sql/query_avail_tx.sql | 3 ++- sql/query_eth_tx.sql | 6 +++--- src/main.rs | 4 ++++ src/models.rs | 5 +++++ 6 files changed, 37 insertions(+), 17 deletions(-) rename .sqlx/{query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json => query-1780f7cc46df7e368f058c4d6604abdd8bac5de0a9ac71c248ac7c841edec225.json} (60%) rename .sqlx/{query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json => query-b4e72b9ba8a6b9d8947e2fdd6b58ecad9ad0c9bd398c13fdd879dced0dc81c43.json} (66%) diff --git a/.sqlx/query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json b/.sqlx/query-1780f7cc46df7e368f058c4d6604abdd8bac5de0a9ac71c248ac7c841edec225.json similarity index 60% rename from .sqlx/query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json rename to .sqlx/query-1780f7cc46df7e368f058c4d6604abdd8bac5de0a9ac71c248ac7c841edec225.json index 88a668e..c5dc080 100644 --- a/.sqlx/query-045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f.json +++ b/.sqlx/query-1780f7cc46df7e368f058c4d6604abdd8bac5de0a9ac71c248ac7c841edec225.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND (ai.ext_success = $3\n OR ai.ext_success is null)\n\nORDER BY be.message_id DESC\nlimit 1000", + "query": "SELECT be.message_id,\n be.sender,\n be.receiver,\n be.amount,\n be.source_block_hash,\n be.source_transaction_hash,\n be.block_number as source_block_height,\n be.timestamp,\n ai.block_height as \"destination_block_height?: i32\",\n ai.ext_index as \"destination_tx_index?: i32\",\n COALESCE(\n CASE\n WHEN be.status = 'in_progress' AND aet.message_id IS NULL THEN 'in_progress'::status\n WHEN be.status = 'in_progress' AND aet.message_id IS NOT NULL THEN 'bridged'::status\n ELSE be.status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM bridge_event be\n LEFT JOIN public.avail_execute_table AS aet\n ON be.message_id = aet.message_id\n LEFT JOIN public.avail_indexer AS ai on ai.id = aet.id\nWHERE be.sender = $1\n AND be.event_type = $2\n AND (ai.ext_success = $3\n OR ai.ext_success is null)\nORDER BY be.timestamp DESC\nLIMIT 1000", "describe": { "columns": [ { @@ -40,27 +40,31 @@ }, { "ordinal": 7, + "name": "timestamp", + "type_info": "Int8" + }, + { + "ordinal": 8, "name": "destination_block_height?: i32", "type_info": "Int4" }, { - "ordinal": 8, + "ordinal": 9, "name": "destination_tx_index?: i32", "type_info": "Int4" }, { - "ordinal": 9, + "ordinal": 10, "name": "final_status!: BridgeStatusEnum", "type_info": { "Custom": { "name": "status", "kind": { "Enum": [ - "bridged", + "initiated", "in_progress", "claim_ready", - "initialized", - "initiated" + "bridged" ] } } @@ -84,8 +88,9 @@ false, false, false, + false, null ] }, - "hash": "045d2c6adf0df49f5fb26bf8e21848edc00915b87bc7f911f4cc65760fedd33f" + "hash": "1780f7cc46df7e368f058c4d6604abdd8bac5de0a9ac71c248ac7c841edec225" } diff --git a/.sqlx/query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json b/.sqlx/query-b4e72b9ba8a6b9d8947e2fdd6b58ecad9ad0c9bd398c13fdd879dced0dc81c43.json similarity index 66% rename from .sqlx/query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json rename to .sqlx/query-b4e72b9ba8a6b9d8947e2fdd6b58ecad9ad0c9bd398c13fdd879dced0dc81c43.json index 5b0354d..cbc8b56 100644 --- a/.sqlx/query-9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88.json +++ b/.sqlx/query-b4e72b9ba8a6b9d8947e2fdd6b58ecad9ad0c9bd398c13fdd879dced0dc81c43.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT ai.id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND es.type = $2\n AND (be.event_type = $3 or be.event_type is null)\n AND ai.ext_success = $4\nORDER BY es.id DESC\nLIMIT 1000;\n", + "query": "SELECT ai.id AS message_id,\n ai.signature_address AS sender,\n es.to AS \"receiver!\",\n COALESCE(es.amount, '0')::text AS \"amount!\",\n ai.block_hash AS source_block_hash,\n ai.ext_hash AS source_transaction_hash,\n ai.block_height AS source_block_height,\n ai.ext_index AS source_tx_index,\n es.block_timestamp,\n be.source_transaction_hash AS \"destination_tx_hash?: String\",\n COALESCE(\n CASE\n WHEN be.message_id IS NOT NULL THEN 'bridged'::status\n END,\n 'in_progress'::status\n ) ::status AS \"final_status!: BridgeStatusEnum\"\nFROM avail_send_message_table es\n INNER JOIN public.avail_indexer AS ai\n ON ai.id = es.id\n LEFT JOIN public.bridge_event AS be\n ON es.id = be.message_id\nWHERE ai.signature_address = $1\n AND es.type = $2\n AND (be.event_type = $3 or be.event_type is null)\n AND ai.ext_success = $4\nORDER BY es.block_timestamp DESC\nLIMIT 1000;\n", "describe": { "columns": [ { @@ -45,22 +45,26 @@ }, { "ordinal": 8, + "name": "block_timestamp", + "type_info": "Int8" + }, + { + "ordinal": 9, "name": "destination_tx_hash?: String", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 10, "name": "final_status!: BridgeStatusEnum", "type_info": { "Custom": { "name": "status", "kind": { "Enum": [ - "bridged", + "initiated", "in_progress", "claim_ready", - "initialized", - "initiated" + "bridged" ] } } @@ -85,8 +89,9 @@ false, false, false, + false, null ] }, - "hash": "9e1ba88ba6987620787b440dcefd3c1c17d0a7a6556c2a54c96e19e5c2859b88" + "hash": "b4e72b9ba8a6b9d8947e2fdd6b58ecad9ad0c9bd398c13fdd879dced0dc81c43" } diff --git a/sql/query_avail_tx.sql b/sql/query_avail_tx.sql index 441be09..66e9d44 100644 --- a/sql/query_avail_tx.sql +++ b/sql/query_avail_tx.sql @@ -6,6 +6,7 @@ SELECT ai.id AS message_id, ai.ext_hash AS source_transaction_hash, ai.block_height AS source_block_height, ai.ext_index AS source_tx_index, + es.block_timestamp, be.source_transaction_hash AS "destination_tx_hash?: String", COALESCE( CASE @@ -22,5 +23,5 @@ WHERE ai.signature_address = $1 AND es.type = $2 AND (be.event_type = $3 or be.event_type is null) AND ai.ext_success = $4 -ORDER BY es.id DESC +ORDER BY es.block_timestamp DESC LIMIT 1000; diff --git a/sql/query_eth_tx.sql b/sql/query_eth_tx.sql index 5511110..3a1dfed 100644 --- a/sql/query_eth_tx.sql +++ b/sql/query_eth_tx.sql @@ -5,6 +5,7 @@ SELECT be.message_id, be.source_block_hash, be.source_transaction_hash, be.block_number as source_block_height, + be.timestamp, ai.block_height as "destination_block_height?: i32", ai.ext_index as "destination_tx_index?: i32", COALESCE( @@ -23,6 +24,5 @@ WHERE be.sender = $1 AND be.event_type = $2 AND (ai.ext_success = $3 OR ai.ext_success is null) - -ORDER BY be.message_id DESC -limit 1000 \ No newline at end of file +ORDER BY be.timestamp DESC +LIMIT 1000 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7c4af7d..6c32428 100755 --- a/src/main.rs +++ b/src/main.rs @@ -166,6 +166,7 @@ async fn transactions( tx.source_transaction_hash, tx.amount, tx.final_status, + tx.timestamp, estimate, Some(tx.source_block_height), None, @@ -226,6 +227,7 @@ async fn transactions( tx.source_transaction_hash, tx.amount, tx.final_status, + tx.block_timestamp, estimate, Some(tx.source_block_height), Some(tx.source_tx_index), @@ -236,6 +238,8 @@ async fn transactions( } } + transaction_data_results.sort_unstable_by(|a, b| b.timestamp.cmp(&a.timestamp)); + Ok(Json(json!(transaction_data_results))) } diff --git a/src/models.rs b/src/models.rs index 9c39614..dccb254 100644 --- a/src/models.rs +++ b/src/models.rs @@ -308,6 +308,7 @@ pub struct EthTransactionRow { pub source_block_hash: String, pub source_transaction_hash: String, pub amount: String, + pub timestamp: i64, pub final_status: BridgeStatusEnum, pub source_block_height: i32, pub destination_block_height: Option, @@ -324,6 +325,7 @@ pub struct AvailTransactionRow { pub source_block_hash: String, pub source_transaction_hash: String, pub amount: String, + pub block_timestamp: i64, pub final_status: BridgeStatusEnum, pub source_block_height: i32, pub source_tx_index: i32, @@ -348,6 +350,7 @@ pub struct TransactionData { pub source_tx_hash: String, pub amount: String, pub status: BridgeStatusEnum, + pub timestamp: i64, #[serde(skip_serializing_if = "Option::is_none")] pub claim_estimate: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -372,6 +375,7 @@ impl TransactionData { source_tx_hash: String, amount: String, status: BridgeStatusEnum, + timestamp: i64, claim_estimate: Option, source_block_number: Option, source_tx_index: Option, @@ -388,6 +392,7 @@ impl TransactionData { source_tx_hash, amount, status, + timestamp, claim_estimate, source_block_number, source_tx_index,