renegade_sdk/renegade_wallet_client/actions/
deposit.rs

1//! Deposit into an account balance
2
3use alloy::primitives::Address;
4use renegade_circuit_types::Amount;
5use renegade_crypto::fields::scalar_to_u256;
6use renegade_darkpool_types::balance::DarkpoolBalance;
7use renegade_darkpool_types::balance::DarkpoolStateBalance;
8use renegade_external_api::{
9    http::balance::{DEPOSIT_BALANCE_ROUTE, DepositBalanceRequest, DepositBalanceResponse},
10    types::ApiDepositPermit,
11};
12use renegade_solidity_abi::v2::{
13    IDarkpoolV2, relayer_types::u128_to_u256, transfer_auth::deposit::create_deposit_permit,
14};
15
16use crate::{
17    RenegadeClientError,
18    actions::{NON_BLOCKING_PARAM, construct_http_path},
19    client::RenegadeClient,
20    websocket::{DEFAULT_TASK_TIMEOUT, TaskWaiter},
21};
22
23// --- Public Actions --- //
24impl RenegadeClient {
25    /// Deposit funds into an account balance. Waits for the deposit task to
26    /// complete before returning the post-deposit balance.
27    pub async fn deposit(&self, mint: Address, amount: Amount) -> Result<(), RenegadeClientError> {
28        let request = self.build_deposit_request(mint, amount).await?;
29
30        let path = self.build_deposit_request_path(mint, false)?;
31
32        self.relayer_client.post::<_, DepositBalanceResponse>(&path, request).await?;
33
34        Ok(())
35    }
36
37    /// Enqueues a deposit task in the relayer. Returns the post-deposit
38    /// balance, and a `TaskWaiter` that can be used to await task
39    /// completion.
40    pub async fn enqueue_deposit(
41        &self,
42        mint: Address,
43        amount: Amount,
44    ) -> Result<TaskWaiter, RenegadeClientError> {
45        let request = self.build_deposit_request(mint, amount).await?;
46
47        let path = self.build_deposit_request_path(mint, false)?;
48
49        let DepositBalanceResponse { task_id, .. } =
50            self.relayer_client.post(&path, request).await?;
51
52        let task_waiter = self.watch_task(task_id, DEFAULT_TASK_TIMEOUT).await?;
53
54        Ok(task_waiter)
55    }
56}
57
58// --- Private Helpers --- //
59impl RenegadeClient {
60    /// Builds the request to deposit a balance
61    async fn build_deposit_request(
62        &self,
63        mint: Address,
64        amount: Amount,
65    ) -> Result<DepositBalanceRequest, RenegadeClientError> {
66        let permit = self.build_deposit_permit(mint, amount).await?;
67
68        let from_address = self.get_account_address();
69        let authority = self.get_schnorr_public_key().into();
70
71        Ok(DepositBalanceRequest { from_address, amount, authority, permit })
72    }
73
74    /// Builds the request to deposit a balance
75    async fn build_deposit_permit(
76        &self,
77        mint: Address,
78        amount: Amount,
79    ) -> Result<ApiDepositPermit, RenegadeClientError> {
80        // First, we check if a balance already exists for the token being deposited
81        let existing_balance = self.get_balance_by_mint(mint).await;
82
83        let state_balance = if let Ok(balance) = existing_balance {
84            // If a balance already exists for the token being deposited,
85            // we update its amount, progressing its cryptographic state accordingly.
86
87            let mut state_balance: DarkpoolStateBalance =
88                crate::renegade_wallet_client::conversions::api_balance_to_state_balance(balance)?;
89
90            state_balance.inner.amount += amount;
91            let new_amount = state_balance.inner.amount;
92            let new_amount_public_share = state_balance.stream_cipher_encrypt(&new_amount);
93            state_balance.public_share.amount = new_amount_public_share;
94
95            state_balance.compute_recovery_id();
96
97            state_balance
98        } else {
99            // If this is a deposit into a new balance, we create the balance state object &
100            // progress its cryptographic state accordingly.
101
102            let balance = DarkpoolBalance::new(
103                mint,
104                self.get_account_address(),
105                self.get_relayer_fee_recipient(),
106                self.get_schnorr_public_key(),
107            )
108            .with_amount(amount);
109
110            let (mut recovery_seed_csprng, mut share_seed_csprng) =
111                self.get_account_seeds().await?;
112
113            let balance_recovery_stream_seed = recovery_seed_csprng.next().unwrap();
114            let balance_share_stream_seed = share_seed_csprng.next().unwrap();
115
116            let mut state_balance = DarkpoolStateBalance::new(
117                balance,
118                balance_share_stream_seed,
119                balance_recovery_stream_seed,
120            );
121
122            state_balance.compute_recovery_id();
123
124            state_balance
125        };
126
127        let commitment = scalar_to_u256(&state_balance.compute_commitment());
128
129        let deposit = IDarkpoolV2::Deposit {
130            from: self.get_account_address(),
131            token: mint,
132            amount: u128_to_u256(amount),
133        };
134
135        let (witness, signature) = create_deposit_permit(
136            commitment,
137            deposit,
138            self.get_chain_id(),
139            self.get_darkpool_address(),
140            self.get_permit2_address(),
141            self.get_account_signer(),
142        )
143        .map_err(RenegadeClientError::signing)?;
144
145        Ok(ApiDepositPermit {
146            nonce: witness.nonce,
147            deadline: witness.deadline,
148            signature: signature.as_bytes().to_vec(),
149        })
150    }
151
152    /// Builds the request path for the deposit balance endpoint
153    fn build_deposit_request_path(
154        &self,
155        mint: Address,
156        non_blocking: bool,
157    ) -> Result<String, RenegadeClientError> {
158        let path = construct_http_path!(DEPOSIT_BALANCE_ROUTE, "account_id" => self.get_account_id(), "mint" => mint);
159        let query_string =
160            serde_urlencoded::to_string(&[(NON_BLOCKING_PARAM, non_blocking.to_string())])
161                .map_err(RenegadeClientError::serde)?;
162
163        Ok(format!("{path}?{query_string}"))
164    }
165}