Skip to main content

interchained-usdx

🪙 interchained-usdx

Atomic, Redis-backed USDx wallet engine for the Interchained ecosystem.
Four independent modules. Zero application-level locks. Battle-tested in production.

PyPI Python 3.11+ Redis 6+ Tests: 24 passed Lua atomic License: GPLv3 async-first


⚡ The Lore

In the Interchained ecosystem, USDx is the stable settlement layer — minted from real crypto deposits, redeemable for native ITC tokens at a provably-fair OTC rate. It powers creator earnings, node operator rewards, game prize pools, and marketplace settlements.

Every balance mutation — credit, debit, transfer, freeze — executes as a single Redis Lua script. Atomically. No application-level locks. No race conditions. No double-spends. Just raw, nanosecond-precision financial logic running in the kernel of a battle-hardened in-memory database.

This is the engine behind $50k+ in transaction volume and 275+ live users on the Interchained network. Now open-sourced under GPLv3 for any project that demands the same standard.


📦 Install

pip install interchained-usdx

🗺️ What's inside

Module Class What it does
usdx.wallet USDxWallet Atomic Redis balance engine — credit / debit / transfer / freeze
usdx.payments NowPaymentsService Multi-chain crypto deposit handler via NowPayments (22 coins)
usdx.psbt PSBTBuilder + ITCRPCClient ITC blockchain PSBT payout builder, wallet-funded with auto fee estimation
usdx.otc OTCRateEngine Dynamic ITC ↔ USDx exchange rate driven by on-chain reserve / circulating supply

All four modules are independently usable — pull only what you need.


🏗️ Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Your Application                      │
│                                                             │
│   from usdx import USDxWallet, NowPaymentsService,          │
│                    PSBTBuilder, OTCRateEngine               │
└────────────┬──────────────┬───────────────┬─────────────────┘
             │              │               │
             ▼              ▼               ▼
     ┌──────────────┐ ┌──────────┐ ┌──────────────────┐
     │  USDxWallet  │ │NowPaymts │ │  PSBTBuilder     │
     │  (Lua core)  │ │  (HTTP)  │ │  (ITC RPC)       │
     └──────┬───────┘ └────┬─────┘ └──────┬───────────┘
            │              │              │
     ┌──────▼──────────────▼──────┐      │
     │          Redis 6+           │      │
     │  (atomic Lua scripts run    │      │
     │   inside the server,        │      │
     │   no app-level locks)       │      ▼
     └─────────────────────────────┘  ITC Node RPC
                                      (PSBT payouts)

🚀 Quick start

1 — Balance engine

import asyncio
import redis.asyncio as aioredis
from usdx import USDxWallet, InsufficientBalance, AccountFrozen

async def main():
    r = aioredis.from_url("redis://localhost:6379/0")
    wallet = USDxWallet(r)

    # ── Credit ───────────────────────────────────────────────
    tx = await wallet.credit("alice", 20.74, description="Welcome bonus", tx_type="bonus")
    print(tx.balance_after)   # 20.74

    # Premium creators get a 1.5× earnings multiplier
    tx = await wallet.credit("alice", 10.00, multiplier=1.5, description="Article view")
    print(tx.amount)          # 15.0  ← applied inside the Lua script

    # ── Debit ────────────────────────────────────────────────
    try:
        tx = await wallet.debit("alice", 3.99, description="Monthly subscription")
    except InsufficientBalance:
        print("Not enough funds")
    except AccountFrozen:
        print("Account is locked (fraud protection)")

    # ── Atomic transfer (single Lua round-trip) ───────────────
    debit_tx, credit_tx = await wallet.transfer("alice", "bob", 5.00, description="Tip")

    # ── Account snapshot ─────────────────────────────────────
    info = await wallet.get_info("alice")
    print(info.balance, info.total_credited, info.is_frozen)

    # ── Full transaction history ──────────────────────────────
    history = await wallet.get_history("alice", limit=10)
    for tx in history:
        print(f"{tx.direction:6s}  {tx.amount:8.2f}  {tx.description}")

    # ── Fraud protection ─────────────────────────────────────
    await wallet.freeze("alice")    # blocks all outgoing transfers
    await wallet.unfreeze("alice")  # restores full access

asyncio.run(main())

2 — Custom Redis namespace

# Isolate keys per application / environment
wallet = USDxWallet(r, key_prefix="myapp:balance:", tx_prefix="myapp:tx:")

3 — NowPayments deposits

Accept 22 cryptocurrencies — BTC, ETH, USDT, TRX, BNB, XRP, DOGE, LTC, AVAX, and more.

from usdx import NowPaymentsService, USDxWallet

wallet = USDxWallet(redis_client)

async def on_confirmed(username: str, amount: float, currency: str, payment_id: str) -> bool:
    """Called automatically when a crypto payment reaches 'finished' status."""
    await wallet.credit(
        username, amount,
        description=f"Deposit via {currency.upper()}",
        tx_type="deposit",
        metadata={"payment_id": payment_id, "currency": currency},
    )
    return True  # return False to mark as failed without consuming idempotency token

svc = NowPaymentsService(
    redis_client,
    api_key="YOUR_NOWPAYMENTS_API_KEY",
    on_confirmed=on_confirmed,
    success_url="https://yourapp.com/wallet?status=success",
    cancel_url="https://yourapp.com/wallet?status=cancelled",
    namespace="myapp",       # Redis key namespace — keeps keys isolated
    order_prefix="myapp",    # Prefix in NowPayments order_id field
)

# Create a deposit invoice — redirect the user to payment_url
invoice = await svc.create_deposit_invoice("alice", 25.00, "usdttrc20")
print(invoice["payment_url"])     # → https://nowpayments.io/payment/...

# Check a payment's current status
status = await svc.check_payment_status(payment_id)
print(status["status"])           # "waiting" | "confirming" | "finished" | "failed"

# Handle an IPN webhook (wire this into your web framework)
await svc.handle_ipn_callback(ipn_payload)

# Manually trigger processing (idempotent — safe to call multiple times)
result = await svc.process_completed_payment(payment_id)

4 — PSBT withdrawals (ITC blockchain)

Build, sign, and broadcast multi-output ITC payouts — wallet-funded, with automatic fee estimation.

from usdx import ITCRPCClient, PSBTBuilder, PSBTError

rpc = ITCRPCClient(
    rpc_url="http://127.0.0.1:8332",
    wallet_name="hot-wallet",
    username="rpcuser",
    password="rpcpass",
    fallback_fee=10.0,      # sat/byte fallback if estimatesmartfee fails
)
builder = PSBTBuilder(rpc, conf_target=6)   # target 6-block confirmation

# Build a batch payout — as many recipients as you need
payouts = [
    {"address": "itc1qrecipient1...", "amount": 12.50},
    {"address": "itc1qrecipient2...", "amount":  7.25},
    {"address": "itc1qrecipient3...", "amount": 42.00},
]

tx_hex, info = await builder.create_payout(payouts)
print(f"Total payout : {info['total_payout']:.4f} ITC")
print(f"Network fee  : {info['fee']:.4f} ITC")
print(f"Est. size    : {info['estimated_size']} bytes")

txid = await builder.broadcast(tx_hex)
print(f"Broadcast txid: {txid}")

5 — OTC rate engine

Drive a dynamic ITC ↔ USDx exchange rate from on-chain reserve and circulating supply.

from usdx import OTCRateEngine

engine = OTCRateEngine(
    redis_client,
    default_itc_reserve=10_000.0,   # initial ITC in the treasury reserve
    default_usdx_supply=1_000.0,    # initial USDx in circulation
)
await engine.initialize()           # seeds defaults on first run, no-op after

# Read the current rate
rate = await engine.get_rate()      # ITC per 1 USDx (e.g. 10.5)

# Update the reserve after a user deposits crypto
await engine.increment_supply(25.0, source="alice_deposit")
await engine.decrement_itc_reserve(12.5, source="alice_withdrawal")

# Full treasury snapshot
stats = await engine.get_stats()
# {
#   "itc_reserve":   9987.5,
#   "usdx_supply":   1025.0,
#   "current_rate":  9.74,
#   "paused":        False,
#   "last_updated":  "2026-06-12T14:30:00Z"
# }

🔑 Redis key layout

Every key prefix is configurable at construction time.

usdx:balance:{user_id}           → HASH   balance / total_credited / total_debited / is_frozen / created_at / last_updated
usdx:tx:{user_id}                → ZSET   JSON transaction records, score = UTC timestamp (ms)
usdx:otc:itc_reserve             → STRING ITC reserve amount
usdx:otc:usdx_supply             → STRING circulating USDx supply
usdx:otc:rate_history            → ZSET   historical rate snapshots
usdx:payment:invoice:{id}        → HASH   NowPayments invoice state
usdx:payment:user:{u}:invoices   → ZSET   per-user invoice index

⚛️ Atomicity guarantee

Every state-changing wallet operation is a single EVAL call — no multi-exec, no application-level locks, no read-modify-write round trips.

credit(user, 10.00)
  └─► EVAL _LUA_CREDIT  ──► Redis executes atomically:
                                HINCRBYFLOAT balance
                                HINCRBYFLOAT total_credited
                                HSET last_updated
                              ◄── returns [new_balance]

If Redis crashes mid-operation: the script either ran fully or not at all. If two processes call debit simultaneously: one wins, the other gets INSUFFICIENT_BALANCE. No coordination required between workers.


🧪 Tests

pip install -e ".[dev]"   # installs pytest, fakeredis, lupa, ruff, mypy
pytest                    # 24 tests — all green
ruff check usdx/          # linting
mypy usdx/                # strict type-checking

Note: lupa (Lua runtime) is required for fakeredis to execute EVAL in tests. It is listed in [dev] extras and installed automatically.


📁 Examples

See examples/ for runnable code:

Example Description
quickstart.py End-to-end async script covering every method
fastapi_wallet_app/ Full REST API skeleton — drop-in FastAPI service with Pydantic models, lifespan Redis, and Swagger UI

🔌 Companion package

The TypeScript HTTP client — @interchained/usdx — pairs with this package:

import { USDxClient } from "@interchained/usdx";
const client = new USDxClient({ baseUrl: "https://api.yourapp.com" });
const balance = await client.getBalance("alice");

Zero production dependencies. Works in Node.js 18+, browsers, Deno, and Bun.


🤝 Contributing

  1. Fork → branch → make your changes
  2. pip install -e ".[dev]" and run pytest — all 24 must stay green
  3. ruff check usdx/ && mypy usdx/ — zero warnings
  4. Open a PR with a clear description of what changed and why

Please report security issues via private message, not public issues.


📜 License

GNU General Public License v3.0 or later. See LICENSE for the full text.

interchained-usdx  Copyright (C) 2024  Interchained
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under the terms of the GPLv3.

Metadata

Release files for interchained-usdx 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for interchained-usdx 0.1.1
File Size Uploaded
interchained_usdx-0.1.1.tar.gz 35.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for interchained-usdx 0.1.1
File Interpreter ABI Platform
interchained_usdx-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 68.7 kB

Release files / interchained_usdx-0.1.1.tar.gz

Download URL interchained_usdx-0.1.1.tar.gz
Size 35.2 kB
Tags Source
SHA-256 checksum
How to use checksums
fb24b1375b1167ddc29628c1e17a11007abaa49b0801874facfecc93b5f035a2
BLAKE2b-256 checksum
How to use checksums
bde921a6fb667598528f42bd552647ad4f46e75c776eb44e4ce111b8865c3420
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release files / interchained_usdx-0.1.1-py3-none-any.whl

Download URL interchained_usdx-0.1.1-py3-none-any.whl
Size 33.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0d037408504bc1e65c6f58045170248b93ee3dc2a3bebc68946b8e0e225ccfbb
BLAKE2b-256 checksum
How to use checksums
5c8784c341e571f02d864b5cf497ecdb517eaf17e6308c9b25c1cce753a37f9d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page