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:
defiQUG
2025-12-12 10:59:41 -08:00
parent 26b5aaf932
commit 651ff4f7eb
281 changed files with 24813 additions and 2 deletions

61
docs/api/error-catalog.md Normal file
View File

@@ -0,0 +1,61 @@
# Error Catalog
This document maps Solidity reason codes to HTTP status codes and provides error handling guidance.
## Reason Code to HTTP Status Mapping
| Reason Code | HTTP Status | Description |
|------------|-------------|-------------|
| `OK` | 200 | Operation successful |
| `PAUSED` | 503 | Token is paused |
| `FROM_FROZEN` | 403 | Source account is frozen |
| `TO_FROZEN` | 403 | Destination account is frozen |
| `FROM_NOT_COMPLIANT` | 403 | Source account not compliant |
| `TO_NOT_COMPLIANT` | 403 | Destination account not compliant |
| `LIEN_BLOCK` | 403 | Transfer blocked by active lien (hard freeze mode) |
| `INSUFF_FREE_BAL` | 403 | Insufficient free balance (encumbered mode) |
| `BRIDGE_ONLY` | 403 | Token in bridge-only mode |
| `NOT_ALLOWED_ROUTE` | 403 | Payment rail not allowed |
| `UNAUTHORIZED` | 401 | Unauthorized operation |
| `CONFIG_ERROR` | 500 | Configuration error |
## Standard Error Response Format
```json
{
"code": "ERROR_CODE",
"message": "Human-readable error message",
"reasonCode": "PAUSED",
"details": {
"token": "0x1234...",
"account": "0xabcd..."
},
"requestId": "uuid-here"
}
```
## Retry Rules
### Retryable Errors (5xx)
- `500` Internal Server Error - Retry with exponential backoff
- `503` Service Unavailable - Retry with exponential backoff
- `502` Bad Gateway - Retry with exponential backoff
### Non-Retryable Errors (4xx)
- `400` Bad Request - Do not retry, fix request
- `401` Unauthorized - Do not retry, refresh token
- `403` Forbidden - Do not retry, check permissions
- `404` Not Found - Do not retry, check resource ID
- `409` Conflict - Do not retry, check idempotency key
## Idempotency
Endpoints marked with `x-idempotency: true` accept an `Idempotency-Key` header. Requests with the same key within 24 hours return the same response without re-executing.
## Error Handling Best Practices
1. Always include `reasonCode` in error responses for transfer operations
2. Use `requestId` for correlation in logs
3. Provide actionable error messages
4. Include relevant context in `details` field

View File

@@ -0,0 +1,246 @@
# Integration Cookbook
This document provides step-by-step guides for common integration flows.
## Table of Contents
1. [Deploy a Token](#deploy-a-token)
2. [Place a Lien](#place-a-lien)
3. [Submit ISO-20022 Message](#submit-iso-20022-message)
4. [Generate and Dispatch Packet](#generate-and-dispatch-packet)
5. [Bridge Lock/Unlock](#bridge-lockunlock)
## Deploy a Token
### REST API
```bash
POST /v1/tokens
Authorization: Bearer <token>
Idempotency-Key: <uuid>
{
"name": "USD Wrapped",
"symbol": "USDW",
"decimals": 18,
"issuer": "0x1234...",
"defaultLienMode": "ENCUMBERED",
"bridgeOnly": false
}
```
### GraphQL
```graphql
mutation {
deployToken(input: {
name: "USD Wrapped"
symbol: "USDW"
decimals: 18
issuer: "0x1234..."
defaultLienMode: ENCUMBERED
}) {
code
address
policy {
lienMode
}
}
}
```
## Place a Lien
### REST API
```bash
POST /v1/liens
Authorization: Bearer <token>
{
"debtor": "0xabcd...",
"amount": "1000000000000000000",
"priority": 1,
"reasonCode": "DEBT_ENFORCEMENT"
}
```
### GraphQL
```graphql
mutation {
placeLien(input: {
debtor: "0xabcd..."
amount: "1000000000000000000"
priority: 1
reasonCode: DEBT_ENFORCEMENT
}) {
lienId
amount
active
}
}
```
## Submit ISO-20022 Message
### Inbound (from rail adapter)
```bash
POST /v1/iso/inbound
Authorization: Bearer <token> (or mTLS)
Idempotency-Key: <uuid>
Content-Type: application/json
{
"msgType": "pacs.008",
"instructionId": "0x1234...",
"payloadHash": "0xabcd...",
"payload": "<Document>...</Document>",
"rail": "FEDWIRE"
}
```
### Outbound (from client)
```bash
POST /v1/iso/outbound
Authorization: Bearer <token>
Idempotency-Key: <uuid>
{
"msgType": "pain.001",
"instructionId": "0x1234...",
"payloadHash": "0xabcd...",
"payload": "<Document>...</Document>",
"rail": "SEPA",
"token": "0x5678...",
"amount": "1000000000000000000",
"accountRefId": "0xdef0...",
"counterpartyRefId": "0x9876..."
}
```
## Generate and Dispatch Packet
### Step 1: Generate Packet
```bash
POST /v1/packets
Authorization: Bearer <token>
Idempotency-Key: <uuid>
{
"triggerId": "abc123...",
"channel": "PDF"
}
```
### Step 2: Dispatch Packet
```bash
POST /v1/packets/{packetId}/dispatch
Authorization: Bearer <token>
Idempotency-Key: <uuid>
{
"channel": "EMAIL",
"recipient": "recipient@example.com"
}
```
### Step 3: Record Acknowledgement
```bash
POST /v1/packets/{packetId}/ack
Authorization: Bearer <token>
Idempotency-Key: <uuid>
{
"status": "ACCEPTED",
"ackId": "ack-123"
}
```
## Bridge Lock/Unlock
### Lock Tokens
```bash
POST /v1/bridge/lock
Authorization: Bearer <token>
{
"token": "0x1234...",
"amount": "1000000000000000000",
"targetChain": "0x0000...0001",
"targetRecipient": "0xabcd..."
}
```
### Unlock Tokens
```bash
POST /v1/bridge/unlock
Authorization: Bearer <token>
Idempotency-Key: <uuid>
{
"lockId": "lock-123",
"token": "0x1234...",
"to": "0xabcd...",
"amount": "1000000000000000000",
"sourceChain": "0x0000...0001",
"sourceTx": "0x5678...",
"proof": "0xdef0..."
}
```
## Webhook Integration
### Register Webhook
```bash
POST /v1/webhooks
Authorization: Bearer <token>
{
"url": "https://example.com/webhooks",
"events": ["triggers.created", "liens.placed"],
"secret": "webhook-secret"
}
```
### Webhook Payload
```json
{
"eventId": "uuid",
"eventType": "triggers.created",
"occurredAt": "2024-01-01T00:00:00Z",
"payload": {
"triggerId": "abc123...",
"rail": "FEDWIRE"
},
"signatures": [{
"signer": "system",
"signature": "hmac-sha256-signature"
}]
}
```
### Verify Webhook Signature
```javascript
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const expectedSignature = hmac.update(JSON.stringify(payload)).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
```

View File

@@ -0,0 +1,95 @@
# ISO-20022 Mapping Handbook
This document provides detailed guidance on ISO-20022 message processing and mapping to canonical formats.
## Message Type Overview
| Message Type | Direction | Purpose | Trigger Action |
|-------------|-----------|---------|----------------|
| `pain.001` | Outbound | Customer Credit Transfer Initiation | `validateAndLock()``markSubmitted()` |
| `pacs.008` | Outbound | FIToFICustomerCreditTransfer | `validateAndLock()``markSubmitted()` |
| `pacs.009` | Outbound | FinancialInstitutionCreditTransfer | `validateAndLock()``markSubmitted()` |
| `camt.054` | Inbound | BankToCustomerDebitCreditNotification | `confirmSettled()` (mint tokens) |
| `pacs.002` | Inbound | Payment Status Report | `confirmSettled()` or `confirmRejected()` |
| `pacs.004` | Return | Payment Return | `confirmRejected()` (release escrow) |
| `camt.056` | Cancellation | FIToFIPaymentCancellationRequest | `confirmCancelled()` (release escrow) |
## Field Mapping
### pain.001 Mapping
```yaml
instructionId: Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf/PmtId/InstrId
endToEndId: Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf/PmtId/EndToEndId
amount: Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf/Amt/InstdAmt
currency: Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf/Amt/InstdAmt/@Ccy
debtorAccount: Document/CstmrCdtTrfInitn/PmtInf/DbtrAcct/Id/Othr/Id
creditorAccount: Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf/CdtrAcct/Id/Othr/Id
```
### pacs.008 Mapping
```yaml
instructionId: Document/FIToFICstmrCdtTrf/GrpHdr/MsgId
endToEndId: Document/FIToFICstmrCdtTrf/CdtTrfTxInf/PmtId/EndToEndId
amount: Document/FIToFICstmrCdtTrf/CdtTrfTxInf/IntrBkSttlmAmt
currency: Document/FIToFICstmrCdtTrf/CdtTrfTxInf/IntrBkSttlmAmt/@Ccy
```
### pacs.002 Status Mapping
| Status Code | Meaning | Action |
|------------|---------|--------|
| `ACSC` | AcceptedSettlementCompleted | `confirmSettled()` |
| `RJCT` | Rejected | `confirmRejected()` |
| `PNDG` | Pending | Wait for final status |
| `CANC` | Cancelled | `confirmCancelled()` |
## Processing Flow
### Outbound Flow
1. Client submits `pain.001` or `pacs.008` via `/v1/iso/outbound`
2. ISO Router normalizes message to canonical format
3. Orchestrator creates trigger in `CREATED` state
4. Orchestrator validates and locks funds (`VALIDATED` state)
5. Adapter submits to rail (`SUBMITTED_TO_RAIL` state)
6. Trigger moves to `PENDING` state
7. Rail adapter receives `pacs.002` or `camt.054`
8. Orchestrator confirms settled/rejected (`SETTLED`/`REJECTED` state)
### Inbound Flow
1. Rail adapter receives `camt.054` or `pacs.002`
2. Adapter submits via `/v1/iso/inbound` (mTLS)
3. ISO Router normalizes message
4. Orchestrator creates trigger
5. For `camt.054` with credit: mint tokens and `confirmSettled()`
6. For `pacs.002` with `RJCT`: `confirmRejected()`
## Idempotency
All ISO-20022 message submissions are idempotent by `instructionId`. Duplicate submissions with the same `instructionId` are rejected.
## Payload Storage
- Full ISO-20022 XML payloads stored off-chain
- Only `payloadHash` stored on-chain in trigger
- Payloads can be retrieved via API for reconciliation
## Reconciliation
Use `instructionId` and `endToEndId` for end-to-end reconciliation:
```bash
GET /v1/triggers?instructionId=0x1234...
GET /v1/triggers?endToEndId=0xabcd...
```
## Error Handling
Invalid ISO-20022 messages trigger `confirmRejected()` with appropriate reason codes:
- Schema validation errors → `CONFIG_ERROR`
- Missing required fields → `CONFIG_ERROR`
- Invalid amounts → `CONFIG_ERROR`

View File

@@ -0,0 +1,248 @@
# Swagger UI Documentation Guide
## Overview
The Swagger UI provides interactive, browser-based documentation for the eMoney Token Factory API. It automatically generates documentation from the OpenAPI 3.1 specification.
## Accessing the Documentation
### Local Development
```bash
cd api/tools/swagger-ui
pnpm install
pnpm run dev
```
Visit: **http://localhost:8080/api-docs**
### Production
The Swagger UI can be:
- Deployed as a standalone service
- Embedded in the main API server
- Served via CDN
- Generated as static HTML
## Features
### 1. Interactive API Explorer
- **Browse Endpoints**: Navigate through all API endpoints organized by tags
- **View Schemas**: Explore request/response data models
- **See Examples**: View example payloads for each endpoint
- **Filter**: Search and filter endpoints by tag or keyword
### 2. Try It Out
- **Test API Calls**: Execute API requests directly from the browser
- **Set Parameters**: Fill in path, query, and body parameters
- **View Responses**: See real API responses with status codes
- **Debug**: Inspect request/response headers and bodies
### 3. Authentication
- **OAuth2**: Test OAuth2 client credentials flow
- **mTLS**: Configure mutual TLS for adapter endpoints
- **API Key**: Set API keys for internal services
- **Token Persistence**: Authorization tokens persist across page reloads
### 4. Schema Documentation
- **Data Models**: View all data structures with field descriptions
- **Enums**: See all possible enum values
- **Relationships**: Understand how models relate to each other
- **Examples**: View example JSON for each model
## Using the Documentation
### Finding an Endpoint
1. Use the search box to filter endpoints
2. Expand tags to see related endpoints
3. Click on an endpoint to see details
### Testing an Endpoint
1. Click "Try it out" button
2. Fill in required parameters
3. Click "Execute"
4. View the response below
### Setting Authentication
1. Click "Authorize" button at the top
2. Enter your OAuth2 token or API key
3. Click "Authorize"
4. Token will be used for all requests
### Exporting the Spec
- **JSON**: Visit `/openapi.json`
- **YAML**: Visit `/openapi.yaml`
- **Download**: Use the download button in Swagger UI
## Endpoint Categories
### Tokens
- Deploy new tokens
- Manage token policies
- Mint/burn operations
- Clawback and force transfer
### Liens
- Place liens on accounts
- Reduce or release liens
- Query lien information
- Check encumbrance
### Compliance
- Set compliance profiles
- Freeze/unfreeze accounts
- Manage risk tiers
- Set jurisdiction information
### Mappings
- Link accounts to wallets
- Query bidirectional mappings
- Manage provider connections
### Triggers
- Submit ISO-20022 messages
- Query trigger status
- Manage trigger lifecycle
- View trigger history
### ISO-20022
- Submit inbound messages
- Submit outbound messages
- Normalize messages
- Track message processing
### Packets
- Generate packets
- Dispatch packets
- Track acknowledgements
- Download packet files
### Bridge
- Lock tokens for cross-chain
- Unlock tokens with proofs
- Query lock status
- View supported corridors
## Best Practices
### For Developers
1. **Start with Examples**: Use the example payloads as starting points
2. **Test Locally**: Use Swagger UI to test before writing code
3. **Check Schemas**: Understand data models before integration
4. **Use Try It Out**: Validate your understanding of endpoints
### For Integration Teams
1. **Share Links**: Share specific endpoint URLs with team members
2. **Export Specs**: Download OpenAPI spec for code generation
3. **Document Issues**: Use Swagger UI to demonstrate API issues
4. **Validate Requests**: Use Try It Out to validate request formats
## Troubleshooting
### Endpoint Not Showing
- Check if endpoint is in OpenAPI spec
- Verify tag is correct
- Check for syntax errors in spec
### Try It Out Not Working
- Verify server is running
- Check CORS settings
- Ensure authentication is set
- Check network tab for errors
### Authentication Failing
- Verify token format
- Check token expiration
- Ensure correct OAuth2 flow
- Check server logs
## Integration
### Embed in Main API
Add to `api/services/rest-api/src/index.ts`:
```typescript
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';
import { join } from 'path';
const openapiSpec = YAML.load(join(__dirname, '../../packages/openapi/v1/openapi.yaml'));
app.use('/docs', swaggerUi.serve, swaggerUi.setup(openapiSpec));
```
### Standalone Deployment
Deploy as separate service:
- Lightweight and fast
- Can be behind CDN
- No API dependencies
- Easy to update
### Static HTML Generation
Generate standalone HTML file:
```bash
cd api/tools/swagger-ui
pnpm run generate:standalone
```
Output: `static/standalone.html` (can be opened directly in browser)
## Advanced Configuration
### Custom Theme
Edit `src/index.ts`:
```typescript
const swaggerOptions = {
customCss: `
.swagger-ui .info .title { color: #your-color; }
`,
};
```
### Default Values
Set default server URL:
```typescript
swaggerOptions: {
url: 'https://api.emoney.example.com/v1',
}
```
### OAuth2 Configuration
Configure OAuth2 redirect:
```typescript
swaggerOptions: {
oauth2RedirectUrl: 'https://your-domain.com/oauth2-redirect.html',
}
```
## Support
For issues or questions:
- Check OpenAPI spec syntax
- Verify server configuration
- Review Swagger UI logs
- Consult Swagger UI documentation

View File

@@ -0,0 +1,153 @@
# API Versioning Policy
This document defines the versioning strategy for all API types in the eMoney Token Factory system.
## Versioning Schemes
### REST API
- **URL-based versioning**: `/v1/`, `/v2/`, etc.
- **Additive changes only**: New fields, endpoints, or query parameters are allowed
- **Breaking changes**: Require new version (e.g., `/v2/`)
- **Deprecation window**: Minimum 6 months before removal
### GraphQL API
- **Schema versioning**: Single schema with deprecation warnings
- **Field deprecation**: Use `@deprecated` directive
- **Breaking changes**: Add new fields, deprecate old ones
- **Removal**: After deprecation period (minimum 6 months)
### AsyncAPI
- **Event type versioning**: `triggers.state.updated.v1`, `triggers.state.updated.v2`
- **Event envelope**: Versioned separately
- **Backward compatibility**: Old event types supported for 6 months
### gRPC/Protobuf
- **Package versioning**: `emoney.orchestrator.v1`, `emoney.orchestrator.v2`
- **Service versioning**: New service versions for breaking changes
- **Message compatibility**: Follow Protobuf compatibility rules
## Breaking Change Definition
A change is considered breaking if it:
1. Removes an endpoint, field, or parameter
2. Changes the type of a field or parameter
3. Changes the semantics of an endpoint
4. Removes an enum value
5. Changes authentication requirements
6. Changes error response format
## Non-Breaking Changes
These changes are allowed without version bump:
1. Adding new endpoints
2. Adding optional fields to requests/responses
3. Adding new enum values
4. Adding new query parameters
5. Improving error messages
6. Adding new response fields
## Deprecation Process
1. **Announcement**: Mark as deprecated in API documentation
2. **Warning Period**: 6 months minimum
3. **Removal**: Remove in next major version
### GraphQL Deprecation Example
```graphql
type Token {
code: String!
address: String!
oldField: String @deprecated(reason: "Use newField instead")
newField: String!
}
```
### REST Deprecation Example
```http
GET /v1/tokens
Deprecation: true
Sunset: 2024-07-01
Link: <https://api.emoney.example.com/docs/v2>; rel="successor-version"
```
## Version Lifecycle
1. **Alpha**: Internal testing only
2. **Beta**: Public beta, may have breaking changes
3. **Stable**: Production-ready, follows versioning policy
4. **Deprecated**: Scheduled for removal
5. **Sunset**: No longer supported
## Migration Guide
When a new version is released:
1. Provide migration guide in documentation
2. Offer SDK updates
3. Provide compatibility layer if possible
4. Support both versions during transition period
## Examples
### REST API Versioning
```
/v1/tokens # Version 1
/v2/tokens # Version 2 (breaking changes)
/v1/tokens # Still supported during transition
```
### GraphQL Deprecation
```graphql
# v1
type Token {
oldName: String!
}
# v2 (additive)
type Token {
oldName: String! @deprecated(reason: "Use name instead")
name: String!
}
# v3 (removal)
type Token {
name: String!
}
```
### AsyncAPI Versioning
```yaml
# v1
channels:
triggers.created.v1:
# ...
# v2
channels:
triggers.created.v2:
# ...
triggers.created.v1: # Still supported
# ...
```
## Compliance
All API changes must:
1. Follow semantic versioning principles
2. Maintain backward compatibility within major version
3. Provide deprecation warnings before removal
4. Document migration path
5. Support transition period (minimum 6 months)