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