1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use crate::fuel_core_graphql_api::ports::{
    OffChainDatabase,
    OnChainDatabase,
};
use fuel_core_storage::{
    iter::{
        BoxedIter,
        IterDirection,
    },
    not_found,
    tables::{
        ContractsAssets,
        ContractsRawCode,
    },
    Result as StorageResult,
    StorageAsRef,
};
use fuel_core_types::{
    fuel_types::{
        AssetId,
        ContractId,
    },
    fuel_vm::Salt,
    services::graphql_api::ContractBalance,
};

pub trait ContractQueryData: Send + Sync {
    fn contract_id(&self, id: ContractId) -> StorageResult<ContractId>;

    fn contract_bytecode(&self, id: ContractId) -> StorageResult<Vec<u8>>;

    fn contract_salt(&self, id: ContractId) -> StorageResult<Salt>;

    fn contract_balance(
        &self,
        contract_id: ContractId,
        asset_id: AssetId,
    ) -> StorageResult<ContractBalance>;

    fn contract_balances(
        &self,
        contract_id: ContractId,
        start_asset: Option<AssetId>,
        direction: IterDirection,
    ) -> BoxedIter<StorageResult<ContractBalance>>;
}

impl<D: OnChainDatabase + OffChainDatabase + ?Sized> ContractQueryData for D {
    fn contract_id(&self, id: ContractId) -> StorageResult<ContractId> {
        let contract_exists = self.storage::<ContractsRawCode>().contains_key(&id)?;
        if contract_exists {
            Ok(id)
        } else {
            Err(not_found!(ContractsRawCode))
        }
    }

    fn contract_bytecode(&self, id: ContractId) -> StorageResult<Vec<u8>> {
        let contract = self
            .storage::<ContractsRawCode>()
            .get(&id)?
            .ok_or(not_found!(ContractsRawCode))?
            .into_owned();

        Ok(contract.into())
    }

    fn contract_salt(&self, id: ContractId) -> StorageResult<Salt> {
        self.contract_salt(&id)
    }

    fn contract_balance(
        &self,
        contract_id: ContractId,
        asset_id: AssetId,
    ) -> StorageResult<ContractBalance> {
        let amount = self
            .storage::<ContractsAssets>()
            .get(&(&contract_id, &asset_id).into())?
            .ok_or(not_found!(ContractsAssets))?
            .into_owned();

        Ok(ContractBalance {
            owner: contract_id,
            amount,
            asset_id,
        })
    }

    fn contract_balances(
        &self,
        contract_id: ContractId,
        start_asset: Option<AssetId>,
        direction: IterDirection,
    ) -> BoxedIter<StorageResult<ContractBalance>> {
        self.contract_balances(contract_id, start_asset, direction)
    }
}