Skip to main content

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        .await?;
99
100        ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
101        let st_result = state_transition
102            .broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
103            .await?;
104        match st_result {
105            StateTransitionProofResult::VerifiedAddressInfos(address_infos) => {
106                let expected_addresses =
107                    self.keys().copied().collect::<BTreeSet<PlatformAddress>>();
108                collect_address_infos_from_proof(address_infos, &expected_addresses)
109            }
110            other => Err(Error::InvalidProvedResponse(format!(
111                "address info proof was expected for {:?}, but received {:?}",
112                state_transition, other
113            ))),
114        }
115    }
116}
117
118#[allow(clippy::too_many_arguments)]
119async fn create_address_funding_from_asset_lock_transition<S: Signer<PlatformAddress>>(
120    asset_lock_proof: AssetLockProof,
121    asset_lock_private_key: &[u8],
122    inputs: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
123    outputs: BTreeMap<PlatformAddress, Option<Credits>>,
124    fee_strategy: AddressFundsFeeStrategy,
125    signer: &S,
126    user_fee_increase: UserFeeIncrease,
127    sdk: &Sdk,
128) -> Result<StateTransition, ProtocolError> {
129    AddressFundingFromAssetLockTransition::try_from_asset_lock_with_signer(
130        asset_lock_proof,
131        asset_lock_private_key,
132        inputs,
133        outputs,
134        fee_strategy,
135        signer,
136        user_fee_increase,
137        sdk.version(),
138    )
139    .await
140}