-
Notifications
You must be signed in to change notification settings - Fork 57
feat(sdk): address funding fee quote client #4446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/address-funding-fee-quote-query
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| //! State-aware funding fee quote — a thin wrapper over the SDK's | ||
| //! `getAddressFundingFeeQuote` call using the wallet's own SDK handle. | ||
|
|
||
| use super::wallet::PlatformAddressWallet; | ||
| use crate::error::PlatformWalletError; | ||
| use dash_sdk::platform::address_funding_fee_quote::{ | ||
| quote_address_funding_fee, AddressFundingFeeQuote, AddressFundingFeeQuoteQuery, | ||
| }; | ||
| use dpp::address_funds::PlatformAddress; | ||
| use dpp::prelude::UserFeeIncrease; | ||
|
|
||
| impl PlatformAddressWallet { | ||
| /// Fetches a state-aware fee quote for funding `recipient` with a fresh | ||
| /// asset lock (0 address inputs, 1 remainder output). | ||
| /// | ||
| /// `prepared_outpoint` carries the exact outpoint when the wallet has | ||
| /// already built and signed the lock transaction; `None` lets the node | ||
| /// use a deterministic placeholder with the same expected search depth. | ||
| /// | ||
| /// The quote is planning data from a single node (no proof): sizing the | ||
| /// lock stays governed by `minimum_required_lock_credits` plus the | ||
| /// application's own margin policy. There is no offline fallback — a | ||
| /// network failure surfaces as an error. | ||
| pub async fn quote_funding_fee( | ||
| &self, | ||
| recipient: PlatformAddress, | ||
| prepared_outpoint: Option<[u8; 36]>, | ||
| user_fee_increase: UserFeeIncrease, | ||
| ) -> Result<AddressFundingFeeQuote, PlatformWalletError> { | ||
| Ok(quote_address_funding_fee( | ||
| &self.sdk, | ||
| AddressFundingFeeQuoteQuery { | ||
| recipient, | ||
| asset_lock_outpoint: prepared_outpoint, | ||
| user_fee_increase, | ||
| signable_bytes_len_hint: None, | ||
| }, | ||
| ) | ||
| .await?) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,7 +34,8 @@ use drive_proof_verifier::types::token_info::{IdentitiesTokenInfos, IdentityToke | |
| use drive_proof_verifier::types::token_status::TokenStatuses; | ||
| use drive::grovedb::GroveTrunkQueryResult; | ||
| use drive_proof_verifier::types::{ | ||
| AddressInfo, Contenders, ContestedResources, CurrentQuorumsInfo, ElementFetchRequestItem, | ||
| AddressFundingFeeQuote, AddressInfo, Contenders, ContestedResources, CurrentQuorumsInfo, | ||
| ElementFetchRequestItem, | ||
| IdentityBalanceAndRevision, IndexMap, MasternodeProtocolVote, MostRecentShieldedAnchor, | ||
| PlatformAddressTrunkState, PrefundedSpecializedBalance, ProposerBlockCounts, | ||
| RecentAddressBalanceChanges, RecentCompactedAddressBalanceChanges, RetrievedValues, | ||
|
|
@@ -502,6 +503,7 @@ impl_mock_response!(TotalCreditsInPlatform); | |
| impl_mock_response!(ElementFetchRequestItem); | ||
| impl_mock_response!(EvoNodeStatus); | ||
| impl_mock_response!(CurrentQuorumsInfo); | ||
| impl_mock_response!(AddressFundingFeeQuote); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: MockResponse support is not connected to FetchUnproved The new serialization implementation satisfies the source: ['codex'] |
||
| impl_mock_response!(Group); | ||
| impl_mock_response!(TokenPricingSchedule); | ||
| impl_mock_response!(RewardDistributionMoment); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| //! A state-aware fee quote for a 0-input / 1-output address funding from a | ||
| //! fresh asset lock, served by the node's `getAddressFundingFeeQuote` query. | ||
| //! | ||
| //! The node prices the exact production operations with tree depths measured | ||
| //! from its committed state, adds the validation-operation fees execution | ||
| //! records, and applies the requested user fee increase. The response is a | ||
| //! computed value, not state — it carries no proof, so treat the quote as | ||
| //! planning data: sizing a funding lock stays governed by | ||
| //! `minimum_required_lock_credits` plus the wallet's own margin policy. | ||
|
|
||
| use crate::platform::proto; | ||
| use crate::platform::query::Query; | ||
| use crate::platform::QuerySettings; | ||
| use crate::{error::Error, Sdk}; | ||
| use dapi_grpc::platform::v0::GetAddressFundingFeeQuoteRequest; | ||
| use dpp::address_funds::PlatformAddress; | ||
| use dpp::prelude::UserFeeIncrease; | ||
| use dpp::version::PlatformVersion; | ||
| pub use drive_proof_verifier::types::AddressFundingFeeQuote; | ||
| use rs_dapi_client::RequestSettings; | ||
|
|
||
| use crate::platform::FetchUnproved; | ||
|
|
||
| /// Parameters of an address funding fee quote. | ||
| #[derive(Debug, Clone)] | ||
| pub struct AddressFundingFeeQuoteQuery { | ||
| /// The funding recipient. | ||
| pub recipient: PlatformAddress, | ||
| /// The exact planned asset lock outpoint (txid bytes followed by the | ||
| /// vout as four little-endian bytes), when the wallet has already built | ||
| /// and signed the lock transaction. `None` lets the node derive a | ||
| /// deterministic placeholder — for a fresh (absent) outpoint both have | ||
| /// the same expected search depth. | ||
| pub asset_lock_outpoint: Option<[u8; 36]>, | ||
|
Comment on lines
+29
to
+34
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: Keep asset-lock outpoints typed until wire encoding The public query exposes an asset-lock outpoint as source: ['codex'] |
||
| /// The user fee increase the quote should include; the SDK's chain-lock | ||
| /// retry loop can raise a funding up to 14 units above the base. | ||
| pub user_fee_increase: UserFeeIncrease, | ||
| /// Length of the future transition's signable bytes when known; `None` | ||
| /// uses the node's default. Clamped server-side, so it cannot understate | ||
| /// the fee. | ||
| pub signable_bytes_len_hint: Option<u32>, | ||
| } | ||
|
|
||
| impl Query<GetAddressFundingFeeQuoteRequest> for AddressFundingFeeQuoteQuery { | ||
| fn query( | ||
| &self, | ||
| _settings: &QuerySettings<'_>, | ||
| ) -> Result<GetAddressFundingFeeQuoteRequest, Error> { | ||
| Ok(GetAddressFundingFeeQuoteRequest { | ||
| version: Some(proto::get_address_funding_fee_quote_request::Version::V0( | ||
| proto::get_address_funding_fee_quote_request::GetAddressFundingFeeQuoteRequestV0 { | ||
| address: self.recipient.to_bytes(), | ||
| asset_lock_outpoint: self | ||
| .asset_lock_outpoint | ||
| .map(|outpoint| outpoint.to_vec()) | ||
| .unwrap_or_default(), | ||
| user_fee_increase: self.user_fee_increase as u32, | ||
| signable_bytes_len_hint: self.signable_bytes_len_hint.unwrap_or_default(), | ||
| }, | ||
| )), | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| /// Fetches a state-aware address funding fee quote from the network. | ||
| /// | ||
| /// Fails with a protocol error when the node quoted with a protocol version | ||
| /// this client does not know — a quote priced under unknown rules must not be | ||
| /// displayed as if it were understood. | ||
| pub async fn quote_address_funding_fee( | ||
| sdk: &Sdk, | ||
| query: AddressFundingFeeQuoteQuery, | ||
| ) -> Result<AddressFundingFeeQuote, Error> { | ||
| let (quote, _metadata) = AddressFundingFeeQuote::fetch_unproved_with_settings( | ||
| sdk, | ||
| query, | ||
| RequestSettings::default(), | ||
| ) | ||
| .await?; | ||
| let quote = quote.ok_or_else(|| { | ||
| Error::Generic("address funding fee quote response carried no data".to_string()) | ||
| })?; | ||
|
|
||
| // Fail fast on a version this client doesn't know. | ||
| PlatformVersion::get(quote.protocol_version).map_err(dpp::ProtocolError::from)?; | ||
|
|
||
| Ok(quote) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| fn test_settings(request_settings: &RequestSettings) -> QuerySettings<'_> { | ||
| QuerySettings { | ||
| request_settings, | ||
| protocol_version: PlatformVersion::latest(), | ||
| prove: false, | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_query_maps_placeholder_and_exact_outpoint() { | ||
| let recipient = PlatformAddress::P2pkh([7; 20]); | ||
| let request_settings = RequestSettings::default(); | ||
| let settings = test_settings(&request_settings); | ||
|
|
||
| let placeholder = AddressFundingFeeQuoteQuery { | ||
| recipient, | ||
| asset_lock_outpoint: None, | ||
| user_fee_increase: 3, | ||
| signable_bytes_len_hint: None, | ||
| }; | ||
| let request = placeholder.query(&settings).expect("query"); | ||
| let Some(proto::get_address_funding_fee_quote_request::Version::V0(v0)) = request.version | ||
| else { | ||
| panic!("expected V0 request"); | ||
| }; | ||
| assert_eq!(v0.address, recipient.to_bytes()); | ||
| assert!(v0.asset_lock_outpoint.is_empty(), "placeholder sends empty"); | ||
| assert_eq!(v0.user_fee_increase, 3); | ||
| assert_eq!(v0.signable_bytes_len_hint, 0, "server default"); | ||
|
|
||
| let exact = AddressFundingFeeQuoteQuery { | ||
| recipient, | ||
| asset_lock_outpoint: Some([0xAB; 36]), | ||
| user_fee_increase: 0, | ||
| signable_bytes_len_hint: Some(390), | ||
| }; | ||
| let request = exact.query(&settings).expect("query"); | ||
| let Some(proto::get_address_funding_fee_quote_request::Version::V0(v0)) = request.version | ||
| else { | ||
| panic!("expected V0 request"); | ||
| }; | ||
| assert_eq!(v0.asset_lock_outpoint, vec![0xAB; 36]); | ||
| assert_eq!(v0.signable_bytes_len_hint, 390); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Suggestion: Wallet quotes always discard a known signable-length hint
The server uses a calibrated 390-byte default but explicitly supports the real signable length for larger funding transactions. This wrapper always sends
None, even though its documentedprepared_outpointcase represents an already-built and signed lock whose caller may know the future transition size. Expose an optionalsignable_bytes_len_hintparameter, or provide a wallet method accepting a fully specifiedAddressFundingFeeQuoteQuery, so callers can request the more accurate hashing charge.source: ['codex']
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in this update — Wallet quotes always discard a known signable-length hint no longer present.
Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.