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