feat: wire production settlement rails for M2 mint, transfers, and bank SWIFT
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m31s
CI/CD Pipeline / Security Scanning (push) Successful in 3m4s
CI/CD Pipeline / Lint and Format (push) Failing after 45s
CI/CD Pipeline / Terraform Validation (push) Failing after 28s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 28s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 44s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 36s
Validation / validate-genesis (push) Successful in 30s
Validation / validate-terraform (push) Failing after 30s
Validation / validate-kubernetes (push) Failing after 10s
Validation / validate-smart-contracts (push) Failing after 12s
Validation / validate-security (push) Failing after 1m52s
Validation / validate-documentation (push) Failing after 18s
Verify Deployment / Verify Deployment (push) Failing after 57s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 02:54:53 -07:00
parent 17e5cb3222
commit 458f3b420b
19 changed files with 538 additions and 120 deletions

View File

@@ -10,16 +10,12 @@ import {
type SettlementRecord,
} from '@dbis/settlement-core';
import { loadConfig, loadOfficeProfile, settlementOfficeId } from '../config';
import { fetchGlBalances, postJournalEntry } from '../adapters/fineract';
import { fetchGlBalances, postJournalEntry, resolveGlAccountId } from '../adapters/fineract';
import { HybxProductionRail } from '../adapters/hybx-production';
import { archiveIso20022, requestTokenMint } from '../adapters/omnl';
import { buildMt102Stub, buildMt103Stub, forwardSwiftToListener } from '../adapters/swift';
import { settlementStore } from '../store/settlement-store';
function glId(code: string): number {
return parseInt(code, 10);
}
export async function processExternalTransfer(
input: ExternalTransferRequest,
): Promise<SettlementRecord> {
@@ -63,13 +59,17 @@ export async function processExternalTransfer(
const creditGl = gl?.creditGl ?? profile.settlement.glM1 ?? cfg.moneySupply.glM1;
if (amount > 0) {
const [debitGlId, creditGlId] = await Promise.all([
resolveGlAccountId(debitGl),
resolveGlAccountId(creditGl),
]);
const je = await postJournalEntry({
officeId: req.officeId,
transactionDate: req.valueDate,
referenceNumber: req.idempotencyKey,
comments: verbiage.slice(0, 500),
debits: [{ glAccountId: glId(debitGl), amount }],
credits: [{ glAccountId: glId(creditGl), amount }],
debits: [{ glAccountId: debitGlId, amount }],
credits: [{ glAccountId: creditGlId, amount }],
});
advance('FINERACT_POSTED', { fineractJournalRef: String(je.resourceId) });
} else {

View File

@@ -7,14 +7,10 @@ import {
type SettlementRecord,
} from '@dbis/settlement-core';
import { loadConfig, loadOfficeProfile, settlementOfficeId } from '../config';
import { fetchGlBalances, postJournalEntry } from '../adapters/fineract';
import { fetchGlBalances, postJournalEntry, resolveGlAccountId } from '../adapters/fineract';
import { requestTokenMint } from '../adapters/omnl';
import { settlementStore } from '../store/settlement-store';
function glId(code: string): number {
return parseInt(code, 10);
}
export async function processTokenLoad(input: TokenLoadRequest): Promise<SettlementRecord> {
const cfg = loadConfig();
const profile = loadOfficeProfile();
@@ -79,13 +75,17 @@ export async function processTokenLoad(input: TokenLoadRequest): Promise<Settlem
const amount = parseFloat(loadAmount) || 0;
if (amount > 0) {
const [debitGlId, creditGlId] = await Promise.all([
resolveGlAccountId(profile.settlement.glM2 ?? GL_CODES.M2_BROAD),
resolveGlAccountId(GL_CODES.CRYPTO_TOKEN_LIABILITY),
]);
const je = await postJournalEntry({
officeId,
transactionDate: now.slice(0, 10),
referenceNumber: req.idempotencyKey,
comments: `M2 token load ${req.lineId}`,
debits: [{ glAccountId: glId(profile.settlement.glM2 ?? GL_CODES.M2_BROAD), amount }],
credits: [{ glAccountId: glId(GL_CODES.CRYPTO_TOKEN_LIABILITY), amount }],
debits: [{ glAccountId: debitGlId, amount }],
credits: [{ glAccountId: creditGlId, amount }],
});
advance('FINERACT_POSTED', { fineractJournalRef: String(je.resourceId) });
} else {

View File

@@ -1,16 +1,12 @@
import { v4 as uuidv4 } from 'uuid';
import { GL_CODES, type TransferRequest, type SettlementRecord } from '@dbis/settlement-core';
import { loadConfig, loadOfficeProfile, settlementOfficeId } from '../config';
import { postJournalEntry } from '../adapters/fineract';
import { postJournalEntry, resolveGlAccountId } from '../adapters/fineract';
import { HybxProductionRail } from '../adapters/hybx-production';
import { archiveIso20022 } from '../adapters/omnl';
import { archiveIso20022, requestTokenTransfer } from '../adapters/omnl';
import { buildMt103Stub, forwardSwiftToListener } from '../adapters/swift';
import { settlementStore } from '../store/settlement-store';
function glId(code: string): number {
return parseInt(code, 10);
}
export async function processTransfer(input: TransferRequest): Promise<SettlementRecord> {
const cfg = loadConfig();
const profile = loadOfficeProfile();
@@ -64,13 +60,17 @@ export async function processTransfer(input: TransferRequest): Promise<Settlemen
const amount = parseFloat(req.amount) || 0;
if (amount > 0) {
const [debitGlId, creditGlId] = await Promise.all([
resolveGlAccountId(profile.settlement.glM2 ?? GL_CODES.M2_BROAD),
resolveGlAccountId(profile.settlement.glSettlement ?? GL_CODES.SETTLEMENT_SUSPENSE),
]);
const je = await postJournalEntry({
officeId,
transactionDate: now.slice(0, 10),
referenceNumber: req.idempotencyKey,
comments: `${req.rail} transfer ${req.tokenSymbol}`,
debits: [{ glAccountId: glId(profile.settlement.glM2 ?? GL_CODES.M2_BROAD), amount }],
credits: [{ glAccountId: glId(profile.settlement.glSettlement ?? GL_CODES.SETTLEMENT_SUSPENSE), amount }],
debits: [{ glAccountId: debitGlId, amount }],
credits: [{ glAccountId: creditGlId, amount }],
});
advance('FINERACT_POSTED', { fineractJournalRef: String(je.resourceId) });
} else {
@@ -116,12 +116,33 @@ export async function processTransfer(input: TransferRequest): Promise<Settlemen
advance('ISO20022_ARCHIVED');
}
advance('CHAIN_MINT_REQUESTED', {
verbiageDocument: [
record.verbiageDocument,
`On-chain ERC-20 transfer prepared: ${req.tokenAddress}${req.recipientAddress}`,
].join('\n'),
});
if (!isExternal && req.tokenAddress && cfg.production.allowChainMintExecute) {
const xfer = await requestTokenTransfer({
tokenAddress: req.tokenAddress,
amount: req.amount,
recipient: req.recipientAddress,
settlementRef: record.settlementId,
dryRun: false,
symbol: req.tokenSymbol,
});
advance('CHAIN_MINT_REQUESTED', {
chainTxHash: xfer.txHash,
verbiageDocument: [
record.verbiageDocument,
`On-chain ERC-20 transfer: ${req.tokenAddress}${req.recipientAddress}`,
xfer.txHash ? `tx ${xfer.txHash}` : `status ${xfer.status}`,
].join('\n'),
});
} else {
advance('CHAIN_MINT_REQUESTED', {
verbiageDocument: [
record.verbiageDocument,
isExternal
? 'Bank rail (SWIFT/IBAN) — no on-chain transfer'
: `On-chain ERC-20 transfer prepared: ${req.tokenAddress}${req.recipientAddress}`,
].join('\n'),
});
}
advance('SETTLED');
return record;
} catch (err) {