Skip to content

Commit 091811f

Browse files
feat(cat-gateway): cardano/assets V2 endpoint (#3606)
* Make stake_address optional parameter for the cardano assets endpoint * Document remaining items * Return information about all stake addresses * Remove unused code
1 parent 22ce517 commit 091811f

File tree

4 files changed

+174
-3
lines changed

4 files changed

+174
-3
lines changed

catalyst-gateway/bin/src/service/api/cardano/staking/assets_get.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ struct TxoInfo {
8585
}
8686

8787
/// Building a full stake info response from the provided arguments.
88-
async fn build_full_stake_info_response(
88+
pub(crate) async fn build_full_stake_info_response(
8989
stake_address: Cip19StakeAddress,
9090
provided_network: Option<Network>,
9191
slot_num: Option<SlotNo>,
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! An implementation of the GET `v2/cardano/assets` endpoint.
2+
3+
use std::collections::HashSet;
4+
5+
use anyhow::Context;
6+
use futures::future::try_join_all;
7+
use poem_openapi::payload::Json;
8+
use poem_openapi_derive::ApiResponse;
9+
10+
use crate::service::{
11+
api::cardano::staking::assets_get::build_full_stake_info_response,
12+
common::{
13+
auth::{none_or_rbac::NoneOrRBAC, rbac::token::CatalystRBACTokenV1},
14+
objects::cardano::{network::Network, stake_info::FullStakeInfo},
15+
responses::WithErrorResponses,
16+
types::{
17+
cardano::{cip19_stake_address::Cip19StakeAddress, slot_no::SlotNo},
18+
generic::error_msg::ErrorMessage,
19+
},
20+
},
21+
};
22+
23+
/// An endpoint responses.
24+
#[derive(ApiResponse)]
25+
pub(crate) enum ResponsesV2 {
26+
/// ## Ok
27+
///
28+
/// The amount of ADA staked by the queried stake address, as at the indicated slot.
29+
#[oai(status = 200)]
30+
Ok(Json<FullStakeInfo>),
31+
32+
/// ## Not Found
33+
///
34+
/// The queried stake address was not found at the requested slot number.
35+
#[oai(status = 404)]
36+
NotFound,
37+
38+
/// Response for unprocessable content.
39+
#[oai(status = 422)]
40+
UnprocessableContent(Json<ErrorMessage>),
41+
}
42+
43+
/// All responses.
44+
pub type AllResponsesV2 = WithErrorResponses<ResponsesV2>;
45+
46+
/// Get Cardano assets V2 endpoint.
47+
pub(crate) async fn endpoint(
48+
address: Option<Cip19StakeAddress>,
49+
network: Option<Network>,
50+
slot: Option<SlotNo>,
51+
auth: NoneOrRBAC,
52+
) -> AllResponsesV2 {
53+
let token: Option<_> = auth.into();
54+
if address.is_none() && token.is_none() {
55+
return ResponsesV2::UnprocessableContent(Json(
56+
"Either stake address parameter or token must be provided"
57+
.to_string()
58+
.into(),
59+
))
60+
.into();
61+
}
62+
63+
let stake_addresses = match stake_addresses(address, token).await {
64+
Err(e) => return AllResponsesV2::handle_error(&e),
65+
Ok(addresses) => addresses,
66+
};
67+
let infos: Vec<_> = match try_join_all(
68+
stake_addresses
69+
.into_iter()
70+
.map(|a| build_full_stake_info_response(a, network.clone(), slot)),
71+
)
72+
.await
73+
{
74+
Err(e) => return AllResponsesV2::handle_error(&e),
75+
Ok(r) => r.into_iter().flatten().collect(),
76+
};
77+
78+
let info = infos.into_iter().reduce(|mut acc, mut info| {
79+
acc.persistent.0.ada_amount = acc
80+
.persistent
81+
.0
82+
.ada_amount
83+
.saturating_add(info.persistent.0.ada_amount);
84+
acc.persistent.0.slot_number =
85+
std::cmp::max(acc.persistent.0.slot_number, info.persistent.0.slot_number);
86+
acc.persistent
87+
.0
88+
.assets
89+
.append(&mut info.persistent.0.assets);
90+
91+
acc.volatile.0.ada_amount = acc
92+
.volatile
93+
.0
94+
.ada_amount
95+
.saturating_add(info.volatile.0.ada_amount);
96+
acc.volatile.0.slot_number =
97+
std::cmp::max(acc.volatile.0.slot_number, info.volatile.0.slot_number);
98+
acc.volatile.0.assets.append(&mut info.volatile.0.assets);
99+
100+
acc
101+
});
102+
103+
match info {
104+
Some(i) => ResponsesV2::Ok(Json(i)).into(),
105+
None => ResponsesV2::NotFound.into(),
106+
}
107+
}
108+
109+
/// Returns a stake address from provided parameters.
110+
async fn stake_addresses(
111+
address: Option<Cip19StakeAddress>,
112+
token: Option<CatalystRBACTokenV1>,
113+
) -> anyhow::Result<HashSet<Cip19StakeAddress>> {
114+
if let Some(address) = address {
115+
return Ok([address].into());
116+
}
117+
118+
if let Some(mut token) = token {
119+
// This should never fail as a valid RBAC token can only be constructed after building a
120+
// corresponding registration chain.
121+
let chain = token
122+
.reg_chain()
123+
.await?
124+
.context("Missing registration chain for token")?;
125+
Ok(chain
126+
.stake_addresses()
127+
.into_iter()
128+
.map(Into::into)
129+
.collect())
130+
} else {
131+
Ok(HashSet::new())
132+
}
133+
}

catalyst-gateway/bin/src/service/api/cardano/staking/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::service::common::{
1313
};
1414

1515
mod assets_get;
16+
mod assets_get_v2;
1617

1718
/// Cardano Staking API Endpoints
1819
pub(crate) struct Api;
@@ -54,4 +55,41 @@ impl Api {
5455
))
5556
.await
5657
}
58+
59+
/// Get staked assets v2.
60+
///
61+
/// This endpoint returns the total Cardano's staked assets to the corresponded
62+
/// user's stake address or RBAC token.
63+
#[oai(
64+
path = "/v2/cardano/assets/",
65+
method = "get",
66+
operation_id = "stakedAssetsGetV2"
67+
)]
68+
async fn staked_ada_get_v2(
69+
&self,
70+
/// An optional stake address of the user.
71+
/// Should be a valid Bech32 encoded address followed by the https://cips.cardano.org/cip/CIP-19/#stake-addresses.
72+
Query(stake_address): Query<Option<Cip19StakeAddress>>,
73+
/// Cardano network type.
74+
/// If omitted network type is identified from the stake address.
75+
/// If specified it must be correspondent to the network type encoded in the stake
76+
/// address.
77+
/// As `preprod` and `preview` network types in the stake address encoded as a
78+
/// `testnet`, to specify `preprod` or `preview` network type use this
79+
/// query parameter.
80+
Query(network): Query<Option<Network>>,
81+
/// A time point at which the assets should be calculated.
82+
/// If omitted latest slot number is used.
83+
asat: Query<Option<cardano::query::AsAt>>,
84+
/// No Authorization required, but Token permitted.
85+
auth: NoneOrRBAC,
86+
) -> assets_get_v2::AllResponsesV2 {
87+
Box::pin(assets_get_v2::endpoint(
88+
stake_address,
89+
network,
90+
SlotNo::into_option(asat.0),
91+
auth,
92+
))
93+
.await
94+
}
5795
}

catalyst-gateway/bin/src/service/common/objects/cardano/stake_info.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl Example for StakeInfo {
8686
to_header = false,
8787
example = true
8888
)]
89-
pub(crate) struct VolatileStakeInfo(StakeInfo);
89+
pub(crate) struct VolatileStakeInfo(pub(crate) StakeInfo);
9090

9191
impl Example for VolatileStakeInfo {
9292
fn example() -> Self {
@@ -102,7 +102,7 @@ impl Example for VolatileStakeInfo {
102102
to_header = false,
103103
example = true
104104
)]
105-
pub(crate) struct PersistentStakeInfo(StakeInfo);
105+
pub(crate) struct PersistentStakeInfo(pub(crate) StakeInfo);
106106

107107
impl Example for PersistentStakeInfo {
108108
fn example() -> Self {

0 commit comments

Comments
 (0)