Skip to main content

iota_graphql_rpc/types/
address.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use async_graphql::{connection::Connection, *};
6
7use crate::{
8    config::DEFAULT_PAGE_SIZE,
9    connection::ScanConnection,
10    types::{
11        balance::{self, Balance},
12        coin::Coin,
13        cursor::Page,
14        iota_address::IotaAddress,
15        iota_names_registration::{NameFormat, NameRegistration},
16        move_object::MoveObject,
17        object::{self, ObjectFilter},
18        owner::OwnerImpl,
19        stake::StakedIota,
20        transaction_block::{self, TransactionBlock, TransactionBlockFilter},
21        type_filter::ExactTypeFilter,
22    },
23};
24
25#[derive(Clone, Debug, PartialEq, Eq, Copy)]
26pub(crate) struct Address {
27    pub address: IotaAddress,
28    /// The checkpoint sequence number at which this was viewed at.
29    pub checkpoint_viewed_at: u64,
30}
31
32/// The possible relationship types for a transaction block: sent or received.
33#[derive(Enum, Copy, Clone, Eq, PartialEq)]
34pub(crate) enum AddressTransactionBlockRelationship {
35    /// Transactions this address has sent.
36    Sent,
37    /// Transactions that sent objects to this address.
38    Recv,
39}
40
41/// The 32-byte address that is an account address (corresponding to a public
42/// key).
43#[Object]
44impl Address {
45    pub(crate) async fn address(&self) -> IotaAddress {
46        OwnerImpl::from(self).address().await
47    }
48
49    /// Objects owned by this address, optionally `filter`-ed.
50    pub(crate) async fn objects(
51        &self,
52        ctx: &Context<'_>,
53        first: Option<u64>,
54        after: Option<object::Cursor>,
55        last: Option<u64>,
56        before: Option<object::Cursor>,
57        filter: Option<ObjectFilter>,
58    ) -> Result<Connection<String, MoveObject>> {
59        OwnerImpl::from(self)
60            .objects(ctx, first, after, last, before, filter)
61            .await
62    }
63
64    /// Total balance of all coins with marker type owned by this address. If
65    /// type is not supplied, it defaults to `0x2::iota::IOTA`.
66    pub(crate) async fn balance(
67        &self,
68        ctx: &Context<'_>,
69        type_: Option<ExactTypeFilter>,
70    ) -> Result<Option<Balance>> {
71        OwnerImpl::from(self).balance(ctx, type_).await
72    }
73
74    /// The balances of all coin types owned by this address.
75    pub(crate) async fn balances(
76        &self,
77        ctx: &Context<'_>,
78        first: Option<u64>,
79        after: Option<balance::Cursor>,
80        last: Option<u64>,
81        before: Option<balance::Cursor>,
82    ) -> Result<Connection<String, Balance>> {
83        OwnerImpl::from(self)
84            .balances(ctx, first, after, last, before)
85            .await
86    }
87
88    /// The coin objects for this address.
89    ///
90    /// `type` is a filter on the coin's type parameter, defaulting to
91    /// `0x2::iota::IOTA`.
92    pub(crate) async fn coins(
93        &self,
94        ctx: &Context<'_>,
95        first: Option<u64>,
96        after: Option<object::Cursor>,
97        last: Option<u64>,
98        before: Option<object::Cursor>,
99        type_: Option<ExactTypeFilter>,
100    ) -> Result<Connection<String, Coin>> {
101        OwnerImpl::from(self)
102            .coins(ctx, first, after, last, before, type_)
103            .await
104    }
105
106    /// The `0x3::staking_pool::StakedIota` objects owned by this address.
107    pub(crate) async fn staked_iotas(
108        &self,
109        ctx: &Context<'_>,
110        first: Option<u64>,
111        after: Option<object::Cursor>,
112        last: Option<u64>,
113        before: Option<object::Cursor>,
114    ) -> Result<Connection<String, StakedIota>> {
115        OwnerImpl::from(self)
116            .staked_iotas(ctx, first, after, last, before)
117            .await
118    }
119
120    /// The name explicitly configured as the default name pointing to this
121    /// address.
122    pub(crate) async fn iota_names_default_name(
123        &self,
124        ctx: &Context<'_>,
125        format: Option<NameFormat>,
126    ) -> Result<Option<String>> {
127        OwnerImpl::from(self)
128            .iota_names_default_name(ctx, format)
129            .await
130    }
131
132    /// The NameRegistration NFTs owned by this address. These grant the
133    /// owner the capability to manage the associated name.
134    pub(crate) async fn iota_names_registrations(
135        &self,
136        ctx: &Context<'_>,
137        first: Option<u64>,
138        after: Option<object::Cursor>,
139        last: Option<u64>,
140        before: Option<object::Cursor>,
141    ) -> Result<Connection<String, NameRegistration>> {
142        OwnerImpl::from(self)
143            .iota_names_registrations(ctx, first, after, last, before)
144            .await
145    }
146
147    /// Similar behavior to the `transactionBlocks` in Query but supporting the
148    /// additional `AddressTransactionBlockRelationship` filter, which
149    /// defaults to `SENT`.
150    ///
151    /// `scanLimit` restricts the number of candidate transactions scanned when
152    /// gathering a page of results. It is required for queries that apply
153    /// more than two complex filters (on function, kind, sender, recipient,
154    /// input object, changed object, or ids), and can be at most
155    /// `serviceConfig.maxScanLimit`.
156    ///
157    /// When the scan limit is reached the page will be returned even if it has
158    /// fewer than `first` results when paginating forward (`last` when
159    /// paginating backwards). If there are more transactions to scan,
160    /// `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to
161    /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set
162    /// to the last transaction that was scanned as opposed to the last (or
163    /// first) transaction in the page.
164    ///
165    /// Requesting the next (or previous) page after this cursor will resume the
166    /// search, scanning the next `scanLimit` many transactions in the
167    /// direction of pagination, and so on until all transactions in the
168    /// scanning range have been visited.
169    ///
170    /// By default, the scanning range includes all transactions known to
171    /// GraphQL, but it can be restricted by the `after` and `before`
172    /// cursors, and the `beforeCheckpoint`, `afterCheckpoint` and
173    /// `atCheckpoint` filters.
174    #[graphql(
175        complexity = "first.or(last).unwrap_or(DEFAULT_PAGE_SIZE as u64) as usize * child_complexity"
176    )]
177    async fn transaction_blocks(
178        &self,
179        ctx: &Context<'_>,
180        first: Option<u64>,
181        after: Option<transaction_block::Cursor>,
182        last: Option<u64>,
183        before: Option<transaction_block::Cursor>,
184        relation: Option<AddressTransactionBlockRelationship>,
185        filter: Option<TransactionBlockFilter>,
186        scan_limit: Option<u64>,
187    ) -> Result<ScanConnection<String, TransactionBlock>> {
188        use AddressTransactionBlockRelationship as R;
189        let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?;
190
191        let Some(filter) = filter.unwrap_or_default().intersect(match relation {
192            // Relationship defaults to "signer" if none is supplied.
193            Some(R::Sent) | None => TransactionBlockFilter {
194                sent_address: Some(self.address),
195                ..Default::default()
196            },
197
198            Some(R::Recv) => TransactionBlockFilter {
199                recv_address: Some(self.address),
200                ..Default::default()
201            },
202        }) else {
203            return Ok(ScanConnection::new(false, false));
204        };
205
206        TransactionBlock::paginate(ctx, page, filter, self.checkpoint_viewed_at, scan_limit)
207            .await
208            .extend()
209    }
210}
211
212impl From<&Address> for OwnerImpl {
213    fn from(address: &Address) -> Self {
214        OwnerImpl {
215            address: address.address,
216            checkpoint_viewed_at: address.checkpoint_viewed_at,
217        }
218    }
219}