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    connection::ScanConnection,
9    types::{
10        balance::{self, Balance},
11        coin::Coin,
12        cursor::Page,
13        iota_address::IotaAddress,
14        iota_names_registration::{DomainFormat, IotaNamesRegistration},
15        move_object::MoveObject,
16        object::{self, ObjectFilter},
17        owner::OwnerImpl,
18        stake::StakedIota,
19        transaction_block::{self, TransactionBlock, TransactionBlockFilter},
20        type_filter::ExactTypeFilter,
21    },
22};
23
24#[derive(Clone, Debug, PartialEq, Eq, Copy)]
25pub(crate) struct Address {
26    pub address: IotaAddress,
27    /// The checkpoint sequence number at which this was viewed at.
28    pub checkpoint_viewed_at: u64,
29}
30
31/// The possible relationship types for a transaction block: sign, sent,
32/// received, or paid.
33#[derive(Enum, Copy, Clone, Eq, PartialEq)]
34pub(crate) enum AddressTransactionBlockRelationship {
35    /// Transactions this address has signed either as a sender or as a sponsor.
36    Sign,
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 domain explicitly configured as the default domain pointing to this
121    /// address.
122    pub(crate) async fn iota_names_default_name(
123        &self,
124        ctx: &Context<'_>,
125        format: Option<DomainFormat>,
126    ) -> Result<Option<String>> {
127        OwnerImpl::from(self)
128            .iota_names_default_name(ctx, format)
129            .await
130    }
131
132    /// The IotaNamesRegistration NFTs owned by this address. These grant the
133    /// owner the capability to manage the associated domain.
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, IotaNamesRegistration>> {
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 `SIGN`.
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    async fn transaction_blocks(
175        &self,
176        ctx: &Context<'_>,
177        first: Option<u64>,
178        after: Option<transaction_block::Cursor>,
179        last: Option<u64>,
180        before: Option<transaction_block::Cursor>,
181        relation: Option<AddressTransactionBlockRelationship>,
182        filter: Option<TransactionBlockFilter>,
183        scan_limit: Option<u64>,
184    ) -> Result<ScanConnection<String, TransactionBlock>> {
185        use AddressTransactionBlockRelationship as R;
186        let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?;
187
188        let Some(filter) = filter.unwrap_or_default().intersect(match relation {
189            // Relationship defaults to "signer" if none is supplied.
190            Some(R::Sign) | None => TransactionBlockFilter {
191                sign_address: Some(self.address),
192                ..Default::default()
193            },
194
195            Some(R::Recv) => TransactionBlockFilter {
196                recv_address: Some(self.address),
197                ..Default::default()
198            },
199        }) else {
200            return Ok(ScanConnection::new(false, false));
201        };
202
203        TransactionBlock::paginate(ctx, page, filter, self.checkpoint_viewed_at, scan_limit)
204            .await
205            .extend()
206    }
207}
208
209impl From<&Address> for OwnerImpl {
210    fn from(address: &Address) -> Self {
211        OwnerImpl {
212            address: address.address,
213            checkpoint_viewed_at: address.checkpoint_viewed_at,
214        }
215    }
216}