Metadata-Version: 2.4
Name: interchained-usdx
Version: 0.1.0
Summary: Atomic USDx wallet — Redis-backed balance engine, NowPayments deposits, ITC PSBT withdrawals, OTC rate engine
Project-URL: Homepage, https://github.com/interchained/usdx-py
Project-URL: Repository, https://github.com/interchained/usdx-py
Project-URL: Issues, https://github.com/interchained/usdx-py/issues
Project-URL: Changelog, https://github.com/interchained/usdx-py/blob/main/CHANGELOG.md
Author-email: Interchained <dev@interchained.org>
License: GPL-3.0-or-later
License-File: LICENSE
Keywords: atomic,balance,blockchain,crypto,interchained,nowpayments,psbt,redis,usdx,wallet
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: httpx>=0.24.0
Requires-Dist: redis[asyncio]>=4.5.0
Provides-Extra: dev
Requires-Dist: fakeredis>=2.20; extra == 'dev'
Requires-Dist: lupa>=2.0; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: ruff>=0.3; extra == 'dev'
Description-Content-Type: text/markdown


<p align="center">
  <img src="https://img.shields.io/badge/interchained-usdx-blueviolet?style=for-the-badge&logo=redis&logoColor=white" alt="interchained-usdx"/>
</p>

<h1 align="center">🪙 interchained-usdx</h1>

<p align="center">
  <strong>Atomic, Redis-backed USDx wallet engine for the Interchained ecosystem.</strong><br/>
  Four independent modules. Zero application-level locks. Battle-tested in production.
</p>

<p align="center">
  <a href="https://pypi.org/project/interchained-usdx/"><img src="https://img.shields.io/pypi/v/interchained-usdx?color=blueviolet&style=flat-square&label=PyPI" alt="PyPI"/></a>
  <img src="https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python 3.11+"/>
  <img src="https://img.shields.io/badge/redis-6%2B-DC382D?style=flat-square&logo=redis&logoColor=white" alt="Redis 6+"/>
  <img src="https://img.shields.io/badge/tests-24%20passed-brightgreen?style=flat-square" alt="Tests: 24 passed"/>
  <img src="https://img.shields.io/badge/atomicity-lua%20scripts-orange?style=flat-square" alt="Lua atomic"/>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-GPLv3-blue?style=flat-square" alt="License: GPLv3"/></a>
  <img src="https://img.shields.io/badge/async-first-9b59b6?style=flat-square" alt="async-first"/>
</p>

---

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

```bash
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](https://nowpayments.io/) (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

```python
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

```python
# 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.

```python
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.

```python
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.

```python
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

```bash
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/`](examples/) for runnable code:

| Example | Description |
|---|---|
| [`quickstart.py`](examples/quickstart.py) | End-to-end async script covering every method |
| [`fastapi_wallet_app/`](examples/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`](https://github.com/interchained/usdx-js) — pairs with this package:

```ts
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](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.
```
