nym_credential_proxy_lib/shared_state/
nyxd_client.rs1use crate::error::CredentialProxyError;
5use crate::helpers::LockTimer;
6use nym_ecash_contract_common::msg::ExecuteMsg;
7use nym_validator_client::nyxd::contract_traits::NymContractsProvider;
8use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult;
9use nym_validator_client::nyxd::{Coin, Config, CosmWasmClient, NyxdClient};
10use nym_validator_client::{DirectSigningHttpRpcNyxdClient, nyxd};
11use std::ops::Deref;
12use std::sync::Arc;
13use std::time::Duration;
14use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
15use tracing::{instrument, warn};
16
17#[derive(Clone)]
18pub struct ChainClient(Arc<RwLock<DirectSigningHttpRpcNyxdClient>>);
19
20impl ChainClient {
21 pub fn new(mnemonic: bip39::Mnemonic) -> Result<Self, CredentialProxyError> {
22 let network_details = nym_network_defaults::NymNetworkDetails::new_from_env();
23 let client_config = nyxd::Config::try_from_nym_network_details(&network_details)?;
24
25 let nyxd_url = network_details
26 .endpoints
27 .first()
28 .ok_or_else(|| CredentialProxyError::NoNyxEndpointsAvailable)?
29 .nyxd_url
30 .as_str();
31
32 Self::new_with_config(client_config, nyxd_url, mnemonic)
33 }
34
35 pub fn new_with_config(
36 client_config: Config,
37 nyxd_url: &str,
38 mnemonic: bip39::Mnemonic,
39 ) -> Result<Self, CredentialProxyError> {
40 let client = NyxdClient::connect_with_mnemonic(client_config, nyxd_url, mnemonic)?;
41
42 if client.ecash_contract_address().is_none() {
43 return Err(CredentialProxyError::UnavailableEcashContract);
44 }
45
46 if client.dkg_contract_address().is_none() {
47 return Err(CredentialProxyError::UnavailableDKGContract);
48 }
49
50 Ok(ChainClient(Arc::new(RwLock::new(client))))
51 }
52
53 pub async fn query_chain(&self) -> ChainReadPermit<'_> {
54 let _acquire_timer = LockTimer::new("acquire chain query permit");
55 self.0.read().await
56 }
57
58 pub async fn start_chain_tx(&self) -> ChainWritePermit<'_> {
59 let _acquire_timer = LockTimer::new("acquire exclusive chain write permit");
60
61 ChainWritePermit {
62 lock_timer: LockTimer::new("exclusive chain access permit"),
63 inner: self.0.write().await,
64 }
65 }
66}
67
68pub type ChainReadPermit<'a> = RwLockReadGuard<'a, DirectSigningHttpRpcNyxdClient>;
69
70pub struct ChainWritePermit<'a> {
72 #[allow(dead_code)]
74 lock_timer: LockTimer,
75 inner: RwLockWriteGuard<'a, DirectSigningHttpRpcNyxdClient>,
76}
77
78impl ChainWritePermit<'_> {
79 #[instrument(skip(self, memo, info), err(Display))]
80 pub async fn make_deposits(
81 self,
82 memo: String,
83 info: Vec<(String, Coin)>,
84 ) -> Result<ExecuteResult, CredentialProxyError> {
85 let address = self.inner.address();
86 let starting_sequence = self.inner.get_sequence(&address).await?.sequence;
87
88 let ecash_contract = self
89 .inner
90 .ecash_contract_address()
91 .ok_or(CredentialProxyError::UnavailableEcashContract)?;
92 let deposit_messages = info
93 .into_iter()
94 .map(|(identity_key, amount)| {
95 (
96 ExecuteMsg::DepositTicketBookFunds { identity_key },
97 vec![amount],
98 )
99 })
100 .collect::<Vec<_>>();
101
102 let res = self
103 .inner
104 .execute_multiple(ecash_contract, deposit_messages, None, memo)
105 .await?;
106
107 loop {
108 let updated_sequence = self.inner.get_sequence(&address).await?.sequence;
109
110 if updated_sequence > starting_sequence {
111 break;
112 }
113 warn!("wrong sequence number... waiting before releasing chain lock");
114 tokio::time::sleep(Duration::from_millis(50)).await;
115 }
116
117 Ok(res)
118 }
119}
120
121impl Deref for ChainWritePermit<'_> {
122 type Target = DirectSigningHttpRpcNyxdClient;
123
124 fn deref(&self) -> &Self::Target {
125 self.inner.deref()
126 }
127}