LayerHopdocs

SDKs & tools

Python SDK

The layerhop Python package — sync and async httpx clients for quotes, transactions, order tracking and webhook verification.

Open in ClaudeOpen in ChatGPT

layerhop is a typed client for the REST API with a sync LayerHop and an async AsyncLayerHop. It mirrors the TypeScript SDK. Python 3.9+; the only runtime dependency is httpx.

Source: github.com/layerhop/layerhop-python — source repository (opens at launch).

The SDK never holds keys. It returns unsigned transactions; your own wallet library (web3.py, eth-account, solders, a KMS…) signs and broadcasts them.

Install#

Terminal
pip install layerhop
# inside the monorepo:
pip install -e sdks/python

Quickstart#

Amounts are integer strings in base units (100 USDC = "100000000"), never floats.

python
from layerhop import LayerHop

lh = LayerHop()  # LayerHop(api_key="...", integrator="myapp", base_url="http://localhost:8787")

# 1. Quote
q = lh.quote("base", "USDC", "100000000", "solana", "SOL",
             sender="0xYourEvmAddress", recipient="YourSolanaAddress")
best = q["best"]

# 2. Build the unsigned transaction
tx = lh.build_tx(best["quoteId"])

# 3. Sign + send with YOUR wallet library (web3.py shown)
if tx["vm"] == "evm":
    # send tx["approval"] first when present
    tx_hash = w3.eth.send_transaction({"to": tx["to"], "data": tx["data"],
                                       "value": int(tx["value"]), "chainId": tx["chainId"]}).hex()

# 4. Report the broadcast (enables tracking, history and webhooks)
lh.report_submitted(best["quoteId"], tx_hash)

# 5. Track until filled / refunded / failed
for s in lh.watch(best["provider"], tx_hash):
    print(s["state"])

For Solana, tx["serializedTx"] is a base64 VersionedTransaction; sign it with solders. When tx.get("submitViaApi") is true, only sign it and call lh.submit(quote_id, signed_b64) (why).

Async:

python
from layerhop import AsyncLayerHop

async with AsyncLayerHop() as lh:
    q = await lh.quote("base", "USDC", "100000000", "solana", "SOL", sender=s, recipient=r)
    async for st in lh.watch(q["best"]["provider"], tx_hash):
        print(st["state"])

Methods#

Method Endpoint
health() · chains() · tokens(chain) GET /v1/health · /v1/chains · /v1/tokens
quote(src_chain, src_token, amount, dst_chain, dst_token, *, sender, recipient, slippage_bps=50) GET /v1/quote
build_tx(quote_id) POST /v1/orders → the tx
simulate(quote_id) POST /v1/simulate
submit(quote_id, signed_transaction) POST /v1/orders/submit (Solana submitViaApi only)
report_submitted(quote_id, tx_hash) POST /v1/orders/{quoteId}/submitted
status(provider, id) GET /v1/orders/{provider}/{id}
watch(provider, id, *, interval=3.0, timeout=1800) Polls status and yields on each change until final
list_orders(*, address, status, cursor, limit) GET /v1/orders

Options: base_url, api_key (sent as x-api-key), channel, integrator, integrator_fee_bps, timeout, http_client / transport. Responses are dicts typed with TypedDict, with the API's camelCase keys.

Errors#

Non-2xx responses raise LayerHopApiError with code, status, message and request_id. The code is one of the stable error codes.

python
from layerhop import LayerHopApiError

try:
    lh.build_tx(quote_id)
except LayerHopApiError as e:
    if e.code == "QUOTE_EXPIRED":
        ...  # re-quote

Webhooks#

python
from layerhop import verify_webhook_signature

ok = verify_webhook_signature(secret, request.get_data(), request.headers["LayerHop-Signature"])

The header is t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>. Verify it against the raw body. The check rejects timestamps more than 300 s off (tolerance) and compares in constant time.