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,53 @@
import axios from 'axios';
function authHeaders(): Record<string, string> {
const base = (process.env.OMNL_FINERACT_BASE_URL || '').replace(/\/$/, '');
const tenant = process.env.OMNL_FINERACT_TENANT || 'omnl';
const user =
process.env.OMNL_FINERACT_USER?.trim() ||
process.env.OMNL_FINERACT_USERNAME?.trim() ||
'ali_hospitallers_tenant';
const pass = process.env.OMNL_FINERACT_PASSWORD || '';
if (!base || !pass) throw new Error('Fineract credentials required');
const auth = Buffer.from(`${user}:${pass}`).toString('base64');
return {
Authorization: `Basic ${auth}`,
'Fineract-Platform-TenantId': tenant,
'Content-Type': 'application/json',
};
}
export async function postJournalEntry(entry: {
officeId: number;
transactionDate: string;
referenceNumber: string;
comments: string;
debits: { glAccountId: number; amount: number }[];
credits: { glAccountId: number; amount: number }[];
}): Promise<{ resourceId: number }> {
const base = (process.env.OMNL_FINERACT_BASE_URL || '').replace(/\/$/, '');
const { data } = await axios.post(`${base}/journalentries`, entry, {
headers: authHeaders(),
timeout: 60000,
});
return { resourceId: data.resourceId ?? data.entityId ?? 0 };
}
export async function fetchGlBalances(officeId: number): Promise<Record<string, string>> {
const base = (process.env.OMNL_FINERACT_BASE_URL || '').replace(/\/$/, '');
try {
const { data } = await axios.get(`${base}/runreports/General Ledger Report`, {
headers: authHeaders(),
params: { R_officeId: officeId, genericResultSet: false },
timeout: 60000,
});
const out: Record<string, string> = {};
const rows = Array.isArray(data) ? data : (data as { data?: unknown[] })?.data ?? [];
for (const row of rows as { glCode?: string; balance?: number }[]) {
if (row.glCode) out[row.glCode] = String(row.balance ?? 0);
}
return out;
} catch {
return {};
}
}

View File

@@ -0,0 +1,60 @@
import axios from 'axios';
import { loadHybxConfig } from '@dbis/integration-foundation';
/** Production HYBX rail client for real settlement dispatch */
export class HybxProductionRail {
private readonly baseUrl: string;
private readonly apiKey: string;
private readonly clientSecret: string;
constructor() {
const cfg = loadHybxConfig({ testMode: false });
if (cfg.environment === 'production' && process.env.SETTLEMENT_ALLOW_HYBX_PRODUCTION !== '1') {
throw new Error('SETTLEMENT_ALLOW_HYBX_PRODUCTION=1 required for live HYBX rails');
}
this.baseUrl = cfg.baseUrl.replace(/\/$/, '');
this.apiKey = cfg.apiKey;
this.clientSecret = cfg.clientSecret;
}
async dispatchPayment(payload: {
idempotencyKey: string;
amount: string;
currency: string;
creditorIban: string;
creditorBic?: string;
beneficiaryName: string;
remittanceInfo: string;
rail: string;
}): Promise<{ paymentId: string; status: string }> {
const sidecar = process.env.HYBX_MIFOS_FINERACT_SIDECAR_URL?.replace(/\/$/, '');
const url = sidecar ? `${sidecar}/v1/rtgs/payments` : `${this.baseUrl}/v1/payments`;
const { data } = await axios.post(
url,
{
externalId: payload.idempotencyKey,
amount: payload.amount,
currency: payload.currency,
beneficiary: {
iban: payload.creditorIban,
bic: payload.creditorBic,
name: payload.beneficiaryName,
},
remittanceInformation: payload.remittanceInfo,
rail: payload.rail,
},
{
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey,
Authorization: `Bearer ${this.clientSecret}`,
},
timeout: 120000,
},
);
return {
paymentId: String(data.paymentId ?? data.id ?? payload.idempotencyKey),
status: String(data.status ?? 'DISPATCHED'),
};
}
}

View File

@@ -0,0 +1,59 @@
import axios from 'axios';
export async function archiveIso20022(payload: {
messageType: string;
direction: 'INBOUND' | 'OUTBOUND';
raw: string;
settlementRef: string;
}): Promise<{ messageId: string }> {
const base = (process.env.TOKEN_AGGREGATION_URL || 'http://localhost:3000').replace(/\/$/, '');
const key = process.env.OMNL_API_KEY || '';
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (key) headers.Authorization = `Bearer ${key}`;
const { data } = await axios.post(
`${base}/api/v1/omnl/iso20022/messages`,
{
messageType: payload.messageType,
direction: payload.direction,
payload: payload.raw,
metadata: { settlementRef: payload.settlementRef },
},
{ headers, timeout: 60000 },
);
return { messageId: String(data.messageId ?? data.id ?? payload.settlementRef) };
}
export async function requestTokenMint(payload: {
lineId: string;
amount: string;
recipient: string;
settlementRef: string;
dryRun: boolean;
symbol?: string;
tokenAddress?: string;
}): Promise<{ txHash?: string; status: string }> {
const base = (process.env.TOKEN_AGGREGATION_URL || 'http://localhost:3000').replace(/\/$/, '');
const key = process.env.OMNL_API_KEY || '';
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (key) headers.Authorization = `Bearer ${key}`;
try {
const { data } = await axios.post(
`${base}/api/v1/omnl/settlement/token-load`,
payload,
{ headers, timeout: 120000 },
);
return {
txHash: data.txHash,
status: String(data.status ?? (payload.dryRun ? 'DRY_RUN' : 'SUBMITTED')),
};
} catch (err) {
if (axios.isAxiosError(err) && err.response?.status === 404) {
return {
status: payload.dryRun ? 'DRY_RUN' : 'PENDING_CHAIN_ENDPOINT',
};
}
throw err;
}
}

View File

@@ -0,0 +1,56 @@
import axios from 'axios';
export async function forwardSwiftToListener(raw: string): Promise<{ accepted: boolean }> {
const url = (process.env.SWIFT_LISTENER_URL || 'http://192.168.11.114:8788').replace(/\/$/, '');
const token = process.env.SWIFT_LISTENER_WEBHOOK_TOKEN || '';
const headers: Record<string, string> = { 'Content-Type': 'text/plain' };
if (token) headers.Authorization = `Bearer ${token}`;
await axios.post(`${url}/inbound`, raw, { headers, timeout: 60000 });
return { accepted: true };
}
export function buildMt103Stub(fields: {
senderRef: string;
currency: string;
amount: string;
valueDate: string;
orderingCustomer: string;
beneficiary: string;
iban: string;
bic?: string;
remittance: string;
}): string {
const bicLine = fields.bic ? `:57A:${fields.bic}\n` : '';
return `{1:F01OMNLUS33XXXX0000000000}{2:I103BANKUS33XXXXN}{4:
:20:${fields.senderRef}
:23B:CRED
:32A:${fields.valueDate.replace(/-/g, '').slice(2)}${fields.currency}${fields.amount.replace('.', ',')}
:50K:${fields.orderingCustomer}
:59:/${fields.iban}
${fields.beneficiary}
${bicLine}:70:${fields.remittance}
:71A:OUR
-}`;
}
export function buildMt102Stub(fields: {
senderRef: string;
currency: string;
amount: string;
valueDate: string;
orderingInstitution: string;
beneficiary: string;
iban: string;
remittance: string;
}): string {
return `{1:F01OMNLUS33XXXX0000000000}{2:I102BANKUS33XXXXN}{4:
:20:${fields.senderRef}
:21:NONREF
:32B:${fields.currency}${fields.amount.replace('.', ',')}
:50A:${fields.orderingInstitution}
:59:/${fields.iban}
${fields.beneficiary}
:70:${fields.remittance}
-}`;
}