dynamic-waas-sdk 0.0.2

Rust SDK for Dynamic Labs WaaS — create and manage MPC wallets from a backend service. v1 stateless contract.
Documentation
//! Customer-side delegated wallet client.
//!
//! Mirrors `@dynamic-labs-wallet/node`'s `DelegatedWalletClient`. Same
//! MPC relay flow as [`DynamicWalletClient`], but auth is a per-wallet
//! delegated API key (sourced from a Dynamic webhook) instead of an
//! org-scoped JWT. Single-phase construction — the wallet API key IS
//! the credential, so no token-exchange step is required.
//!
//! Operations are implemented as free functions that take a
//! [`DynamicWalletClient`] reference (see `run_keygen`, `run_sign_ecdsa`,
//! etc.). The delegated client exposes its inner client via
//! [`Self::inner`] so the same orchestration helpers can be reused.
//!
//! [`DynamicWalletClient`]: crate::DynamicWalletClient

use dynamic_waas_sdk_core::Result;
use tracing::{debug, instrument};

use crate::client::{DynamicWalletClient, DynamicWalletClientOpts};

/// Construction options for [`DelegatedWalletClient`].
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct DelegatedWalletClientOpts {
    /// The Dynamic environment id (UUID) this client targets.
    pub environment_id: String,
    /// Per-wallet delegated API key, decrypted from a webhook payload
    /// via [`decrypt_delegated_webhook_data`]. Used as the
    /// `x-dyn-wallet-api-key` request header.
    ///
    /// [`decrypt_delegated_webhook_data`]: crate::decrypt_delegated_webhook_data
    pub wallet_api_key: String,
    /// Override the Dynamic API base URL. Defaults to production.
    pub base_api_url: Option<String>,
    /// Override the MPC relay URL. Defaults to the value matching
    /// `base_api_url`'s detected environment.
    pub base_mpc_relay_url: Option<String>,
}

impl DelegatedWalletClientOpts {
    pub fn new(environment_id: impl Into<String>, wallet_api_key: impl Into<String>) -> Self {
        Self {
            environment_id: environment_id.into(),
            wallet_api_key: wallet_api_key.into(),
            base_api_url: None,
            base_mpc_relay_url: None,
        }
    }

    #[must_use]
    pub fn base_api_url(mut self, url: impl Into<String>) -> Self {
        self.base_api_url = Some(url.into());
        self
    }

    #[must_use]
    pub fn base_mpc_relay_url(mut self, url: impl Into<String>) -> Self {
        self.base_mpc_relay_url = Some(url.into());
        self
    }
}

/// Customer-side delegated `WaaS` client. Acts on behalf of one end-user
/// wallet at a time using a delegated API key sourced from a webhook.
///
/// Wraps a [`DynamicWalletClient`] configured in delegated mode — pass
/// [`Self::inner`] into the existing `run_keygen` / `run_sign_*` /
/// `run_export_*` orchestration helpers exactly like with the server SDK.
///
/// [`DynamicWalletClient`]: crate::DynamicWalletClient
pub struct DelegatedWalletClient {
    inner: DynamicWalletClient,
}

impl DelegatedWalletClient {
    /// Construct a fully-authenticated delegated client. There is no
    /// separate `authenticate_*` call — the `wallet_api_key` provided
    /// in `opts` is the credential.
    pub fn new(opts: DelegatedWalletClientOpts) -> Result<Self> {
        let wallet_api_key = opts.wallet_api_key.clone();
        let inner_opts = DynamicWalletClientOpts {
            environment_id: opts.environment_id,
            base_api_url: opts.base_api_url,
            base_mpc_relay_url: opts.base_mpc_relay_url,
        };
        let inner = DynamicWalletClient::new_delegated(inner_opts, wallet_api_key)?;
        Ok(Self { inner })
    }

    /// Borrow the underlying [`DynamicWalletClient`]. Pass this into
    /// orchestration helpers (e.g. `run_sign_ecdsa(client.inner(), opts)`).
    ///
    /// [`DynamicWalletClient`]: crate::DynamicWalletClient
    pub fn inner(&self) -> &DynamicWalletClient {
        &self.inner
    }

    /// Dynamic environment id this client is configured for.
    pub fn environment_id(&self) -> &str {
        self.inner.environment_id()
    }

    /// Revoke the delegation for the given wallet. After this call the
    /// wallet API key carried by this client will no longer authenticate
    /// signing requests on Dynamic's side.
    ///
    /// **Stubbed for v0.2.0** — the matching Node SDK endpoint is also
    /// stubbed and currently logs without hitting the API. Once the
    /// canonical endpoint lands on the Node side this will be wired
    /// through `ApiClient`.
    #[instrument(skip(self), level = "debug")]
    pub async fn revoke_delegation(&self, wallet_id: &str) -> Result<()> {
        debug!(
            wallet_id,
            "delegation revoke requested — endpoint is a stub, no API call made"
        );
        Ok(())
    }
}