dash_sdk/platform/transition/
top_up_address.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use super::address_inputs::collect_address_infos_from_proof;
4use super::broadcast::BroadcastStateTransition;
5use super::put_settings::PutSettings;
6use super::validation::ensure_valid_state_transition_structure;
7use crate::{Error, Sdk};
8use dpp::address_funds::{AddressFundsFeeStrategy, PlatformAddress};
9use dpp::dashcore::PrivateKey;
10use dpp::errors::consensus::basic::state_transition::TransitionNoOutputsError;
11use dpp::fee::Credits;
12use dpp::identity::signer::Signer;
13use dpp::prelude::{AddressNonce, AssetLockProof, UserFeeIncrease};
14use dpp::state_transition::address_funding_from_asset_lock_transition::methods::AddressFundingFromAssetLockTransitionMethodsV0;
15use dpp::state_transition::address_funding_from_asset_lock_transition::AddressFundingFromAssetLockTransition;
16use dpp::state_transition::proof_result::StateTransitionProofResult;
17use dpp::state_transition::StateTransition;
18use dpp::ProtocolError;
19use drive_proof_verifier::types::AddressInfos;
20
21/// Trait for topping up Platform addresses using various funding sources.
22#[async_trait::async_trait]
23pub trait TopUpAddress<S: Signer<PlatformAddress>> {
24    /// Tops up addresses using the provided funding source and fee strategy.
25    ///
26    /// Returns proof-backed [`AddressInfos`] for the funded addresses.
27    async fn top_up(
28        &self,
29        sdk: &Sdk,
30        asset_lock_proof: AssetLockProof,
31        asset_lock_private_key: PrivateKey,
32        fee_strategy: AddressFundsFeeStrategy,
33        signer: &S,
34        settings: Option<PutSettings>,
35    ) -> Result<AddressInfos, Error>;
36}
37
38pub type AddressWithBalance = (PlatformAddress, Option<Credits>);
39pub type AddressesWithBalances = BTreeMap<PlatformAddress, Option<Credits>>;
40
41#[async_trait::async_trait]
42impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressWithBalance
43where
44    BTreeMap<PlatformAddress, Option<Credits>>: TopUpAddress<S>,
45{
46    async fn top_up(
47        &self,
48        sdk: &Sdk,
49        asset_lock_proof: AssetLockProof,
50        asset_lock_private_key: PrivateKey,
51        fee_strategy: AddressFundsFeeStrategy,
52        signer: &S,
53        settings: Option<PutSettings>,
54    ) -> Result<AddressInfos, Error> {
55        BTreeMap::from([(self.0, self.1)])
56            .top_up(
57                sdk,
58                asset_lock_proof,
59                asset_lock_private_key,
60                fee_strategy,
61                signer,
62                settings,
63            )
64            .await
65    }
66}
67
68#[async_trait::async_trait]
69impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressesWithBalances {
70    async fn top_up(
71        &self,
72        sdk: &Sdk,
73        asset_lock_proof: AssetLockProof,
74        asset_lock_private_key: PrivateKey,
75        fee_strategy: AddressFundsFeeStrategy,
76        signer: &S,
77        settings: Option<PutSettings>,
78    ) -> Result<AddressInfos, Error> {
79        if self.is_empty() {
80            return Err(Error::from(TransitionNoOutputsError::new()));
81        }
82
83        let user_fee_increase = settings
84            .as_ref()
85            .and_then(|settings| settings.user_fee_increase)
86            .unwrap_or_default();
87
88        let state_transition = create_address_funding_from_asset_lock_transition(
89            asset_lock_proof,
90            asset_lock_private_key.inner.as_ref(),
91            BTreeMap::new(),
92            self.clone(),
93            fee_strategy,
94            signer,
95            user_fee_increase,
96            sdk,
97        )?;
98
99        ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
100        let st_result = state_transition
101            .broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
102            .await?;
103        match st_result {
104            StateTransitionProofResult::VerifiedAddressInfos(address_infos) => {
105                let expected_addresses =
106                    self.keys().copied().collect::<BTreeSet<PlatformAddress>>();
107                collect_address_infos_from_proof(address_infos, &expected_addresses)
108            }
109            other => Err(Error::InvalidProvedResponse(format!(
110                "address info proof was expected for {:?}, but received {:?}",
111                state_transition, other
112            ))),
113        }
114    }
115}
116
117#[allow(clippy::too_many_arguments)]
118fn create_address_funding_from_asset_lock_transition<S: Signer<PlatformAddress>>(
119    asset_lock_proof: AssetLockProof,
120    asset_lock_private_key: &[u8],
121    inputs: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
122    outputs: BTreeMap<PlatformAddress, Option<Credits>>,
123    fee_strategy: AddressFundsFeeStrategy,
124    signer: &S,
125    user_fee_increase: UserFeeIncrease,
126    sdk: &Sdk,
127) -> Result<StateTransition, ProtocolError> {
128    AddressFundingFromAssetLockTransition::try_from_asset_lock_with_signer(
129        asset_lock_proof,
130        asset_lock_private_key,
131        inputs,
132        outputs,
133        fee_strategy,
134        signer,
135        user_fee_increase,
136        sdk.version(),
137    )
138}