fuel_core/schema/
contract.rs

1use crate::{
2    fuel_core_graphql_api::{
3        IntoApiResult,
4        query_costs,
5    },
6    schema::{
7        ReadViewProvider,
8        scalars::{
9            AssetId,
10            ContractId,
11            HexString,
12            Salt,
13            U64,
14        },
15    },
16};
17use async_graphql::{
18    Context,
19    InputObject,
20    Object,
21    connection::{
22        Connection,
23        EmptyFields,
24    },
25};
26use fuel_core_storage::{
27    not_found,
28    tables::ContractsRawCode,
29};
30use fuel_core_types::{
31    fuel_types,
32    services::graphql_api,
33};
34use futures::StreamExt;
35
36pub struct Contract(pub(crate) fuel_types::ContractId);
37
38impl From<fuel_types::ContractId> for Contract {
39    fn from(id: fuel_types::ContractId) -> Self {
40        Self(id)
41    }
42}
43
44#[Object]
45impl Contract {
46    async fn id(&self) -> ContractId {
47        self.0.into()
48    }
49
50    #[graphql(complexity = "query_costs().bytecode_read")]
51    async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result<HexString> {
52        let query = ctx.read_view()?;
53        query
54            .contract_bytecode(self.0)
55            .map(HexString)
56            .map_err(Into::into)
57    }
58
59    #[graphql(complexity = "query_costs().storage_read")]
60    async fn salt(&self, ctx: &Context<'_>) -> async_graphql::Result<Salt> {
61        let query = ctx.read_view()?;
62        query
63            .contract_salt(&self.0)
64            .map(Into::into)
65            .map_err(Into::into)
66    }
67}
68
69#[derive(Default)]
70pub struct ContractQuery;
71
72#[Object]
73impl ContractQuery {
74    #[graphql(complexity = "query_costs().storage_read + child_complexity")]
75    async fn contract(
76        &self,
77        ctx: &Context<'_>,
78        #[graphql(desc = "ID of the Contract")] id: ContractId,
79    ) -> async_graphql::Result<Option<Contract>> {
80        let query = ctx.read_view()?;
81        query
82            .contract_exists(id.0)
83            .and_then(|contract_exists| {
84                if contract_exists {
85                    Ok(id.0)
86                } else {
87                    Err(not_found!(ContractsRawCode))
88                }
89            })
90            .into_api_result()
91    }
92}
93
94pub struct ContractBalance(graphql_api::ContractBalance);
95
96#[Object]
97impl ContractBalance {
98    async fn contract(&self) -> ContractId {
99        self.0.owner.into()
100    }
101
102    async fn amount(&self) -> U64 {
103        self.0.amount.into()
104    }
105
106    async fn asset_id(&self) -> AssetId {
107        self.0.asset_id.into()
108    }
109}
110
111#[derive(InputObject)]
112struct ContractBalanceFilterInput {
113    /// Filter assets based on the `contractId` field
114    contract: ContractId,
115}
116
117#[derive(Default)]
118pub struct ContractBalanceQuery;
119
120#[Object]
121impl ContractBalanceQuery {
122    #[graphql(complexity = "query_costs().storage_read")]
123    async fn contract_balance(
124        &self,
125        ctx: &Context<'_>,
126        contract: ContractId,
127        asset: AssetId,
128    ) -> async_graphql::Result<ContractBalance> {
129        let contract_id = contract.into();
130        let asset_id = asset.into();
131        let query = ctx.read_view()?;
132        query
133            .contract_balance(contract_id, asset_id)
134            .into_api_result()
135            .map(|result| {
136                result.unwrap_or_else(|| {
137                    graphql_api::ContractBalance {
138                        owner: contract_id,
139                        amount: 0,
140                        asset_id,
141                    }
142                    .into()
143                })
144            })
145    }
146
147    #[graphql(complexity = "{\
148        query_costs().storage_iterator\
149        + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \
150        + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\
151    }")]
152    async fn contract_balances(
153        &self,
154        ctx: &Context<'_>,
155        filter: ContractBalanceFilterInput,
156        first: Option<i32>,
157        after: Option<String>,
158        last: Option<i32>,
159        before: Option<String>,
160    ) -> async_graphql::Result<
161        Connection<AssetId, ContractBalance, EmptyFields, EmptyFields>,
162    > {
163        let query = ctx.read_view()?;
164
165        crate::schema::query_pagination(after, before, first, last, |start, direction| {
166            let balances = query
167                .contract_balances(
168                    filter.contract.into(),
169                    (*start).map(Into::into),
170                    direction,
171                )
172                .map(|balance| {
173                    let balance = balance?;
174                    let asset_id = balance.asset_id;
175
176                    Ok((asset_id.into(), balance.into()))
177                });
178
179            Ok(balances)
180        })
181        .await
182    }
183}
184
185impl From<graphql_api::ContractBalance> for ContractBalance {
186    fn from(balance: graphql_api::ContractBalance) -> Self {
187        ContractBalance(balance)
188    }
189}