diff --git a/frontend-dapp/src/features/central-bank/CentralBankDashboard.tsx b/frontend-dapp/src/features/central-bank/CentralBankDashboard.tsx
index 2ccde1a..3670e34 100644
--- a/frontend-dapp/src/features/central-bank/CentralBankDashboard.tsx
+++ b/frontend-dapp/src/features/central-bank/CentralBankDashboard.tsx
@@ -136,7 +136,7 @@ export default function CentralBankDashboard() {
-
+
@@ -238,7 +238,7 @@ export default function CentralBankDashboard() {
M3 token liabilityGL {office?.settlement?.glM3 ?? '2300'}
- M4 near moneyGL {office?.settlement?.glM4NearMoney ?? '1000'} + {office?.settlement?.glM4Contingent ?? '1410'}
+ M4 near moneyGL {office?.settlement?.glM4NearMoney ?? '1000'} + in-flight SWIFT
Inter-office (HO)GL 1410
diff --git a/frontend-dapp/src/features/office24/Office24Dashboard.tsx b/frontend-dapp/src/features/office24/Office24Dashboard.tsx
index 5804c2a..64e2c62 100644
--- a/frontend-dapp/src/features/office24/Office24Dashboard.tsx
+++ b/frontend-dapp/src/features/office24/Office24Dashboard.tsx
@@ -126,7 +126,7 @@ export default function Office24Dashboard() {
-
+
diff --git a/packages/settlement-core/dist/money-supply.d.ts b/packages/settlement-core/dist/money-supply.d.ts
index 911355e..05129d7 100644
--- a/packages/settlement-core/dist/money-supply.d.ts
+++ b/packages/settlement-core/dist/money-supply.d.ts
@@ -34,3 +34,12 @@ export declare function m3TokenLoadAmount(requested: string, m2Broad: string, m3
};
/** All settlement layers M0–M4 for production routing. */
export declare const ALL_MONEY_LAYERS: MoneyLayer[];
+/** M4 outbound SWIFT/RTGS: move M1/M2 into GL 1000 settlement suspense while in flight. */
+export declare function resolveM4OutboundJournal(moneyLayers: MoneyLayer[], gl: {
+ glM1: string;
+ glM2: string;
+ glM4NearMoney: string;
+}): {
+ debitGl: string;
+ creditGl: string;
+} | null;
diff --git a/packages/settlement-core/dist/money-supply.js b/packages/settlement-core/dist/money-supply.js
index d3d279d..eb4d31b 100644
--- a/packages/settlement-core/dist/money-supply.js
+++ b/packages/settlement-core/dist/money-supply.js
@@ -5,6 +5,7 @@ exports.aggregateMetaFiatLayers = aggregateMetaFiatLayers;
exports.buildMoneySupplySnapshot = buildMoneySupplySnapshot;
exports.m2FiatToTokenLoadAmount = m2FiatToTokenLoadAmount;
exports.m3TokenLoadAmount = m3TokenLoadAmount;
+exports.resolveM4OutboundJournal = resolveM4OutboundJournal;
/** IPSAS GL mapping for OMNL central-bank money aggregates M0–M4 */
exports.GL_CODES = {
M0_RESERVE: '1050',
@@ -46,7 +47,8 @@ function buildMoneySupplySnapshot(officeId, balances) {
const pending = balances[exports.GL_CODES.SETTLEMENT_SUSPENSE] ?? '0';
const contingent = balances[exports.GL_CODES.TRADE_FINANCE_CONTINGENT] ?? '0';
const inFlight = balances.inFlightSwift ?? '0';
- const m4Num = (parseFloat(pending) || 0) + (parseFloat(contingent) || 0) + (parseFloat(inFlight) || 0);
+ // M4 near money = GL 1000 settlement suspense + in-flight SWIFT (GL 1410 is interoffice HO receivable)
+ const m4Num = (parseFloat(pending) || 0) + (parseFloat(inFlight) || 0);
return {
asOf: new Date().toISOString(),
officeId,
@@ -103,3 +105,16 @@ function m3TokenLoadAmount(requested, m2Broad, m3Outstanding) {
}
/** All settlement layers M0–M4 for production routing. */
exports.ALL_MONEY_LAYERS = ['M0', 'M1', 'M2', 'M3', 'M4'];
+/** M4 outbound SWIFT/RTGS: move M1/M2 into GL 1000 settlement suspense while in flight. */
+function resolveM4OutboundJournal(moneyLayers, gl) {
+ if (!moneyLayers.includes('M4'))
+ return null;
+ const suspense = gl.glM4NearMoney || exports.GL_CODES.SETTLEMENT_SUSPENSE;
+ if (moneyLayers.includes('M2')) {
+ return { debitGl: gl.glM2 || exports.GL_CODES.M2_BROAD, creditGl: suspense };
+ }
+ if (moneyLayers.includes('M1')) {
+ return { debitGl: gl.glM1 || exports.GL_CODES.M1_CIRCULATING, creditGl: suspense };
+ }
+ return { debitGl: exports.GL_CODES.M2_BROAD, creditGl: suspense };
+}
diff --git a/packages/settlement-core/src/money-supply.ts b/packages/settlement-core/src/money-supply.ts
index 9199416..2326c66 100644
--- a/packages/settlement-core/src/money-supply.ts
+++ b/packages/settlement-core/src/money-supply.ts
@@ -49,8 +49,8 @@ export function buildMoneySupplySnapshot(
const pending = balances[GL_CODES.SETTLEMENT_SUSPENSE] ?? '0';
const contingent = balances[GL_CODES.TRADE_FINANCE_CONTINGENT] ?? '0';
const inFlight = balances.inFlightSwift ?? '0';
- const m4Num =
- (parseFloat(pending) || 0) + (parseFloat(contingent) || 0) + (parseFloat(inFlight) || 0);
+ // M4 near money = GL 1000 settlement suspense + in-flight SWIFT (GL 1410 is interoffice HO receivable)
+ const m4Num = (parseFloat(pending) || 0) + (parseFloat(inFlight) || 0);
return {
asOf: new Date().toISOString(),
officeId,
@@ -116,3 +116,20 @@ export function m3TokenLoadAmount(requested: string, m2Broad: string, m3Outstand
/** All settlement layers M0–M4 for production routing. */
export const ALL_MONEY_LAYERS: MoneyLayer[] = ['M0', 'M1', 'M2', 'M3', 'M4'];
+
+/** M4 outbound SWIFT/RTGS: move M1/M2 into GL 1000 settlement suspense while in flight. */
+export function resolveM4OutboundJournal(moneyLayers: MoneyLayer[], gl: {
+ glM1: string;
+ glM2: string;
+ glM4NearMoney: string;
+}): { debitGl: string; creditGl: string } | null {
+ if (!moneyLayers.includes('M4')) return null;
+ const suspense = gl.glM4NearMoney || GL_CODES.SETTLEMENT_SUSPENSE;
+ if (moneyLayers.includes('M2')) {
+ return { debitGl: gl.glM2 || GL_CODES.M2_BROAD, creditGl: suspense };
+ }
+ if (moneyLayers.includes('M1')) {
+ return { debitGl: gl.glM1 || GL_CODES.M1_CIRCULATING, creditGl: suspense };
+ }
+ return { debitGl: GL_CODES.M2_BROAD, creditGl: suspense };
+}
diff --git a/services/settlement-middleware/dist/workflows/external-transfer.js b/services/settlement-middleware/dist/workflows/external-transfer.js
index 69fa49b..77cfc41 100644
--- a/services/settlement-middleware/dist/workflows/external-transfer.js
+++ b/services/settlement-middleware/dist/workflows/external-transfer.js
@@ -43,8 +43,15 @@ async function processExternalTransfer(input) {
const amount = parseFloat(req.amount) || 0;
const tf = req.tradeFinance;
const gl = tf ? (0, settlement_core_1.resolveInstrumentGl)(tf.instrument) : null;
- const debitGl = gl?.debitGl ?? profile.settlement.glSettlement ?? cfg.moneySupply.glSettlement;
- const creditGl = gl?.creditGl ?? profile.settlement.glM1 ?? cfg.moneySupply.glM1;
+ const m4Journal = !gl
+ ? (0, settlement_core_1.resolveM4OutboundJournal)(req.moneyLayers, {
+ glM1: profile.settlement.glM1 ?? cfg.moneySupply.glM1,
+ glM2: profile.settlement.glM2 ?? cfg.moneySupply.glM2,
+ glM4NearMoney: profile.settlement.glM4NearMoney ?? cfg.moneySupply.glM4NearMoney ?? cfg.moneySupply.glSettlement,
+ })
+ : null;
+ const debitGl = gl?.debitGl ?? m4Journal?.debitGl ?? profile.settlement.glM2 ?? cfg.moneySupply.glM2;
+ const creditGl = gl?.creditGl ?? m4Journal?.creditGl ?? profile.settlement.glM4NearMoney ?? cfg.moneySupply.glSettlement;
if (amount > 0) {
const [debitGlId, creditGlId] = await Promise.all([
(0, fineract_1.resolveGlAccountId)(debitGl),
diff --git a/services/settlement-middleware/dist/workflows/transfer.js b/services/settlement-middleware/dist/workflows/transfer.js
index cf8f7d0..1984db5 100644
--- a/services/settlement-middleware/dist/workflows/transfer.js
+++ b/services/settlement-middleware/dist/workflows/transfer.js
@@ -56,15 +56,29 @@ async function processTransfer(input) {
verbiageDocument: [
`OMNL ${req.rail} transfer · ${req.tokenSymbol}`,
`Layers ${record.request.moneyLayers.join('/')} · ${req.amount}`,
- `To ${req.recipientAddress}`,
- isExternal ? `IBAN ${req.creditorIban}` : 'Internal / Chain 138',
+ isExternal
+ ? `IBAN ${req.creditorIban} · M4 GL ${profile.settlement.glM4NearMoney ?? '1000'} suspense`
+ : `To ${req.recipientAddress} · internal chain`,
].join('\n'),
});
const amount = parseFloat(req.amount) || 0;
if (amount > 0) {
+ const layers = record.request.moneyLayers;
+ const m4Journal = isExternal
+ ? (0, settlement_core_1.resolveM4OutboundJournal)(layers, {
+ glM1: profile.settlement.glM1 ?? settlement_core_1.GL_CODES.M1_CIRCULATING,
+ glM2: profile.settlement.glM2 ?? settlement_core_1.GL_CODES.M2_BROAD,
+ glM4NearMoney: profile.settlement.glM4NearMoney ?? profile.settlement.glSettlement ?? settlement_core_1.GL_CODES.SETTLEMENT_SUSPENSE,
+ })
+ : null;
+ const debitGl = m4Journal?.debitGl ?? profile.settlement.glM2 ?? settlement_core_1.GL_CODES.M2_BROAD;
+ const creditGl = m4Journal?.creditGl
+ ?? (isExternal
+ ? (profile.settlement.glSettlement ?? settlement_core_1.GL_CODES.SETTLEMENT_SUSPENSE)
+ : (profile.settlement.glM3 ?? settlement_core_1.GL_CODES.M3_TOKEN_LIABILITY));
const [debitGlId, creditGlId] = await Promise.all([
- (0, fineract_1.resolveGlAccountId)(profile.settlement.glM2 ?? settlement_core_1.GL_CODES.M2_BROAD),
- (0, fineract_1.resolveGlAccountId)(profile.settlement.glSettlement ?? settlement_core_1.GL_CODES.SETTLEMENT_SUSPENSE),
+ (0, fineract_1.resolveGlAccountId)(debitGl),
+ (0, fineract_1.resolveGlAccountId)(creditGl),
]);
const je = await (0, fineract_1.postJournalEntry)({
officeId,
diff --git a/services/settlement-middleware/src/workflows/external-transfer.ts b/services/settlement-middleware/src/workflows/external-transfer.ts
index b952249..dd5f437 100644
--- a/services/settlement-middleware/src/workflows/external-transfer.ts
+++ b/services/settlement-middleware/src/workflows/external-transfer.ts
@@ -1,10 +1,12 @@
import { v4 as uuidv4 } from 'uuid';
import {
buildMoneySupplySnapshot,
+ GL_CODES,
isIbanValid,
m2FiatToTokenLoadAmount,
m3TokenLoadAmount,
resolveInstrumentGl,
+ resolveM4OutboundJournal,
rollExternalTransferVerbiage,
swiftKindForRequest,
type ExternalTransferRequest,
@@ -56,8 +58,15 @@ export async function processExternalTransfer(
const amount = parseFloat(req.amount) || 0;
const tf = req.tradeFinance;
const gl = tf ? resolveInstrumentGl(tf.instrument) : null;
- const debitGl = gl?.debitGl ?? profile.settlement.glSettlement ?? cfg.moneySupply.glSettlement;
- const creditGl = gl?.creditGl ?? profile.settlement.glM1 ?? cfg.moneySupply.glM1;
+ const m4Journal = !gl
+ ? resolveM4OutboundJournal(req.moneyLayers, {
+ glM1: profile.settlement.glM1 ?? cfg.moneySupply.glM1,
+ glM2: profile.settlement.glM2 ?? cfg.moneySupply.glM2,
+ glM4NearMoney: profile.settlement.glM4NearMoney ?? cfg.moneySupply.glM4NearMoney ?? cfg.moneySupply.glSettlement,
+ })
+ : null;
+ const debitGl = gl?.debitGl ?? m4Journal?.debitGl ?? profile.settlement.glM2 ?? cfg.moneySupply.glM2;
+ const creditGl = gl?.creditGl ?? m4Journal?.creditGl ?? profile.settlement.glM4NearMoney ?? cfg.moneySupply.glSettlement;
if (amount > 0) {
const [debitGlId, creditGlId] = await Promise.all([
diff --git a/services/settlement-middleware/src/workflows/transfer.ts b/services/settlement-middleware/src/workflows/transfer.ts
index d370e91..2025e64 100644
--- a/services/settlement-middleware/src/workflows/transfer.ts
+++ b/services/settlement-middleware/src/workflows/transfer.ts
@@ -1,5 +1,5 @@
import { v4 as uuidv4 } from 'uuid';
-import { GL_CODES, type TransferRequest, type SettlementRecord } from '@dbis/settlement-core';
+import { GL_CODES, resolveM4OutboundJournal, type TransferRequest, type SettlementRecord } from '@dbis/settlement-core';
import { loadConfig, loadOfficeProfile, settlementOfficeId } from '../config';
import { postJournalEntry, resolveGlAccountId } from '../adapters/fineract';
import { HybxProductionRail } from '../adapters/hybx-production';
@@ -57,16 +57,30 @@ export async function processTransfer(input: TransferRequest): Promise 0) {
+ const layers = record.request.moneyLayers;
+ const m4Journal = isExternal
+ ? resolveM4OutboundJournal(layers, {
+ glM1: profile.settlement.glM1 ?? GL_CODES.M1_CIRCULATING,
+ glM2: profile.settlement.glM2 ?? GL_CODES.M2_BROAD,
+ glM4NearMoney: profile.settlement.glM4NearMoney ?? profile.settlement.glSettlement ?? GL_CODES.SETTLEMENT_SUSPENSE,
+ })
+ : null;
+ const debitGl = m4Journal?.debitGl ?? profile.settlement.glM2 ?? GL_CODES.M2_BROAD;
+ const creditGl = m4Journal?.creditGl
+ ?? (isExternal
+ ? (profile.settlement.glSettlement ?? GL_CODES.SETTLEMENT_SUSPENSE)
+ : (profile.settlement.glM3 ?? GL_CODES.M3_TOKEN_LIABILITY));
const [debitGlId, creditGlId] = await Promise.all([
- resolveGlAccountId(profile.settlement.glM2 ?? GL_CODES.M2_BROAD),
- resolveGlAccountId(profile.settlement.glSettlement ?? GL_CODES.SETTLEMENT_SUSPENSE),
+ resolveGlAccountId(debitGl),
+ resolveGlAccountId(creditGl),
]);
const je = await postJournalEntry({
officeId,