Initial project setup: Add contracts, API definitions, tests, and documentation
- Add Foundry project configuration (foundry.toml, foundry.lock) - Add Solidity contracts (TokenFactory138, BridgeVault138, ComplianceRegistry, etc.) - Add API definitions (OpenAPI, GraphQL, gRPC, AsyncAPI) - Add comprehensive test suite (unit, integration, fuzz, invariants) - Add API services (REST, GraphQL, orchestrator, packet service) - Add documentation (ISO20022 mapping, runbooks, adapter guides) - Add development tools (RBC tool, Swagger UI, mock server) - Update OpenZeppelin submodules to v5.0.0
This commit is contained in:
27
api/services/orchestrator/src/index.ts
Normal file
27
api/services/orchestrator/src/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* ISO-20022 Orchestrator Service
|
||||
* Manages trigger state machine and coordinates rail adapters
|
||||
*/
|
||||
|
||||
import express from 'express';
|
||||
import { orchestratorRouter } from './routes/orchestrator';
|
||||
import { triggerStateMachine } from './services/state-machine';
|
||||
import { isoRouter } from './services/iso-router';
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3002;
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
// Orchestrator API routes
|
||||
app.use('/v1/orchestrator', orchestratorRouter);
|
||||
|
||||
// ISO-20022 router
|
||||
app.use('/v1/iso', isoRouter);
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Orchestrator service listening on port ${PORT}`);
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
||||
47
api/services/orchestrator/src/routes/orchestrator.ts
Normal file
47
api/services/orchestrator/src/routes/orchestrator.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Orchestrator API routes
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { triggerStateMachine } from '../services/state-machine';
|
||||
|
||||
export const orchestratorRouter = Router();
|
||||
|
||||
orchestratorRouter.post('/triggers/:triggerId/validate-and-lock', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const trigger = await triggerStateMachine.validateAndLock(req.params.triggerId);
|
||||
res.json(trigger);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
orchestratorRouter.post('/triggers/:triggerId/mark-submitted', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { railTxRef } = req.body;
|
||||
const trigger = await triggerStateMachine.markSubmitted(req.params.triggerId, railTxRef);
|
||||
res.json(trigger);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
orchestratorRouter.post('/triggers/:triggerId/confirm-settled', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const trigger = await triggerStateMachine.confirmSettled(req.params.triggerId);
|
||||
res.json(trigger);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
orchestratorRouter.post('/triggers/:triggerId/confirm-rejected', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { reason } = req.body;
|
||||
const trigger = await triggerStateMachine.confirmRejected(req.params.triggerId, reason);
|
||||
res.json(trigger);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
60
api/services/orchestrator/src/services/iso-router.ts
Normal file
60
api/services/orchestrator/src/services/iso-router.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* ISO-20022 Router
|
||||
* Routes ISO-20022 messages to appropriate handlers and creates canonical messages
|
||||
*/
|
||||
|
||||
import { Router } from 'express';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
// Load ISO-20022 mappings
|
||||
const mappingsPath = join(__dirname, '../../../packages/schemas/iso20022-mapping/message-mappings.yaml');
|
||||
const mappings = yaml.load(readFileSync(mappingsPath, 'utf-8')) as any;
|
||||
|
||||
export const isoRouter = Router();
|
||||
|
||||
export const isoRouterService = {
|
||||
/**
|
||||
* Normalize ISO-20022 message to canonical format
|
||||
*/
|
||||
async normalizeMessage(msgType: string, payload: string, rail: string): Promise<any> {
|
||||
const mapping = mappings.mappings[msgType];
|
||||
if (!mapping) {
|
||||
throw new Error(`Unknown message type: ${msgType}`);
|
||||
}
|
||||
|
||||
// TODO: Parse XML payload and extract fields according to mapping
|
||||
// TODO: Create canonical message
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* Create trigger from canonical message
|
||||
*/
|
||||
async createTrigger(canonicalMessage: any, rail: string): Promise<string> {
|
||||
// TODO: Create trigger in database/state
|
||||
// TODO: Publish trigger.created event
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* Route inbound message
|
||||
*/
|
||||
async routeInbound(msgType: string, payload: string, rail: string): Promise<string> {
|
||||
const canonicalMessage = await this.normalizeMessage(msgType, payload, rail);
|
||||
const triggerId = await this.createTrigger(canonicalMessage, rail);
|
||||
return triggerId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Route outbound message
|
||||
*/
|
||||
async routeOutbound(msgType: string, payload: string, rail: string, config: any): Promise<string> {
|
||||
const canonicalMessage = await this.normalizeMessage(msgType, payload, rail);
|
||||
// TODO: Additional validation for outbound
|
||||
const triggerId = await this.createTrigger(canonicalMessage, rail);
|
||||
return triggerId;
|
||||
},
|
||||
};
|
||||
|
||||
81
api/services/orchestrator/src/services/state-machine.ts
Normal file
81
api/services/orchestrator/src/services/state-machine.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Trigger state machine
|
||||
* Manages trigger lifecycle: CREATED -> VALIDATED -> SUBMITTED -> PENDING -> SETTLED/REJECTED
|
||||
*/
|
||||
|
||||
export enum TriggerState {
|
||||
CREATED = 'CREATED',
|
||||
VALIDATED = 'VALIDATED',
|
||||
SUBMITTED_TO_RAIL = 'SUBMITTED_TO_RAIL',
|
||||
PENDING = 'PENDING',
|
||||
SETTLED = 'SETTLED',
|
||||
REJECTED = 'REJECTED',
|
||||
CANCELLED = 'CANCELLED',
|
||||
RECALLED = 'RECALLED',
|
||||
}
|
||||
|
||||
export interface Trigger {
|
||||
triggerId: string;
|
||||
state: TriggerState;
|
||||
rail: string;
|
||||
msgType: string;
|
||||
instructionId: string;
|
||||
// ... other fields
|
||||
}
|
||||
|
||||
export const triggerStateMachine = {
|
||||
/**
|
||||
* Validate and lock trigger
|
||||
*/
|
||||
async validateAndLock(triggerId: string): Promise<Trigger> {
|
||||
// TODO: Validate trigger, lock funds on-chain
|
||||
// Transition: CREATED -> VALIDATED
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark trigger as submitted to rail
|
||||
*/
|
||||
async markSubmitted(triggerId: string, railTxRef: string): Promise<Trigger> {
|
||||
// TODO: Update trigger with rail transaction reference
|
||||
// Transition: VALIDATED -> SUBMITTED_TO_RAIL -> PENDING
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm trigger settled
|
||||
*/
|
||||
async confirmSettled(triggerId: string): Promise<Trigger> {
|
||||
// TODO: Finalize on-chain, release locks if needed
|
||||
// Transition: PENDING -> SETTLED
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm trigger rejected
|
||||
*/
|
||||
async confirmRejected(triggerId: string, reason?: string): Promise<Trigger> {
|
||||
// TODO: Release locks, handle rejection
|
||||
// Transition: PENDING -> REJECTED
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if state transition is valid
|
||||
*/
|
||||
isValidTransition(from: TriggerState, to: TriggerState): boolean {
|
||||
const validTransitions: Record<TriggerState, TriggerState[]> = {
|
||||
[TriggerState.CREATED]: [TriggerState.VALIDATED, TriggerState.CANCELLED],
|
||||
[TriggerState.VALIDATED]: [TriggerState.SUBMITTED_TO_RAIL, TriggerState.CANCELLED],
|
||||
[TriggerState.SUBMITTED_TO_RAIL]: [TriggerState.PENDING, TriggerState.REJECTED],
|
||||
[TriggerState.PENDING]: [TriggerState.SETTLED, TriggerState.REJECTED, TriggerState.RECALLED],
|
||||
[TriggerState.SETTLED]: [],
|
||||
[TriggerState.REJECTED]: [],
|
||||
[TriggerState.CANCELLED]: [],
|
||||
[TriggerState.RECALLED]: [],
|
||||
};
|
||||
|
||||
return validTransitions[from]?.includes(to) ?? false;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user