Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4891bd4
docs(dashpay): DIP-13 invitations spec (reviewed + synced)
shumkov Jul 8, 2026
1d8b490
feat(platform-wallet): DIP-13 invitation link codec
shumkov Jul 8, 2026
c60b3e3
feat(platform-wallet): path-gated invitation voucher-key export
shumkov Jul 8, 2026
9e40ee8
feat(platform-wallet): invitation create + claim flows
shumkov Jul 8, 2026
5039946
fix(platform-wallet): fold invitation Rust-core review findings
shumkov Jul 8, 2026
6a730a9
test(qa): add DIP-13 invitation scenarios to the example-app QA contract
shumkov Jul 8, 2026
f59edf6
feat(platform-wallet): invitation create + claim FFI
shumkov Jul 8, 2026
55c9537
fix(platform-wallet): scrub the exported voucher scalar in create_inv…
shumkov Jul 8, 2026
4d39bdc
feat(swift-sdk): createInvitation / claimInvitation wrappers on Manag…
shumkov Jul 8, 2026
2b2a871
feat(platform-wallet): persist inviter-side invitation records (Rust …
shumkov Jul 8, 2026
8fd0613
feat(swift-sdk): add DashPay create-invitation UI (DIP-13)
shumkov Jul 8, 2026
bd60102
feat(platform-wallet-ffi): add parse-invitation preview FFI + Swift w…
shumkov Jul 8, 2026
5967661
feat(swift-sdk): add DashPay claim-invitation flow (DIP-13)
shumkov Jul 8, 2026
4e0dfbe
feat(swift-sdk): handle dashpay:// invitation deep links
shumkov Jul 8, 2026
01cd566
test(platform-wallet-storage): allow-list invitations.rs read-only re…
shumkov Jul 8, 2026
a50a806
fix(swift-sdk): present invitation claim sheet on cold-launch deep link
shumkov Jul 8, 2026
0b51001
fix(rs-sdk-ffi): enforce fully-hardened invitation export path
shumkov Jul 8, 2026
2d568b7
fix(platform-wallet): fold invitation review findings
shumkov Jul 8, 2026
c2ddfcf
docs(dip15): sync spec §4.1/§6 with as-built code + note scheme limit…
shumkov Jul 8, 2026
a001d26
fix(swift-sdk): re-present invitation claim sheet on a second deep link
shumkov Jul 8, 2026
10fbc57
fix(platform-wallet): persist asset-lock account used-index across re…
shumkov Jul 10, 2026
74675fc
test(platform-wallet): pin asset-lock funding-index persistence
shumkov Jul 10, 2026
55937e1
fix(platform-wallet): gate invitation broadcast on funding-index persist
shumkov Jul 10, 2026
096e52d
feat(platform-wallet-ffi): topup-from-existing-asset-lock FFI (invita…
shumkov Jul 10, 2026
ce6a04d
feat(platform-wallet-ffi): invitation persistence callback (Sent-invi…
shumkov Jul 10, 2026
4e49286
test(platform-wallet-ffi): pin InvitationEntryFFI ABI size (layout gu…
shumkov Jul 10, 2026
9a6188c
docs(dip15): reviewed Sent-invitations persistence + reclaim spec
shumkov Jul 10, 2026
c1f0c45
feat(swift-sdk): Sent-invitations SwiftData bridge + list (DIP-13)
shumkov Jul 10, 2026
0b12d2d
fix(rs-sdk-ffi): revert over-strict hardened invitation export gate
shumkov Jul 10, 2026
3f498ba
fix(platform-wallet): persist invitation record for non-hardened fund…
shumkov Jul 10, 2026
7b371fe
fix(SwiftExampleApp): add PersistentInvitation to Storage Explorer views
shumkov Jul 10, 2026
4e3a9b0
style(platform-wallet-ffi): sort invitation_persistence re-export
shumkov Jul 10, 2026
30ab23f
Merge branch 'v4.1-dev' into feat/dip15-dashpay-invitations
shumkov Jul 10, 2026
d7ebdaa
feat(swift-sdk): reclaim an unclaimed DashPay invitation as credits
shumkov Jul 10, 2026
3029ffe
feat(platform-wallet): enforce a minimum invitation amount
shumkov Jul 10, 2026
261e2f2
fix(swift-sdk): raise invitation default above the claimable floor
shumkov Jul 10, 2026
d14c02e
fix(swift-sdk): narrow already-consumed classifier to canonical Displ…
shumkov Jul 10, 2026
139a478
fix(swift-sdk): disable Cancel during an in-flight invitation reclaim
shumkov Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
636 changes: 636 additions & 0 deletions docs/dashpay/DIP15_INVITATIONS_SPEC.md

Large diffs are not rendered by default.

294 changes: 294 additions & 0 deletions docs/dashpay/INVITATIONS_PERSISTENCE_SWIFT_SPEC.md

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions packages/rs-platform-wallet-ffi/src/dashpay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,20 @@ impl platform_wallet::ContactCryptoProvider for ResolverContactCryptoProvider {
.map_err(|e| platform_wallet::PlatformWalletError::InvalidIdentityData(e.to_string()))
}

async fn export_invitation_private_key(
&self,
path: &key_wallet::bip32::DerivationPath,
) -> Result<dashcore::secp256k1::SecretKey, platform_wallet::PlatformWalletError> {
let scalar = self
.signer
.export_invitation_private_key(path)
.map_err(|e| {
platform_wallet::PlatformWalletError::InvalidIdentityData(e.to_string())
})?;
dashcore::secp256k1::SecretKey::from_slice(scalar.as_ref())
.map_err(|e| platform_wallet::PlatformWalletError::InvalidIdentityData(e.to_string()))
}

async fn account_reference(
&self,
path: &key_wallet::bip32::DerivationPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

use dashcore::hashes::Hash;
use dpp::identity::accessors::IdentityGettersV0;
use dpp::prelude::Identifier;
use platform_wallet::AssetLockFunding;
use rs_sdk_ffi::{SignerHandle, VTableSigner};

Expand Down Expand Up @@ -241,3 +242,80 @@ pub unsafe extern "C" fn platform_wallet_resume_identity_with_existing_asset_loc
*out_identity_handle = handle;
PlatformWalletFFIResult::ok()
}

/// Top up an EXISTING identity from an already-tracked asset lock — the
/// inviter's "reclaim into an existing identity" path.
///
/// Sister to [`platform_wallet_resume_identity_with_existing_asset_lock_signer`]
/// (which registers a NEW identity from the lock): this consumes the lock as an
/// IdentityTopUp against `identity_id` instead. Used to reclaim an unclaimed
/// DashPay invitation voucher — the value comes back as Platform **credits** on
/// the inviter's own identity (the on-chain DASH is an OP_RETURN burn, so there
/// is nothing to spend back on L1). No per-identity-key signer is needed (a
/// top-up creates no keys); only the Core-side asset-lock signature, produced by
/// the inviter's own resolver, which re-derives the voucher key at the invitation
/// funding path. The `FromExistingAssetLock` resume + IS→CL fallback logic lives
/// in `top_up_identity_with_funding`; this FFI is a thin marshaler.
///
/// # Safety
/// - `out_point` must be a valid, non-null `*const OutPointFFI`; the caller
/// retains ownership.
/// - `identity_id` must point to 32 readable bytes.
/// - `core_signer_handle` must be a valid, non-destroyed
/// `*mut MnemonicResolverHandle`; the caller retains ownership.
/// - `out_new_balance` must be a valid `*mut u64`.
#[no_mangle]
pub unsafe extern "C" fn platform_wallet_topup_identity_with_existing_asset_lock_signer(
wallet_handle: Handle,
out_point: *const OutPointFFI,
identity_id: *const [u8; 32],
core_signer_handle: *mut MnemonicResolverHandle,
out_new_balance: *mut u64,
) -> PlatformWalletFFIResult {
check_ptr!(out_point);
check_ptr!(identity_id);
check_ptr!(core_signer_handle);
check_ptr!(out_new_balance);
// FFI-safe sentinel before any fallible work.
*out_new_balance = 0;

let out_point_ffi = *out_point;
let reclaim_outpoint = dashcore::OutPoint {
txid: dashcore::Txid::from_byte_array(out_point_ffi.txid),
vout: out_point_ffi.vout,
};
let identity_id = Identifier::from(*identity_id);

let core_signer_addr = core_signer_handle as usize;

let option = PLATFORM_WALLET_STORAGE.with_item(wallet_handle, |wallet| {
let identity_wallet = wallet.identity().clone();
let wallet_id = wallet.wallet_id();
let network = wallet.sdk().network;
block_on_worker(async move {
// SAFETY: see the fn-level safety doc — the handle is pinned alive
// for the duration of this FFI call.
let asset_lock_signer = unsafe {
MnemonicResolverCoreSigner::new(
core_signer_addr as *mut MnemonicResolverHandle,
wallet_id,
network,
)
};
identity_wallet
.top_up_identity_with_funding(
&identity_id,
AssetLockFunding::FromExistingAssetLock {
out_point: reclaim_outpoint,
},
&asset_lock_signer,
None,
)
.await
})
});
let result = unwrap_option_or_return!(option);
let new_balance = unwrap_result_or_return!(result);
*out_new_balance = new_balance;
PlatformWalletFFIResult::ok()
}
Loading
Loading