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

# Sign SVM Transactions

> Learn how to sign and send Solana transactions using Dynamic's Python SDK

## 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

* [Created a Solana wallet](/python/svm/create-wallet)
* `solders` and `httpx` installed for transaction construction and broadcasting

## Quick Start: Send a Transaction

`send_transaction` builds the wire transaction, signs the message, and broadcasts it for you:

```python theme={"system"}
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

```python theme={"system"}
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:

```python theme={"system"}
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.

```python theme={"system"}
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:

```python theme={"system"}
import base58

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

## Verifying a Signature

```python theme={"system"}
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

```python theme={"system"}
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

* [Sign messages](/python/svm/sign-messages)
* [Delegated access for SVM wallets](/python/svm/delegated-access)
