Skip to main content

soroban_cli/config/
alias.rs

1use std::{collections::HashMap, convert::Infallible, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4use stellar_strkey::Contract;
5
6use super::locator;
7use crate::config::token::UnresolvedToken;
8use crate::tx::builder;
9
10#[derive(Serialize, Deserialize, Default)]
11pub struct Data {
12    pub ids: HashMap<String, String>,
13}
14
15/// The reserved, built-in contract alias. It resolves to the native asset (XLM)
16/// Stellar Asset Contract for the current network and cannot be created,
17/// overwritten, or removed by users.
18pub const NATIVE: &str = "native";
19
20/// Returns `true` if `alias` is the reserved, built-in alias that users cannot
21/// create, overwrite, or remove.
22#[must_use]
23pub fn is_reserved(alias: &str) -> bool {
24    alias == NATIVE
25}
26
27/// Resolves the reserved, built-in alias to its contract for `network_passphrase`,
28/// or returns `None` if `alias` is not reserved. This is the single source of
29/// truth for what the reserved alias points to, so resolution stays consistent
30/// across `get_contract_id`, `alias show`, and `alias ls`.
31#[must_use]
32pub fn resolve_reserved(
33    alias: &str,
34    locator: &locator::Args,
35    network_passphrase: &str,
36) -> Option<Contract> {
37    if !is_reserved(alias) {
38        return None;
39    }
40
41    // The reserved alias points at the native asset's Stellar Asset Contract.
42    // Route it through the shared token resolver so its id stays identical to
43    // every other `native` resolution. `locator` is unused for native (there is
44    // no issuer alias to look up) but is threaded through for signature parity
45    // with the resolver; native therefore can never fail to resolve.
46    let resolved = UnresolvedToken::Asset(builder::Asset::Native)
47        .resolve(locator, network_passphrase)
48        .expect("the reserved native alias always resolves to its SAC");
49    Some(resolved.contract_id)
50}
51
52/// Errors if `alias` is a reserved, built-in alias. Call this before doing any
53/// work (building, simulating, deploying, or writing config) so that a reserved
54/// alias fails fast.
55pub fn validate_reserved_aliases(alias: &str) -> Result<(), locator::Error> {
56    if is_reserved(alias) {
57        return Err(locator::Error::ContractAliasReserved(alias.to_owned()));
58    }
59    Ok(())
60}
61
62/// Address can be either a contract address, C.. or eventually an alias of a contract address.
63#[derive(Clone, Debug)]
64pub enum UnresolvedContract {
65    Resolved(stellar_strkey::Contract),
66    Alias(String),
67}
68
69impl Default for UnresolvedContract {
70    fn default() -> Self {
71        UnresolvedContract::Alias(String::default())
72    }
73}
74
75impl FromStr for UnresolvedContract {
76    type Err = Infallible;
77
78    fn from_str(value: &str) -> Result<Self, Self::Err> {
79        Ok(stellar_strkey::Contract::from_str(value).map_or_else(
80            |_| UnresolvedContract::Alias(value.to_string()),
81            UnresolvedContract::Resolved,
82        ))
83    }
84}
85
86impl UnresolvedContract {
87    pub fn resolve_contract_id(
88        &self,
89        locator: &locator::Args,
90        network_passphrase: &str,
91    ) -> Result<stellar_strkey::Contract, locator::Error> {
92        match self {
93            UnresolvedContract::Resolved(contract) => Ok(*contract),
94            UnresolvedContract::Alias(alias) => {
95                Self::resolve_alias(alias, locator, network_passphrase)
96            }
97        }
98    }
99
100    pub fn resolve_alias(
101        alias: &str,
102        locator: &locator::Args,
103        network_passphrase: &str,
104    ) -> Result<stellar_strkey::Contract, locator::Error> {
105        locator
106            .get_contract_id(alias, network_passphrase)?
107            .ok_or_else(|| locator::Error::ContractNotFound(alias.to_owned()))
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn native_is_reserved() {
117        assert!(is_reserved("native"));
118        assert!(validate_reserved_aliases("native").is_err());
119    }
120
121    #[test]
122    fn regular_aliases_are_not_reserved() {
123        assert!(!is_reserved("my-token"));
124        assert!(validate_reserved_aliases("my-token").is_ok());
125    }
126}