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

Dynamic’s Python SDK exposes two transaction methods on DynamicSvmWalletClient:
  • send_transaction — signs a serialized message and broadcasts it via JSON-RPC. Returns the base58-encoded transaction signature. This is the path most apps want.
  • sign_transaction — signs only. Returns a hex-encoded Ed25519 signature you assemble into a transaction yourself. Note that this is asymmetric with sign_message, which returns a base58 signature — the SDK uses different encodings on purpose.
Both methods sign the transaction message (not the full transaction). Pass bytes(tx.message) or tx.serialize_message().

Prerequisites

Quick Start: Send a Transaction

send_transaction builds the wire transaction, signs the message, and broadcasts it for you:
import asyncio
import os
from solders.pubkey import Pubkey
from solders.system_program import transfer, TransferParams
from solders.message import Message
from solders.hash import Hash
import httpx

from dynamic_wallet_sdk import DynamicSvmWalletClient


async def get_recent_blockhash(rpc_url: str) -> str:
    async with httpx.AsyncClient() as http:
        resp = await http.post(rpc_url, json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "getLatestBlockhash",
            "params": [{"commitment": "finalized"}],
        })
        return resp.json()["result"]["value"]["blockhash"]


async def main():
    rpc_url = "https://api.mainnet-beta.solana.com"
    from_address = "YourBase58WalletAddress"
    to_address = "RecipientBase58Address"

    from_pubkey = Pubkey.from_string(from_address)
    to_pubkey = Pubkey.from_string(to_address)

    blockhash = await get_recent_blockhash(rpc_url)
    instruction = transfer(TransferParams(
        from_pubkey=from_pubkey,
        to_pubkey=to_pubkey,
        lamports=1_000_000,
    ))
    message = Message.new_with_blockhash(
        [instruction],
        from_pubkey,
        Hash.from_string(blockhash),
    )

    async with DynamicSvmWalletClient(os.environ["DYNAMIC_ENV_ID"]) as client:
        await client.authenticate_api_token(os.environ["DYNAMIC_API_TOKEN"])

        tx_signature = await client.send_transaction(
            address=from_address,
            message_bytes=bytes(message),
            rpc_url=rpc_url,
        )
        print(f"Transaction: {tx_signature}")  # base58 tx signature

asyncio.run(main())
rpc_url resolution: explicit argument → DynamicSvmWalletClient(default_rpc_url=...) → falls back to https://api.devnet.solana.com.

Signing with a Password-Protected Wallet

tx_signature = await client.send_transaction(
    address=from_address,
    message_bytes=bytes(message),
    rpc_url=rpc_url,
    password="your-wallet-password",
)

Gas Sponsorship

Pass sponsor=True to have Dynamic pay network fees for the transaction:
tx_signature = await client.send_transaction(
    address=from_address,
    message_bytes=bytes(message),
    rpc_url=rpc_url,
    sponsor=True,
)
Gas sponsorship requires the feature to be enabled for your environment in the Dynamic dashboard.

Manual Signing and Broadcasting

When you need to inspect, log, or relay the signed transaction yourself, use sign_transaction to get the raw signature and assemble the wire transaction by hand.
import asyncio
import os
from solders.pubkey import Pubkey
from solders.transaction import Transaction
from solders.system_program import transfer, TransferParams
from solders.message import Message
from solders.hash import Hash
import httpx

from dynamic_wallet_sdk import DynamicSvmWalletClient


async def send_sol(
    from_address: str,
    to_address: str,
    lamports: int,
    rpc_url: str,
    password: str | None = None,
):
    from_pubkey = Pubkey.from_string(from_address)
    to_pubkey = Pubkey.from_string(to_address)

    async with httpx.AsyncClient() as http:
        resp = await http.post(rpc_url, json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "getLatestBlockhash",
            "params": [{"commitment": "finalized"}],
        })
        blockhash = resp.json()["result"]["value"]["blockhash"]

    instruction = transfer(TransferParams(
        from_pubkey=from_pubkey,
        to_pubkey=to_pubkey,
        lamports=lamports,
    ))
    message = Message.new_with_blockhash(
        [instruction],
        from_pubkey,
        Hash.from_string(blockhash),
    )

    async with DynamicSvmWalletClient(os.environ["DYNAMIC_ENV_ID"]) as client:
        await client.authenticate_api_token(os.environ["DYNAMIC_API_TOKEN"])

        sig_hex = await client.sign_transaction(
            address=from_address,
            message_bytes=bytes(message),
            password=password,
        )

    sig_bytes = bytes.fromhex(sig_hex)
    tx = Transaction.populate(message, [sig_bytes])

    async with httpx.AsyncClient() as http:
        resp = await http.post(rpc_url, json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "sendTransaction",
            "params": [
                bytes(tx).hex(),
                {"encoding": "base64", "skipPreflight": False},
            ],
        })
        return resp.json()["result"]
sign_transaction returns the signature as hex (64 bytes / 128 hex chars). Convert to bytes with bytes.fromhex(sig_hex) before inserting into the transaction. If a downstream consumer expects base58:
import base58

sig_b58 = base58.b58encode(bytes.fromhex(sig_hex)).decode()

Verifying a Signature

import nacl.signing
import base58

sig_bytes = bytes.fromhex(signature_hex)
pubkey_bytes = base58.b58decode(address)

verify_key = nacl.signing.VerifyKey(pubkey_bytes)
verify_key.verify(message_bytes, sig_bytes)
print("Signature verified")

Error Handling

from dynamic_wallet_sdk import DynamicSDKError

try:
    sig_hex = await client.sign_transaction(
        address=address,
        message_bytes=bytes(message),
    )
except DynamicSDKError as e:
    print(f"Signing failed: {e}")

Next Steps