Official Python SDK for the WeEasyCrypto tenant API — deposit addresses, balances, withdrawals, Ed25519 request signing and verified webhooks.
Project description
weeasycrypto (Python)
Official Python SDK for the WeEasyCrypto tenant API: deposit addresses, balances, deposits, withdrawals, and signed webhooks.
- One runtime dependency. HTTP via
urllib; Ed25519 (request signing and webhook verification) via the auditedcryptographypackage (the stdlib has none). A custom transport is injectable for proxies, test stubs, or record/replay. - Signing you never hand-write. Every request carries an Ed25519
signature (
X-Timestamp+X-Signature) computed over the exact bytes sent on the wire — the platform holds only your public key and can verify, never forge. Webhook deliveries are Ed25519-signed too (v1a, per-tenant platform key) — verified with your tenant's public key, ±300 s tolerance. - Amounts are decimal strings, never floats.
parse_amount/format_amountuse pure integer arithmetic.
⚠️ Server-side only. Your Ed25519
private_keymust never reach a browser or mobile bundle — it was generated locally when the key was issued and the platform cannot recover it.
Install
pip install weeasycrypto
Quickstart
import os
from weeasycrypto import WeEasyCrypto
cv = WeEasyCrypto(
base_url="https://api.example.com",
api_key=os.environ["WEEASYCRYPTO_API_KEY"], # keyId, e.g. "ck_…"
private_key=os.environ["WEEASYCRYPTO_PRIVATE_KEY"], # hex Ed25519 seed, generated at issuance
webhook_public_key=os.environ.get("WEEASYCRYPTO_WEBHOOK_PUBLIC_KEY"), # portal → Settings → Integrations
)
# Issue a deposit address
addr = cv.addresses.create(label="user-42", chain_key="tron")
addr.family # 'EVM' | 'TRON' | 'BTC' | 'SOL' | 'TON'
# Balances — amounts are decimal strings, never floats
balances = cv.balances.list()
# Deposits with auto-pagination
for d in cv.deposits.iterate(status="CONFIRMED"):
print(d.tx_hash, d.amount)
Withdrawals — the idempotency contract
request_id is your idempotency key. Persist it in your own database
before calling; the SDK deliberately never generates one, because a
regenerated key after a crash is how double-payouts happen.
from weeasycrypto import ConflictError
try:
w = cv.withdrawals.create(
request_id="wd-20260710-0001", # yours, persisted first
chain_key="eth-mainnet",
token_symbol="USDT",
to="0x…",
amount="100.5", # always a string
)
except ConflictError as err:
if err.is_duplicate_request:
# Safety signal, not a failure: an earlier attempt already created it.
# Look the withdrawal up via the id you stored against this request_id.
...
else:
raise
# Poll to a terminal status (webhooks are the push-based alternative)
final = cv.withdrawals.wait_until_final(w.id)
# final.status: 'CONFIRMED' | 'FAILED' | 'REJECTED' | 'CANCELED'
if final.status == "CONFIRMED":
print(final.tx_hash)
Network errors on withdrawals.create / create_batch are retried
automatically with the same request_id — that can never double-spend.
addresses.create has no idempotency key, so it is never auto-retried;
list() recent addresses before creating again if the outcome was unknown.
Webhooks
Deliveries carry X-Webhook-Signature: t={ts},v1a={hex} — an Ed25519
signature over {ts}.{body} made with a per-tenant platform key.
Verification needs only your tenant's public key (webhookSignPublicKey
in the merchant portal, Settings → Integrations); there is no shared
secret to protect.
# Flask example — raw body must be the exact bytes received.
from weeasycrypto import WebhookVerificationError
@app.post("/webhook")
def webhook():
try:
event = cv.webhooks.parse(
request.get_data(), request.headers.get("X-Webhook-Signature")
)
except WebhookVerificationError:
return "", 400
if event.type == "deposit.confirmed":
credit(event.data["depositId"], event.data["amount"])
elif event.type == "withdrawal.completed":
mark_paid(event.data["requestId"])
# New event types appear over time — ignore what you don't know.
return "", 200
Errors
WeEasyCryptoError
├─ APIError (status / code / request_id)
│ ├─ AuthenticationError(401) ├─ PermissionDeniedError(403)
│ ├─ NotFoundError(404) ├─ ConflictError(409) # .is_duplicate_request
│ ├─ ValidationError(422) └─ ServerError(5xx)
│ └─ RateLimitError(429) # .retry_after_ms
├─ NetworkError # no HTTP response; safe to replay withdrawals
├─ WebhookVerificationError
└─ TimeoutError # wait_until_final budget exceeded
Testing
python3 -m venv .venv && .venv/bin/pip install -e . pytest
.venv/bin/pytest
Signing is verified against the shared golden vectors in
../vectors/signing-vectors.json.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file weeasycrypto-0.2.0.tar.gz.
File metadata
- Download URL: weeasycrypto-0.2.0.tar.gz
- Upload date:
- Size: 20.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6a781399c341709d4323ff9858b4c6db8f349b0eec541e9366ca3ec5142429cc
|
|
| MD5 |
4914a33ba422efb634c6107f20c0d1eb
|
|
| BLAKE2b-256 |
5227a1241a97fc12392a7b05a9d873c82273d9255d473a6660bdac9d411287db
|
File details
Details for the file weeasycrypto-0.2.0-py3-none-any.whl.
File metadata
- Download URL: weeasycrypto-0.2.0-py3-none-any.whl
- Upload date:
- Size: 21.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc00b9c9180c5c468ed9d9a8eca4e03dfb04d3c5cdb3a1df1417eeaef791dae4
|
|
| MD5 |
9f59bb018f39d38b485eb64dd7ee955c
|
|
| BLAKE2b-256 |
4a970f34e8b2f435f13999c03651a1015bd290613bac3592f0e750d148568426
|