Metadata-Version: 2.4
Name: dynamic-wallet-sdk
Version: 0.6.0
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Rust
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Topic :: Security :: Cryptography
Classifier: Typing :: Typed
Requires-Dist: httpx>=0.27
Requires-Dist: cryptography>=42.0
Requires-Dist: eth-account>=0.13
Requires-Dist: eth-hash[pycryptodome]>=0.7
Requires-Dist: base58>=2.1
Requires-Dist: solders>=0.21
Requires-Dist: pytest>=8.0 ; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
Requires-Dist: pytest-cov>=5.0 ; extra == 'dev'
Requires-Dist: respx>=0.21 ; extra == 'dev'
Requires-Dist: maturin>=1.5 ; extra == 'dev'
Requires-Dist: pynacl>=1.5 ; extra == 'dev'
Provides-Extra: dev
Summary: Dynamic MPC Wallet SDK for Python — create and manage multi-party computation wallets for EVM and Solana
Author-email: Dynamic Labs <support@dynamic.xyz>
License: Apache-2.0
Requires-Python: >=3.11
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://docs.dynamic.xyz
Project-URL: Homepage, https://dynamic.xyz
Project-URL: Repository, https://github.com/dynamic-labs/dynamic-waas-sdk

# Dynamic Wallet SDK for Python

A Python SDK for [Dynamic](https://www.dynamic.xyz/)'s MPC (Multi-Party Computation) wallet infrastructure. Create, sign with, broadcast from, and manage non-custodial wallets from Python — no browser required.

## Installation

```bash
pip install dynamic-wallet-sdk
```

Pre-built wheels are provided for Linux (x86_64, ARM64) and macOS (x86_64, Apple Silicon). No Rust toolchain required.

## Requirements

- Python 3.11+
- A Dynamic environment ID and API token — from the [Dynamic dashboard](https://app.dynamic.xyz/)

## Quick Start

### EVM Wallet

```python
import asyncio
from dynamic_wallet_sdk.evm.client import DynamicEvmWalletClient

async def main():
    async with DynamicEvmWalletClient("your-environment-id") as client:
        await client.authenticate_api_token("your-api-token")

        # Create wallet (MPC keygen). Returns WalletProperties.
        wp = await client.create_wallet_account(password="backup-password")
        address = wp.account_address
        print(f"Created EVM wallet: {address}")

        # Sign a message (EIP-191)
        signature = await client.sign_message(message="Hello, Dynamic!", address=address)
        print(f"Signature: {signature}")

        # Sign EIP-712 typed data
        typed_data = {
            "types": {...},
            "primaryType": "...",
            "domain": {...},
            "message": {...},
        }
        sig = await client.sign_typed_data(address, typed_data)

        # Sign a legacy transaction and broadcast it
        tx = {
            "to": "0xRecipient...",
            "value": 0,
            "nonce": 0,
            "gas": 21000,
            "gasPrice": 20_000_000_000,
            "chainId": 1,
            "data": "0x",
        }
        tx_hash = await client.send_transaction(
            address=address, tx=tx, rpc_url="https://mainnet.infura.io/v3/YOUR_KEY"
        )
        print(f"Tx hash: {tx_hash}")

asyncio.run(main())
```

### Solana (SVM) Wallet

```python
import asyncio
from dynamic_wallet_sdk.svm.client import DynamicSvmWalletClient

async def main():
    async with DynamicSvmWalletClient("your-environment-id") as client:
        await client.authenticate_api_token("your-api-token")

        wp = await client.create_wallet_account(password="backup-password")
        address = wp.account_address
        print(f"Created Solana wallet: {address}")

        signature = await client.sign_message(message="Hello from Solana!", address=address)
        print(f"Signature (base58): {signature}")

        # Sign a transaction message and broadcast it
        message_bytes = bytes(...)   # serialized Solana tx message
        tx_sig = await client.send_transaction(
            address=address,
            message_bytes=message_bytes,
            rpc_url="https://api.mainnet-beta.solana.com",
        )
        print(f"Tx signature: {tx_sig}")

asyncio.run(main())
```

### Delegated Signing

For server-side signing where key shares are held by your server:

```python
from dynamic_wallet_sdk.delegated.client import (
    create_delegated_evm_client,
    delegated_sign_message,
)
from dynamic_wallet_sdk.delegated.decrypt import decrypt_delegated_webhook_data

# 1. Decrypt the webhook payload you received from Dynamic
decrypted = decrypt_delegated_webhook_data(
    private_key_pem=rsa_private_key,
    encrypted_delegated_key_share=webhook["encryptedDelegatedShare"],
    encrypted_wallet_api_key=webhook["encryptedWalletApiKey"],
)

# 2. Create a client and sign
client = await create_delegated_evm_client("env-id", "api-key")
sig = await delegated_sign_message(
    client,
    wallet_id="wallet-id",
    wallet_api_key=decrypted.decrypted_wallet_api_key,
    key_share=decrypted.decrypted_delegated_share["secretShare"],
    message="0x...",
    chain_name="EVM",
    is_formatted=True,
)
```

## Key Concepts

### Key Backup and Recovery

Pass a `password` to `create_wallet_account()` to encrypt and back up key shares at keygen time. The password is never sent to Dynamic — only AES-256-GCM ciphertext is stored.

**Auto-recovery on sign:** pass `password=` directly to any sign call and the SDK will recover shares automatically if they are not already in memory:

```python
# Shares were lost (process restarted, etc.) — just pass password= and signing works
signature = await client.sign_message(
    message="Hello!", address=address, password="backup-password"
)
```

**Explicit recovery:**

```python
shares = await client.recover_key_shares(address, password="backup-password")
```

### Session Rehydration

To rehydrate a known wallet in a new session without fetching all user wallets:

```python
wp = await client.load_wallet(address)
# wp.account_address, wp.wallet_id, and backup metadata are populated
# Signing with password= will auto-recover key shares as needed
```

### Transaction Broadcasting

Every chain client exposes two broadcast patterns:

**`send_transaction()` — sign and broadcast in one call:**

```python
# EVM
tx_hash = await evm_client.send_transaction(address, tx, rpc_url="https://...")

# SVM
tx_sig = await svm_client.send_transaction(address, message_bytes, rpc_url="https://...")
```

**Standalone utilities — sign first, broadcast separately:**

```python
from dynamic_wallet_sdk.evm import broadcast_raw_transaction
from dynamic_wallet_sdk.svm import attach_signature, broadcast_raw_transaction as svm_broadcast

# EVM: build the signed raw tx yourself, then broadcast
tx_hash = await broadcast_raw_transaction(signed_tx_hex, rpc_url)

# SVM: attach signature to message bytes, then broadcast
signed_tx = attach_signature(message_bytes, sig_hex)   # → bytes
tx_sig    = await svm_broadcast(signed_tx, rpc_url)
```

RPC URLs are supplied by the developer — use any JSON-RPC node (Infura, Alchemy, Helius, QuickNode, a local node, etc.). The SDK is RPC-provider-agnostic.

### Threshold Signature Schemes

The SDK supports two TSS configurations:

| Scheme         | Parties | Threshold | Client Shares | Server Shares |
| -------------- | ------- | --------- | ------------- | ------------- |
| `TWO_OF_TWO`   | 2       | 2         | 1             | 1             |
| `TWO_OF_THREE` | 3       | 2         | 2             | 1             |

### Chain Support

| Chain        | Algorithm | Client Class             |
| ------------ | --------- | ------------------------ |
| EVM          | ECDSA     | `DynamicEvmWalletClient` |
| SVM (Solana) | Ed25519   | `DynamicSvmWalletClient` |

## Links

- [Dynamic Dashboard](https://app.dynamic.xyz/)
- [Dynamic Docs](https://docs.dynamic.xyz/)

