> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dfns.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK development

> Architecture and usage patterns for the DFNS Rust SDK, including client configuration, user action signing, API domains, delegated signing, and error handling.

## Request signing

All state-changing requests made to the DFNS API must be cryptographically signed. `DfnsClient` handles the challenge flow automatically when you configure a signer: it calls `/auth/action/init`, hands you the challenge, and replays your assertion on the real request.

<Info>
  For a detailed explanation of request signing and User Action Challenges, see [Signing requests](/guides/developers/signing-requests).
</Info>

## Client configuration

Both `DfnsClient` and `DfnsDelegatedClient` take the same `Options`:

```rust theme={null}
use dfns_sdk_rust::{DfnsClient, Options};

let client = DfnsClient::new(Options {
    base_url: String::new(),
    auth_token: "your-auth-token".to_string(),
    signer: None,
    http: None,
})?;
```

| Field        | Description                                                                                                                   |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `base_url`   | DFNS API base URL. An empty string defaults to `https://api.dfns.io`.                                                         |
| `auth_token` | Service Account token, Personal Access Token, or user login token. See [Required headers](/api-reference/auth).               |
| `signer`     | `Option<Arc<dyn UserActionSigner>>`. Required for state-changing operations on `DfnsClient`, unused by `DfnsDelegatedClient`. |
| `http`       | `Option<reqwest::Client>` to reuse your own HTTP client (connection pool, proxy, timeouts).                                   |

## Implementing a signer

The SDK does not ship a key signer. It defines the `UserActionSigner` trait and leaves the credential-specific crypto to you, so the same client works with a raw key, an HSM, or a KMS:

```rust theme={null}
use async_trait::async_trait;
use dfns_sdk_rust::error::Error;
use dfns_sdk_rust::signer::{
    CredentialAssertion, CredentialAssertionData, UserActionChallenge, UserActionSigner,
};

struct MyKeySigner {
    cred_id: String,
    // private key material, HSM handle, KMS client, ...
}

#[async_trait]
impl UserActionSigner for MyKeySigner {
    async fn sign(&self, challenge: &UserActionChallenge) -> Result<CredentialAssertion, Error> {
        // 1. Build the clientData JSON for `challenge.challenge` and base64url-encode it.
        // 2. Sign the clientData bytes with your credential's private key.
        // 3. Return the assertion, with the signature base64url-encoded.
        Ok(CredentialAssertion {
            kind: "Key".to_string(),
            credential_assertion: CredentialAssertionData {
                cred_id: self.cred_id.clone(),
                client_data: client_data_base64url,
                signature: signature_base64url,
            },
        })
    }
}
```

| Field         | Description                                                                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`        | `"Key"` for key credentials (Service Accounts, Personal Access Tokens), `"Fido2"` for passkeys.                                                                |
| `cred_id`     | ID of the credential registered with your token. Find it in the DFNS Dashboard under `Settings` > `Service Accounts` or `Settings` > `Personal Access Tokens`. |
| `client_data` | Base64url-encoded `clientData` JSON built from the challenge.                                                                                                  |
| `signature`   | Base64url-encoded signature over the `clientData` bytes.                                                                                                       |

<Warning>
  The exact `clientData` shape and stringification rules are defined in [Credentials data](/api-reference/auth/credentials-data#key-password-protected-key-and-recovery-credential). Incorrect stringification causes `Unable to verify signature` errors.
</Warning>

Calling a state-changing method without a configured signer returns `Error::SignerRequired`.

## Available API domains

The client provides typed access to all DFNS API domains:

| Domain                   | Description                              |
| ------------------------ | ---------------------------------------- |
| `client.wallets`         | Wallet creation, listing, and management |
| `client.keys`            | Key management operations                |
| `client.policies`        | Policy rules and approvals               |
| `client.permissions`     | Access control and permissions           |
| `client.webhooks`        | Webhook subscriptions                    |
| `client.signers`         | Signer management                        |
| `client.staking`         | Staking operations                       |
| `client.networks`        | Network information                      |
| `client.exchanges`       | Exchange integrations                    |
| `client.fee_sponsors`    | Fee sponsorship                          |
| `client.swaps`           | Token swap operations                    |
| `client.vaults`          | Vault operations                         |
| `client.payins`          | Payin operations                         |
| `client.payouts`         | Payout operations                        |
| `client.allocations`     | Allocation management                    |
| `client.address_watches` | Address watch operations                 |
| `client.agreements`      | Agreement management                     |
| `client.auth`            | Authentication and user management       |

## Delegated signing

`DfnsDelegatedClient` exposes the same domains, but every operation that requires a user action signature is split into two methods:

| Method                                                        | Returns                                                |
| ------------------------------------------------------------- | ------------------------------------------------------ |
| `<operation>_init(body)`                                      | The `UserActionChallenge` to be signed by the end user |
| `<operation>_complete(body, challenge_identifier, assertion)` | The operation result                                   |

Read-only methods are identical on both clients. See [Delegated wallets](/guides/developers/delegated-wallets) for a full implementation guide.

## Error handling

All methods return `Result<T, dfns_sdk_rust::Error>`:

```rust theme={null}
use dfns_sdk_rust::Error;

match client.wallets.get_wallet("invalid-wallet-id".to_string()).await {
    Ok(wallet) => println!("Wallet: {}", wallet.id),
    Err(Error::Api { status, body }) => {
        eprintln!("API error (status {}): {}", status, body.message);
    }
    Err(err) => eprintln!("Error: {}", err),
}
```

| Variant                     | Cause                                             |
| --------------------------- | ------------------------------------------------- |
| `Api { status, body }`      | Non-2xx response with a decodable DFNS error body |
| `ApiRaw { status, raw }`    | Non-2xx response with an undecodable body         |
| `Config(String)`            | Invalid base URL or auth token                    |
| `SignerRequired`            | A signed operation was issued without a signer    |
| `Signer(String)`            | Your `UserActionSigner` implementation failed     |
| `Transport(reqwest::Error)` | Connection, timeout, or other transport failure   |
| `Serde(serde_json::Error)`  | Request or response (de)serialization failure     |
