aptos_sdk/api/ans.rs
1//! Aptos Names Service (ANS) client -- **scaffold / not yet implemented**.
2//!
3//! This module exists so `AGENTS.md` can stop referencing a missing file,
4//! and so future work can land in one obvious place. **Nothing here issues
5//! a network call yet.**
6//!
7//! # Scope when implemented
8//!
9//! At minimum we want:
10//!
11//! - `lookup(name)` -- resolve a `.apt` / sub-domain string into an
12//! [`AccountAddress`].
13//! - `reverse_lookup(address)` -- find the primary name registered to an
14//! address, if any.
15//!
16//! At extension time:
17//!
18//! - Register / renew flows (entry-function builders against the on-chain
19//! ANS contract -- requires pinned contract addresses per network and a
20//! plan for how the SDK should pick them).
21//! - Sub-domain queries.
22//!
23//! # Why not implemented yet
24//!
25//! The TS SDK's ANS module hard-codes contract addresses per network and
26//! relies on the indexer for some lookups. Mirroring that 1:1 without
27//! making network-specific decisions visible to callers needs design
28//! work that's tracked separately from the May-2026 audit follow-ups. See
29//! `AUDIT_SUMMARY_2026-05.md` and the audit follow-up commits for context.
30
31use crate::api::FullnodeClient;
32use crate::error::{AptosError, AptosResult};
33use crate::types::AccountAddress;
34
35/// Skeleton client for Aptos Names Service.
36///
37/// All methods currently return
38/// [`AptosError::Internal`]
39/// so callers fail fast rather than silently accepting placeholder values.
40#[derive(Debug, Clone)]
41pub struct AnsClient {
42 #[allow(dead_code)] // wired up when lookups land
43 fullnode: FullnodeClient,
44}
45
46impl AnsClient {
47 /// Constructs a new ANS client bound to a given fullnode.
48 #[must_use]
49 pub fn new(fullnode: FullnodeClient) -> Self {
50 Self { fullnode }
51 }
52
53 /// Resolves an ANS name (e.g. `"alice.apt"`) to its registered
54 /// [`AccountAddress`].
55 ///
56 /// # Errors
57 ///
58 /// Currently always returns [`AptosError::Internal`]; the lookup is
59 /// scheduled but not yet wired up. Track progress against the audit
60 /// follow-up issue cited in this module's docs.
61 pub async fn lookup(&self, _name: &str) -> AptosResult<AccountAddress> {
62 Err(AptosError::Internal(
63 "ANS lookup is not yet implemented in the Rust SDK (tracked as an audit \
64 follow-up); use the on-chain `0x...::router::get_address` view \
65 function directly for now"
66 .to_string(),
67 ))
68 }
69
70 /// Finds the primary ANS name registered to a given address, if any.
71 ///
72 /// # Errors
73 ///
74 /// Currently always returns [`AptosError::Internal`]; the reverse
75 /// lookup is scheduled but not yet wired up.
76 pub async fn reverse_lookup(&self, _address: AccountAddress) -> AptosResult<Option<String>> {
77 Err(AptosError::Internal(
78 "ANS reverse lookup is not yet implemented in the Rust SDK \
79 (tracked as an audit follow-up)"
80 .to_string(),
81 ))
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::config::AptosConfig;
89
90 #[tokio::test]
91 async fn lookup_is_unsupported() {
92 let fullnode = FullnodeClient::new(AptosConfig::testnet()).unwrap();
93 let ans = AnsClient::new(fullnode);
94 let err = ans.lookup("alice.apt").await.unwrap_err();
95 assert!(matches!(err, AptosError::Internal(_)));
96 }
97
98 #[tokio::test]
99 async fn reverse_lookup_is_unsupported() {
100 let fullnode = FullnodeClient::new(AptosConfig::testnet()).unwrap();
101 let ans = AnsClient::new(fullnode);
102 let err = ans.reverse_lookup(AccountAddress::ONE).await.unwrap_err();
103 assert!(matches!(err, AptosError::Internal(_)));
104 }
105}