rspace-online/modules/rexchange/schemas.ts

270 lines
6.7 KiB
TypeScript

/**
* rExchange Schemas — P2P on/off-ramp exchange intents, trades, and reputation.
*
* DocId formats:
* {space}:rexchange:intents → ExchangeIntentsDoc (active buy/sell intents)
* {space}:rexchange:trades → ExchangeTradesDoc (active & historical trades)
* {space}:rexchange:reputation → ExchangeReputationDoc (per-member trading reputation)
*/
import type { DocSchema } from '../../shared/local-first/document';
// ── Enums / Literals ──
export type ExchangeSide = 'buy' | 'sell';
export type TokenId = 'cusdc' | 'myco' | 'fusdc';
export type FiatCurrency = 'EUR' | 'USD' | 'GBP' | 'BRL' | 'MXN' | 'INR' | 'NGN' | 'ARS';
export type RateType = 'fixed' | 'market_plus_bps';
export type IntentStatus = 'active' | 'matched' | 'completed' | 'cancelled' | 'expired';
export type TradeStatus =
| 'proposed'
| 'accepted'
| 'escrow_locked'
| 'fiat_sent'
| 'fiat_confirmed'
| 'completed'
| 'disputed'
| 'resolved'
| 'cancelled'
| 'timed_out';
// ── Exchange Intent ──
export interface ExchangeIntent {
id: string;
creatorDid: string;
creatorName: string;
side: ExchangeSide;
tokenId: TokenId;
fiatCurrency: FiatCurrency;
tokenAmountMin: number; // base units (6 decimals)
tokenAmountMax: number;
rateType: RateType;
rateFixed?: number; // fiat per token (e.g. 0.98 EUR/cUSDC)
rateMarketBps?: number; // basis points spread over market rate
paymentMethods: string[]; // "SEPA", "Revolut", "PIX", "M-Pesa", "Cash", etc.
isStandingOrder: boolean; // LP flag — re-activates after fill
autoAccept: boolean; // skip manual match acceptance
allowInstitutionalFallback: boolean; // escalate to HyperSwitch if unmatched
minCounterpartyReputation?: number; // 0-100
preferredCounterparties?: string[]; // DID list
status: IntentStatus;
createdAt: number;
expiresAt?: number;
}
// ── Trade Chat Message ──
export interface TradeChatMessage {
id: string;
senderDid: string;
senderName: string;
text: string;
timestamp: number;
}
// ── Exchange Trade ──
export interface ExchangeTrade {
id: string;
buyIntentId: string;
sellIntentId: string;
buyerDid: string;
buyerName: string;
sellerDid: string;
sellerName: string;
tokenId: TokenId;
tokenAmount: number; // agreed amount in base units
fiatCurrency: FiatCurrency;
fiatAmount: number; // agreed fiat amount
agreedRate: number; // fiat per token
paymentMethod: string;
escrowTxId?: string;
status: TradeStatus;
acceptances: Record<string, boolean>; // did → accepted?
chatMessages: TradeChatMessage[];
fiatConfirmDeadline?: number; // timestamp — 24h default
disputeReason?: string;
resolution?: 'released_to_buyer' | 'returned_to_seller';
createdAt: number;
completedAt?: number;
}
// ── Liquidity Pools ──
export type PoolStatus = 'active' | 'paused' | 'withdrawn';
export interface LiquidityPosition {
id: string;
creatorDid: string;
creatorName: string;
tokenId: TokenId;
fiatCurrency: FiatCurrency;
// Token side (sellable)
tokenCommitted: number; // base units locked for selling
tokenRemaining: number; // decrements as sells fill
// Fiat side (buyable)
fiatCommitted: number; // fiat amount committed for buying tokens
fiatRemaining: number; // decrements as buys fill
// Spread = the LP's fee (applied as market_plus_bps on both sides)
spreadBps: number; // e.g., 50 = 0.5% each side, 1% round-trip
// Payment
paymentMethods: string[];
// Linked standing order IDs (created automatically)
buyIntentId: string;
sellIntentId: string;
// Earnings tracking
feesEarnedToken: number; // token base units earned from spread
feesEarnedFiat: number; // fiat earned from spread
tradesMatched: number;
// State
status: PoolStatus;
createdAt: number;
updatedAt: number;
}
export interface ExchangePoolsDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
positions: Record<string, LiquidityPosition>;
}
// ── Reputation ──
export interface ExchangeReputationRecord {
did: string;
tradesCompleted: number;
tradesCancelled: number;
disputesRaised: number;
disputesLost: number;
totalVolumeBase: number; // total token volume in base units
avgConfirmTimeMs: number;
score: number; // 0-100
badges: string[]; // 'verified_seller', 'liquidity_provider', 'top_trader'
}
// ── Documents ──
export interface ExchangeIntentsDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
intents: Record<string, ExchangeIntent>;
}
export interface ExchangeTradesDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
trades: Record<string, ExchangeTrade>;
}
export interface ExchangeReputationDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
records: Record<string, ExchangeReputationRecord>;
}
// ── DocId helpers ──
export function exchangeIntentsDocId(space: string) {
return `${space}:rexchange:intents` as const;
}
export function exchangeTradesDocId(space: string) {
return `${space}:rexchange:trades` as const;
}
export function exchangeReputationDocId(space: string) {
return `${space}:rexchange:reputation` as const;
}
export function exchangePoolsDocId(space: string) {
return `${space}:rexchange:pools` as const;
}
// ── Schema registrations ──
export const exchangeIntentsSchema: DocSchema<ExchangeIntentsDoc> = {
module: 'rexchange',
collection: 'intents',
version: 1,
init: (): ExchangeIntentsDoc => ({
meta: {
module: 'rexchange',
collection: 'intents',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
intents: {},
}),
};
export const exchangeTradesSchema: DocSchema<ExchangeTradesDoc> = {
module: 'rexchange',
collection: 'trades',
version: 1,
init: (): ExchangeTradesDoc => ({
meta: {
module: 'rexchange',
collection: 'trades',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
trades: {},
}),
};
export const exchangeReputationSchema: DocSchema<ExchangeReputationDoc> = {
module: 'rexchange',
collection: 'reputation',
version: 1,
init: (): ExchangeReputationDoc => ({
meta: {
module: 'rexchange',
collection: 'reputation',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
records: {},
}),
};
export const exchangePoolsSchema: DocSchema<ExchangePoolsDoc> = {
module: 'rexchange',
collection: 'pools',
version: 1,
init: (): ExchangePoolsDoc => ({
meta: {
module: 'rexchange',
collection: 'pools',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
positions: {},
}),
};