Skip to main content

soroban_cli/commands/ledger/entry/fetch/
trustline.rs

1use std::fmt::Debug;
2
3use super::args::Args;
4use crate::{
5    commands::config::{self, locator},
6    xdr::{
7        AccountId, AlphaNum12, AlphaNum4, AssetCode, LedgerKey, LedgerKeyTrustLine, MuxedAccount,
8        PublicKey, TrustLineAsset, Uint256,
9    },
10};
11use clap::Parser;
12use stellar_strkey::ed25519::PublicKey as Ed25519PublicKey;
13
14#[derive(Parser, Debug, Clone)]
15#[group(skip)]
16pub struct Cmd {
17    #[command(flatten)]
18    pub args: Args,
19
20    /// Account alias or address to lookup
21    #[arg(long)]
22    pub account: String,
23
24    /// Assets to get trustline info for
25    #[arg(long, required = true)]
26    pub asset: Vec<String>,
27
28    /// If account is a seed phrase use this hd path, default is 0
29    #[arg(long)]
30    pub hd_path: Option<usize>,
31}
32
33#[derive(thiserror::Error, Debug)]
34pub enum Error {
35    #[error(transparent)]
36    Config(#[from] config::key::Error),
37    #[error("provided asset is invalid: {0}")]
38    InvalidAsset(String),
39    #[error("provided data name is invalid: {0}")]
40    InvalidDataName(String),
41    #[error("native assets do not have trustlines")]
42    NativeAsset,
43    #[error(transparent)]
44    Locator(#[from] locator::Error),
45    #[error(transparent)]
46    Run(#[from] super::args::Error),
47}
48
49impl Cmd {
50    pub async fn run(&self) -> Result<(), Error> {
51        let mut ledger_keys = vec![];
52        self.insert_asset_keys(&mut ledger_keys)?;
53        Ok(self.args.run(ledger_keys).await?)
54    }
55
56    fn insert_asset_keys(&self, ledger_keys: &mut Vec<LedgerKey>) -> Result<(), Error> {
57        let acc = self.muxed_account(&self.account)?;
58        for asset in &self.asset {
59            let asset = if asset.contains(':') {
60                let mut parts = asset.split(':');
61                let code = parts.next().ok_or(Error::InvalidAsset(asset.clone()))?;
62                let issuer = parts.next().ok_or(Error::InvalidAsset(asset.clone()))?;
63                if parts.next().is_some() {
64                    Err(Error::InvalidAsset(asset.clone()))?;
65                }
66                let source_bytes = Ed25519PublicKey::from_string(issuer)
67                    .map_err(|_| Error::InvalidAsset(asset.clone()))?
68                    .0;
69                let issuer = AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(source_bytes)));
70
71                let asset_code: AssetCode = code
72                    .parse()
73                    .map_err(|_| Error::InvalidAsset(asset.clone()))?;
74                match asset_code {
75                    AssetCode::CreditAlphanum4(asset_code) => {
76                        TrustLineAsset::CreditAlphanum4(AlphaNum4 { asset_code, issuer })
77                    }
78                    AssetCode::CreditAlphanum12(asset_code) => {
79                        TrustLineAsset::CreditAlphanum12(AlphaNum12 { asset_code, issuer })
80                    }
81                }
82            } else if matches!(asset.as_str(), "XLM" | "xlm" | "native") {
83                Err(Error::NativeAsset)?
84            } else {
85                Err(Error::InvalidAsset(asset.clone()))?
86            };
87
88            let key = LedgerKey::Trustline(LedgerKeyTrustLine {
89                account_id: acc.clone().account_id(),
90                asset,
91            });
92
93            ledger_keys.push(key);
94        }
95        Ok(())
96    }
97
98    fn muxed_account(&self, account: &str) -> Result<MuxedAccount, Error> {
99        Ok(self
100            .args
101            .locator
102            .read_key(account)?
103            .muxed_account(self.hd_path)?)
104    }
105}