/** * rExchange module — P2P crypto/fiat exchange within communities. * * Community members post buy/sell intents for CRDT tokens (cUSDC, $MYCO, fUSDC) * against fiat currencies. Solver matches intents, escrow handles settlement. * * All state stored in Automerge documents via SyncServer. * Doc layout: * {space}:rexchange:intents → ExchangeIntentsDoc * {space}:rexchange:trades → ExchangeTradesDoc * {space}:rexchange:reputation → ExchangeReputationDoc */ import { Hono } from 'hono'; import { renderShell } from '../../server/shell'; import { getModuleInfoList } from '../../shared/module'; import type { RSpaceModule } from '../../shared/module'; import type { SyncServer } from '../../server/local-first/sync-server'; import { renderLanding } from './landing'; import { exchangeIntentsSchema, exchangeTradesSchema, exchangeReputationSchema, } from './schemas'; import { createExchangeRoutes, startSolverCron, stopSolverCron } from './exchange-routes'; const routes = new Hono(); // ── SyncServer ref (set during onInit) ── let _syncServer: SyncServer | null = null; // ── Mount exchange routes ── const exchangeRoutes = createExchangeRoutes(() => _syncServer); routes.route('/', exchangeRoutes); // ── Page routes ── routes.get('/', (c) => { const space = c.req.param('space') || 'demo'; return c.html(renderShell({ title: `${space} — rExchange | rSpace`, moduleId: 'rexchange', spaceSlug: space, modules: getModuleInfoList(), theme: 'dark', body: ``, scripts: ``, })); }); // ── Module export ── export const exchangeModule: RSpaceModule = { id: 'rexchange', name: 'rExchange', icon: '💱', description: 'P2P crypto/fiat exchange with escrow & reputation', canvasShapes: ['folk-exchange-node'], canvasToolIds: ['create_exchange_node'], scoping: { defaultScope: 'space', userConfigurable: false }, docSchemas: [ { pattern: '{space}:rexchange:intents', description: 'Buy/sell intent order book', init: exchangeIntentsSchema.init }, { pattern: '{space}:rexchange:trades', description: 'Active and historical trades', init: exchangeTradesSchema.init }, { pattern: '{space}:rexchange:reputation', description: 'Per-member exchange reputation', init: exchangeReputationSchema.init }, ], routes, landingPage: renderLanding, async onInit(ctx) { _syncServer = ctx.syncServer; startSolverCron(() => _syncServer); }, feeds: [ { id: 'exchange-trades', name: 'Exchange Trades', kind: 'economic', description: 'P2P exchange trade completions', filterable: true, }, ], outputPaths: [ { path: 'canvas', name: 'Order Book', icon: '📊', description: 'Visual order book with buy/sell orbs' }, { path: 'collaborate', name: 'My Trades', icon: '🤝', description: 'Active trades and chat' }, ], onboardingActions: [ { label: 'Post Intent', icon: '💱', description: 'Post a buy or sell intent', type: 'create', href: '/rexchange' }, ], };