87 lines
3.0 KiB
TypeScript
87 lines
3.0 KiB
TypeScript
/**
|
|
* 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: `<folk-exchange-app space="${space}"></folk-exchange-app>`,
|
|
scripts: `<script type="module" src="/modules/rexchange/folk-exchange-app.js?v=1"></script>`,
|
|
}));
|
|
});
|
|
|
|
// ── 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' },
|
|
],
|
|
};
|