fuel_core/schema/
storage.rs

1use crate::{
2    fuel_core_graphql_api::database::ReadView,
3    graphql_api::{
4        api_service::ReadDatabase,
5        require_historical_execution,
6    },
7    schema::{
8        contract::ContractBalance,
9        scalars::{
10            AssetId,
11            Bytes32,
12            ContractId,
13            HexString,
14            U32,
15        },
16    },
17};
18use async_graphql::{
19    Context,
20    Object,
21    Subscription,
22};
23use fuel_core_services::stream::Stream;
24use fuel_core_types::fuel_types;
25use futures::{
26    StreamExt,
27    TryStreamExt,
28};
29
30#[derive(Default)]
31pub struct StorageQuery;
32
33#[Object]
34impl StorageQuery {
35    /// Get storage slot values for a contract at a specific block height.
36    /// Use the latest block height if not provided.
37    /// Requires historical execution config to be enabled.
38    async fn contract_slot_values(
39        &self,
40        ctx: &Context<'_>,
41        contract_id: ContractId,
42        block_height: Option<U32>,
43        storage_slots: Vec<Bytes32>,
44    ) -> async_graphql::Result<Vec<StorageSlot>> {
45        require_historical_execution(ctx)?;
46
47        let view_block_height = if let Some(block_height) = block_height {
48            block_height.0.into()
49        } else {
50            let read_view: &ReadView = ctx.data_unchecked();
51            read_view.latest_height()?
52        };
53
54        let read_database: &ReadDatabase = ctx.data_unchecked();
55        let view_at = read_database.view_at(view_block_height)?;
56        let storage_slots = storage_slots
57            .into_iter()
58            .map(|x| x.into())
59            .collect::<Vec<_>>();
60
61        let stream = view_at
62            .contract_slot_values(contract_id.into(), storage_slots)
63            .map(|result| result.map(|(key, value)| StorageSlot { key, value }))
64            .try_collect()
65            .await?;
66
67        Ok(stream)
68    }
69
70    /// Get balance values for a contract at a specific block height.
71    /// Use the latest block height if not provided.
72    /// Requires historical execution config to be enabled.
73    async fn contract_balance_values(
74        &self,
75        ctx: &Context<'_>,
76        contract_id: ContractId,
77        block_height: Option<U32>,
78        assets: Vec<AssetId>,
79    ) -> async_graphql::Result<Vec<ContractBalance>> {
80        require_historical_execution(ctx)?;
81
82        let view_block_height = if let Some(block_height) = block_height {
83            block_height.0.into()
84        } else {
85            let read_view: &ReadView = ctx.data_unchecked();
86            read_view.latest_height()?
87        };
88
89        let read_database: &ReadDatabase = ctx.data_unchecked();
90        let view_at = read_database.view_at(view_block_height)?;
91        let assets = assets.into_iter().map(|x| x.into()).collect::<Vec<_>>();
92
93        let stream = view_at
94            .contract_balance_values(contract_id.into(), assets)
95            .map(|result| result.map(Into::into))
96            .try_collect()
97            .await?;
98
99        Ok(stream)
100    }
101}
102
103#[derive(Default)]
104pub struct StorageSubscription;
105
106#[Subscription]
107impl StorageSubscription {
108    async fn contract_storage_slots<'a>(
109        &self,
110        ctx: &Context<'a>,
111        contract_id: ContractId,
112    ) -> async_graphql::Result<
113        impl Stream<Item = async_graphql::Result<StorageSlot>> + 'a + use<'a>,
114    > {
115        require_historical_execution(ctx)?;
116        let read_view: &ReadView = ctx.data_unchecked();
117
118        let stream = read_view
119            .contract_storage_slots(contract_id.into())
120            .map(|result| {
121                result
122                    .map(|(key, value)| StorageSlot { key, value })
123                    .map_err(|e| anyhow::anyhow!(e).into())
124            });
125
126        Ok(stream)
127    }
128
129    async fn contract_storage_balances<'a>(
130        &self,
131        ctx: &Context<'a>,
132        contract_id: ContractId,
133    ) -> async_graphql::Result<
134        impl Stream<Item = async_graphql::Result<ContractBalance>> + 'a + use<'a>,
135    > {
136        require_historical_execution(ctx)?;
137        let read_view: &ReadView = ctx.data_unchecked();
138
139        let stream =
140            read_view
141                .contract_storage_balances(contract_id.into())
142                .map(|result| {
143                    result
144                        .map(Into::into)
145                        .map_err(|e| anyhow::anyhow!(e).into())
146                });
147
148        Ok(stream)
149    }
150}
151
152pub struct StorageSlot {
153    key: fuel_types::Bytes32,
154    value: Vec<u8>,
155}
156
157#[Object]
158impl StorageSlot {
159    async fn key(&self) -> Bytes32 {
160        self.key.into()
161    }
162
163    async fn value(&self) -> HexString {
164        HexString(self.value.clone())
165    }
166}