new-site/scripts/workers/crypto_offramp/__init__.py
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

95 lines
3.2 KiB
Python

"""Crypto offramp module.
Converts coins received by SHKeeper into USD at RelayFi bank via
Coinbase Prime — sole provider per plan. See
/home/justin/.claude/plans/swirling-napping-sonnet.md.
Exports:
Quote, ExecutionResult, ProviderStatus — dataclasses
Rail — literal type alias
OfframpError, InsufficientBalanceError, KycHoldError — exception hierarchy
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
from decimal import Decimal
from typing import Literal, Optional
Rail = Literal["ach", "wire", "rtp"]
# Supported coins (XMR intentionally excluded — Coinbase Prime doesn't offramp it).
SUPPORTED_COINS: set[str] = {
"BTC", "ETH", "MATIC", "LTC", "TRX", "BNB", "DOGE", "USDC",
}
@dataclass
class Quote:
"""Pre-execute price quote from the offramp provider."""
from_coin: str
to_currency: str = "USD"
amount_coin: Decimal = Decimal("0")
estimated_usd_cents: int = 0
fx_rate_usd: Decimal = Decimal("0") # 1 coin = N USD
fee_usd_cents: int = 0 # provider fee estimate
valid_until: Optional[datetime] = None
quote_id: Optional[str] = None # provider-side quote handle, if any
@dataclass
class ExecutionResult:
"""Outcome of calling `execute()` — the sell + wire request was accepted."""
provider_ref: str # Prime order id / transfer id
accepted_usd_cents: int # what the provider promised to settle
fill_price_usd: Decimal # actual coin→USD price achieved
state: Literal["submitted", "partial", "completed", "failed"]
rail: Rail
memo: Optional[str] = None # e.g., "PW-ORDER-FO-2026-0042"
raw: dict = field(default_factory=dict) # provider-native response for debugging
@dataclass
class ProviderStatus:
"""Current state of a previously-executed offramp."""
provider_ref: str
state: Literal[
"submitted", "selling", "selling_complete", "wiring", "completed",
"failed", "held",
]
settled_usd_cents: int = 0
fill_price_usd: Optional[Decimal] = None
wire_tx_id: Optional[str] = None
error_message: Optional[str] = None
raw: dict = field(default_factory=dict)
# ── Exception hierarchy ─────────────────────────────────────────────────
class OfframpError(Exception):
"""Base for all offramp errors."""
class InsufficientBalanceError(OfframpError):
"""Exchange side doesn't have enough of the coin credited yet."""
class KycHoldError(OfframpError):
"""Exchange paused our withdrawal for AML/KYC review — admin must resolve."""
class SlippageExceededError(OfframpError):
"""Realized fill price was outside MAX_SLIPPAGE_BPS of quote."""
class ProviderDegradedError(OfframpError):
"""Repeated 5xx/429 — circuit breaker tripped."""
__all__ = [
"Rail", "SUPPORTED_COINS",
"Quote", "ExecutionResult", "ProviderStatus",
"OfframpError", "InsufficientBalanceError", "KycHoldError",
"SlippageExceededError", "ProviderDegradedError",
]