feat: OMNL Bank online production — 128 chains, Central Bank UI, settlement stack
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m22s
CI/CD Pipeline / Security Scanning (push) Successful in 2m30s
CI/CD Pipeline / Lint and Format (push) Failing after 44s
CI/CD Pipeline / Terraform Validation (push) Failing after 27s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 31s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 56s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 29s
Validation / validate-genesis (push) Successful in 34s
Validation / validate-terraform (push) Failing after 39s
Validation / validate-kubernetes (push) Failing after 14s
Validation / validate-smart-contracts (push) Failing after 20s
Validation / validate-security (push) Failing after 1m19s
Validation / validate-documentation (push) Failing after 27s
Verify Deployment / Verify Deployment (push) Failing after 1m13s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-28 08:44:42 -07:00
parent 2b1a3ba6af
commit 5dd67e2b1d
113 changed files with 13223 additions and 98 deletions

View File

@@ -0,0 +1,39 @@
export type OmnlChainEntry = {
chainId: number;
name: string;
type: 'evm' | 'non-evm';
nativeSymbol: string;
tier: string;
omnlRole: string;
status: 'active' | 'staged' | 'reserved';
rpcEnv: string;
explorerUrl?: string;
rpcDefault?: string;
settlement: {
m2LoadEnabled: boolean;
swapEnabled: boolean;
bridgeEnabled: boolean;
officeId: number;
};
complianceEnv?: string;
};
export type OmnlChainRegistry = {
version: string;
brand: string;
maxCapacity: number;
primaryChainId: number;
mirrorChainId: number;
settlementOfficeId: number;
chains: OmnlChainEntry[];
stats?: {
total: number;
active: number;
staged: number;
reserved: number;
};
};
export declare function resolveChainRegistryPath(): string;
export declare function loadOmnlChainRegistry(): OmnlChainRegistry;
export declare function getActiveChains(): OmnlChainEntry[];
export declare function getChainById(chainId: number): OmnlChainEntry | undefined;
export declare function getSupportedChainIds128(): number[];

View File

@@ -0,0 +1,43 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveChainRegistryPath = resolveChainRegistryPath;
exports.loadOmnlChainRegistry = loadOmnlChainRegistry;
exports.getActiveChains = getActiveChains;
exports.getChainById = getChainById;
exports.getSupportedChainIds128 = getSupportedChainIds128;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
let cached = null;
function resolveChainRegistryPath() {
if (process.env.OMNL_SUPPORTED_CHAINS_CONFIG)
return process.env.OMNL_SUPPORTED_CHAINS_CONFIG;
const candidates = [
path_1.default.resolve(process.cwd(), 'config/omnl-supported-chains.v1.json'),
path_1.default.resolve(__dirname, '../../../config/omnl-supported-chains.v1.json'),
path_1.default.resolve(__dirname, '../../../../config/omnl-supported-chains.v1.json'),
];
for (const p of candidates) {
if (fs_1.default.existsSync(p))
return p;
}
return candidates[1];
}
function loadOmnlChainRegistry() {
if (cached)
return cached;
const p = resolveChainRegistryPath();
cached = JSON.parse(fs_1.default.readFileSync(p, 'utf8'));
return cached;
}
function getActiveChains() {
return loadOmnlChainRegistry().chains.filter((c) => c.status === 'active');
}
function getChainById(chainId) {
return loadOmnlChainRegistry().chains.find((c) => c.chainId === chainId);
}
function getSupportedChainIds128() {
return loadOmnlChainRegistry().chains.map((c) => c.chainId);
}

View File

@@ -0,0 +1,7 @@
export * from './types';
export * from './money-supply';
export * from './instruments';
export * from './verbiage';
export * from './state-machine';
export * from './m2-token-registry';
export * from './chain-registry';

23
packages/settlement-core/dist/index.js vendored Normal file
View File

@@ -0,0 +1,23 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./types"), exports);
__exportStar(require("./money-supply"), exports);
__exportStar(require("./instruments"), exports);
__exportStar(require("./verbiage"), exports);
__exportStar(require("./state-machine"), exports);
__exportStar(require("./m2-token-registry"), exports);
__exportStar(require("./chain-registry"), exports);

View File

@@ -0,0 +1,13 @@
import type { SwiftMessageKind, TradeFinanceInstrument } from './types';
/** SWIFT / trade-finance instrument routing */
export declare const INSTRUMENT_ROUTING: Record<TradeFinanceInstrument, {
swift: SwiftMessageKind[];
glDebit: string;
glCredit: string;
}>;
export declare function resolveInstrumentGl(instrument: TradeFinanceInstrument): {
debitGl: string;
creditGl: string;
swiftKinds: SwiftMessageKind[];
};
export declare function isIbanValid(iban: string): boolean;

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.INSTRUMENT_ROUTING = void 0;
exports.resolveInstrumentGl = resolveInstrumentGl;
exports.isIbanValid = isIbanValid;
/** SWIFT / trade-finance instrument routing */
exports.INSTRUMENT_ROUTING = {
SBLC: { swift: ['MT103', 'MT202'], glDebit: '1410', glCredit: '2100' },
DLC: { swift: ['MT103', 'MT102'], glDebit: '1410', glCredit: '1000' },
BG: { swift: ['MT103'], glDebit: '1410', glCredit: '2100' },
LS: { swift: ['MT102', 'MT103'], glDebit: '1000', glCredit: '2200' },
};
function resolveInstrumentGl(instrument) {
const r = exports.INSTRUMENT_ROUTING[instrument];
return { debitGl: r.glDebit, creditGl: r.glCredit, swiftKinds: r.swift };
}
function isIbanValid(iban) {
const normalized = iban.replace(/\s/g, '').toUpperCase();
if (!/^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/.test(normalized))
return false;
const rearranged = normalized.slice(4) + normalized.slice(0, 4);
const numeric = rearranged.replace(/[A-Z]/g, (c) => String(c.charCodeAt(0) - 55));
let remainder = 0;
for (const ch of numeric) {
remainder = (remainder * 10 + parseInt(ch, 10)) % 97;
}
return remainder === 1;
}

View File

@@ -0,0 +1,44 @@
import type { MoneyLayer } from './types';
export type M2TokenCapabilities = {
symbol: string;
name: string;
address: string;
decimals: number;
native?: boolean;
currencyCode?: string;
moneyLayer: MoneyLayer;
omnlLine: string;
loadFromGl: string;
swappable: boolean;
convertible: boolean;
transferableInternal: boolean;
transferableExternal: boolean;
};
export type M2TokenRegistryFile = {
version: string;
chainId: number;
defaultMoneyLayer: MoneyLayer;
defaultLoadFromGl: string;
tokens: M2TokenCapabilities[];
};
export declare function inferCurrencyCode(symbol: string): string;
export declare function resolveOmnlLineM2(currencyCode?: string, symbol?: string): string;
export declare function withM2Capabilities(token: {
symbol: string;
name: string;
address: string;
decimals: number;
native?: boolean;
currencyCode?: string;
omnlLine?: string;
}, overrides?: Partial<M2TokenCapabilities>): M2TokenCapabilities;
export declare function mergeExchangeTokensWithM2Registry(baseTokens: {
symbol: string;
name: string;
address: string;
decimals: number;
native?: boolean;
omnlLine?: string;
}[], registry: M2TokenRegistryFile): M2TokenCapabilities[];
export declare function findM2TokenByAddress(registry: M2TokenRegistryFile, address: string): M2TokenCapabilities | undefined;
export declare function findM2TokenBySymbol(registry: M2TokenRegistryFile, symbol: string): M2TokenCapabilities | undefined;

View File

@@ -0,0 +1,86 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.inferCurrencyCode = inferCurrencyCode;
exports.resolveOmnlLineM2 = resolveOmnlLineM2;
exports.withM2Capabilities = withM2Capabilities;
exports.mergeExchangeTokensWithM2Registry = mergeExchangeTokensWithM2Registry;
exports.findM2TokenByAddress = findM2TokenByAddress;
exports.findM2TokenBySymbol = findM2TokenBySymbol;
const money_supply_1 = require("./money-supply");
const CURRENCY_FROM_SYMBOL = {
USD: 'USD',
EUR: 'EUR',
GBP: 'GBP',
AUD: 'AUD',
JPY: 'JPY',
CHF: 'CHF',
CAD: 'CAD',
XAU: 'XAU',
BTC: 'BTC',
ETH: 'ETH',
BNB: 'BNB',
POL: 'POL',
AVAX: 'AVAX',
CRO: 'CRO',
XDAI: 'XDAI',
CELO: 'CELO',
WEMIX: 'WEMIX',
};
function inferCurrencyCode(symbol) {
const s = symbol.toUpperCase();
for (const [key, code] of Object.entries(CURRENCY_FROM_SYMBOL)) {
if (s.includes(key))
return code;
}
if (s.startsWith('C') && s.length > 1) {
for (const [key, code] of Object.entries(CURRENCY_FROM_SYMBOL)) {
if (s.slice(1).includes(key))
return code;
}
}
return 'USD';
}
function resolveOmnlLineM2(currencyCode, symbol) {
const ccy = (currencyCode || inferCurrencyCode(symbol ?? 'USD')).toUpperCase();
return `${ccy}-M2`;
}
function withM2Capabilities(token, overrides = {}) {
const currencyCode = token.currencyCode ?? inferCurrencyCode(token.symbol);
return {
symbol: token.symbol,
name: token.name,
address: token.address,
decimals: token.decimals,
native: token.native,
currencyCode,
moneyLayer: 'M2',
omnlLine: token.omnlLine ?? resolveOmnlLineM2(currencyCode, token.symbol),
loadFromGl: money_supply_1.GL_CODES.M2_BROAD,
swappable: true,
convertible: true,
transferableInternal: true,
transferableExternal: true,
...overrides,
};
}
function mergeExchangeTokensWithM2Registry(baseTokens, registry) {
const byAddress = new Map();
for (const t of registry.tokens) {
byAddress.set(t.address.toLowerCase(), t);
}
for (const t of baseTokens) {
const key = t.address.toLowerCase();
if (!byAddress.has(key)) {
byAddress.set(key, withM2Capabilities(t));
}
}
return Array.from(byAddress.values()).sort((a, b) => a.symbol.localeCompare(b.symbol));
}
function findM2TokenByAddress(registry, address) {
const lower = address.toLowerCase();
return registry.tokens.find((t) => t.address.toLowerCase() === lower);
}
function findM2TokenBySymbol(registry, symbol) {
const s = symbol.trim().toLowerCase();
return registry.tokens.find((t) => t.symbol.toLowerCase() === s);
}

View File

@@ -0,0 +1,26 @@
import type { MoneyLayer, MoneySupplySnapshot } from './types';
/** IPSAS GL mapping for OMNL central-bank money aggregates */
export declare const GL_CODES: {
readonly M0_RESERVE: "1050";
readonly M1_LIABILITY: "2000";
readonly M1_CIRCULATING: "2100";
readonly M2_BROAD: "2200";
readonly SETTLEMENT_SUSPENSE: "1000";
readonly TRADE_FINANCE_CONTINGENT: "1410";
readonly CRYPTO_TOKEN_LIABILITY: "2300";
};
/** Policy caps from hybx-omnl-policy (1.2× M0 reserve → M1 capacity; 5× M1 → M2 broad) */
export declare const POLICY: {
readonly M0_BACKING_MULTIPLIER: 1.2;
readonly M1_CAP_MULTIPLIER: 5;
readonly M2_META_FIAT_RAIL_CONVERSION: true;
};
export declare function aggregateMetaFiatLayers(layers: Partial<Record<MoneyLayer, string>>): {
metaFiatTotal: string;
breakdown: Record<MoneyLayer, string>;
};
export declare function buildMoneySupplySnapshot(officeId: number, balances: Partial<Record<string, string>>): MoneySupplySnapshot;
export declare function m2FiatToTokenLoadAmount(fiatAmount: string, m2Available: string): {
loadAmount: string;
fullyBacked: boolean;
};

View File

@@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.POLICY = exports.GL_CODES = void 0;
exports.aggregateMetaFiatLayers = aggregateMetaFiatLayers;
exports.buildMoneySupplySnapshot = buildMoneySupplySnapshot;
exports.m2FiatToTokenLoadAmount = m2FiatToTokenLoadAmount;
/** IPSAS GL mapping for OMNL central-bank money aggregates */
exports.GL_CODES = {
M0_RESERVE: '1050',
M1_LIABILITY: '2000',
M1_CIRCULATING: '2100',
M2_BROAD: '2200',
SETTLEMENT_SUSPENSE: '1000',
TRADE_FINANCE_CONTINGENT: '1410',
CRYPTO_TOKEN_LIABILITY: '2300',
};
/** Policy caps from hybx-omnl-policy (1.2× M0 reserve → M1 capacity; 5× M1 → M2 broad) */
exports.POLICY = {
M0_BACKING_MULTIPLIER: 1.2,
M1_CAP_MULTIPLIER: 5,
M2_META_FIAT_RAIL_CONVERSION: true,
};
function aggregateMetaFiatLayers(layers) {
const breakdown = {
M0: layers.M0 ?? '0',
M1: layers.M1 ?? '0',
M2: layers.M2 ?? '0',
M00: layers.M00 ?? '0',
};
const sum = [breakdown.M0, breakdown.M1, breakdown.M2]
.map((v) => parseFloat(v) || 0)
.reduce((a, b) => a + b, 0);
return { metaFiatTotal: sum.toFixed(2), breakdown };
}
function buildMoneySupplySnapshot(officeId, balances) {
const m0 = balances[exports.GL_CODES.M0_RESERVE] ?? '0';
const m1 = balances[exports.GL_CODES.M1_CIRCULATING] ?? '0';
const m2 = balances[exports.GL_CODES.M2_BROAD] ?? '0';
const pending = balances[exports.GL_CODES.SETTLEMENT_SUSPENSE] ?? '0';
const m0Num = parseFloat(m0) || 0;
const m1Num = parseFloat(m1) || 0;
return {
asOf: new Date().toISOString(),
officeId,
M0: {
gl1050: m0,
backingRatio: exports.POLICY.M0_BACKING_MULTIPLIER,
},
M1: {
circulating: m1,
gl2000: balances[exports.GL_CODES.M1_LIABILITY] ?? m1,
gl2100: m1,
capMultiple: exports.POLICY.M1_CAP_MULTIPLIER,
},
M2: {
broadMoney: m2,
gl2200: m2,
metaFiatPending: pending,
},
metaFiatRealRail: {
pendingSettlement: pending,
inFlightSwift: balances['inFlightSwift'] ?? '0',
},
};
}
function m2FiatToTokenLoadAmount(fiatAmount, m2Available) {
const fiat = parseFloat(fiatAmount) || 0;
const m2 = parseFloat(m2Available) || 0;
const load = Math.min(fiat, m2);
return {
loadAmount: load.toFixed(2),
fullyBacked: load >= fiat && fiat > 0,
};
}

View File

@@ -0,0 +1,3 @@
import type { SettlementPhase } from './types';
export declare function nextPhase(current: SettlementPhase): SettlementPhase | null;
export declare function canAdvance(from: SettlementPhase, to: SettlementPhase): boolean;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nextPhase = nextPhase;
exports.canAdvance = canAdvance;
const ORDER = [
'RECEIVED',
'VALIDATED',
'VERBIAGE_ROLLED',
'FINERACT_POSTED',
'HYBX_RAIL_DISPATCHED',
'ISO20022_ARCHIVED',
'CHAIN_MINT_REQUESTED',
'SETTLED',
];
function nextPhase(current) {
if (current === 'FAILED' || current === 'SETTLED')
return null;
const idx = ORDER.indexOf(current);
if (idx < 0 || idx >= ORDER.length - 1)
return null;
return ORDER[idx + 1];
}
function canAdvance(from, to) {
const fi = ORDER.indexOf(from);
const ti = ORDER.indexOf(to);
if (fi < 0 || ti < 0)
return to === 'FAILED';
return ti === fi + 1 || to === 'FAILED';
}

102
packages/settlement-core/dist/types.d.ts vendored Normal file
View File

@@ -0,0 +1,102 @@
/** OMNL central-bank settlement domain types */
export type MoneyLayer = 'M0' | 'M1' | 'M2' | 'M00';
export type PaymentRail = 'SWIFT' | 'SEPA' | 'TARGET2' | 'RTGS' | 'ACH' | 'FEDWIRE' | 'INTERNAL' | 'CHAIN138';
export type TradeFinanceInstrument = 'SBLC' | 'DLC' | 'BG' | 'LS';
export type SwiftMessageKind = 'MT103' | 'MT102' | 'MT202' | 'MT910';
export type SettlementPhase = 'RECEIVED' | 'VALIDATED' | 'VERBIAGE_ROLLED' | 'FINERACT_POSTED' | 'HYBX_RAIL_DISPATCHED' | 'ISO20022_ARCHIVED' | 'CHAIN_MINT_REQUESTED' | 'SETTLED' | 'FAILED';
export type ExternalTransferRequest = {
idempotencyKey: string;
officeId: number;
valueDate: string;
currency: string;
amount: string;
debtorIban?: string;
creditorIban: string;
creditorBic?: string;
beneficiaryName: string;
orderingName?: string;
remittanceInfo: string;
moneyLayers: MoneyLayer[];
rail: PaymentRail;
swiftMessageKind?: SwiftMessageKind;
tradeFinance?: {
instrument: TradeFinanceInstrument;
reference: string;
expiryDate?: string;
};
rwaVerbiage?: {
assetClass: string;
tokenLineId: string;
externalTransferRoll: boolean;
};
convertToCrypto?: {
enabled: boolean;
targetChainId: number;
tokenLineId: string;
recipientAddress: string;
};
};
export type TokenLoadRequest = {
idempotencyKey: string;
officeId?: number;
lineId: string;
symbol?: string;
tokenAddress?: string;
amount: string;
recipientAddress: string;
currency?: string;
};
export type TransferRequest = {
idempotencyKey: string;
officeId?: number;
tokenAddress: string;
tokenSymbol: string;
amount: string;
recipientAddress: string;
senderAddress?: string;
moneyLayers: MoneyLayer[];
rail: 'INTERNAL' | 'CHAIN138' | 'SWIFT' | 'RTGS';
/** External SWIFT/HYBX beneficiary IBAN when rail is external */
creditorIban?: string;
beneficiaryName?: string;
remittanceInfo?: string;
currency?: string;
};
export type SettlementRecord = {
settlementId: string;
idempotencyKey: string;
phase: SettlementPhase;
request: ExternalTransferRequest;
fineractJournalRef?: string;
hybxPaymentId?: string;
iso20022MessageId?: string;
chainTxHash?: string;
verbiageDocument?: string;
errors: string[];
createdAt: string;
updatedAt: string;
};
export type MoneySupplySnapshot = {
asOf: string;
officeId: number;
M0: {
reserveOz?: string;
gl1050: string;
backingRatio: number;
};
M1: {
circulating: string;
gl2000: string;
gl2100: string;
capMultiple: number;
};
M2: {
broadMoney: string;
gl2200: string;
metaFiatPending: string;
};
metaFiatRealRail: {
pendingSettlement: string;
inFlightSwift: string;
};
};

View File

@@ -0,0 +1,3 @@
"use strict";
/** OMNL central-bank settlement domain types */
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,6 @@
import type { ExternalTransferRequest, SwiftMessageKind } from './types';
/** OMNL central-bank external transfer verbiage roll (audit + operator copy) */
export declare function rollExternalTransferVerbiage(req: ExternalTransferRequest): string;
export declare function buildMt103Narrative(req: ExternalTransferRequest): string;
export declare function buildMt102Narrative(req: ExternalTransferRequest): string;
export declare function swiftKindForRequest(req: ExternalTransferRequest): SwiftMessageKind;

View File

@@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.rollExternalTransferVerbiage = rollExternalTransferVerbiage;
exports.buildMt103Narrative = buildMt103Narrative;
exports.buildMt102Narrative = buildMt102Narrative;
exports.swiftKindForRequest = swiftKindForRequest;
/** OMNL central-bank external transfer verbiage roll (audit + operator copy) */
function rollExternalTransferVerbiage(req) {
const lines = [
'=== OMNL CENTRAL BANK — EXTERNAL TRANSFER VERBIAGE ROLL ===',
`Settlement ref: ${req.idempotencyKey}`,
`Value date: ${req.valueDate}`,
`Amount: ${req.amount} ${req.currency}`,
`Rail: ${req.rail}`,
`Money layers: ${req.moneyLayers.join(' + ')}`,
`Beneficiary: ${req.beneficiaryName}`,
`Creditor IBAN: ${req.creditorIban}`,
];
if (req.creditorBic)
lines.push(`Creditor BIC: ${req.creditorBic}`);
if (req.debtorIban)
lines.push(`Debtor IBAN: ${req.debtorIban}`);
if (req.orderingName)
lines.push(`Ordering party: ${req.orderingName}`);
lines.push(`Remittance: ${req.remittanceInfo}`);
if (req.swiftMessageKind)
lines.push(`SWIFT message: ${req.swiftMessageKind}`);
if (req.tradeFinance) {
lines.push(`Trade finance: ${req.tradeFinance.instrument} ref ${req.tradeFinance.reference}`);
}
if (req.rwaVerbiage?.externalTransferRoll) {
lines.push(`RWA roll: ${req.rwaVerbiage.assetClass} → line ${req.rwaVerbiage.tokenLineId}`);
}
if (req.convertToCrypto?.enabled) {
lines.push(`Post-settlement crypto conversion: chain ${req.convertToCrypto.targetChainId}${req.convertToCrypto.recipientAddress}`);
}
lines.push('', 'DISCLAIMER: Internal OMNL operator verbiage — not SWIFT NET transmission.', 'Fineract SoR + ISO 20022 archive bind settlement finality.');
return lines.join('\n');
}
function buildMt103Narrative(req) {
return `/BNF/${req.beneficiaryName}//${req.remittanceInfo}`.slice(0, 140);
}
function buildMt102Narrative(req) {
const tf = req.tradeFinance ? `/${req.tradeFinance.instrument}/${req.tradeFinance.reference}` : '';
return `/BNF/${req.beneficiaryName}${tf}//${req.remittanceInfo}`.slice(0, 140);
}
function swiftKindForRequest(req) {
if (req.swiftMessageKind)
return req.swiftMessageKind;
if (req.tradeFinance?.instrument === 'LS' || req.tradeFinance?.instrument === 'DLC') {
return 'MT102';
}
return 'MT103';
}

View File

@@ -0,0 +1,47 @@
{
"name": "@dbis/settlement-core",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@dbis/settlement-core",
"version": "0.1.0",
"devDependencies": {
"@types/node": "^20.11.0",
"typescript": "^5.4.0"
}
},
"node_modules/@types/node": {
"version": "20.19.43",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
}
}
}

View File

@@ -0,0 +1,17 @@
{
"name": "@dbis/settlement-core",
"version": "0.1.0",
"private": true,
"description": "OMNL central-bank settlement: M0/M1/M2 money supply, SWIFT rails, trade-finance instruments, verbiage",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"scripts": {
"build": "tsc",
"test": "node --test dist/**/*.test.js"
},
"devDependencies": {
"@types/node": "^20.11.0",
"typescript": "^5.4.0"
}
}

View File

@@ -0,0 +1,67 @@
import fs from 'fs';
import path from 'path';
export type OmnlChainEntry = {
chainId: number;
name: string;
type: 'evm' | 'non-evm';
nativeSymbol: string;
tier: string;
omnlRole: string;
status: 'active' | 'staged' | 'reserved';
rpcEnv: string;
explorerUrl?: string;
rpcDefault?: string;
settlement: {
m2LoadEnabled: boolean;
swapEnabled: boolean;
bridgeEnabled: boolean;
officeId: number;
};
complianceEnv?: string;
};
export type OmnlChainRegistry = {
version: string;
brand: string;
maxCapacity: number;
primaryChainId: number;
mirrorChainId: number;
settlementOfficeId: number;
chains: OmnlChainEntry[];
stats?: { total: number; active: number; staged: number; reserved: number };
};
let cached: OmnlChainRegistry | null = null;
export function resolveChainRegistryPath(): string {
if (process.env.OMNL_SUPPORTED_CHAINS_CONFIG) return process.env.OMNL_SUPPORTED_CHAINS_CONFIG;
const candidates = [
path.resolve(process.cwd(), 'config/omnl-supported-chains.v1.json'),
path.resolve(__dirname, '../../../config/omnl-supported-chains.v1.json'),
path.resolve(__dirname, '../../../../config/omnl-supported-chains.v1.json'),
];
for (const p of candidates) {
if (fs.existsSync(p)) return p;
}
return candidates[1];
}
export function loadOmnlChainRegistry(): OmnlChainRegistry {
if (cached) return cached;
const p = resolveChainRegistryPath();
cached = JSON.parse(fs.readFileSync(p, 'utf8')) as OmnlChainRegistry;
return cached;
}
export function getActiveChains(): OmnlChainEntry[] {
return loadOmnlChainRegistry().chains.filter((c) => c.status === 'active');
}
export function getChainById(chainId: number): OmnlChainEntry | undefined {
return loadOmnlChainRegistry().chains.find((c) => c.chainId === chainId);
}
export function getSupportedChainIds128(): number[] {
return loadOmnlChainRegistry().chains.map((c) => c.chainId);
}

View File

@@ -0,0 +1,7 @@
export * from './types';
export * from './money-supply';
export * from './instruments';
export * from './verbiage';
export * from './state-machine';
export * from './m2-token-registry';
export * from './chain-registry';

View File

@@ -0,0 +1,31 @@
import type { SwiftMessageKind, TradeFinanceInstrument } from './types';
/** SWIFT / trade-finance instrument routing */
export const INSTRUMENT_ROUTING: Record<
TradeFinanceInstrument,
{ swift: SwiftMessageKind[]; glDebit: string; glCredit: string }
> = {
SBLC: { swift: ['MT103', 'MT202'], glDebit: '1410', glCredit: '2100' },
DLC: { swift: ['MT103', 'MT102'], glDebit: '1410', glCredit: '1000' },
BG: { swift: ['MT103'], glDebit: '1410', glCredit: '2100' },
LS: { swift: ['MT102', 'MT103'], glDebit: '1000', glCredit: '2200' },
};
export function resolveInstrumentGl(
instrument: TradeFinanceInstrument,
): { debitGl: string; creditGl: string; swiftKinds: SwiftMessageKind[] } {
const r = INSTRUMENT_ROUTING[instrument];
return { debitGl: r.glDebit, creditGl: r.glCredit, swiftKinds: r.swift };
}
export function isIbanValid(iban: string): boolean {
const normalized = iban.replace(/\s/g, '').toUpperCase();
if (!/^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/.test(normalized)) return false;
const rearranged = normalized.slice(4) + normalized.slice(0, 4);
const numeric = rearranged.replace(/[A-Z]/g, (c) => String(c.charCodeAt(0) - 55));
let remainder = 0;
for (const ch of numeric) {
remainder = (remainder * 10 + parseInt(ch, 10)) % 97;
}
return remainder === 1;
}

View File

@@ -0,0 +1,135 @@
import type { MoneyLayer } from './types';
import { GL_CODES } from './money-supply';
export type M2TokenCapabilities = {
symbol: string;
name: string;
address: string;
decimals: number;
native?: boolean;
currencyCode?: string;
moneyLayer: MoneyLayer;
omnlLine: string;
loadFromGl: string;
swappable: boolean;
convertible: boolean;
transferableInternal: boolean;
transferableExternal: boolean;
};
export type M2TokenRegistryFile = {
version: string;
chainId: number;
defaultMoneyLayer: MoneyLayer;
defaultLoadFromGl: string;
tokens: M2TokenCapabilities[];
};
const CURRENCY_FROM_SYMBOL: Record<string, string> = {
USD: 'USD',
EUR: 'EUR',
GBP: 'GBP',
AUD: 'AUD',
JPY: 'JPY',
CHF: 'CHF',
CAD: 'CAD',
XAU: 'XAU',
BTC: 'BTC',
ETH: 'ETH',
BNB: 'BNB',
POL: 'POL',
AVAX: 'AVAX',
CRO: 'CRO',
XDAI: 'XDAI',
CELO: 'CELO',
WEMIX: 'WEMIX',
};
export function inferCurrencyCode(symbol: string): string {
const s = symbol.toUpperCase();
for (const [key, code] of Object.entries(CURRENCY_FROM_SYMBOL)) {
if (s.includes(key)) return code;
}
if (s.startsWith('C') && s.length > 1) {
for (const [key, code] of Object.entries(CURRENCY_FROM_SYMBOL)) {
if (s.slice(1).includes(key)) return code;
}
}
return 'USD';
}
export function resolveOmnlLineM2(currencyCode?: string, symbol?: string): string {
const ccy = (currencyCode || inferCurrencyCode(symbol ?? 'USD')).toUpperCase();
return `${ccy}-M2`;
}
export function withM2Capabilities(
token: {
symbol: string;
name: string;
address: string;
decimals: number;
native?: boolean;
currencyCode?: string;
omnlLine?: string;
},
overrides: Partial<M2TokenCapabilities> = {},
): M2TokenCapabilities {
const currencyCode = token.currencyCode ?? inferCurrencyCode(token.symbol);
return {
symbol: token.symbol,
name: token.name,
address: token.address,
decimals: token.decimals,
native: token.native,
currencyCode,
moneyLayer: 'M2',
omnlLine: token.omnlLine ?? resolveOmnlLineM2(currencyCode, token.symbol),
loadFromGl: GL_CODES.M2_BROAD,
swappable: true,
convertible: true,
transferableInternal: true,
transferableExternal: true,
...overrides,
};
}
export function mergeExchangeTokensWithM2Registry(
baseTokens: {
symbol: string;
name: string;
address: string;
decimals: number;
native?: boolean;
omnlLine?: string;
}[],
registry: M2TokenRegistryFile,
): M2TokenCapabilities[] {
const byAddress = new Map<string, M2TokenCapabilities>();
for (const t of registry.tokens) {
byAddress.set(t.address.toLowerCase(), t);
}
for (const t of baseTokens) {
const key = t.address.toLowerCase();
if (!byAddress.has(key)) {
byAddress.set(key, withM2Capabilities(t));
}
}
return Array.from(byAddress.values()).sort((a, b) => a.symbol.localeCompare(b.symbol));
}
export function findM2TokenByAddress(
registry: M2TokenRegistryFile,
address: string,
): M2TokenCapabilities | undefined {
const lower = address.toLowerCase();
return registry.tokens.find((t) => t.address.toLowerCase() === lower);
}
export function findM2TokenBySymbol(
registry: M2TokenRegistryFile,
symbol: string,
): M2TokenCapabilities | undefined {
const s = symbol.trim().toLowerCase();
return registry.tokens.find((t) => t.symbol.toLowerCase() === s);
}

View File

@@ -0,0 +1,82 @@
import type { MoneyLayer, MoneySupplySnapshot } from './types';
/** IPSAS GL mapping for OMNL central-bank money aggregates */
export const GL_CODES = {
M0_RESERVE: '1050',
M1_LIABILITY: '2000',
M1_CIRCULATING: '2100',
M2_BROAD: '2200',
SETTLEMENT_SUSPENSE: '1000',
TRADE_FINANCE_CONTINGENT: '1410',
CRYPTO_TOKEN_LIABILITY: '2300',
} as const;
/** Policy caps from hybx-omnl-policy (1.2× M0 reserve → M1 capacity; 5× M1 → M2 broad) */
export const POLICY = {
M0_BACKING_MULTIPLIER: 1.2,
M1_CAP_MULTIPLIER: 5,
M2_META_FIAT_RAIL_CONVERSION: true,
} as const;
export function aggregateMetaFiatLayers(
layers: Partial<Record<MoneyLayer, string>>,
): { metaFiatTotal: string; breakdown: Record<MoneyLayer, string> } {
const breakdown: Record<MoneyLayer, string> = {
M0: layers.M0 ?? '0',
M1: layers.M1 ?? '0',
M2: layers.M2 ?? '0',
M00: layers.M00 ?? '0',
};
const sum = [breakdown.M0, breakdown.M1, breakdown.M2]
.map((v) => parseFloat(v) || 0)
.reduce((a, b) => a + b, 0);
return { metaFiatTotal: sum.toFixed(2), breakdown };
}
export function buildMoneySupplySnapshot(
officeId: number,
balances: Partial<Record<string, string>>,
): MoneySupplySnapshot {
const m0 = balances[GL_CODES.M0_RESERVE] ?? '0';
const m1 = balances[GL_CODES.M1_CIRCULATING] ?? '0';
const m2 = balances[GL_CODES.M2_BROAD] ?? '0';
const pending = balances[GL_CODES.SETTLEMENT_SUSPENSE] ?? '0';
const m0Num = parseFloat(m0) || 0;
const m1Num = parseFloat(m1) || 0;
return {
asOf: new Date().toISOString(),
officeId,
M0: {
gl1050: m0,
backingRatio: POLICY.M0_BACKING_MULTIPLIER,
},
M1: {
circulating: m1,
gl2000: balances[GL_CODES.M1_LIABILITY] ?? m1,
gl2100: m1,
capMultiple: POLICY.M1_CAP_MULTIPLIER,
},
M2: {
broadMoney: m2,
gl2200: m2,
metaFiatPending: pending,
},
metaFiatRealRail: {
pendingSettlement: pending,
inFlightSwift: balances['inFlightSwift'] ?? '0',
},
};
}
export function m2FiatToTokenLoadAmount(fiatAmount: string, m2Available: string): {
loadAmount: string;
fullyBacked: boolean;
} {
const fiat = parseFloat(fiatAmount) || 0;
const m2 = parseFloat(m2Available) || 0;
const load = Math.min(fiat, m2);
return {
loadAmount: load.toFixed(2),
fullyBacked: load >= fiat && fiat > 0,
};
}

View File

@@ -0,0 +1,26 @@
import type { SettlementPhase } from './types';
const ORDER: SettlementPhase[] = [
'RECEIVED',
'VALIDATED',
'VERBIAGE_ROLLED',
'FINERACT_POSTED',
'HYBX_RAIL_DISPATCHED',
'ISO20022_ARCHIVED',
'CHAIN_MINT_REQUESTED',
'SETTLED',
];
export function nextPhase(current: SettlementPhase): SettlementPhase | null {
if (current === 'FAILED' || current === 'SETTLED') return null;
const idx = ORDER.indexOf(current);
if (idx < 0 || idx >= ORDER.length - 1) return null;
return ORDER[idx + 1];
}
export function canAdvance(from: SettlementPhase, to: SettlementPhase): boolean {
const fi = ORDER.indexOf(from);
const ti = ORDER.indexOf(to);
if (fi < 0 || ti < 0) return to === 'FAILED';
return ti === fi + 1 || to === 'FAILED';
}

View File

@@ -0,0 +1,113 @@
/** OMNL central-bank settlement domain types */
export type MoneyLayer = 'M0' | 'M1' | 'M2' | 'M00';
export type PaymentRail =
| 'SWIFT'
| 'SEPA'
| 'TARGET2'
| 'RTGS'
| 'ACH'
| 'FEDWIRE'
| 'INTERNAL'
| 'CHAIN138';
export type TradeFinanceInstrument = 'SBLC' | 'DLC' | 'BG' | 'LS';
export type SwiftMessageKind = 'MT103' | 'MT102' | 'MT202' | 'MT910';
export type SettlementPhase =
| 'RECEIVED'
| 'VALIDATED'
| 'VERBIAGE_ROLLED'
| 'FINERACT_POSTED'
| 'HYBX_RAIL_DISPATCHED'
| 'ISO20022_ARCHIVED'
| 'CHAIN_MINT_REQUESTED'
| 'SETTLED'
| 'FAILED';
export type ExternalTransferRequest = {
idempotencyKey: string;
officeId: number;
valueDate: string;
currency: string;
amount: string;
debtorIban?: string;
creditorIban: string;
creditorBic?: string;
beneficiaryName: string;
orderingName?: string;
remittanceInfo: string;
moneyLayers: MoneyLayer[];
rail: PaymentRail;
swiftMessageKind?: SwiftMessageKind;
tradeFinance?: {
instrument: TradeFinanceInstrument;
reference: string;
expiryDate?: string;
};
rwaVerbiage?: {
assetClass: string;
tokenLineId: string;
externalTransferRoll: boolean;
};
convertToCrypto?: {
enabled: boolean;
targetChainId: number;
tokenLineId: string;
recipientAddress: string;
};
};
export type TokenLoadRequest = {
idempotencyKey: string;
officeId?: number;
lineId: string;
symbol?: string;
tokenAddress?: string;
amount: string;
recipientAddress: string;
currency?: string;
};
export type TransferRequest = {
idempotencyKey: string;
officeId?: number;
tokenAddress: string;
tokenSymbol: string;
amount: string;
recipientAddress: string;
senderAddress?: string;
moneyLayers: MoneyLayer[];
rail: 'INTERNAL' | 'CHAIN138' | 'SWIFT' | 'RTGS';
/** External SWIFT/HYBX beneficiary IBAN when rail is external */
creditorIban?: string;
beneficiaryName?: string;
remittanceInfo?: string;
currency?: string;
};
export type SettlementRecord = {
settlementId: string;
idempotencyKey: string;
phase: SettlementPhase;
request: ExternalTransferRequest;
fineractJournalRef?: string;
hybxPaymentId?: string;
iso20022MessageId?: string;
chainTxHash?: string;
verbiageDocument?: string;
errors: string[];
createdAt: string;
updatedAt: string;
};
export type MoneySupplySnapshot = {
asOf: string;
officeId: number;
M0: { reserveOz?: string; gl1050: string; backingRatio: number };
M1: { circulating: string; gl2000: string; gl2100: string; capMultiple: number };
M2: { broadMoney: string; gl2200: string; metaFiatPending: string };
metaFiatRealRail: { pendingSettlement: string; inFlightSwift: string };
};

View File

@@ -0,0 +1,58 @@
import type { ExternalTransferRequest, SwiftMessageKind, TradeFinanceInstrument } from './types';
/** OMNL central-bank external transfer verbiage roll (audit + operator copy) */
export function rollExternalTransferVerbiage(req: ExternalTransferRequest): string {
const lines = [
'=== OMNL CENTRAL BANK — EXTERNAL TRANSFER VERBIAGE ROLL ===',
`Settlement ref: ${req.idempotencyKey}`,
`Value date: ${req.valueDate}`,
`Amount: ${req.amount} ${req.currency}`,
`Rail: ${req.rail}`,
`Money layers: ${req.moneyLayers.join(' + ')}`,
`Beneficiary: ${req.beneficiaryName}`,
`Creditor IBAN: ${req.creditorIban}`,
];
if (req.creditorBic) lines.push(`Creditor BIC: ${req.creditorBic}`);
if (req.debtorIban) lines.push(`Debtor IBAN: ${req.debtorIban}`);
if (req.orderingName) lines.push(`Ordering party: ${req.orderingName}`);
lines.push(`Remittance: ${req.remittanceInfo}`);
if (req.swiftMessageKind) lines.push(`SWIFT message: ${req.swiftMessageKind}`);
if (req.tradeFinance) {
lines.push(
`Trade finance: ${req.tradeFinance.instrument} ref ${req.tradeFinance.reference}`,
);
}
if (req.rwaVerbiage?.externalTransferRoll) {
lines.push(
`RWA roll: ${req.rwaVerbiage.assetClass} → line ${req.rwaVerbiage.tokenLineId}`,
);
}
if (req.convertToCrypto?.enabled) {
lines.push(
`Post-settlement crypto conversion: chain ${req.convertToCrypto.targetChainId}${req.convertToCrypto.recipientAddress}`,
);
}
lines.push(
'',
'DISCLAIMER: Internal OMNL operator verbiage — not SWIFT NET transmission.',
'Fineract SoR + ISO 20022 archive bind settlement finality.',
);
return lines.join('\n');
}
export function buildMt103Narrative(req: ExternalTransferRequest): string {
return `/BNF/${req.beneficiaryName}//${req.remittanceInfo}`.slice(0, 140);
}
export function buildMt102Narrative(req: ExternalTransferRequest): string {
const tf = req.tradeFinance ? `/${req.tradeFinance.instrument}/${req.tradeFinance.reference}` : '';
return `/BNF/${req.beneficiaryName}${tf}//${req.remittanceInfo}`.slice(0, 140);
}
export function swiftKindForRequest(req: ExternalTransferRequest): SwiftMessageKind {
if (req.swiftMessageKind) return req.swiftMessageKind;
if (req.tradeFinance?.instrument === 'LS' || req.tradeFinance?.instrument === 'DLC') {
return 'MT102';
}
return 'MT103';
}

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}