sui_rosetta/
block.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use axum::extract::State;
5use axum::{Extension, Json};
6use axum_extra::extract::WithRejection;
7use prost_types::FieldMask;
8use sui_rpc::field::FieldMaskUtil;
9use sui_rpc::proto::sui::rpc::v2::GetTransactionRequest;
10use tracing::debug;
11
12use crate::operations::Operations;
13use crate::types::{
14    BlockRequest, BlockResponse, BlockTransactionRequest, BlockTransactionResponse, Transaction,
15    TransactionIdentifier,
16};
17use crate::{Error, OnlineServerContext, SuiEnv};
18
19// This module implements the [Mesh Block API](https://docs.cdp.coinbase.com/mesh/mesh-api-spec/api-reference#block)
20
21/// Get a block by its Block Identifier.
22/// [Mesh API Spec](https://docs.cdp.coinbase.com/api-reference/mesh/block/get-a-block)
23pub async fn block(
24    State(state): State<OnlineServerContext>,
25    Extension(env): Extension<SuiEnv>,
26    WithRejection(Json(request), _): WithRejection<Json<BlockRequest>, Error>,
27) -> Result<BlockResponse, Error> {
28    debug!("Called /block endpoint: {:?}", request.block_identifier);
29    env.check_network_identifier(&request.network_identifier)?;
30    let blocks = state.blocks();
31    if let Some(index) = request.block_identifier.index {
32        blocks.get_block_by_index(index).await
33    } else if let Some(hash) = request.block_identifier.hash {
34        blocks.get_block_by_hash(hash).await
35    } else {
36        blocks.current_block().await
37    }
38}
39
40/// Get a transaction in a block by its Transaction Identifier.
41/// [Mesh API Spec](https://docs.cdp.coinbase.com/api-reference/mesh/block/get-a-block-transaction)
42pub async fn transaction(
43    State(context): State<OnlineServerContext>,
44    Extension(env): Extension<SuiEnv>,
45    WithRejection(Json(request), _): WithRejection<Json<BlockTransactionRequest>, Error>,
46) -> Result<BlockTransactionResponse, Error> {
47    env.check_network_identifier(&request.network_identifier)?;
48    let digest = request.transaction_identifier.hash;
49
50    let request = GetTransactionRequest::default()
51        .with_digest(digest.to_string())
52        .with_read_mask(FieldMask::from_paths([
53            "digest",
54            "transaction.sender",
55            "transaction.gas_payment",
56            "transaction.kind",
57            "effects.gas_object",
58            "effects.gas_used",
59            "effects.status",
60            "balance_changes",
61            "events.events.event_type",
62            "events.events.json",
63            "events.events.contents",
64        ]));
65
66    let mut client = context.client.clone();
67    let response = client
68        .ledger_client()
69        .get_transaction(request)
70        .await?
71        .into_inner();
72
73    let operations = Operations::try_from_executed_transaction(
74        response
75            .transaction
76            .ok_or_else(|| Error::DataError("Response missing transaction".to_string()))?,
77        &context.coin_metadata_cache,
78    )
79    .await?;
80
81    let transaction = Transaction {
82        transaction_identifier: TransactionIdentifier { hash: digest },
83        operations,
84        related_transactions: vec![],
85        metadata: None,
86    };
87
88    Ok(BlockTransactionResponse { transaction })
89}