Skip to main content

Documentation Index

Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Delegated access allows your server to perform wallet operations on behalf of users with their permission. Dynamic sends delegation credentials to your webhook, and your server uses them to sign without the user being online. This guide covers the server-side implementation using the Python SDK. For conceptual background on the delegation workflow, see Delegated Access Overview.

Prerequisites

How Delegation Works

  1. User approves delegation in your application (client-side)
  2. Dynamic sends delegation credentials to your webhook:
    • walletId — the user’s wallet ID
    • encryptedWalletApiKey — temporary API key, encrypted with your RSA public key
    • encryptedDelegatedShare — the server key share, encrypted with your RSA public key
  3. Your server decrypts the credentials and stores them securely
  4. Your server uses the credentials to sign on the user’s behalf

Step 1: Decrypt the Webhook Payload

Dynamic encrypts delegation credentials with your RSA public key. Decrypt them before use:
from dynamic_wallet_sdk.delegated.decrypt import decrypt_delegated_webhook_data

# webhook_body is the parsed JSON from Dynamic's webhook POST
decrypted = decrypt_delegated_webhook_data(
    private_key_pem=your_rsa_private_key_pem,  # str or bytes
    encrypted_delegated_key_share=webhook_body["encryptedDelegatedShare"],
    encrypted_wallet_api_key=webhook_body["encryptedWalletApiKey"],
)

wallet_api_key = decrypted.decrypted_wallet_api_key
key_share = decrypted.decrypted_delegated_share["secretShare"]
Store wallet_api_key and key_share encrypted in your database alongside the walletId.

Step 2: Create a Delegated Client

create_delegated_evm_client returns a fully authenticated DynamicEvmWalletClient — no follow-up authenticate_api_token call is needed.
from dynamic_wallet_sdk.delegated.client import create_delegated_evm_client

client = await create_delegated_evm_client(
    environment_id="your-environment-id",
    api_key="your-server-api-key",
)

Step 3: Sign a Message

delegated_sign_message for EVM returns a signature object exposing r, s, and v byte components. Most EVM tooling expects the standard 0x-prefixed 132-character r || s || v hex string, so convert it before passing the signature downstream.
from dynamic_wallet_sdk.delegated.client import delegated_sign_message

sig_obj = await delegated_sign_message(
    client,
    wallet_id="wallet-id-from-webhook",
    wallet_api_key=wallet_api_key,
    key_share=key_share,
    message="0x...",   # hex-encoded keccak256 hash of the message
    chain_name="EVM",
    is_formatted=True,
)

signature_hex = "0x" + sig_obj.r.hex() + sig_obj.s.hex() + format(sig_obj.v, "02x")
print(f"Signature: {signature_hex}")
is_formatted:
  • Truemessage is already the 32-byte keccak256 hash (hex)
  • Falsemessage is plain text; the SDK applies EIP-191 prefixing and hashing for you

Complete Webhook Handler Example

import os
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

RSA_PRIVATE_KEY = os.environ["RSA_PRIVATE_KEY_PEM"]

async def handle_delegation_webhook(webhook_body: dict):
    """Store delegation credentials received from Dynamic."""
    decrypted = decrypt_delegated_webhook_data(
        private_key_pem=RSA_PRIVATE_KEY,
        encrypted_delegated_key_share=webhook_body["encryptedDelegatedShare"],
        encrypted_wallet_api_key=webhook_body["encryptedWalletApiKey"],
    )

    # Store encrypted in your database
    await db.store_delegation(
        wallet_id=webhook_body["walletId"],
        wallet_api_key=encrypt(decrypted.decrypted_wallet_api_key),
        key_share=encrypt(decrypted.decrypted_delegated_share["secretShare"]),
    )


async def sign_on_behalf_of_user(wallet_id: str, message_hash: str) -> str:
    """Sign using stored delegation credentials."""
    credentials = await db.get_delegation(wallet_id)

    client = await create_delegated_evm_client(
        environment_id=os.environ["DYNAMIC_ENV_ID"],
        api_key=os.environ["DYNAMIC_API_KEY"],
    )

    sig_obj = await delegated_sign_message(
        client,
        wallet_id=wallet_id,
        wallet_api_key=decrypt(credentials.wallet_api_key),
        key_share=decrypt(credentials.key_share),
        message=message_hash,
        chain_name="EVM",
        is_formatted=True,
    )

    return "0x" + sig_obj.r.hex() + sig_obj.s.hex() + format(sig_obj.v, "02x")

Security Considerations

  • Never log delegation credentials (wallet API key, key share)
  • Store credentials encrypted at rest — treat them like passwords
  • Use secure environment variables for your RSA private key and server API key
  • The RSA private key used for decryption should never leave your server

Next Steps