fuel_core/schema/
storage.rs1use 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 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 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}