stellar_registry_cli/
contract.rs

1use sha2::{Digest, Sha256};
2
3use soroban_rpc as rpc;
4use stellar_cli::{
5    commands::{contract::invoke, NetworkRunnable},
6    config::{self, network::Network, UnresolvedContract},
7    xdr::{self, WriteXdr},
8};
9
10pub trait NetworkContract {
11    fn contract_id(&self) -> Result<stellar_strkey::Contract, config::Error>;
12
13    fn contract_sc_address(&self) -> Result<xdr::ScAddress, config::Error> {
14        Ok(xdr::ScAddress::Contract(xdr::ContractId(xdr::Hash(
15            self.contract_id()?.0,
16        ))))
17    }
18
19    fn invoke_registry(
20        &self,
21        slop: &[&str],
22        fee: Option<&stellar_cli::fee::Args>,
23        view_only: bool,
24    ) -> impl std::future::Future<Output = Result<String, invoke::Error>> + Send;
25
26    fn rpc_client(&self) -> Result<rpc::Client, config::Error>;
27}
28
29impl NetworkContract for config::Args {
30    fn contract_id(&self) -> Result<stellar_strkey::Contract, config::Error> {
31        let Network {
32            network_passphrase, ..
33        } = &self.get_network()?;
34        let contract: UnresolvedContract = unsafe {
35            if let Ok(contract_id) = std::env::var("STELLAR_REGISTRY_CONTRACT_ID") {
36                contract_id.parse()
37            } else {
38                contract_id(network_passphrase).to_string().parse()
39            }
40            .unwrap_unchecked()
41        };
42        Ok(contract.resolve_contract_id(&self.locator, network_passphrase)?)
43    }
44
45    async fn invoke_registry(
46        &self,
47        slop: &[&str],
48        fee: Option<&stellar_cli::fee::Args>,
49        view_only: bool,
50    ) -> Result<String, invoke::Error> {
51        invoke_registry(slop, self, fee, view_only).await
52    }
53
54    fn rpc_client(&self) -> Result<rpc::Client, config::Error> {
55        Ok(rpc::Client::new(&self.get_network()?.rpc_url)?)
56    }
57}
58
59pub fn build_invoke_cmd(
60    slop: &[&str],
61    config: &stellar_cli::config::Args,
62    fee: Option<&stellar_cli::fee::Args>,
63    view_only: bool,
64) -> Result<invoke::Cmd, config::Error> {
65    Ok(invoke::Cmd {
66        contract_id: UnresolvedContract::Resolved(config.contract_id()?),
67        slop: slop.iter().map(Into::into).collect(),
68        config: config.clone(),
69        fee: fee.cloned().unwrap_or_default(),
70        send: view_only.then_some(invoke::Send::No).unwrap_or_default(),
71        ..Default::default()
72    })
73}
74
75pub async fn invoke_registry(
76    slop: &[&str],
77    config: &stellar_cli::config::Args,
78    fee: Option<&stellar_cli::fee::Args>,
79    view_only: bool,
80) -> Result<String, invoke::Error> {
81    Ok(build_invoke_cmd(slop, config, fee, view_only)?
82        .run_against_rpc_server(Some(&stellar_cli::commands::global::Args::default()), None)
83        .await?
84        .into_result()
85        .expect("Failed to parse JSON"))
86}
87
88pub fn stellar_address() -> stellar_strkey::ed25519::PublicKey {
89    "GAMPJROHOAW662FINQ4XQOY2ULX5IEGYXCI4SMZYE75EHQBR6PSTJG3M"
90        .parse()
91        .unwrap()
92}
93
94pub fn contract_id(network_passphrase: &str) -> stellar_strkey::Contract {
95    let network_id = xdr::Hash(Sha256::digest(network_passphrase.as_bytes()).into());
96    let preimage = xdr::HashIdPreimage::ContractId(xdr::HashIdPreimageContractId {
97        network_id,
98        contract_id_preimage: xdr::ContractIdPreimage::Address(
99            xdr::ContractIdPreimageFromAddress {
100                address: xdr::ScAddress::Account(xdr::AccountId(
101                    xdr::PublicKey::PublicKeyTypeEd25519(stellar_address().0.into()),
102                )),
103                salt: xdr::Uint256([0; 32]),
104            },
105        ),
106    });
107    let preimage_xdr = preimage
108        .to_xdr(xdr::Limits::none())
109        .expect("HashIdPreimage should not fail encoding to xdr");
110    stellar_strkey::Contract(Sha256::digest(preimage_xdr).into())
111}
112
113#[cfg(test)]
114mod generate_id {
115    use stellar_cli::config::network::passphrase::*;
116
117    fn test_contract_id((passphrase, contract_id): (&str, &str)) {
118        assert_eq!(
119            &super::contract_id(passphrase).to_string(),
120            contract_id,
121            "{passphrase}"
122        );
123    }
124    #[test]
125    fn futurenet() {
126        test_contract_id((
127            FUTURENET,
128            "CACPZCQSLEGF6QOSBF42X6LOUQXQB2EJRDKNKQO6US6ZZH5FD6EB325M",
129        ));
130    }
131
132    #[test]
133    fn testnet() {
134        test_contract_id((
135            TESTNET,
136            "CBCOGWBDGBFWR5LQFKRQUPFIG6OLOON35PBKUPB6C542DFZI3OMBOGHX",
137        ));
138    }
139
140    #[test]
141    fn mainnet() {
142        test_contract_id((
143            MAINNET,
144            "CC3SILHAJ5O75KMSJ5J6I5HV753OTPWEVMZUYHS4QEM2ZTISQRAOMMF4",
145        ));
146    }
147
148    #[test]
149    fn local() {
150        test_contract_id((
151            LOCAL,
152            "CCMHAZ6QTUUF2W4PUBW5BAI6R75BVKIUVHJU6IBQTWCS5RBASDOKHF7T",
153        ));
154    }
155}