diff --git a/config/deployment-omnl.production.env.example b/config/deployment-omnl.production.env.example index 72822e6..ece7ef2 100644 --- a/config/deployment-omnl.production.env.example +++ b/config/deployment-omnl.production.env.example @@ -50,6 +50,9 @@ CHAIN_138_RPC_URL=http://192.168.11.221:8545 # === Mirror chain 651940 === CHAIN_651940_RPC_URL=https://mainnet-rpc.alltra.global +# === Z Chain — deploy via scripts/deployment/deploy-z-ecosystem-production.sh === +# Portal: https://zblockchainsystem.com — see config/z-ecosystem.local.env.example + # === Major L1/L2 RPC (set per chain — CHAIN__RPC_URL) === CHAIN_1_RPC_URL=https://eth.llamarpc.com CHAIN_56_RPC_URL=https://bsc-dataseed.binance.org diff --git a/config/ecosystem-links.v1.json b/config/ecosystem-links.v1.json new file mode 100644 index 0000000..d6b5072 --- /dev/null +++ b/config/ecosystem-links.v1.json @@ -0,0 +1,128 @@ +{ + "$schema": "OMNL ecosystem link map — canonical URLs for portals, APIs, and local dev", + "version": "1.2.0", + "updated": "2026-07-03", + "notes": [ + "Z Ecosystem lives on zblockchainsystem.com — see config/z-ecosystem-links.v1.json", + "Public nginx exposes /settlement/* and /exchange/* without /api/v1/... prefix", + "Tunnel save file logs/public-url-{operator}.txt stores primary portal URL (Z Online Bank)", + "/omnl/* operator UIs ship in frontend dist (public/omnl) — portal-serve proxies /api to explorer", + "POST /z-bank/settle-m0-m4 exists in settlement-middleware source; production may use POST /settlement/external-transfer until deployed", + "SPA anchors #z-full-settlement and #z-settlement-protocols exist on /central-bank" + ], + "tunnel": { + "ephemeral": true, + "saveFile": "logs/public-url-ali.txt", + "primaryPath": "/central-bank", + "apps": { + "bridge": "/bridge", + "hub": "/hub", + "zOnlineBank": "/central-bank", + "zFullSettlement": "/central-bank#z-full-settlement", + "wireProtocols": "/central-bank#z-settlement-protocols", + "office24": "/office-24", + "omnlCompliance": "/omnl/compliance", + "omnlDashboard": "/omnl/dashboard", + "omnlTerminal": "/omnl/terminal", + "trade": "/trade", + "swap": "/swap", + "reserve": "/reserve", + "history": "/history", + "wallets": "/wallets", + "admin": "/admin", + "docs": "/docs" + }, + "nginxOn3002": { + "settlementHealth": "/settlement/health", + "exchangeHealth": "/exchange/health", + "apiCatalog": "/api/v1/omnl/catalog" + }, + "brokenOnServeOnly": [] + }, + "subjects": [ + { "id": "bridge", "label": "Bridge", "path": "/bridge", "group": "app" }, + { "id": "hub", "label": "DBIS Hub", "path": "/hub", "group": "app" }, + { "id": "zOnlineBank", "label": "Z Online Bank", "path": "/central-bank", "group": "app" }, + { "id": "zFullSettlement", "label": "Z M0-M4 settlement", "path": "/central-bank#z-full-settlement", "group": "app" }, + { "id": "wireProtocols", "label": "Wire protocols", "path": "/central-bank#z-settlement-protocols", "group": "app" }, + { "id": "office24", "label": "Office 24 Central Bank", "path": "/office-24", "group": "app" }, + { "id": "trade", "label": "DBIS Trade", "path": "/trade", "group": "app" }, + { "id": "swap", "label": "Digital Swap", "path": "/swap", "group": "app" }, + { "id": "reserve", "label": "Reserve", "path": "/reserve", "group": "app" }, + { "id": "history", "label": "History", "path": "/history", "group": "app" }, + { "id": "wallets", "label": "Wallets", "path": "/wallets", "group": "app" }, + { "id": "admin", "label": "Admin", "path": "/admin", "group": "app" }, + { "id": "docs", "label": "Developers", "path": "/docs", "group": "app" }, + { "id": "omnlCompliance", "label": "OMNL Compliance", "path": "/omnl/compliance", "group": "console" }, + { "id": "omnlDashboard", "label": "OMNL API snapshot", "path": "/omnl/dashboard", "group": "console" }, + { "id": "omnlTerminal", "label": "Settlement terminal", "path": "/omnl/terminal", "group": "console" }, + { "id": "apiCatalog", "label": "API catalog", "path": "/api/v1/omnl/catalog", "group": "api" }, + { "id": "gazetteNotices", "label": "GUOSMM gazette registry", "path": "/api/v1/omnl/gazette/notices", "group": "api" }, + { "id": "integrationStatus", "label": "Integration status", "path": "/api/v1/omnl/integration-status", "group": "api" }, + { "id": "settlementHealth", "label": "Settlement health", "path": "/settlement/health", "group": "api" }, + { "id": "exchangeHealth", "label": "Exchange health", "path": "/exchange/health", "group": "api" } + ], + "local": { + "frontend": { + "port": 3002, + "bridge": "http://localhost:3002/bridge", + "centralBank": "http://localhost:3002/central-bank", + "hub": "http://localhost:3002/hub", + "office24": "http://localhost:3002/office-24", + "trade": "http://localhost:3002/trade", + "swap": "http://localhost:3002/swap" + }, + "backend": { + "omnlCatalog": "http://localhost:3000/api/v1/omnl/catalog", + "compliance": "http://localhost:3000/omnl/compliance", + "omnlDashboard": "http://localhost:3000/omnl/dashboard", + "settlementTerminal": "http://localhost:3000/omnl/terminal", + "settlementHealth": "http://localhost:3011/api/v1/settlement/health", + "settlementChains": "http://localhost:3011/api/v1/settlement/chains", + "moneySupplyPublic": "http://localhost:3011/api/v1/settlement/money-supply/public", + "exchangeHealth": "http://localhost:3012/api/v1/exchange/health" + }, + "nginx3002": { + "settlementHealth": "http://localhost:3002/settlement/health", + "exchangeHealth": "http://localhost:3002/exchange/health", + "apiCatalog": "http://localhost:3002/api/v1/omnl/catalog" + } + }, + "productionPortals": { + "zOnlineBank": "https://online.omdnl.org/central-bank", + "centralBankSecure": "https://secure.omdnl.org/central-bank", + "office24": "https://office24.omdnl.org/office-24", + "exchangeTrade": "https://exchange.omdnl.org/trade", + "forexTrade": "https://forex.omdnl.org/trade", + "omnlBankHybx": "https://bank.omnl.hybxfinance.io", + "exchangeDbisMirror": "https://exchange.d-bis.org/trade", + "centralBankDbisMirror": "https://secure.d-bis.org/central-bank", + "forexDbisMirror": "https://forex.d-bis.org/trade" + }, + "productionApis": { + "explorerRoot": "https://explorer.d-bis.org/api/v1", + "catalog": "https://explorer.d-bis.org/api/v1/omnl/catalog", + "gazetteNotices": "https://explorer.d-bis.org/api/v1/omnl/gazette/notices", + "complianceUi": "https://explorer.d-bis.org/omnl/compliance", + "omnlDashboardUi": "https://explorer.d-bis.org/omnl/dashboard", + "settlementTerminalUi": "https://explorer.d-bis.org/omnl/terminal", + "settlementHealth": "https://secure.omdnl.org/settlement/health", + "settlementChains": "https://secure.omdnl.org/settlement/chains", + "moneySupplyPublic": "https://secure.omdnl.org/settlement/public/money-supply", + "externalTransfer": "https://secure.omdnl.org/settlement/external-transfer", + "exchangeHealth": "https://exchange.omdnl.org/exchange/health", + "exchangeTokens": "https://exchange.omdnl.org/exchange/tokens", + "chain138Rpc": "https://rpc-http-pub.d-bis.org", + "chain138Explorer": "https://explorer.d-bis.org", + "fineractSwagger": "https://omnl.hybxfinance.io/fineract-provider/swagger-ui/index.html", + "swiftListenerPublic": "https://swift-listener.d-bis.org" + }, + "infra": { + "gitea": "https://gitea.d-bis.org", + "smomRepo": "https://gitea.d-bis.org/d-bis/smom-dbis-138.git", + "guosmmGazette": "https://gazette.shosjj.org/notices/2026", + "swiftListenerLan": "http://192.168.11.114:8788", + "fineractSidecarLan": "http://192.168.11.89:8080", + "chain138RpcLan": "http://192.168.11.211:8545" + } +} diff --git a/config/genesis-z-chain-900002.json b/config/genesis-z-chain-900002.json new file mode 100644 index 0000000..57481b5 --- /dev/null +++ b/config/genesis-z-chain-900002.json @@ -0,0 +1,37 @@ +{ + "config": { + "chainId": 900002, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "mergeNetSplitBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "ibft2": { + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 + } + }, + "nonce": "0x0", + "timestamp": "0x0", + "gasLimit": "0x1c9c380", + "difficulty": "0x1", + "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { + "balance": "0x33b2e3c9fd0803ce8000000", + "comment": "Besu dev key #1 — funded for local deploy" + } + } +} diff --git a/config/guosmm-notices.v1.json b/config/guosmm-notices.v1.json new file mode 100644 index 0000000..0a77eab --- /dev/null +++ b/config/guosmm-notices.v1.json @@ -0,0 +1,115 @@ +{ + "$schema": "GUOSMM official gazette notices — OMNL / ISB member banks", + "version": "1.0.0", + "gazetteBase": "https://gazette.shosjj.org/notices/2026", + "updated": "2026-07-03", + "notices": [ + { + "documentId": "GUOSMM-B-2026-001", + "series": "B", + "title": "Central Bank Charter — OMNL", + "documentType": "Central Bank Charter", + "url": "https://gazette.shosjj.org/notices/2026/GUOSMM-B-2026-001", + "publishedAt": "2026-06-12T12:00:00Z", + "effectiveAt": "2026-06-13T00:00:00Z", + "signedAt": "2026-07-02T00:45:01.965Z", + "signingAuthority": "Sovereign Military Order of Malta", + "contentSha256": "685ebdbfd6a9c1b2c0b7963b7771bf96e96d002e822107207df66f7f593b8032", + "republication": "v2", + "omnlLei": "98450070C57395F6B906", + "scope": ["omnl", "emcb", "central-bank", "office-24", "settlement"], + "enablingActs": ["GUOSMM-A-2026-001"], + "notes": "EMCB model — Treasury issues SCUDO; OMNL administers; not issuer" + }, + { + "documentId": "GUOSMM-C-2026-003", + "series": "C", + "title": "zBank — ISB Membership Admission and Operations Charter", + "documentType": "ISB Membership Admission and Operations Charter", + "url": "https://gazette.shosjj.org/notices/2026/GUOSMM-C-2026-003", + "publishedAt": "2026-07-01T20:00:00Z", + "effectiveAt": "2026-05-02T00:00:00Z", + "operationsCommencement": "2026-05-02T00:00:00Z", + "signedAt": "2026-07-03T03:24:07.594Z", + "signingAuthority": "Sovereign Military Order of Malta", + "contentSha256": "c685fd513cbc1e5d20d8f91605f33a8e824680a58b85c67a1a8ef12cf42b71eb", + "licenseId": "LICENSE-ZBANK-SHOSJJ-2026-001", + "licenseClass": "entity.bank", + "entity": "zBank", + "fineractOfficeId": 29, + "fineractExternalId": "ZBANK-OMNL", + "issuingDelegate": "pack-dbis", + "prudentialSupervisor": "pack-omnl", + "parentActs": [ + "GUOSMM-A-2026-001", + "GUOSMM-A-2026-002", + "GUOSMM-A-2026-003", + "GUOSMM-B-2026-001", + "GUOSMM-B-2026-002", + "GUOSMM-C-2026-001", + "GUOSMM-C-2026-002" + ], + "scope": ["z-bank", "onex-bank", "central-bank", "office-29"] + }, + { + "documentId": "GUOSMM-C-2026-004", + "series": "C", + "title": "NovaBank — ISB Membership Admission and Operations Charter", + "documentType": "ISB Membership Admission and Operations Charter", + "url": "https://gazette.shosjj.org/notices/2026/GUOSMM-C-2026-004", + "publishedAt": "2026-07-01T20:00:00Z", + "effectiveAt": "2026-05-02T00:00:00Z", + "operationsCommencement": "2026-05-02T00:00:00Z", + "signedAt": "2026-07-03T03:24:08.276Z", + "signingAuthority": "Sovereign Military Order of Malta", + "contentSha256": "6c524ad5207ae9ca8d53cd4f847593f175740291ad2f519c554bbb823b304b50", + "licenseId": "LICENSE-NOVABANK-SHOSJJ-2026-001", + "licenseClass": "entity.bank", + "entity": "NovaBank", + "fineractOfficeId": 30, + "fineractExternalId": "NOVABANK-OMNL", + "issuingDelegate": "pack-dbis", + "prudentialSupervisor": "pack-omnl", + "parentActs": [ + "GUOSMM-A-2026-001", + "GUOSMM-A-2026-002", + "GUOSMM-A-2026-003", + "GUOSMM-B-2026-001", + "GUOSMM-B-2026-002", + "GUOSMM-C-2026-001", + "GUOSMM-C-2026-002" + ], + "scope": ["nova-bank", "central-bank", "office-30"] + }, + { + "documentId": "GUOSMM-C-2026-005", + "series": "C", + "title": "AnakaBank — ISB Membership Admission and Operations Charter", + "documentType": "ISB Membership Admission and Operations Charter", + "url": "https://gazette.shosjj.org/notices/2026/GUOSMM-C-2026-005", + "publishedAt": "2026-07-01T20:00:00Z", + "effectiveAt": "2026-05-02T00:00:00Z", + "operationsCommencement": "2026-05-02T00:00:00Z", + "signedAt": "2026-07-03T03:24:08.926Z", + "signingAuthority": "Sovereign Military Order of Malta", + "contentSha256": "b93879aeb6e1c4791084b1e327682ccbf9b482a2b9b4fbbaff193e6e7a8f5f3e", + "licenseId": "LICENSE-ANAKABANK-SHOSJJ-2026-001", + "licenseClass": "entity.bank", + "entity": "AnakaBank", + "fineractOfficeId": 31, + "fineractExternalId": "ANAKABANK-OMNL", + "issuingDelegate": "pack-dbis", + "prudentialSupervisor": "pack-omnl", + "parentActs": [ + "GUOSMM-A-2026-001", + "GUOSMM-A-2026-002", + "GUOSMM-A-2026-003", + "GUOSMM-B-2026-001", + "GUOSMM-B-2026-002", + "GUOSMM-C-2026-001", + "GUOSMM-C-2026-002" + ], + "scope": ["anaka-bank", "central-bank", "office-31"] + } + ] +} diff --git a/config/hybx-omnl-cross-chain-lines.json b/config/hybx-omnl-cross-chain-lines.json index 96ecaf6..12967aa 100644 --- a/config/hybx-omnl-cross-chain-lines.json +++ b/config/hybx-omnl-cross-chain-lines.json @@ -1,24 +1,24 @@ { "version": "1.0.0", "updated": "2026-06-20", - "description": "OMNL office settlement lines \u2014 treasury EVM wallets + canonical M1 token anchors per chain.", + "description": "OMNL office settlement lines — treasury EVM wallets + canonical M1 token anchors per chain.", "lines": [ { "lineId": "0xa16da49f1c038e9d798245a68f33c539b595df5ed39f4c566345c381ac5fd8ad", "officeId": 1, "externalId": "1", - "entityName": "OMNL Head Office (DBIS) \u2013 Central Bank", + "entityName": "OMNL Head Office (DBIS) – Central Bank", "walletId": "HYBX-OFFICE-001", "treasuryWalletMainnet": "0x9baa0efd06a496f401b2fb8508192fec675b6986", "treasuryWalletChain138": "0xe41a19d5e62d83c086efe71ccecae397d32d2336", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -33,13 +33,13 @@ "treasuryWalletMainnet": "0x8a72f33cb9f1110dfa2b0a950b887b814614e7ce", "treasuryWalletChain138": "0xcda90617d6b86698ef54ff1e6bdccbe5df8a54f1", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -54,13 +54,13 @@ "treasuryWalletMainnet": "0x31d23172792db7d38c19a0d9136b47e5b9cb61bb", "treasuryWalletChain138": "0x6fba59d90a891588d14bc2ecd63371ebc1974759", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -75,13 +75,13 @@ "treasuryWalletMainnet": "0x476eac28c5a369556590026a33bf59f7798e2121", "treasuryWalletChain138": "0x42c4091ab2080a029b3371b473cc22d27ed7170a", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -96,13 +96,13 @@ "treasuryWalletMainnet": "0x3908d64ec91ad908ed0740654905b85f40c96892", "treasuryWalletChain138": "0xc536e3a0bb2ada09077f6fef49710ca2c5c7f404", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -117,13 +117,13 @@ "treasuryWalletMainnet": "0x75aa3ef512ff29a0475954c0a478b6b8a0e7485b", "treasuryWalletChain138": "0xd1f1d00a3706fc5d680ac8a5bd49dcce17408ea2", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -138,13 +138,13 @@ "treasuryWalletMainnet": "0x2f0d8f2b449eddbb0e1d57df5b8bab7bc1853fe3", "treasuryWalletChain138": "0xd437cbf6bf13f2c3b43abd0e5468e66edfe01299", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -159,13 +159,13 @@ "treasuryWalletMainnet": "0xf75948728a2379b485dc4695233c102f0a261089", "treasuryWalletChain138": "0x7287fe01c4ab5f70e3d24dd6bc24ae925552bd40", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -180,13 +180,13 @@ "treasuryWalletMainnet": "0xeaffdc8cff3096712beb4cdc0b0e076d8f3f68c6", "treasuryWalletChain138": "0xa7ebf614c6e47db20266be996db135d048fa20f7", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -201,13 +201,13 @@ "treasuryWalletMainnet": "0x73f9180c4d219e3cca43b1c02895c685c4153414", "treasuryWalletChain138": "0xe5736303f0b60f9dc14ef45b71547c228f261e6e", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -222,13 +222,13 @@ "treasuryWalletMainnet": "0x16cecc9f7d048c2a8829eea46b3c30fb134523fc", "treasuryWalletChain138": "0xac894ac9727fae7ff1c82103d74c5eafe5429d1a", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -243,13 +243,13 @@ "treasuryWalletMainnet": "0x7090a3acdda8514d6b07aff27f91ee8430d0a61c", "treasuryWalletChain138": "0xdd0a1f686b87349bdc5aa909078688bc065a19f2", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -264,13 +264,13 @@ "treasuryWalletMainnet": "0x5921918623c5fa3b3798467972497c6809d95790", "treasuryWalletChain138": "0xcf0acb4afb1e07dc7fb6a6cbbf40d302641f9eaa", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -285,13 +285,13 @@ "treasuryWalletMainnet": "0xa312cc28d812832d03c72cb822b2b460ade174d1", "treasuryWalletChain138": "0x8112c72ecfc0493b2cc7622218bc015df97d8ceb", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -306,13 +306,13 @@ "treasuryWalletMainnet": "0x09c8bbb678649ef543b0ca6e2d0248ed374be70e", "treasuryWalletChain138": "0x291069ab31457997037ac59296f4fff2b55a033c", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -327,13 +327,13 @@ "treasuryWalletMainnet": "0xaca2ab7b574d19cee64b004baa46994abf9bf0f2", "treasuryWalletChain138": "0x0ad3a2a39dbf0c7b1646c2c57902f5756b95e93f", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -348,13 +348,13 @@ "treasuryWalletMainnet": "0x45a70949a11fc813c82d44f80aec9fc07ff819e4", "treasuryWalletChain138": "0x3a3ae3e4355ac2a7cbeb1b12d2900eb37763f0b9", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -369,13 +369,13 @@ "treasuryWalletMainnet": "0x6c2e2c8b8b5ecb82246ceabbcbc862dc2b77634a", "treasuryWalletChain138": "0x3d3475d9e9bc261372b99dd4f2b0df5dc40623dd", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -390,13 +390,13 @@ "treasuryWalletMainnet": "0xea6eab4953f7f54f7c1001375e57d573bec66161", "treasuryWalletChain138": "0x106e4a810aff37a1bc62d30d414ec575e83e7539", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -411,13 +411,13 @@ "treasuryWalletMainnet": "0x834a95c1f4216e90677334339074fda872b97847", "treasuryWalletChain138": "0xfd371f2457a6a58f11c80cc1c4ca2305f4674db3", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -432,13 +432,13 @@ "treasuryWalletMainnet": "0x2e4d9d09801dc09e90b3752db42c1a8e6d033520", "treasuryWalletChain138": "0xa08621a479bc263e563b2eadb7490d1c07039ba7", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -453,13 +453,13 @@ "treasuryWalletMainnet": "0x7af34ec0e3d65f147ba6134471bff7bb25f785d0", "treasuryWalletChain138": "0xa31bf188d4235aa509f6ead064587ee99e6029c6", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -474,13 +474,13 @@ "treasuryWalletMainnet": "0x8c219794fdb74cf8ded871fab6135e40c4168da9", "treasuryWalletChain138": "0x204fa6d8c72a21116de75b37abf0d1cd642ddcfa", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -495,13 +495,13 @@ "treasuryWalletMainnet": "0x87dab11386841366682d6c5fd3cd90587552f8cd", "treasuryWalletChain138": "0xc93522ae4ab7482572cc29edd68fbb6d5ec6a585", "chains": { - "138": { - "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" - }, "1": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", @@ -516,13 +516,80 @@ "treasuryWalletMainnet": "0xfc4D24Bda60240D6dfa1a702eE83c65002cf5218", "treasuryWalletChain138": "0x934adfFFbe4D079b7dbDcE183805dBcBeC7818cb", "chains": { + "1": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" + } + }, + "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", + "inboundSettlementType": "gnosis-safe" + }, + { + "lineId": "0x9f3e2a1b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f", + "officeId": 29, + "externalId": "ZBANK-OMNL", + "entityName": "zBank (OneX Bank)", + "walletId": "HYBX-OFFICE-029", + "treasuryWalletMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", + "treasuryWalletChain138": "0xc93522ae4ab7482572cc29edd68fbb6d5ec6a585", + "treasuryWalletShivaOneX": "0x0000000000000000000000000000000000000000", + "chains": { + "1": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, "138": { "tokenM0": "0x0000000000000000000000000000000000000000", "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" }, - "1": { + "900001": { "tokenM0": "0x0000000000000000000000000000000000000000", - "tokenM1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + "tokenM1": "0x0000000000000000000000000000000000000000" + }, + "900002": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0x5FbDB2315678afecb367f032d93F642f64180aa3" + } + }, + "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", + "inboundSettlementType": "gnosis-safe", + "integrationRef": "config/onex-shiva-integration.v1.json" + }, + { + "lineId": "0xa1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef12345678", + "officeId": 30, + "externalId": "NOVABANK-OMNL", + "entityName": "NovaBank", + "walletId": "HYBX-OFFICE-030", + "guosmmNotice": "GUOSMM-C-2026-004", + "treasuryWalletMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", + "treasuryWalletChain138": "0xc93522ae4ab7482572cc29edd68fbb6d5ec6a585", + "chains": { + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" + } + }, + "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", + "inboundSettlementType": "gnosis-safe" + }, + { + "lineId": "0xb2c3d4e5f6789012345678901234567890abcdef1234567890abcdef1234567890", + "officeId": 31, + "externalId": "ANAKABANK-OMNL", + "entityName": "AnakaBank", + "walletId": "HYBX-OFFICE-031", + "guosmmNotice": "GUOSMM-C-2026-005", + "treasuryWalletMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", + "treasuryWalletChain138": "0xc93522ae4ab7482572cc29edd68fbb6d5ec6a585", + "chains": { + "138": { + "tokenM0": "0x0000000000000000000000000000000000000000", + "tokenM1": "0xf22258f57794CC8E06237084b353Ab30fFfa640b" } }, "inboundSettlementMainnet": "0xB08502ceE2545eEF782e933CFb80D0663E564da8", diff --git a/config/offices/office-24-settlement.v1.json b/config/offices/office-24-settlement.v1.json index 770b3f1..02ff5e2 100644 --- a/config/offices/office-24-settlement.v1.json +++ b/config/offices/office-24-settlement.v1.json @@ -3,7 +3,7 @@ "version": "1.0.0", "officeId": 24, "externalId": "HOSPITALLERS-ZARDASHT-IRAQ-IRAN", - "name": "HOSPITALLERS — Zardasht (Iraq / Iran Operations)", + "name": "Office 24 Central Bank — HOSPITALLERS Zardasht (Iraq / Iran)", "fineractTenant": "omnl", "tenantUser": "ali_hospitallers_tenant", "adminUser": "ali_hospitallers_admin", @@ -27,5 +27,7 @@ "chain138": { "enabled": true, "rpcEnv": "RPC_URL_138", "defaultLineId": "USD-M2" } }, "omnlLegalName": "ORGANISATION MONDIALE DU NUMERIQUE L.P.B.C.", - "omnlLei": "98450070C57395F6B906" + "omnlLei": "98450070C57395F6B906", + "guosmmEnablingAct": "GUOSMM-B-2026-001", + "guosmmNoticeUrl": "https://gazette.shosjj.org/notices/2026/GUOSMM-B-2026-001" } diff --git a/config/offices/office-29-zbank.v1.json b/config/offices/office-29-zbank.v1.json new file mode 100644 index 0000000..31fa8ec --- /dev/null +++ b/config/offices/office-29-zbank.v1.json @@ -0,0 +1,22 @@ +{ + "$schema": "OMNL Office 29 — zBank (OneX Bank) per GUOSMM-C-2026-003", + "version": "1.0.0", + "officeId": 29, + "externalId": "ZBANK-OMNL", + "name": "zBank — ISB member (OneX Bank)", + "guosmmNotice": "GUOSMM-C-2026-003", + "licenseId": "LICENSE-ZBANK-SHOSJJ-2026-001", + "fineractTenant": "omnl", + "settlement": { + "defaultCurrency": "USD", + "moneyLayers": ["M0", "M1", "M2", "M3", "M4"], + "glM1": "2100", + "glM2": "2200", + "glM4NearMoney": "1000" + }, + "rails": { + "swift": { "enabled": true }, + "hybx": { "enabled": true }, + "chain138": { "enabled": true, "defaultLineId": "USD-M2" } + } +} diff --git a/config/offices/office-30-novabank.v1.json b/config/offices/office-30-novabank.v1.json new file mode 100644 index 0000000..b57a130 --- /dev/null +++ b/config/offices/office-30-novabank.v1.json @@ -0,0 +1,22 @@ +{ + "$schema": "OMNL Office 30 — NovaBank per GUOSMM-C-2026-004", + "version": "1.0.0", + "officeId": 30, + "externalId": "NOVABANK-OMNL", + "name": "NovaBank — ISB member", + "guosmmNotice": "GUOSMM-C-2026-004", + "licenseId": "LICENSE-NOVABANK-SHOSJJ-2026-001", + "fineractTenant": "omnl", + "settlement": { + "defaultCurrency": "USD", + "moneyLayers": ["M0", "M1", "M2", "M3", "M4"], + "glM1": "2100", + "glM2": "2200", + "glM4NearMoney": "1000" + }, + "rails": { + "swift": { "enabled": true }, + "hybx": { "enabled": true }, + "chain138": { "enabled": true, "defaultLineId": "USD-M2" } + } +} diff --git a/config/offices/office-31-anakabank.v1.json b/config/offices/office-31-anakabank.v1.json new file mode 100644 index 0000000..ba054dc --- /dev/null +++ b/config/offices/office-31-anakabank.v1.json @@ -0,0 +1,22 @@ +{ + "$schema": "OMNL Office 31 — AnakaBank per GUOSMM-C-2026-005", + "version": "1.0.0", + "officeId": 31, + "externalId": "ANAKABANK-OMNL", + "name": "AnakaBank — ISB member", + "guosmmNotice": "GUOSMM-C-2026-005", + "licenseId": "LICENSE-ANAKABANK-SHOSJJ-2026-001", + "fineractTenant": "omnl", + "settlement": { + "defaultCurrency": "USD", + "moneyLayers": ["M0", "M1", "M2", "M3", "M4"], + "glM1": "2100", + "glM2": "2200", + "glM4NearMoney": "1000" + }, + "rails": { + "swift": { "enabled": true }, + "hybx": { "enabled": true }, + "chain138": { "enabled": true, "defaultLineId": "USD-M2" } + } +} diff --git a/config/omnl-customer-security.production.v1.json b/config/omnl-customer-security.production.v1.json index c09a5a8..50d1f52 100644 --- a/config/omnl-customer-security.production.v1.json +++ b/config/omnl-customer-security.production.v1.json @@ -16,6 +16,8 @@ "https://online.omdnl.org", "https://office24.omdnl.org", "https://digital.omdnl.org", + "https://zblockchainsystem.com", + "https://www.zblockchainsystem.com", "https://exchange.d-bis.org", "https://exchange.omdnl.org", "https://forex.omdnl.org" diff --git a/config/omnl-production-domains.v1.json b/config/omnl-production-domains.v1.json index 89bdf74..8c38d2b 100644 --- a/config/omnl-production-domains.v1.json +++ b/config/omnl-production-domains.v1.json @@ -41,8 +41,9 @@ }, { "host": "digital.omdnl.org", - "product": "digital-swap", - "defaultRoute": "/swap" + "product": "digital-swap-legacy", + "defaultRoute": "/swap", + "notes": "Legacy — prefer https://zblockchainsystem.com/swap" }, { "host": "office24.omdnl.org", diff --git a/config/omnl-supported-chains.v1.json b/config/omnl-supported-chains.v1.json index 83e2cc2..4df619e 100644 --- a/config/omnl-supported-chains.v1.json +++ b/config/omnl-supported-chains.v1.json @@ -1224,37 +1224,23 @@ }, { "chainId": 900001, - "name": "OMNL Expansion Slot 1", + "name": "Shiva Blockchain (OneX)", "type": "evm", - "nativeSymbol": "ETH", - "tier": "reserved", - "omnlRole": "expansion", - "status": "reserved", + "nativeSymbol": "SHIVA", + "tier": "primary", + "omnlRole": "onex-shiva", + "status": "active", "rpcEnv": "CHAIN_900001_RPC_URL", + "explorerUrl": "https://explorer.shiva.onex.omdnl.org", + "rpcDefault": "https://rpc.shiva.onex.omdnl.org", "settlement": { - "m2LoadEnabled": false, - "swapEnabled": false, - "bridgeEnabled": false, - "officeId": 24 + "m2LoadEnabled": true, + "swapEnabled": true, + "bridgeEnabled": true, + "officeId": 29 }, - "complianceEnv": "OMNL_COMPLIANCE_CORE_900001" - }, - { - "chainId": 900002, - "name": "OMNL Expansion Slot 2", - "type": "evm", - "nativeSymbol": "ETH", - "tier": "reserved", - "omnlRole": "expansion", - "status": "reserved", - "rpcEnv": "CHAIN_900002_RPC_URL", - "settlement": { - "m2LoadEnabled": false, - "swapEnabled": false, - "bridgeEnabled": false, - "officeId": 24 - }, - "complianceEnv": "OMNL_COMPLIANCE_CORE_900002" + "complianceEnv": "OMNL_COMPLIANCE_CORE_900001", + "integrationRef": "config/onex-shiva-integration.v1.json" }, { "chainId": 900003, @@ -2312,8 +2298,8 @@ } ], "stats": { - "total": 128, - "active": 61, + "total": 127, + "active": 60, "staged": 3, "reserved": 64 } diff --git a/config/onex-shiva-integration.v1.json b/config/onex-shiva-integration.v1.json new file mode 100644 index 0000000..54d9b63 --- /dev/null +++ b/config/onex-shiva-integration.v1.json @@ -0,0 +1,46 @@ +{ + "$schema": "OneX Wallet + OneX Bank on Shiva Blockchain — OMNL integration", + "version": "1.0.0", + "updated": "2026-07-02", + "brand": { + "wallet": "OneX Wallet", + "bank": "OneX Bank", + "blockchain": "Shiva Blockchain" + }, + "chain": { + "chainId": 900001, + "name": "Shiva Blockchain (OneX)", + "network": "shiva-onex", + "type": "evm", + "nativeSymbol": "SHIVA", + "rpcEnv": "CHAIN_900001_RPC_URL", + "rpcDefault": "https://rpc.shiva.onex.omdnl.org", + "explorerUrl": "https://explorer.shiva.onex.omdnl.org", + "omnlRegistrySlot": 900001 + }, + "fineract": { + "officeId": 29, + "externalId": "ZBANK-OMNL", + "entityName": "zBank", + "hybxWalletId": "HYBX-OFFICE-029", + "m1SpendableGl": "2100" + }, + "bankCorridor": { + "orderingName": "OneX Bank", + "creditorBic": "ONEXSHIV33", + "debtorIban": "SH98ONEX000000000000000029", + "swiftMessageKind": "MT103", + "remittanceTemplate": "OneX Bank → Z Online Bank · Shiva blockchain settlement" + }, + "wallet": { + "thirdwebMetadataName": "OneX Wallet", + "supportedAuth": ["email", "google", "apple", "passkey"], + "defaultTokenLineId": "USD-M2", + "defaultTokenSymbol": "cUSDC" + }, + "settlement": { + "zBridgeOfficeId": 24, + "targetChainId": 900001, + "rails": ["HYBX", "SWIFT", "CHAIN_MINT"] + } +} diff --git a/config/settlement-middleware.production.v1.json b/config/settlement-middleware.production.v1.json index 9af6ed1..27d66c3 100644 --- a/config/settlement-middleware.production.v1.json +++ b/config/settlement-middleware.production.v1.json @@ -47,5 +47,10 @@ "allowHybxProduction": true, "allowChainMintExecute": true, "onlineBankMode": true + }, + "guosmm": { + "emcbCharter": "GUOSMM-B-2026-001", + "registryPath": "config/guosmm-notices.v1.json", + "gazetteBase": "https://gazette.shosjj.org/notices/2026" } } diff --git a/config/swift-listener-chain138.v1.json b/config/swift-listener-chain138.v1.json new file mode 100644 index 0000000..de19e20 --- /dev/null +++ b/config/swift-listener-chain138.v1.json @@ -0,0 +1,110 @@ +{ + "$schema": "OMNL SWIFT FIN / ISO 20022 listener — Chain 138 + Office 24 production", + "schemaVersion": "1.0.0", + "chain138": { + "chainId": 138, + "names": ["chain138", "chain 138", "defi oracle meta", "dbis", "omnl"], + "rpcHint": "RPC_URL_138", + "configSources": [ + "config/ei-matrix-138-assets-and-pools.v1.json", + "config/chain138-iso20022-attestation.v1.json" + ] + }, + "supportedSwiftFinTypes": ["MT103", "MT102", "MT202", "MT910", "MT940", "MT942", "MT950"], + "supportedIso20022Types": [ + "pain.001", + "pacs.008", + "pacs.009", + "pacs.002", + "camt.052", + "camt.053", + "camt.054" + ], + "cToCwSymbolMapping": { + "USD": "cWUSD", + "USDC": "cWUSDC", + "USDT": "cWUSDT", + "EUR": "cWEUR", + "GBP": "cWGBP" + }, + "matchHints": { + "instructionIdPrefixes": ["HOSPITALLERS", "CHAIN138", "OMNL", "HYBX", "ZARDASHT", "Z-BANK", "O24"], + "remittanceKeywords": [ + "chain138", + "cWUSDC", + "cWUSDT", + "cWUSD", + "omnl", + "hybx", + "office 24", + "zardasht", + "hospitallers" + ] + }, + "storage": { + "eventsDir": "reports/swift-listener/events", + "latestStatus": "reports/swift-listener/status-latest.json", + "processedIndex": "reports/swift-listener/processed-index.json", + "inboxDir": "reports/swift-listener/inbox", + "archiveDir": "reports/swift-listener/archive", + "as4InboxDir": "reports/swift-listener/as4-inbox", + "mailInboxDir": "reports/swift-listener/mail-inbox", + "telexInboxDir": "reports/swift-listener/telex-inbox" + }, + "adapters": { + "fileInbox": { "enabled": true, "pollIntervalSec": 30, "archiveOnProcess": true }, + "webhook": { + "enabled": true, + "port": 8788, + "path": "/inbound", + "healthPath": "/health", + "authBearerEnv": "SWIFT_LISTENER_WEBHOOK_TOKEN", + "webhookSecretEnv": "HYBX_WEBHOOK_SECRET", + "webhookSignatureHeader": "x-hybx-signature" + }, + "p2pRail": { + "enabled": false, + "baseUrlEnv": "P2P_BASE_URL", + "pollIntervalSec": 60, + "transactionsPath": "/api/v1/transactions" + }, + "finGateway": { + "enabled": false, + "baseUrlEnv": "FIN_GATEWAY_URL", + "fallbackBaseUrlEnv": "ALLIANCE_ACCESS_URL", + "pollIntervalSec": 120, + "messagesPath": "/messages", + "authBearerEnv": "FIN_GATEWAY_TOKEN" + }, + "swiftFinTcp": { + "enabled": true, + "host": "0.0.0.0", + "portEnv": "SWIFT_FIN_TCP_PORT", + "defaultPort": 5001 + }, + "as4Inbox": { "enabled": false, "pollIntervalSec": 60, "archiveOnProcess": true }, + "mailAttachmentInbox": { "enabled": false, "pollIntervalSec": 120, "archiveOnProcess": true }, + "telexInbox": { "enabled": false, "pollIntervalSec": 120, "archiveOnProcess": true } + }, + "outbound": { + "omnlForward": { + "enabled": true, + "mode": "local_and_api", + "apiPath": "/api/v1/omnl/iso20022/messages", + "tokenAggregationUrlEnv": "TOKEN_AGGREGATION_URL", + "apiKeyEnv": "OMNL_API_KEY", + "localStoreDir": "config/iso20022-omnl/messages" + }, + "intakeGateway": { + "enabled": true, + "gatewayAddressEnv": "ISO20022_INTAKE_GATEWAY_MAINNET", + "gatewayAddressFallback": "0x66C89F91A4e830d87fcE4E5F86aFA71A96FbDa3d", + "rpcUrlEnv": "RPC_URL_138", + "privateKeyEnv": "PRIVATE_KEY", + "executeEnv": "SWIFT_LISTENER_GATEWAY_EXECUTE", + "dryRunDefault": true, + "autoExecuteWhenKeyPresent": true, + "chainId": 138 + } + } +} diff --git a/config/swift-mt-catalog.v1.json b/config/swift-mt-catalog.v1.json new file mode 100644 index 0000000..4d60597 --- /dev/null +++ b/config/swift-mt-catalog.v1.json @@ -0,0 +1,72 @@ +{ + "$schema": "OMNL SWIFT MT catalog — Central Bank, Office 24, Z Online Bank", + "version": "1.0.0", + "settlementOfficeId": 24, + "bic": "OMNLUS33", + "rails": { + "outbound": "POST /settlement/external-transfer (rail SWIFT)", + "inbound": "POST /settlement/swift/inbound", + "listener": "SWIFT listener webhook POST /inbound :8788" + }, + "messageTypes": [ + { + "mt": "MT103", + "name": "Single Customer Credit Transfer", + "use": "Z Bank wire, Office 24 outbound, correspondent payments", + "moneyLayers": ["M0", "M1", "M2", "M4"], + "portal": "/central-bank#z-settlement-protocols", + "settlementField": "swiftMessageKind: MT103" + }, + { + "mt": "MT102", + "name": "Multiple Customer Credit Transfer", + "use": "Bulk payroll, DLC/LS batch legs", + "moneyLayers": ["M1", "M2", "M3", "M4"], + "portal": "/central-bank#z-settlement-protocols" + }, + { + "mt": "MT202", + "name": "General Financial Institution Transfer", + "use": "SBLC cover payment, interbank settlement", + "moneyLayers": ["M4"], + "tradeFinance": ["SBLC"] + }, + { + "mt": "MT910", + "name": "Confirmation of Credit", + "use": "Credit advice to beneficiary bank", + "moneyLayers": ["M4"] + }, + { + "mt": "MT940", + "name": "Customer Statement", + "use": "End-of-day account statement (Fineract reconcile)", + "moneyLayers": ["M0", "M1", "M2"] + }, + { + "mt": "MT942", + "name": "Interim Statement", + "use": "Intraday statement / liquidity monitoring", + "moneyLayers": ["M0", "M1"] + }, + { + "mt": "MT950", + "name": "Statement Message", + "use": "Legacy statement (Fineract GL export)", + "moneyLayers": ["M0", "M1", "M2"] + } + ], + "iso20022": [ + { "type": "pacs.008", "swiftFin": "MT103", "use": "FI-to-FI customer credit" }, + { "type": "pacs.009", "swiftFin": "MT202", "use": "FI-to-FI institution transfer" }, + { "type": "camt.054", "swiftFin": "MT910", "use": "Credit notification" }, + { "type": "camt.053", "swiftFin": "MT940", "use": "Account statement" }, + { "type": "camt.052", "swiftFin": "MT942", "use": "Account report" } + ], + "tradeFinance": { + "SBLC": { "swiftMessages": ["MT103", "MT202"], "endpoint": "POST /settlement/trade-finance/SBLC" }, + "DLC": { "swiftMessages": ["MT103", "MT102"], "endpoint": "POST /settlement/trade-finance/DLC" }, + "BG": { "swiftMessages": ["MT103"], "endpoint": "POST /settlement/trade-finance/BG" }, + "LS": { "swiftMessages": ["MT102", "MT103"], "endpoint": "POST /settlement/trade-finance/LS" } + } +} diff --git a/config/z-chain-deployed.v1.json b/config/z-chain-deployed.v1.json new file mode 100644 index 0000000..54be0c6 --- /dev/null +++ b/config/z-chain-deployed.v1.json @@ -0,0 +1,17 @@ +{ + "$schema": "Z Chain on-chain deployment addresses (chainId 900002)", + "version": "1.0.0", + "updated": "2026-07-07", + "chainId": 900002, + "network": "z-chain", + "status": "active", + "contracts": { + "cUSDC": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "accountWalletRegistry": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" + }, + "deployScript": "script/z-chain/DeployZChainEmoney.s.sol", + "notes": [ + "Run scripts/deployment/deploy-z-chain-contracts.sh after bootstrap-z-chain-local", + "Update addresses here and in hybx-omnl-cross-chain-lines.json chains.900002" + ] +} diff --git a/config/z-chain-integration.v1.json b/config/z-chain-integration.v1.json new file mode 100644 index 0000000..5450e8d --- /dev/null +++ b/config/z-chain-integration.v1.json @@ -0,0 +1,105 @@ +{ + "$schema": "International Z Wallet + Z Chain — Z Ecosystem integration", + "version": "1.0.0", + "updated": "2026-07-05", + "brand": { + "wallet": "International Z Wallet", + "bank": "Z Online Bank", + "blockchain": "Z Chain", + "bot": "Z Bot" + }, + "chain": { + "chainId": 900002, + "name": "Z Chain", + "network": "z-chain", + "type": "evm", + "nativeSymbol": "Z", + "rpcEnv": "CHAIN_900002_RPC_URL", + "rpcDefault": "http://127.0.0.1:8546", + "explorerUrl": "https://explorer.zblockchainsystem.com", + "omnlRegistrySlot": 900002 + }, + "fineract": { + "officeId": 29, + "externalId": "ZBANK-OMNL", + "entityName": "zBank", + "hybxWalletId": "HYBX-OFFICE-029", + "m1SpendableGl": "2100" + }, + "bankCorridor": { + "orderingName": "International Z Wallet", + "creditorBic": "ZCHNOMNL33", + "debtorIban": "Z298ZCHN000000000000000029", + "swiftMessageKind": "MT103", + "remittanceTemplate": "International Z Wallet → Z Online Bank · Z Chain settlement" + }, + "internationalCorridors": [ + { + "id": "IQ-USD", + "label": "Iraq (USD)", + "country": "IQ", + "currency": "USD", + "rails": [ + "SWIFT", + "HYBX", + "CHAIN_MINT" + ] + }, + { + "id": "IR-USD", + "label": "Iran (USD)", + "country": "IR", + "currency": "USD", + "rails": [ + "HYBX", + "CHAIN_MINT" + ] + }, + { + "id": "EU-EUR", + "label": "Europe (EUR)", + "country": "EU", + "currency": "EUR", + "rails": [ + "SWIFT", + "HYBX" + ] + }, + { + "id": "GLOBAL-USD", + "label": "Global (USD)", + "country": "GLOBAL", + "currency": "USD", + "rails": [ + "SWIFT", + "HYBX", + "CHAIN_MINT" + ] + } + ], + "wallet": { + "thirdwebMetadataName": "International Z Wallet", + "supportedAuth": [ + "email", + "google", + "apple", + "passkey" + ], + "defaultTokenLineId": "USD-M2", + "defaultTokenSymbol": "cUSDC" + }, + "settlement": { + "zBridgeOfficeId": 24, + "targetChainId": 900002, + "rails": [ + "HYBX", + "SWIFT", + "CHAIN_MINT" + ] + }, + "contracts": { + "cUSDC": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "accountWalletRegistry": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "deployRef": "config/z-chain-deployed.v1.json" + } +} diff --git a/config/z-chain-network-config.json b/config/z-chain-network-config.json new file mode 100644 index 0000000..3e909e1 --- /dev/null +++ b/config/z-chain-network-config.json @@ -0,0 +1,29 @@ +{ + "genesis": { + "config": { + "chainId": 900002, + "ibft2": { + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 + } + }, + "nonce": "0x0", + "timestamp": "0x0", + "gasLimit": "0x1c9c380", + "difficulty": "0x1", + "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { + "balance": "0x33b2e3c9fd0803ce8000000" + } + } + }, + "blockchain": { + "nodes": { + "generate": true, + "count": 1 + } + } +} diff --git a/config/z-chain-production.v1.json b/config/z-chain-production.v1.json new file mode 100644 index 0000000..3a74668 --- /dev/null +++ b/config/z-chain-production.v1.json @@ -0,0 +1,37 @@ +{ + "$schema": "Z Chain production endpoints — Besu IBFT2 on LXC or Azure", + "version": "1.0.0", + "updated": "2026-07-06", + "chainId": 900002, + "network": "z-chain", + "genesis": "config/genesis-z-chain-900002.json", + "networkConfig": "config/z-chain-network-config.json", + "endpoints": { + "portal": "https://zblockchainsystem.com", + "rpcPublic": "https://rpc.zblockchainsystem.com", + "rpcLan": "http://192.168.11.212:8546", + "explorer": "https://explorer.zblockchainsystem.com", + "walletPortal": "https://zblockchainsystem.com/z-wallet", + "ecosystemHub": "https://zblockchainsystem.com/z", + "digitalSwap": "https://zblockchainsystem.com/swap", + "zBankUi": "https://zblockchainsystem.com/central-bank" + }, + "env": { + "rpc": "CHAIN_900002_RPC_URL", + "compliance": "OMNL_COMPLIANCE_CORE_900002", + "viteRpc": "VITE_RPC_URL_900002", + "viteExplorer": "VITE_Z_CHAIN_EXPLORER_URL" + }, + "besu": { + "containerName": "z-chain-besu-prod", + "rpcPort": 8546, + "dataPath": "/opt/besu-z-chain", + "genesisPath": "/opt/besu-z-chain/config/genesis.json" + }, + "deployScripts": { + "bootstrapLocal": "scripts/deployment/bootstrap-z-chain-local.sh", + "bootstrapProduction": "scripts/deployment/bootstrap-z-chain-production.sh", + "deployContracts": "scripts/deployment/deploy-z-chain-contracts.sh", + "syncAddresses": "scripts/deployment/sync-z-chain-deployed-addresses.mjs" + } +} diff --git a/config/z-ecosystem-links.v1.json b/config/z-ecosystem-links.v1.json new file mode 100644 index 0000000..87ba70e --- /dev/null +++ b/config/z-ecosystem-links.v1.json @@ -0,0 +1,35 @@ +{ + "$schema": "Z Ecosystem link map — zblockchainsystem.com only", + "version": "1.0.0", + "updated": "2026-07-07", + "brand": "Z Blockchain System", + "publicUrl": "https://zblockchainsystem.com", + "routes": { + "hub": "/z", + "wallet": "/z-wallet", + "swap": "/swap", + "onlineBank": "/central-bank", + "zBotHealth": "/api/v1/z-bot/health", + "zWalletCorridors": "/api/v1/z-wallet/corridors", + "settlementHealth": "/settlement/health", + "zBankSettle": "/settlement/z-bank/settle-m0-m4" + }, + "productionPortals": { + "zEcosystem": "https://zblockchainsystem.com/z", + "zWallet": "https://zblockchainsystem.com/z-wallet", + "zSwap": "https://zblockchainsystem.com/swap", + "zOnlineBank": "https://zblockchainsystem.com/central-bank" + }, + "subdomains": { + "rpc": "https://rpc.zblockchainsystem.com", + "explorer": "https://explorer.zblockchainsystem.com" + }, + "local": { + "frontend": "http://localhost:3002", + "zHub": "http://localhost:3002/z", + "zWallet": "http://localhost:3002/z-wallet", + "zBotHealth": "http://localhost:3000/api/v1/z-bot/health", + "settlementHealth": "http://localhost:3011/api/v1/settlement/health", + "chainRpc": "http://127.0.0.1:8546" + } +} diff --git a/config/z-ecosystem-production-domains.v1.json b/config/z-ecosystem-production-domains.v1.json new file mode 100644 index 0000000..fef5ca0 --- /dev/null +++ b/config/z-ecosystem-production-domains.v1.json @@ -0,0 +1,28 @@ +{ + "version": "1.0.0", + "updated": "2026-07-06", + "brand": "Z Blockchain System", + "publicUrl": "https://zblockchainsystem.com", + "originHost": "dev-bis-zardasht", + "originIp": "76.53.10.34", + "frontendPort": 3002, + "domains": [ + { + "host": "zblockchainsystem.com", + "hostAliases": ["www.zblockchainsystem.com"], + "product": "z-ecosystem", + "defaultRoute": "/z", + "routes": ["/z", "/z-wallet", "/swap", "/central-bank"] + } + ], + "subdomains": { + "rpc": "rpc.zblockchainsystem.com", + "explorer": "explorer.zblockchainsystem.com" + }, + "apis": { + "zBotHealth": "https://zblockchainsystem.com/api/v1/z-bot/health", + "zWalletCorridors": "https://zblockchainsystem.com/api/v1/z-wallet/corridors", + "settlementHealth": "https://zblockchainsystem.com/settlement/health", + "zBankSettle": "https://zblockchainsystem.com/settlement/z-bank/settle-m0-m4" + } +} diff --git a/config/z-ecosystem.local.env.example b/config/z-ecosystem.local.env.example new file mode 100644 index 0000000..631211c --- /dev/null +++ b/config/z-ecosystem.local.env.example @@ -0,0 +1,23 @@ +# Z Ecosystem local overrides — copy to config/z-ecosystem.local.env +# Production portal: https://zblockchainsystem.com (separate from OMNL) + +VITE_ECOSYSTEM=z +Z_SUPPORTED_CHAINS_CONFIG=config/z-supported-chains.v1.json +Z_ECOSYSTEM_PUBLIC_URL=https://zblockchainsystem.com +VITE_Z_ECOSYSTEM_URL=https://zblockchainsystem.com + +# Z Chain (local Hardhat/Besu) +CHAIN_900002_RPC_URL=http://127.0.0.1:8546 +VITE_RPC_URL_900002=http://127.0.0.1:8546 +VITE_Z_CHAIN_EXPLORER_URL=https://explorer.zblockchainsystem.com +VITE_ENABLE_Z_WALLET=true + +# Z Bot — Azure OpenAI (optional) +AZURE_OPENAI_ENDPOINT=https://.openai.azure.com +AZURE_OPENAI_API_KEY= +AZURE_OPENAI_DEPLOYMENT=gpt-4o-mini +AZURE_OPENAI_API_VERSION=2024-02-15-preview + +# Settlement (Z Bank API backend) +SETTLEMENT_MIDDLEWARE_URL=http://127.0.0.1:3011 +SETTLEMENT_MIDDLEWARE_CONFIG=config/settlement-middleware.production.v1.json diff --git a/config/z-ecosystem.production.env.example b/config/z-ecosystem.production.env.example new file mode 100644 index 0000000..f743b52 --- /dev/null +++ b/config/z-ecosystem.production.env.example @@ -0,0 +1,25 @@ +# Z Ecosystem production — copy to config/z-ecosystem.production.env +# Gitea binds :3000 on dev-bis-ali; Z API uses :3100 + +VITE_ECOSYSTEM=z +Z_SUPPORTED_CHAINS_CONFIG=config/z-supported-chains.v1.json +Z_ECOSYSTEM_PUBLIC_URL=https://zblockchainsystem.com +VITE_Z_ECOSYSTEM_URL=https://zblockchainsystem.com + +CHAIN_900002_RPC_URL=https://rpc.zblockchainsystem.com +VITE_RPC_URL_900002=https://rpc.zblockchainsystem.com +VITE_Z_CHAIN_EXPLORER_URL=https://explorer.zblockchainsystem.com +VITE_ENABLE_Z_WALLET=true + +Z_TOKEN_AGGREGATION_PORT=3100 +PORT=3100 +TOKEN_AGGREGATION_URL=http://127.0.0.1:3100 +SETTLEMENT_MIDDLEWARE_URL=http://127.0.0.1:3011 +SETTLEMENT_URL=http://127.0.0.1:3011 +SETTLEMENT_MIDDLEWARE_CONFIG=config/settlement-middleware.production.v1.json + +# Z Bot — Azure OpenAI (optional) +AZURE_OPENAI_ENDPOINT= +AZURE_OPENAI_API_KEY= +AZURE_OPENAI_DEPLOYMENT=gpt-4o-mini +AZURE_OPENAI_API_VERSION=2024-02-15-preview diff --git a/config/z-ecosystem.v1.json b/config/z-ecosystem.v1.json new file mode 100644 index 0000000..da262f0 --- /dev/null +++ b/config/z-ecosystem.v1.json @@ -0,0 +1,50 @@ +{ + "$schema": "Z consumer ecosystem hub links", + "version": "1.1.0", + "updated": "2026-07-06", + "brand": "Z Blockchain System", + "publicUrl": "https://zblockchainsystem.com", + "routes": { + "hub": "/z", + "wallet": "/z-wallet", + "bot": "/z-wallet#z-bot", + "swap": "/swap", + "onlineBank": "/central-bank", + "settlement": "/central-bank#z-full-settlement" + }, + "apps": [ + { + "id": "zWallet", + "label": "International Z Wallet", + "path": "/z-wallet", + "description": "Thirdweb in-app wallet on Z Chain with international corridors" + }, + { + "id": "zBot", + "label": "Z Bot", + "path": "/z-wallet#z-bot", + "description": "In-app assistant for balance, transfers, and corridors" + }, + { + "id": "zChain", + "label": "Z Chain", + "path": "/z", + "chainId": 900002, + "description": "Consumer EVM chain (chainId 900002)" + }, + { + "id": "zSwap", + "label": "Digital Swap", + "path": "/swap", + "description": "M2 load, token swap, settlement rails" + }, + { + "id": "zOnlineBank", + "label": "Z Online Bank", + "path": "/central-bank", + "description": "M0–M4 banking and settlement rails" + } + ], + "integrationRef": "config/z-chain-integration.v1.json", + "productionDomainsRef": "config/z-ecosystem-production-domains.v1.json" +} diff --git a/config/z-supported-chains.v1.json b/config/z-supported-chains.v1.json new file mode 100644 index 0000000..27d6186 --- /dev/null +++ b/config/z-supported-chains.v1.json @@ -0,0 +1,27 @@ +{ + "$schema": "Z Chain supported chains — separate from OMNL 128-chain registry", + "version": "1.0.0", + "brand": "Z Blockchain System", + "primaryChainId": 900002, + "chains": [ + { + "chainId": 900002, + "name": "Z Chain", + "type": "evm", + "nativeSymbol": "Z", + "tier": "primary", + "zRole": "settlement-hub", + "status": "active", + "rpcEnv": "CHAIN_900002_RPC_URL", + "explorerUrl": "https://explorer.zblockchainsystem.com", + "rpcDefault": "http://127.0.0.1:8546", + "settlement": { + "m2LoadEnabled": true, + "swapEnabled": true, + "bridgeEnabled": true + }, + "complianceEnv": "Z_COMPLIANCE_CORE_900002", + "integrationRef": "config/z-chain-integration.v1.json" + } + ] +} diff --git a/contracts/z-chain/AccountWalletRegistryZ.sol b/contracts/z-chain/AccountWalletRegistryZ.sol new file mode 100644 index 0000000..1ade916 --- /dev/null +++ b/contracts/z-chain/AccountWalletRegistryZ.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "@openzeppelin/contracts/access/AccessControl.sol"; + +interface IAccountWalletRegistryZ { + struct WalletLink { + bytes32 walletRefId; + string provider; + bool active; + } + + function linkAccountToWallet(bytes32 accountRefId, bytes32 walletRefId, string calldata provider) external; + function getWalletsForAccount(bytes32 accountRefId) external view returns (WalletLink[] memory); +} + +/** + * @title AccountWalletRegistryZ + * @notice Minimal registry for Z Chain deploy scope + */ +contract AccountWalletRegistryZ is IAccountWalletRegistryZ, AccessControl { + bytes32 public constant ACCOUNT_MANAGER_ROLE = keccak256("ACCOUNT_MANAGER_ROLE"); + + mapping(bytes32 => WalletLink[]) private _accountToWallets; + + constructor(address admin) { + _grantRole(DEFAULT_ADMIN_ROLE, admin); + _grantRole(ACCOUNT_MANAGER_ROLE, admin); + } + + function linkAccountToWallet(bytes32 accountRefId, bytes32 walletRefId, string calldata provider) external { + require(hasRole(ACCOUNT_MANAGER_ROLE, msg.sender), "not manager"); + _accountToWallets[accountRefId].push(WalletLink(walletRefId, provider, true)); + } + + function getWalletsForAccount(bytes32 accountRefId) external view returns (WalletLink[] memory) { + return _accountToWallets[accountRefId]; + } +} diff --git a/contracts/z-chain/ZChainUSDC.sol b/contracts/z-chain/ZChainUSDC.sol new file mode 100644 index 0000000..039d695 --- /dev/null +++ b/contracts/z-chain/ZChainUSDC.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/** + * @title ZChainUSDC + * @notice Minimal M2 stand-in for Z Chain local/dev deploys (cUSDC) + */ +contract ZChainUSDC is ERC20 { + constructor(address admin) ERC20("Z Chain USD", "cUSDC") { + _mint(admin, 1_000_000_000 * 10 ** 6); + } + + function decimals() public pure override returns (uint8) { + return 6; + } +} diff --git a/deploy/nginx/omnl-common-locations.conf b/deploy/nginx/omnl-common-locations.conf index ec6f588..c14955b 100644 --- a/deploy/nginx/omnl-common-locations.conf +++ b/deploy/nginx/omnl-common-locations.conf @@ -1,5 +1,10 @@ # Shared OMNL Bank proxy locations (include inside each server block) +# Z Ecosystem routes live on zblockchainsystem.com only +location ~ ^/(z|z-wallet)(/|$) { + return 302 https://zblockchainsystem.com$request_uri; +} + location /api/v1/ { include /etc/nginx/omnl-portal-auth-current.conf.inc; proxy_pass http://omnl_api/api/v1/; diff --git a/deploy/nginx/z-ecosystem-common-locations.conf b/deploy/nginx/z-ecosystem-common-locations.conf new file mode 100644 index 0000000..8de5a3d --- /dev/null +++ b/deploy/nginx/z-ecosystem-common-locations.conf @@ -0,0 +1,56 @@ +# Z Ecosystem — Z-only nginx locations (no OMNL consoles) + +location /api/v1/ { + proxy_pass http://z_ecosystem_api/api/v1/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 120s; +} + +location /settlement/ { + proxy_pass http://z_ecosystem_settlement/api/v1/settlement/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 120s; +} + +location /exchange/ { + proxy_pass http://z_ecosystem_exchange/api/v1/exchange/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_read_timeout 120s; +} + +# Block OMNL / DBIS-only SPA routes on Z domain +location ~ ^/(hub|office-24|trade|bridge|reserve|history|admin|docs|wallets|omnl)(/|$) { + return 404; +} + +location /health { + return 200 'Z Ecosystem online — chain 900002'; + add_header Content-Type text/plain; +} + +location /assets/ { + proxy_pass http://z_ecosystem_frontend; + proxy_http_version 1.1; + proxy_set_header Host $host; + expires 1y; + add_header Cache-Control "public, immutable"; +} + +location / { + proxy_pass http://z_ecosystem_frontend; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + add_header Cache-Control "no-cache, no-store, must-revalidate" always; +} diff --git a/deploy/nginx/z-ecosystem-domains.conf b/deploy/nginx/z-ecosystem-domains.conf new file mode 100644 index 0000000..a513877 --- /dev/null +++ b/deploy/nginx/z-ecosystem-domains.conf @@ -0,0 +1,49 @@ +# Z Ecosystem — zblockchainsystem.com (Z Chain, Wallet, Bot, Swap, Z Bank) +# Deploy: scripts/deployment/deploy-z-domains.sh +# Independent from OMNL / DBIS portals + +upstream z_ecosystem_frontend { + server 127.0.0.1:3002; +} + +upstream z_ecosystem_api { + server 127.0.0.1:3100; +} + +upstream z_ecosystem_settlement { + server 127.0.0.1:3011; +} + +upstream z_ecosystem_exchange { + server 127.0.0.1:3012; +} + +server { + listen 80; + listen [::]:80; + server_name zblockchainsystem.com www.zblockchainsystem.com; + + add_header X-Z-Product "z-ecosystem" always; + add_header X-Z-Chain "900002" always; + + location = / { + return 302 /z; + } + + include /etc/nginx/snippets/z-ecosystem-common-locations.conf; +} + +server { + listen 80; + listen [::]:80; + server_name rpc.zblockchainsystem.com; + + add_header X-Z-Product "z-chain-rpc" always; + + location / { + proxy_pass http://127.0.0.1:8546; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_read_timeout 120s; + } +} diff --git a/docs/04-configuration/Z_ECOSYSTEM.md b/docs/04-configuration/Z_ECOSYSTEM.md new file mode 100644 index 0000000..56bb577 --- /dev/null +++ b/docs/04-configuration/Z_ECOSYSTEM.md @@ -0,0 +1,157 @@ +# Z Ecosystem — Z Chain, International Z Wallet, Z Bot + +Consumer-facing Z products on OMNL expansion slot **900002**. + +## Components + +| Product | Route | Config | +|---------|-------|--------| +| Z Ecosystem hub | `/z` | `config/z-ecosystem.v1.json` | +| International Z Wallet | `/z-wallet` | `config/z-chain-integration.v1.json` | +| Z Bot (in-app chat) | `/z-wallet#z-bot` | `POST /api/v1/z-bot/chat` | +| Z Online Bank | `/central-bank` | existing z-bank settlement | + +## Local Z Chain bootstrap + +```bash +# Start Besu (chainId 900002) on port 8546 +bash scripts/deployment/bootstrap-z-chain-local.sh + +# Env +export CHAIN_900002_RPC_URL=http://127.0.0.1:8546 +export VITE_RPC_URL_900002=http://127.0.0.1:8546 + +# Deploy contracts (optional) +export PRIVATE_KEY=0x... +bash scripts/deployment/deploy-z-chain-contracts.sh +``` + +Windows: + +```powershell +.\scripts\deployment\bootstrap-z-chain-local.ps1 +``` + +Verify: `cast chain-id --rpc-url http://127.0.0.1:8546` → `900002` (hex `0xdbba2`) + +### One-command complete setup (Windows) + +```powershell +npm install --legacy-peer-deps +.\scripts\deployment\complete-z-chain.ps1 +``` + +Uses Hardhat node when Docker is unavailable. Keeps node running in background (`logs/z-chain-node.pid`). + + +## Frontend env + +```env +VITE_ENABLE_Z_WALLET=true +VITE_RPC_URL_900002=http://127.0.0.1:8546 +VITE_Z_CHAIN_EXPLORER_URL=https://explorer.z.omdnl.org +VITE_THIRDWEB_CLIENT_ID=... +``` + +## API + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/z-wallet/corridors` | International corridor catalog | +| GET | `/api/v1/z-wallet/status/:ref` | Settlement status proxy | +| GET | `/api/v1/z-bot/health` | Z Bot + Azure OpenAI config status | +| POST | `/api/v1/z-bot/chat` | Z Bot assistant | + +Z Bot body: + +```json +{ + "sessionId": "zbot-...", + "message": "What is my balance?", + "walletAddress": "0x...", + "chainId": 900002 +} +``` + +## Z Bot LLM (optional) + +Set Azure OpenAI for live replies; otherwise rule-based fallback: + +```env +AZURE_OPENAI_ENDPOINT=https://.openai.azure.com +AZURE_OPENAI_API_KEY=... +AZURE_OPENAI_DEPLOYMENT=gpt-4o-mini +AZURE_OPENAI_API_VERSION=2024-02-15-preview +``` + +## International corridors + +Defined in `config/z-chain-integration.v1.json`: + +- Iraq (USD) — SWIFT, HYBX, CHAIN_MINT +- Iran (USD) — HYBX, CHAIN_MINT +- Europe (EUR) — SWIFT, HYBX +- Global (USD) — SWIFT, HYBX, CHAIN_MINT + +Transfers submit to `POST /z-bank/settle-m0-m4` (or `/external-transfer` fallback) with `targetChainId: 900002`. + +## Registry + +`config/omnl-supported-chains.v1.json` slot **900002** — `name: Z Chain`, `status: active`, `omnlRole: z-chain`. + +## Production Besu (LXC / VM) + +On the Z Chain host (see `config/z-chain-production.v1.json`): + +```bash +sudo bash scripts/deployment/bootstrap-z-chain-production.sh +``` + +Wire nginx/Cloudflare: + +- `https://zblockchainsystem.com` → portal (`/z`, `/z-wallet`, `/swap`, `/central-bank`) +- `https://rpc.zblockchainsystem.com` → `127.0.0.1:8546` +- `https://explorer.zblockchainsystem.com` → Blockscout (optional) + +Set on Z portal host: + +```env +CHAIN_900002_RPC_URL=https://rpc.zblockchainsystem.com +VITE_RPC_URL_900002=https://rpc.zblockchainsystem.com +VITE_Z_CHAIN_EXPLORER_URL=https://explorer.zblockchainsystem.com +VITE_Z_ECOSYSTEM_URL=https://zblockchainsystem.com +``` + +Deploy: + +```bash +bash scripts/deployment/deploy-z-ecosystem-production.sh +bash scripts/deployment/print-z-dns-records.sh +``` + +## Local dev without Docker + +If Docker Desktop is not running, use Anvil: + +```powershell +.\scripts\deployment\bootstrap-z-chain-anvil.ps1 +``` + +Default Anvil account #0 key: `0xac0974bec39a17e36ba4a590bcafb95bd05417ff81f81088a92f31fcfcf35588` + +```bash +export PRIVATE_KEY=0xac0974bec39a17e36ba4a590bcafb95bd05417ff81f81088a92f31fcfcf35588 +bash scripts/deployment/deploy-z-chain-contracts.sh +node scripts/deployment/sync-z-chain-deployed-addresses.mjs +``` + +## Contract addresses + +After deploy, sync all configs: + +```bash +node scripts/deployment/sync-z-chain-deployed-addresses.mjs +``` + +Updates `z-chain-deployed.v1.json`, `z-chain-integration.v1.json`, and `hybx-omnl-cross-chain-lines.json`. + diff --git a/docs/deployment/DEPLOYMENT_ISSUES_AND_FIXES.md b/docs/deployment/DEPLOYMENT_ISSUES_AND_FIXES.md index 42d98e3..e00ebeb 100644 --- a/docs/deployment/DEPLOYMENT_ISSUES_AND_FIXES.md +++ b/docs/deployment/DEPLOYMENT_ISSUES_AND_FIXES.md @@ -143,7 +143,6 @@ forge script script/DeployTransactionMirror.s.sol \ 3. **RPC Providers**: - Alchemy: https://www.alchemy.com/ - Infura: https://www.infura.io/ - - QuickNode: https://www.quicknode.com/ 4. **Verification**: Contracts will be automatically verified on Etherscan if `ETHERSCAN_API_KEY` is set --- diff --git a/docs/deployment/INFURA_RPC_ISSUE.md b/docs/deployment/INFURA_RPC_ISSUE.md index c3683a3..77cd7a2 100644 --- a/docs/deployment/INFURA_RPC_ISSUE.md +++ b/docs/deployment/INFURA_RPC_ISSUE.md @@ -46,9 +46,6 @@ Switch to Alchemy or another provider: # Alchemy ETHEREUM_MAINNET_RPC=https://eth-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY -# QuickNode -ETHEREUM_MAINNET_RPC=https://your-endpoint.quiknode.pro/YOUR_API_KEY - # Public RPC (not recommended for production) ETHEREUM_MAINNET_RPC=https://eth.llamarpc.com ``` diff --git a/docs/deployment/INFURA_SETTINGS_FIX.md b/docs/deployment/INFURA_SETTINGS_FIX.md index 7a32473..bbfb32f 100644 --- a/docs/deployment/INFURA_SETTINGS_FIX.md +++ b/docs/deployment/INFURA_SETTINGS_FIX.md @@ -62,11 +62,6 @@ Update `.env` line 18 with alternative provider: ETHEREUM_MAINNET_RPC=https://eth-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY ``` -**QuickNode**: -```bash -ETHEREUM_MAINNET_RPC=https://your-endpoint.quiknode.pro/YOUR_API_KEY -``` - **Public RPC** (not recommended for production): ```bash ETHEREUM_MAINNET_RPC=https://eth.llamarpc.com diff --git a/foundry.toml b/foundry.toml index 82344e2..96c48c2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -68,6 +68,12 @@ via_ir = true # Generic deploy profile for post-Dencun EVMs. Do not use for Chain 138. evm_version = "cancun" +[profile.zchain] +optimizer = true +optimizer_runs = 100 +via_ir = true +evm_version = "cancun" + [profile.chain138] optimizer = true optimizer_runs = 100 @@ -169,6 +175,7 @@ cbor_metadata = false # Prevents default localhost:8545 when ETH_RPC_URL not set [rpc_endpoints] chain138 = "http://192.168.11.211:8545" +zchain = "http://127.0.0.1:8546" avalanche = "https://avalanche-c-chain.publicnode.com" arbitrum = "https://arbitrum-one.publicnode.com" cronos = "https://evm.cronos.org" diff --git a/frontend-dapp/.env.example b/frontend-dapp/.env.example index 0e4cc28..82711c6 100644 --- a/frontend-dapp/.env.example +++ b/frontend-dapp/.env.example @@ -8,6 +8,12 @@ VITE_WALLETCONNECT_PROJECT_ID=your_reown_project_id_from_cloud_reown_com VITE_THIRDWEB_CLIENT_ID=your_thirdweb_client_id_here VITE_RPC_URL_138=https://rpc-http-pub.d-bis.org +# Z Chain (chainId 900002) — International Z Wallet +# VITE_ENABLE_Z_WALLET=true +# VITE_RPC_URL_900002=http://127.0.0.1:8546 +# VITE_Z_CHAIN_EXPLORER_URL=https://explorer.z.omdnl.org +# VITE_Z_CHAIN_ID=900002 + # Defi Oracle Meta Testnet (chain ID 2138) — off unless VITE_ENABLE_CHAIN2138 is true or 1 # VITE_ENABLE_CHAIN2138=true # VITE_RPC_URL_2138=https://rpc.public-2138.defi-oracle.io diff --git a/frontend-dapp/.env.local.example b/frontend-dapp/.env.local.example new file mode 100644 index 0000000..b9cbd51 --- /dev/null +++ b/frontend-dapp/.env.local.example @@ -0,0 +1,6 @@ +# Z Ecosystem local dev +VITE_ENABLE_Z_WALLET=true +VITE_Z_ECOSYSTEM_URL=http://localhost:3002 +VITE_RPC_URL_900002=http://127.0.0.1:8546 +VITE_Z_CHAIN_EXPLORER_URL=https://explorer.zblockchainsystem.com +CHAIN_900002_RPC_URL=http://127.0.0.1:8546 diff --git a/frontend-dapp/index.html b/frontend-dapp/index.html index 73345a6..5227525 100644 --- a/frontend-dapp/index.html +++ b/frontend-dapp/index.html @@ -3,10 +3,13 @@ - - OMNL Mission Control - Chain 138 + + + + + DBIS — Chain 138
-
OMNL Mission Control
-
Loading dashboard...
+
DBIS
+
Loading…
diff --git a/frontend-dapp/package.json b/frontend-dapp/package.json index 4cb940f..593c8f6 100644 --- a/frontend-dapp/package.json +++ b/frontend-dapp/package.json @@ -3,8 +3,15 @@ "version": "1.0.0", "type": "module", "scripts": { + "predev": "node scripts/sync-omnl-consoles.mjs", "dev": "vite", + "prebuild": "node scripts/sync-omnl-consoles.mjs", "build": "node --max-old-space-size=4096 ./node_modules/vite/bin/vite.js build", + "build:omnl": "node --max-old-space-size=4096 ./node_modules/vite/bin/vite.js build", + "build:z": "node scripts/build-z.mjs", + "prebuild:omnl": "node scripts/sync-omnl-consoles.mjs", + "prebuild:z": "node -e \"console.log('Z build - skipping OMNL console sync')\"", + "serve:portal": "node scripts/portal-serve.mjs", "build:check": "tsc && NODE_OPTIONS='--max-old-space-size=4096' vite build", "preview": "vite preview", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", @@ -15,6 +22,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", + "@lit/reactive-element": "^2.1.2", "@safe-global/api-kit": "^4.0.1", "@safe-global/protocol-kit": "^1.3.0", "@tanstack/react-query": "^5.90.21", @@ -52,6 +60,7 @@ "eslint-plugin-react-refresh": "^0.4.26", "identity-obj-proxy": "^3.0.0", "jsdom": "^23.2.0", + "serve": "^14.2.6", "ts-jest": "^29.4.6", "typescript": "^5.9.3", "vite": "^5.4.21", diff --git a/frontend-dapp/public/omnl/compliance/index.html b/frontend-dapp/public/omnl/compliance/index.html new file mode 100644 index 0000000..37c848d --- /dev/null +++ b/frontend-dapp/public/omnl/compliance/index.html @@ -0,0 +1,84 @@ + + + + + + + OMNL Compliance Console + + + +
+
+
+

OMNL Compliance Console

+

HYBX · IPSAS / IFRS / US GAAP · Web3 notary & reserve attestation · Chain 138

+
+
+ + + + Legacy dashboard + OpenAPI +
+
+ +
Initializing…
+

+ +
+

Posture

+
+
+ +
+

Pending actions

+
+
+ +
+
+

On-chain evidence

+
+
+
+

Gnosis Safe

+
+
+
+

External visibility

+
+
+
+ +
+

HO nostro & reserve coverage

+
+
+ +
+

Triple-state reconcile

+
+
+ +
+

Sign-offs

+
+
+ + + + +
+ + + + diff --git a/frontend-dapp/public/omnl/dashboard/index.html b/frontend-dapp/public/omnl/dashboard/index.html new file mode 100644 index 0000000..0c475a7 --- /dev/null +++ b/frontend-dapp/public/omnl/dashboard/index.html @@ -0,0 +1,55 @@ + + + + + + HYBX OMNL — status + + + +

OMNL API snapshot

+

→ OMNL Compliance Console (posture, pending actions, Safe tx download)

+

Reads /api/v1/omnl/ipsas/registry and /api/v1/omnl/ipsas/fineract-compare (503 if Fineract env missing; 401 if OMNL_API_KEY is set and no access_token in this page URL).

+

OpenAPI 3 JSON · OMNL API catalog · integration status · registry JSON · matrix JSON · Fineract health

+

IPSAS registry

+
Loading…
+

Fineract compare

+
Loading…
+ + + + diff --git a/frontend-dapp/public/omnl/static/omnl-api-base.js b/frontend-dapp/public/omnl/static/omnl-api-base.js new file mode 100644 index 0000000..2f79ad0 --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-api-base.js @@ -0,0 +1,8 @@ +(function () { + /** auto → same-origin /api/v1 (vite proxy, portal-serve, or token-aggregation). */ + window.resolveOmnlApiBase = function () { + const meta = document.querySelector('meta[name="omnl-api-base"]')?.getAttribute('content')?.trim(); + if (meta && meta !== 'auto') return meta.replace(/\/$/, ''); + return '/api/v1'; + }; +})(); diff --git a/frontend-dapp/public/omnl/static/omnl-compliance-console.css b/frontend-dapp/public/omnl/static/omnl-compliance-console.css new file mode 100644 index 0000000..41ed3ec --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-compliance-console.css @@ -0,0 +1,219 @@ +:root { + --bg: #0c0f14; + --panel: #141a22; + --border: #243044; + --text: #e8edf4; + --muted: #8b9cb3; + --ok: #3dd68c; + --warn: #f5c542; + --bad: #f07178; + --accent: #4ade80; + --mono: ui-monospace, 'Cascadia Code', 'SF Mono', monospace; +} + +* { box-sizing: border-box; } + +body { + margin: 0; + font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.45; +} + +.wrap { + max-width: 72rem; + margin: 0 auto; + padding: 1.25rem 1rem 3rem; +} + +header { + display: flex; + flex-wrap: wrap; + gap: 0.75rem 1.5rem; + align-items: baseline; + justify-content: space-between; + margin-bottom: 1.25rem; +} + +h1 { + font-size: 1.35rem; + font-weight: 650; + margin: 0; +} + +.sub { + color: var(--muted); + font-size: 0.9rem; + margin: 0.25rem 0 0; +} + +.toolbar { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; +} + +button, .btn { + appearance: none; + border: 1px solid var(--border); + background: var(--panel); + color: var(--text); + border-radius: 8px; + padding: 0.45rem 0.75rem; + font-size: 0.85rem; + cursor: pointer; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 0.35rem; +} + +button:hover, .btn:hover { border-color: var(--accent); color: var(--accent); } +button.primary { background: #14532d; border-color: #22c55e; } +button.primary:hover { background: #166534; } + +.grid { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); + margin-bottom: 1rem; +} + +.card { + background: var(--panel); + border: 1px solid var(--border); + border-radius: 12px; + padding: 1rem; +} + +.card h2 { + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--muted); + margin: 0 0 0.75rem; +} + +.section { margin-bottom: 1.25rem; } + +.section > h2 { + font-size: 1rem; + margin: 0 0 0.65rem; +} + +.badge { + display: inline-block; + padding: 0.2rem 0.55rem; + border-radius: 999px; + font-size: 0.78rem; + font-weight: 600; + border: 1px solid transparent; +} + +.badge.ok { background: #143528; color: var(--ok); border-color: #2a6b4a; } +.badge.warn { background: #3a3014; color: var(--warn); border-color: #6b5620; } +.badge.bad { background: #3a1c20; color: var(--bad); border-color: #6b3038; } +.badge.neutral { background: #1c2430; color: var(--muted); border-color: var(--border); } + +.metric { + font-size: 1.1rem; + font-weight: 650; +} + +.metric small { + display: block; + font-size: 0.78rem; + font-weight: 400; + color: var(--muted); + margin-top: 0.15rem; +} + +.actions-list { list-style: none; padding: 0; margin: 0; } + +.actions-list li { + border: 1px solid var(--border); + border-radius: 10px; + padding: 0.85rem 1rem; + margin-bottom: 0.65rem; + background: #10151c; +} + +.actions-list h3 { + margin: 0 0 0.35rem; + font-size: 0.95rem; +} + +.actions-list p { + margin: 0 0 0.5rem; + color: var(--muted); + font-size: 0.88rem; +} + +.link-row { display: flex; flex-wrap: wrap; gap: 0.5rem; } + +.link-row a { + color: var(--accent); + font-size: 0.82rem; + text-decoration: none; +} + +.link-row a:hover { text-decoration: underline; } + +.kv { + display: grid; + grid-template-columns: minmax(8rem, 11rem) 1fr; + gap: 0.35rem 0.75rem; + font-size: 0.86rem; +} + +.kv dt { color: var(--muted); margin: 0; } +.kv dd { margin: 0; font-family: var(--mono); font-size: 0.8rem; word-break: break-all; } + +pre.raw { + background: #090c10; + border: 1px solid var(--border); + border-radius: 10px; + padding: 0.85rem; + overflow: auto; + font-size: 0.72rem; + line-height: 1.35; + max-height: 24rem; +} + +.status-bar { + padding: 0.65rem 0.85rem; + border-radius: 8px; + margin-bottom: 1rem; + font-size: 0.88rem; +} + +.status-bar.loading { background: #1a2430; color: var(--muted); } +.status-bar.error { background: #3a1c20; color: var(--bad); } +.status-bar.ok { background: #143528; color: var(--ok); } + +.hidden { display: none !important; } + +footer { + margin-top: 2rem; + color: var(--muted); + font-size: 0.8rem; +} + +footer a { color: var(--accent); } + +@media (max-width: 640px) { + .kv { grid-template-columns: 1fr; } + .wrap { padding-left: 0.75rem; padding-right: 0.75rem; } + header { flex-direction: column; align-items: stretch; } + .toolbar { width: 100%; } + .toolbar button, + .toolbar .btn { + min-height: 44px; + flex: 1 1 calc(50% - 0.25rem); + justify-content: center; + } + h1 { font-size: 1.15rem; } + pre, table { font-size: 12px; } +} diff --git a/frontend-dapp/public/omnl/static/omnl-compliance-console.js b/frontend-dapp/public/omnl/static/omnl-compliance-console.js new file mode 100644 index 0000000..e4b878c --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-compliance-console.js @@ -0,0 +1,255 @@ +(function () { + const params = new URLSearchParams(window.location.search); + const accessToken = params.get('access_token') || ''; + const apiBase = window.resolveOmnlApiBase + ? window.resolveOmnlApiBase() + : document.querySelector('meta[name="omnl-api-base"]')?.getAttribute('content')?.trim() || '/api/v1'; + + const els = { + status: document.getElementById('load-status'), + grid: document.getElementById('posture-grid'), + actions: document.getElementById('pending-actions'), + evidence: document.getElementById('evidence-kv'), + safe: document.getElementById('safe-kv'), + external: document.getElementById('external-kv'), + triple: document.getElementById('triple-summary'), + hoLiquidity: document.getElementById('ho-liquidity-kv'), + signoffs: document.getElementById('signoffs-summary'), + raw: document.getElementById('raw-json'), + refreshed: document.getElementById('refreshed-at'), + }; + + function apiHeaders() { + const h = { Accept: 'application/json' }; + if (accessToken) h.Authorization = 'Bearer ' + accessToken; + return h; + } + + function apiUrl(path) { + const base = apiBase.replace(/\/$/, ''); + const rel = path.startsWith('/') ? path : '/' + path; + const u = new URL(base + rel, window.location.origin); + if (accessToken) u.searchParams.set('access_token', accessToken); + return u.toString(); + } + + function badge(ok, okLabel, badLabel) { + if (ok === true) return '' + okLabel + ''; + if (ok === false) return '' + badLabel + ''; + return 'Unknown'; + } + + function sevBadge(sev) { + const map = { critical: 'bad', high: 'bad', medium: 'warn', low: 'neutral' }; + return '' + sev + ''; + } + + function renderPosture(data) { + const p = data.posture || {}; + els.grid.innerHTML = [ + card('Reporting', badge(p.reportingCompliant, 'Compliant', 'Not compliant'), ''), + card('Attestation', badge(!p.attestationStale, 'Fresh', 'Stale'), ''), + card('Triple reconcile', badge(p.tripleAligned, 'Aligned', 'Breaks'), ''), + card('Notary gate', badge(p.requireNotarizedEvidence, 'Enforced', 'Not enforced'), 'requireNotarizedEvidence'), + card('Threshold', '
' + (p.complianceThreshold || '—') + '
', 'IPSAS / IFRS / US GAAP'), + card('Policy v', '
' + (p.jurisdictionPolicyVersion ?? '—') + '
', 'ID jurisdiction'), + ].join(''); + } + + function card(title, body, sub) { + return ( + '

' + + title + + '

' + + body + + (sub ? '' + sub + '' : '') + + '
' + ); + } + + function renderActions(actions) { + if (!actions || !actions.length) { + els.actions.innerHTML = '

No pending actions.

'; + return; + } + els.actions.innerHTML = + '
    ' + + actions + .map(function (a) { + const links = (a.links || []) + .map(function (l) { + const href = l.href.startsWith('http') ? l.href : apiUrl(l.href); + const dl = l.href.indexOf('safe-notary-gate-tx') >= 0 ? ' download="omnl-safe-notary-gate-tx.json"' : ''; + return '' + l.label + ''; + }) + .join(''); + return ( + '
  • ' + + sevBadge(a.severity) + + ' ' + + esc(a.title) + + '

    ' + + esc(a.detail) + + '

  • ' + ); + }) + .join('') + + '
'; + } + + function kv(rows) { + return ( + '
' + + rows + .map(function (r) { + return '
' + esc(r[0]) + '
' + esc(String(r[1] ?? '—')) + '
'; + }) + .join('') + + '
' + ); + } + + function renderEvidence(data) { + const pr = data.proofReport; + const gate = data.onChainGate || {}; + const triple = data.tripleReconcile; + els.evidence.innerHTML = kv([ + ['Evidence hash', pr && pr.evidenceHash], + ['Merkle root', pr && pr.merkleRoot], + ['Package 3-of-3', pr && pr.packageNotarized3of3], + ['Reserve 3-of-3', pr && pr.reserveAttested3of3], + ['Reserve store', gate.reserveStore], + ['Notary registry', gate.notaryRegistry], + ['Attestation threshold', gate.attestationThreshold], + ['On-chain R', triple && triple.onChain && triple.onChain.r], + ]); + } + + function renderSafe(data) { + const pr = data.proofReport; + const g = (pr && pr.gnosisSafe) || {}; + const sw = data.safeWallet || {}; + els.safe.innerHTML = kv([ + ['Admin Safe', g.address || data.web3.deployed.GnosisSafeAdmin], + ['Threshold', g.threshold ? g.threshold + '-of-' + g.owners : '—'], + ['Safe Wallet registry', sw.npmRegistryHas138 ? 'chain 138 listed' : 'pending PR #1568'], + ['Registry status', sw.status], + ]); + } + + function renderExternal(data) { + const ev = data.externalVisibility || {}; + const guosmm = ev.guosmmGazette || {}; + const noticeLinks = (guosmm.notices || []) + .map(function (n) { + return '' + esc(n.documentId) + ''; + }) + .join(' · '); + els.external.innerHTML = + kv([ + ['GUOSMM gazette', guosmm.base || '—'], + ['API registry', guosmm.apiRegistry || '/api/v1/omnl/gazette/notices'], + ['Notices', noticeLinks || '—'], + ['DefiLlama chain', ev.defiLlama && ev.defiLlama.chainPage], + ['Pricing PR', ev.defiLlama && ev.defiLlama.pr12094], + ['Bridge TVL PR', ev.defiLlama && ev.defiLlama.pr19451], + ['Safe deployments PR', ev.safeDeployments && ev.safeDeployments.pr1568], + ]); + } + + function renderHoLiquidity(snap) { + if (!els.hoLiquidity) return; + if (!snap) { + els.hoLiquidity.textContent = 'HO liquidity snapshot unavailable.'; + return; + } + const cash = snap.cashInBank || {}; + const cov = snap.reserveCoverage || {}; + els.hoLiquidity.innerHTML = kv([ + ['Generated', snap.generatedAt], + ['Nostro cash (GL 13010)', cash.cashInBankUsd != null ? '$' + Number(cash.cashInBankUsd).toLocaleString() : '—'], + ['Reserve coverage', cov.coverageRatio != null ? cov.coverageRatio.toFixed(2) + '×' : '—'], + ['Reserve assets', cov.reserveAssetsUsd != null ? '$' + Number(cov.reserveAssetsUsd).toLocaleString() : '—'], + ['Issued c* USD', cov.issuedCStarUsd != null ? '$' + Number(cov.issuedCStarUsd).toLocaleString() : '—'], + ['Public API', apiBase + '/omnl/ho-liquidity-snapshot'], + ]); + } + + function renderTriple(data) { + const t = data.tripleReconcile; + if (!t) { + els.triple.textContent = 'Triple-state reconcile unavailable.'; + return; + } + const breaks = (t.breaks || []).length; + els.triple.innerHTML = + badge(t.aligned, 'Aligned', breaks + ' break(s)') + + ' line ' + + esc(t.lineId).slice(0, 18) + + '…'; + } + + function renderSignoffs(data) { + const s = data.signoffs || {}; + els.signoffs.innerHTML = '
' + esc(JSON.stringify(s, null, 2)) + '
'; + } + + function esc(s) { + return String(s) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + } + + function setStatus(kind, msg) { + els.status.className = 'status-bar ' + kind; + els.status.textContent = msg; + } + + async function load() { + setStatus('loading', 'Loading compliance console…'); + try { + const res = await fetch(apiUrl('/omnl/compliance/console'), { headers: apiHeaders() }); + const text = await res.text(); + let data; + try { + data = JSON.parse(text); + } catch { + throw new Error(res.status + ' — non-JSON response'); + } + if (!res.ok) throw new Error(data.error || res.statusText); + + renderPosture(data); + renderActions(data.pendingActions); + renderEvidence(data); + renderSafe(data); + renderExternal(data); + renderTriple(data); + try { + const hoRes = await fetch(apiUrl('/omnl/ho-liquidity-snapshot'), { headers: apiHeaders() }); + if (hoRes.ok) renderHoLiquidity(await hoRes.json()); + } catch (_) { + renderHoLiquidity(null); + } + renderSignoffs(data); + els.raw.textContent = JSON.stringify(data, null, 2); + els.refreshed.textContent = 'Updated ' + new Date(data.generatedAt).toLocaleString(); + setStatus('ok', 'Live — ' + data.pendingActions.length + ' pending action(s)'); + } catch (e) { + setStatus('error', 'Failed to load: ' + (e && e.message ? e.message : e)); + } + } + + document.getElementById('btn-refresh').addEventListener('click', load); + document.getElementById('btn-download-safe').addEventListener('click', function () { + window.location.href = apiUrl('/omnl/compliance/safe-notary-gate-tx'); + }); + document.getElementById('btn-toggle-raw').addEventListener('click', function () { + document.getElementById('raw-section').classList.toggle('hidden'); + }); + + load(); +})(); diff --git a/frontend-dapp/public/omnl/static/omnl-settlement-terminal.css b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.css new file mode 100644 index 0000000..74a8da0 --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.css @@ -0,0 +1,190 @@ +:root { + --font-mono: "Consolas", "Lucida Console", "Courier New", monospace; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-height: 100vh; + font-family: system-ui, sans-serif; +} + +body.theme-black { + background: #0a0a0a; + color: #33ff66; +} + +body.theme-color { + background: #d8d8d8; + color: #111; +} + +.layout { + max-width: 1200px; + margin: 0 auto; + padding: 1rem 1.25rem 2rem; +} + +.topbar { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1rem; + align-items: flex-start; + margin-bottom: 1rem; +} + +h1 { + font-size: 1.15rem; + margin: 0 0 0.25rem; +} + +.sub { + margin: 0; + opacity: 0.85; + font-size: 0.85rem; +} + +.toolbar { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +button, +.btn { + font: inherit; + cursor: pointer; + border: 1px solid currentColor; + background: transparent; + color: inherit; + padding: 0.35rem 0.65rem; + border-radius: 4px; + text-decoration: none; + font-size: 0.85rem; +} + +button.active { + font-weight: 700; +} + +.card { + border: 1px solid currentColor; + border-radius: 6px; + padding: 0.75rem; + margin-bottom: 1rem; +} + +.params { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); + gap: 0.75rem; +} + +.params label { + display: flex; + flex-direction: column; + gap: 0.25rem; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.params input { + font: inherit; + padding: 0.35rem 0.5rem; + border: 1px solid currentColor; + background: transparent; + color: inherit; + border-radius: 4px; +} + +.main { + display: grid; + grid-template-columns: 1fr min(22rem, 100%); + gap: 1rem; +} + +@media (max-width: 900px) { + .main { + grid-template-columns: 1fr; + } +} + +.terminal { + margin: 0; + min-height: 28rem; + max-height: 70vh; + overflow: auto; + font-family: var(--font-mono); + font-size: 0.78rem; + line-height: 1.35; + white-space: pre-wrap; + word-break: break-word; +} + +body.theme-black .terminal { + background: #000; + color: #33ff66; + padding: 1rem; +} + +body.theme-color .terminal { + background: #ececec; + color: #111; + padding: 1rem; +} + +.side h2 { + font-size: 0.85rem; + margin: 0 0 0.5rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cmd-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.35rem; + margin-bottom: 0.75rem; +} + +.alchemy-row { + display: flex; + flex-direction: column; + gap: 0.25rem; + font-size: 0.75rem; + margin-bottom: 0.75rem; +} + +.alchemy-row input { + font: inherit; + padding: 0.35rem; + border: 1px solid currentColor; + background: transparent; + color: inherit; + border-radius: 4px; +} + +.json-out { + margin: 0; + max-height: 16rem; + overflow: auto; + font-family: var(--font-mono); + font-size: 0.7rem; + white-space: pre-wrap; + word-break: break-word; + opacity: 0.9; +} + +footer { + font-size: 0.75rem; + opacity: 0.8; +} + +footer code { + font-family: var(--font-mono); +} diff --git a/frontend-dapp/public/omnl/static/omnl-settlement-terminal.js b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.js new file mode 100644 index 0000000..09e8174 --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.js @@ -0,0 +1,219 @@ +(function () { + const API_BASE = window.resolveOmnlApiBase + ? window.resolveOmnlApiBase() + : document.querySelector('meta[name="omnl-api-base"]')?.getAttribute('content') || '/api/v1'; + const screenEl = document.getElementById('terminal-screen'); + const jsonEl = document.getElementById('json-out'); + const inpRef = document.getElementById('inp-settlement-ref'); + const inpOffice = document.getElementById('inp-office-id'); + const inpDate = document.getElementById('inp-value-date'); + const inpAmount = document.getElementById('inp-amount'); + const inpToken = document.getElementById('inp-token'); + const inpAlchemyAddr = document.getElementById('inp-alchemy-addr'); + + let screenMode = 'black'; + + const inpConnection = document.getElementById('inp-connection'); + const DEFAULT_CONNECTION = 'mk-albert-gate-limited'; + const DEFAULT_WALLET = '0xb3B416BdE671c256aAbFB56358baD91D46BB9c39'; + + (function initFromQuery() { + const p = new URLSearchParams(window.location.search); + const conn = p.get('connectionId') || DEFAULT_CONNECTION; + if (inpConnection) inpConnection.value = conn; + const ref = p.get('settlementRef'); + if (ref) inpRef.value = ref; + })(); + + function qs() { + const p = new URLSearchParams(window.location.search); + const token = inpToken.value.trim() || p.get('access_token') || ''; + const q = new URLSearchParams({ + settlementRef: inpRef.value.trim(), + officeId: String(inpOffice.value || '1'), + valueDate: inpDate.value, + amountUsd: inpAmount.value.trim(), + }); + const conn = inpConnection && inpConnection.value ? inpConnection.value.trim() : ''; + if (conn) q.set('connectionId', conn); + if (token) q.set('access_token', token); + return q; + } + + function authHeaders() { + const token = inpToken.value.trim() || new URLSearchParams(window.location.search).get('access_token') || ''; + return token ? { Authorization: 'Bearer ' + token } : {}; + } + + async function apiGet(path, extra) { + const q = qs(); + if (extra) { + Object.entries(extra).forEach(function (kv) { + q.set(kv[0], kv[1]); + }); + } + const res = await fetch(API_BASE + path + '?' + q.toString(), { headers: authHeaders() }); + const text = await res.text(); + let data; + try { + data = JSON.parse(text); + } catch { + data = { raw: text, status: res.status }; + } + if (!res.ok) throw new Error(data.error || data.raw || res.statusText); + return data; + } + + async function apiPost(path, body) { + const q = qs(); + const res = await fetch(API_BASE + path + '?' + q.toString(), { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...authHeaders() }, + body: JSON.stringify(body), + }); + const data = await res.json().catch(function () { + return {}; + }); + if (!res.ok) throw new Error(data.error || res.statusText); + return data; + } + + function showJson(data) { + jsonEl.textContent = JSON.stringify(data, null, 2); + } + + function showScreen(text) { + screenEl.textContent = text; + } + + function setTheme(mode) { + screenMode = mode === 'color' ? 'color' : 'black'; + document.body.classList.toggle('theme-black', screenMode === 'black'); + document.body.classList.toggle('theme-color', screenMode === 'color'); + document.getElementById('btn-mode-black').classList.toggle('active', screenMode === 'black'); + document.getElementById('btn-mode-color').classList.toggle('active', screenMode === 'color'); + } + + async function loadScreen(artifact) { + const data = await apiGet('/omnl/terminal/screen', { + mode: screenMode, + artifact: artifact || 'package', + format: 'json', + }); + showScreen(data.text || ''); + showJson(data); + } + + async function runCmd(cmd) { + try { + if (cmd === 'help') { + showScreen( + [ + 'OMNL / HYBX SETTLEMENT TERMINAL — COMMANDS', + '', + ' status — integration status', + ' package — full JSON package', + ' pof — proof of funds', + ' debit-note — debit note', + ' remittance — remittance advice', + ' token-san — tokenization sanitized copy', + ' swift-copy — conversion SWIFT copy', + ' screen — terminal screen (current theme)', + ' alchemy balance — eth_getBalance via Alchemy', + '', + 'Toggle Black/Green vs Gray/Black with toolbar buttons.', + ].join('\n') + ); + return; + } + if (cmd === 'status') { + const q = qs(); + const token = q.get('access_token'); + const url = API_BASE + '/omnl/terminal/status' + (token ? '?access_token=' + encodeURIComponent(token) : ''); + const data = await fetch(url, { headers: authHeaders() }).then(function (r) { + return r.json(); + }); + showJson(data); + showScreen(JSON.stringify(data, null, 2)); + return; + } + if (cmd === 'package') { + const data = await apiGet('/omnl/terminal/package'); + showJson(data); + if (data.artifacts && data.artifacts.screens) { + showScreen(data.artifacts.screens[screenMode] || ''); + } + return; + } + if (cmd === 'pof') { + const data = await apiGet('/omnl/terminal/proof-of-funds'); + showJson(data); + await loadScreen('proof-of-funds'); + return; + } + if (cmd === 'debit') { + const data = await apiGet('/omnl/terminal/debit-note'); + showJson(data); + await loadScreen('debit-note'); + return; + } + if (cmd === 'remittance') { + const data = await apiGet('/omnl/terminal/remittance-advice'); + showJson(data); + await loadScreen('remittance-advice'); + return; + } + if (cmd === 'token') { + const data = await apiGet('/omnl/terminal/tokenization-sanitized'); + showJson(data); + await loadScreen('tokenization-sanitized'); + return; + } + if (cmd === 'swift') { + const data = await apiGet('/omnl/terminal/conversion-swift-copy'); + showJson(data); + await loadScreen('conversion-swift-copy'); + return; + } + if (cmd === 'screen') { + await loadScreen('package'); + return; + } + if (cmd === 'alchemy-balance') { + const addr = (inpAlchemyAddr.value || '').trim(); + if (!addr) throw new Error('Set Alchemy address first'); + const data = await apiPost('/omnl/terminal/alchemy/rpc', { + network: 'eth-mainnet', + method: 'eth_getBalance', + params: [addr, 'latest'], + connectionId: (inpConnection && inpConnection.value) || DEFAULT_CONNECTION, + }); + showJson(data); + showScreen('ALCHEMY eth_getBalance\nADDRESS: ' + addr + '\nRESULT: ' + data.result); + return; + } + } catch (e) { + showScreen('ERROR: ' + (e && e.message ? e.message : String(e))); + } + } + + document.getElementById('btn-mode-black').addEventListener('click', function () { + setTheme('black'); + runCmd('screen'); + }); + document.getElementById('btn-mode-color').addEventListener('click', function () { + setTheme('color'); + runCmd('screen'); + }); + document.getElementById('btn-refresh').addEventListener('click', function () { + runCmd('screen'); + }); + document.querySelectorAll('[data-cmd]').forEach(function (btn) { + btn.addEventListener('click', function () { + runCmd(btn.getAttribute('data-cmd')); + }); + }); + + setTheme('black'); + runCmd('screen'); +})(); diff --git a/frontend-dapp/public/omnl/terminal/index.html b/frontend-dapp/public/omnl/terminal/index.html new file mode 100644 index 0000000..17803b4 --- /dev/null +++ b/frontend-dapp/public/omnl/terminal/index.html @@ -0,0 +1,76 @@ + + + + + + + OMNL / HYBX Settlement Terminal + + + +
+
+
+

OMNL / HYBX Settlement Terminal

+

Fineract SoR · Alchemy on-chain · institutional document API

+
+
+ + + + Compliance + Status JSON +
+
+ +
+ + + + + + +
+ +
+
+
Loading…
+
+ +
+ +
+

+ API: /api/v1/omnl/terminal/* · Documents bound to Fineract — not SWIFT NET transmission. + Pass ?access_token= or Bearer when OMNL_API_KEY is set. +

+
+
+ + + + diff --git a/frontend-dapp/scripts/build-z.mjs b/frontend-dapp/scripts/build-z.mjs new file mode 100644 index 0000000..c07bd6b --- /dev/null +++ b/frontend-dapp/scripts/build-z.mjs @@ -0,0 +1,16 @@ +import { spawnSync } from 'node:child_process'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); +const viteBin = path.join(root, 'node_modules', 'vite', 'bin', 'vite.js'); + +process.env.VITE_ECOSYSTEM = 'z'; + +const result = spawnSync( + process.execPath, + ['--max-old-space-size=4096', viteBin, 'build'], + { stdio: 'inherit', env: process.env, cwd: root }, +); + +process.exit(result.status ?? 1); diff --git a/frontend-dapp/scripts/portal-serve.mjs b/frontend-dapp/scripts/portal-serve.mjs new file mode 100644 index 0000000..dba18fd --- /dev/null +++ b/frontend-dapp/scripts/portal-serve.mjs @@ -0,0 +1,142 @@ +/** + * Production-style portal server: static dist + API proxies (matches vite.config.ts). + */ +import http from 'http'; +import https from 'https'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const DIST = path.join(__dirname, '..', 'dist'); +const PORT = Number(process.env.PORT || process.env.OMNL_BANK_FRONTEND_PORT || 3002); +const API_TARGET = process.env.TOKEN_AGGREGATION_URL || 'http://127.0.0.1:3000'; +const SETTLEMENT_TARGET = process.env.SETTLEMENT_URL || process.env.SETTLEMENT_MIDDLEWARE_URL || 'http://127.0.0.1:3011'; +const EXCHANGE_TARGET = process.env.EXCHANGE_URL || ''; +const MIME = { + '.html': 'text/html; charset=utf-8', + '.js': 'application/javascript; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.svg': 'image/svg+xml', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.ico': 'image/x-icon', + '.woff': 'font/woff', + '.woff2': 'font/woff2', + '.ttf': 'font/ttf', + '.map': 'application/json', +}; + +function shouldServeOmnlStatic(url) { + return ( + /^\/omnl\/(compliance|dashboard|terminal)\/?(\?.*)?$/.test(url) || + url.startsWith('/omnl/static/') + ); +} + +function proxyRequest(req, res, targetBase, rewrite) { + const incoming = new URL(req.url, 'http://localhost'); + const target = new URL(targetBase); + const pathname = rewrite ? rewrite(incoming.pathname) : incoming.pathname; + const options = { + protocol: target.protocol, + hostname: target.hostname, + port: target.port || (target.protocol === 'https:' ? 443 : 80), + path: `${pathname}${incoming.search}`, + method: req.method, + headers: { ...req.headers, host: target.host }, + }; + + const lib = target.protocol === 'https:' ? https : http; + const proxyReq = lib.request(options, (proxyRes) => { + res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers); + proxyRes.pipe(res); + }); + proxyReq.on('error', (err) => { + if (!res.headersSent) { + res.writeHead(502, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: 'proxy_error', message: err.message })); + } + }); + req.pipe(proxyReq); +} + +function sendFile(res, filePath, cacheControl) { + const ext = path.extname(filePath).toLowerCase(); + const type = MIME[ext] || 'application/octet-stream'; + const headers = { 'Content-Type': type }; + if (cacheControl) headers['Cache-Control'] = cacheControl; + res.writeHead(200, headers); + fs.createReadStream(filePath).pipe(res); +} + +function resolveStatic(urlPath) { + const decoded = decodeURIComponent(urlPath.split('?')[0]); + if (decoded.endsWith('/')) { + const indexPath = path.join(DIST, decoded, 'index.html'); + if (fs.existsSync(indexPath)) return indexPath; + } + const direct = path.join(DIST, decoded); + if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct; + const withIndex = path.join(DIST, decoded, 'index.html'); + if (fs.existsSync(withIndex)) return withIndex; + return null; +} + +const server = http.createServer((req, res) => { + const url = req.url || '/'; + + if (url.startsWith('/api')) { + return proxyRequest(req, res, API_TARGET); + } + if (url.startsWith('/reserve')) { + return proxyRequest(req, res, API_TARGET); + } + if (url.startsWith('/settlement')) { + return proxyRequest(req, res, SETTLEMENT_TARGET, (pathname) => + pathname.replace(/^\/settlement/, '/api/v1/settlement'), + ); + } + if (url.startsWith('/exchange')) { + if (!EXCHANGE_TARGET) { + res.writeHead(404, { 'Content-Type': 'application/json' }); + return res.end(JSON.stringify({ error: 'not_available', message: 'Z Ecosystem only' })); + } + return proxyRequest(req, res, EXCHANGE_TARGET); + } + if (url.startsWith('/omnl/')) { + res.writeHead(404, { 'Content-Type': 'application/json' }); + return res.end(JSON.stringify({ error: 'not_available', message: 'Z Ecosystem only' })); + } + + const file = resolveStatic(url === '/' ? '/index.html' : url); + if (file) { + const cache = + url.startsWith('/assets/') ? 'public, max-age=31536000, immutable' : 'no-cache, no-store, must-revalidate'; + return sendFile(res, file, cache); + } + + const indexHtml = path.join(DIST, 'index.html'); + if (fs.existsSync(indexHtml)) { + return sendFile(res, indexHtml, 'no-cache, no-store, must-revalidate'); + } + + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('Not found'); +}); + +if (!fs.existsSync(DIST)) { + console.error(`Missing build output: ${DIST}\nRun: npm run build`); + process.exit(1); +} + +server.listen(PORT, () => { + console.log(`portal-serve listening on http://localhost:${PORT}`); + console.log(` dist: ${DIST}`); + console.log(` api: ${API_TARGET}`); + console.log(` settlement: ${SETTLEMENT_TARGET}`); +}); diff --git a/frontend-dapp/scripts/sync-omnl-consoles.mjs b/frontend-dapp/scripts/sync-omnl-consoles.mjs new file mode 100644 index 0000000..1aeac8c --- /dev/null +++ b/frontend-dapp/scripts/sync-omnl-consoles.mjs @@ -0,0 +1,73 @@ +#!/usr/bin/env node +/** + * Copy OMNL operator console static assets into frontend-dapp/public for Vite + portal-serve. + */ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const frontendRoot = path.resolve(__dirname, '..'); +const srcPublic = path.resolve(frontendRoot, '../services/token-aggregation/public'); +const destRoot = path.join(frontendRoot, 'public', 'omnl'); + +const staticFiles = [ + 'omnl-api-base.js', + 'omnl-compliance-console.css', + 'omnl-compliance-console.js', + 'omnl-settlement-terminal.css', + 'omnl-settlement-terminal.js', +]; + +const pageMap = [ + { src: 'omnl-compliance-console.html', dest: 'compliance/index.html' }, + { src: 'omnl-dashboard.html', dest: 'dashboard/index.html' }, + { src: 'omnl-settlement-terminal.html', dest: 'terminal/index.html' }, +]; + +function rmrf(dir) { + if (!fs.existsSync(dir)) return; + for (const ent of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, ent.name); + if (ent.isDirectory()) rmrf(p); + else fs.unlinkSync(p); + } + fs.rmdirSync(dir); +} + +function ensureDir(dir) { + fs.mkdirSync(dir, { recursive: true }); +} + +function copyFile(src, dest) { + ensureDir(path.dirname(dest)); + fs.copyFileSync(src, dest); +} + +if (!fs.existsSync(srcPublic)) { + console.warn('[sync-omnl-consoles] token-aggregation public missing — skip'); + process.exit(0); +} + +if (fs.existsSync(destRoot)) rmrf(destRoot); +ensureDir(path.join(destRoot, 'static')); + +for (const name of staticFiles) { + const src = path.join(srcPublic, name); + if (!fs.existsSync(src)) { + console.warn(`[sync-omnl-consoles] missing ${name}`); + continue; + } + copyFile(src, path.join(destRoot, 'static', name)); +} + +for (const { src, dest } of pageMap) { + const from = path.join(srcPublic, src); + if (!fs.existsSync(from)) { + console.warn(`[sync-omnl-consoles] missing ${src}`); + continue; + } + copyFile(from, path.join(destRoot, dest)); +} + +console.log('[sync-omnl-consoles] synced operator consoles → public/omnl/'); diff --git a/frontend-dapp/src/App.tsx b/frontend-dapp/src/App.tsx index e9e451e..41fbd8d 100644 --- a/frontend-dapp/src/App.tsx +++ b/frontend-dapp/src/App.tsx @@ -1,4 +1,5 @@ -import { BrowserRouter, Routes, Route } from 'react-router-dom' +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' +import { useEffect } from 'react' import { WagmiProvider } from 'wagmi' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ThirdwebProvider } from '@thirdweb-dev/react' @@ -14,11 +15,15 @@ import ReservePage from './pages/ReservePage' import HistoryPage from './pages/HistoryPage' import AdminPanel from './pages/AdminPanel' import DocsPage from './pages/DocsPage' -import WalletsDemoPage from './pages/WalletsDemoPage' import HubPage from './pages/HubPage' +import WalletsDemoPage from './pages/WalletsDemoPage' +import ZEcosystemHubPage from './pages/ZEcosystemHubPage' +import ZWalletPage from './pages/ZWalletPage' import Layout from './components/layout/Layout' import OmnlProductLayout from './components/layout/OmnlProductLayout' +import ZProductLayout from './components/layout/ZProductLayout' import ToastProvider from './components/ui/ToastProvider' +import { isZEcosystem, isOmnlEcosystem } from './config/ecosystemMode' const queryClient = new QueryClient({ defaultOptions: { @@ -35,6 +40,65 @@ const queryClient = new QueryClient({ }) const THIRDWEB_CLIENT_ID = import.meta.env.VITE_THIRDWEB_CLIENT_ID || '542981292d51ec610388ba8985f027d7' +function ExternalRedirect({ to }: { to: string }) { + useEffect(() => { + window.location.replace(to) + }, [to]) + return null +} + +function ZRoutes() { + return ( + + + + + } + > + } /> + } /> + } /> + } /> + + } /> + } /> + + ) +} + +function OmnlRoutes() { + return ( + + }> + } /> + } /> + } /> + } /> + } /> + + + + + } + > + } /> + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + + ) +} + function App() { return ( @@ -48,30 +112,7 @@ function App() { > - - }> - } /> - } /> - } /> - } /> - } /> - - - - - } - > - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - + {isZEcosystem ? : isOmnlEcosystem ? : } diff --git a/frontend-dapp/src/components/admin/AdminDashboard.tsx b/frontend-dapp/src/components/admin/AdminDashboard.tsx index de7936f..f159a5a 100644 --- a/frontend-dapp/src/components/admin/AdminDashboard.tsx +++ b/frontend-dapp/src/components/admin/AdminDashboard.tsx @@ -99,7 +99,7 @@ export default function AdminDashboard() { }, [publicClient, address]) return ( -
+
diff --git a/frontend-dapp/src/components/admin/AuditLogViewer.tsx b/frontend-dapp/src/components/admin/AuditLogViewer.tsx index 7e301a0..eda9694 100644 --- a/frontend-dapp/src/components/admin/AuditLogViewer.tsx +++ b/frontend-dapp/src/components/admin/AuditLogViewer.tsx @@ -73,7 +73,7 @@ export default function AuditLogViewer() { value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="Search logs..." - className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-blue-500" + className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-green-500" />
{(['all', 'success', 'failure'] as const).map((f) => ( diff --git a/frontend-dapp/src/components/admin/FunctionPermissions.tsx b/frontend-dapp/src/components/admin/FunctionPermissions.tsx index b33af3d..26e9f49 100644 --- a/frontend-dapp/src/components/admin/FunctionPermissions.tsx +++ b/frontend-dapp/src/components/admin/FunctionPermissions.tsx @@ -147,7 +147,7 @@ export default function FunctionPermissions() { setSelectedRole(e.target.value)} - className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white focus:outline-none focus:border-blue-500" + className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white focus:outline-none focus:border-green-500" > {roles.map((role) => ( diff --git a/frontend-dapp/src/components/admin/TwoWayBridgeAdmin.tsx b/frontend-dapp/src/components/admin/TwoWayBridgeAdmin.tsx index b7e4ec3..80c1637 100644 --- a/frontend-dapp/src/components/admin/TwoWayBridgeAdmin.tsx +++ b/frontend-dapp/src/components/admin/TwoWayBridgeAdmin.tsx @@ -200,7 +200,7 @@ export default function TwoWayBridgeAdmin() { value={newAdmin} onChange={(e) => setNewAdmin(e.target.value)} placeholder="0x..." - className="flex-1 px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-blue-500" + className="flex-1 px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-green-500" />
@@ -129,7 +129,7 @@ export default function WalletBackup() { value={backupPassword} onChange={(e) => setBackupPassword(e.target.value)} placeholder="Enter password" - className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-blue-500" + className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-green-500" />
@@ -132,7 +132,7 @@ export default function WalletDeployment() { value={newOwner} onChange={(e) => setNewOwner(e.target.value)} placeholder="0x..." - className="flex-1 px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-blue-500 font-mono text-sm" + className="flex-1 px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-green-500 font-mono text-sm" />
@@ -173,7 +173,7 @@ export default function WalletDeploymentEnhanced() { value={newOwner} onChange={(e) => setNewOwner(e.target.value)} placeholder="0x..." - className="flex-1 px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-blue-500 font-mono text-sm" + className="flex-1 px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-green-500 font-mono text-sm" /> diff --git a/frontend-dapp/src/components/icons/NasaIcon.tsx b/frontend-dapp/src/components/icons/NasaIcon.tsx index e587240..7f8a424 100644 --- a/frontend-dapp/src/components/icons/NasaIcon.tsx +++ b/frontend-dapp/src/components/icons/NasaIcon.tsx @@ -23,7 +23,12 @@ export type NasaIconName = | 'globe' | 'money' | 'rail' - | 'admin'; + | 'admin' + | 'dashboard' + | 'code' + | 'shield' + | 'terminal' + | 'plus'; type Props = SVGProps & { name: NasaIconName; @@ -170,6 +175,39 @@ const paths: Record = { ), + dashboard: ( + <> + + + + + + ), + code: ( + <> + + + + ), + shield: ( + <> + + + + ), + terminal: ( + <> + + + + + ), + plus: ( + <> + + + + ), }; export default function NasaIcon({ name, size = 18, className = '', ...props }: Props) { diff --git a/frontend-dapp/src/components/layout/Layout.tsx b/frontend-dapp/src/components/layout/Layout.tsx index e67b3bd..23a5857 100644 --- a/frontend-dapp/src/components/layout/Layout.tsx +++ b/frontend-dapp/src/components/layout/Layout.tsx @@ -1,86 +1,92 @@ import { Link, Outlet, useLocation } from 'react-router-dom' -import { useRef } from 'react' +import { useRef, useState } from 'react' import WalletConnect from '../wallet/WalletConnect' import WalletDisconnectNotice from '../wallet/WalletDisconnectNotice' import { defaultFrontendExplorerUrl } from '../../config/networks' +import MobileNavDrawer from './MobileNavDrawer' +import MobileBottomNav from './MobileBottomNav' export default function Layout() { const location = useLocation() const userInitiatedDisconnectRef = useRef(false) + const [menuOpen, setMenuOpen] = useState(false) const isActive = (path: string) => location.pathname === path + const navItems = [ + { to: '/hub', label: 'DBIS Dashboard', active: isActive('/hub') }, + { to: '/bridge', label: 'Bridge', active: isActive('/bridge') }, + { to: '/swap', label: 'Swap', active: isActive('/swap') }, + { to: '/reserve', label: 'Reserve', active: isActive('/reserve') }, + { to: '/history', label: 'History', active: isActive('/history') }, + { to: '/wallets', label: 'Wallets', active: isActive('/wallets') }, + { to: '/admin', label: 'Admin', active: isActive('/admin') }, + { to: '/docs', label: 'Developers', active: isActive('/docs') }, + ] + return ( -
+
- +
+ ); +} diff --git a/frontend-dapp/src/components/layout/OmnlProductLayout.tsx b/frontend-dapp/src/components/layout/OmnlProductLayout.tsx index 52e401e..56ff748 100644 --- a/frontend-dapp/src/components/layout/OmnlProductLayout.tsx +++ b/frontend-dapp/src/components/layout/OmnlProductLayout.tsx @@ -1,122 +1,92 @@ import { Link, Outlet, useLocation } from 'react-router-dom'; - -import { useRef } from 'react'; - +import { useRef, useState } from 'react'; import WalletConnect from '../wallet/WalletConnect'; - import WalletDisconnectNotice from '../wallet/WalletDisconnectNotice'; - import NasaIcon, { type NasaIconName } from '../icons/NasaIcon'; - - +import MobileNavDrawer from './MobileNavDrawer'; +import MobileBottomNav from './MobileBottomNav'; const PRODUCTS: { path: string; label: string; icon: NasaIconName }[] = [ - - { path: '/hub', label: 'Hub', icon: 'satellite' }, - + { path: '/hub', label: 'DBIS Dashboard', icon: 'dashboard' }, { path: '/central-bank', label: 'Central Bank', icon: 'central-bank' }, - - { path: '/office-24', label: 'Office 24', icon: 'office' }, - - { path: '/trade', label: 'DBIS Trade', icon: 'trade' }, - - { path: '/swap', label: 'Digital Swap', icon: 'swap' }, - + { path: '/office-24', label: 'O24 Central Bank', icon: 'office' }, + { path: '/trade', label: 'Trade', icon: 'trade' }, + { path: '/swap', label: 'Swap', icon: 'swap' }, ]; - - export default function OmnlProductLayout() { - const location = useLocation(); - const userInitiatedDisconnectRef = useRef(false); + const [menuOpen, setMenuOpen] = useState(false); + const isActive = (path: string) => + path === '/hub' ? location.pathname === '/hub' : location.pathname.startsWith(path); + const drawerItems = [ + ...PRODUCTS.map((p) => ({ to: p.path, label: p.label, active: isActive(p.path) })), + { to: '/bridge', label: 'Bridge', active: location.pathname === '/bridge' }, + { to: '/admin', label: 'Admin', active: location.pathname === '/admin' }, + { to: '/wallets', label: 'Wallets', active: location.pathname === '/wallets' }, + ]; - const isActive = (path: string) => location.pathname.startsWith(path); - - + const isBanking = location.pathname === '/central-bank'; return ( - -
- +
- -
- -
- - - - - - - +
+
+ + + + - - OMNL Mission Control - + DBIS OMNL - - -
-
-
+ setMenuOpen(false)} title="DBIS OMNL" items={drawerItems} /> + setMenuOpen(true)} /> +
-
-
- ); - } - diff --git a/frontend-dapp/src/components/layout/ZMobileBottomNav.tsx b/frontend-dapp/src/components/layout/ZMobileBottomNav.tsx new file mode 100644 index 0000000..e8e86cb --- /dev/null +++ b/frontend-dapp/src/components/layout/ZMobileBottomNav.tsx @@ -0,0 +1,41 @@ +import { Link, useLocation } from 'react-router-dom'; +import NasaIcon from '../icons/NasaIcon'; + +type Props = { + onOpenMenu: () => void; +}; + +const TABS = [ + { to: '/z', label: 'Hub', icon: 'dashboard' as const }, + { to: '/z-wallet', label: 'Wallet', icon: 'wallet' as const }, + { to: '/swap', label: 'Swap', icon: 'swap' as const }, + { to: '/central-bank', label: 'Z Bank', icon: 'central-bank' as const }, +]; + +export default function ZMobileBottomNav({ onOpenMenu }: Props) { + const location = useLocation(); + + const isActive = (path: string) => { + if (path === '/z') return location.pathname === '/z' || location.pathname === '/'; + return location.pathname.startsWith(path); + }; + + return ( + + ); +} diff --git a/frontend-dapp/src/components/layout/ZProductLayout.tsx b/frontend-dapp/src/components/layout/ZProductLayout.tsx new file mode 100644 index 0000000..f8d824c --- /dev/null +++ b/frontend-dapp/src/components/layout/ZProductLayout.tsx @@ -0,0 +1,84 @@ +import { Link, Outlet, useLocation } from 'react-router-dom'; +import { useRef, useState } from 'react'; +import WalletConnect from '../wallet/WalletConnect'; +import WalletDisconnectNotice from '../wallet/WalletDisconnectNotice'; +import NasaIcon, { type NasaIconName } from '../icons/NasaIcon'; +import MobileNavDrawer from './MobileNavDrawer'; +import ZMobileBottomNav from './ZMobileBottomNav'; +import { Z_ECOSYSTEM_PUBLIC_URL } from '../../config/zEcosystemDomain'; + +const PRODUCTS: { path: string; label: string; icon: NasaIconName }[] = [ + { path: '/z', label: 'Z Hub', icon: 'dashboard' }, + { path: '/z-wallet', label: 'Z Wallet', icon: 'wallet' }, + { path: '/swap', label: 'Swap', icon: 'swap' }, + { path: '/central-bank', label: 'Z Online Bank', icon: 'central-bank' }, +]; + +export default function ZProductLayout() { + const location = useLocation(); + const userInitiatedDisconnectRef = useRef(false); + const [menuOpen, setMenuOpen] = useState(false); + + const isActive = (path: string) => + path === '/z' ? location.pathname === '/z' : location.pathname.startsWith(path); + + const drawerItems = PRODUCTS.map((p) => ({ to: p.path, label: p.label, active: isActive(p.path) })); + const isBanking = location.pathname === '/central-bank'; + + return ( +
+ +
+
+ + + + Z + + Z Blockchain System + + + + +
+ { userInitiatedDisconnectRef.current = true; }} /> +
+
+
+ + setMenuOpen(false)} title="Z Ecosystem" items={drawerItems} /> + setMenuOpen(true)} /> + +
+ +
+ +
+ {Z_ECOSYSTEM_PUBLIC_URL.replace(/^https?:\/\//, '')} +
+
+ ); +} diff --git a/frontend-dapp/src/components/ui/ToastProvider.tsx b/frontend-dapp/src/components/ui/ToastProvider.tsx index b7a63dd..69d9d1d 100644 --- a/frontend-dapp/src/components/ui/ToastProvider.tsx +++ b/frontend-dapp/src/components/ui/ToastProvider.tsx @@ -34,7 +34,7 @@ export default function ToastProvider() { }, loading: { iconTheme: { - primary: '#3b82f6', + primary: '#22c55e', secondary: '#fff', }, }, diff --git a/frontend-dapp/src/components/wallet/WalletConnect.tsx b/frontend-dapp/src/components/wallet/WalletConnect.tsx index 2e93262..7c58d64 100644 --- a/frontend-dapp/src/components/wallet/WalletConnect.tsx +++ b/frontend-dapp/src/components/wallet/WalletConnect.tsx @@ -128,7 +128,7 @@ export default function WalletConnect({ onBeforeDisconnect }: WalletConnectProps <> @@ -152,7 +152,7 @@ export default function WalletConnect({ onBeforeDisconnect }: WalletConnectProps > {connector.name} {isPending && ( - + diff --git a/frontend-dapp/src/config/chainIcons.ts b/frontend-dapp/src/config/chainIcons.ts index 3177f79..676297a 100644 --- a/frontend-dapp/src/config/chainIcons.ts +++ b/frontend-dapp/src/config/chainIcons.ts @@ -13,6 +13,8 @@ export const CHAIN_ICON_URLS: Record = { 100: `${TW}/xdai/info/logo.png`, 137: `${TW}/polygon/info/logo.png`, 138: ETH_DIAMOND, // Chain 138 (DBIS) - from chain-138.json + 900001: ETH_DIAMOND, // Shiva Blockchain (OneX) + 900002: ETH_DIAMOND, // Z Chain 42161: `${TW}/arbitrum/info/logo.png`, 43114: `${TW}/avalanchec/info/logo.png`, 8453: `${TW}/base/info/logo.png`, diff --git a/frontend-dapp/src/config/dex.ts b/frontend-dapp/src/config/dex.ts index ce0330c..3bb3369 100644 --- a/frontend-dapp/src/config/dex.ts +++ b/frontend-dapp/src/config/dex.ts @@ -9,28 +9,29 @@ function prodOrLocal(envValue: string | undefined, prodPath: string, localUrl: s export const TOKEN_AGGREGATION_URL = prodOrLocal( import.meta.env.VITE_TOKEN_AGGREGATION_URL, - '/api/v1', + 'https://explorer.d-bis.org/api/v1', 'http://localhost:3000', ); export const DBIS_EXCHANGE_URL = prodOrLocal( import.meta.env.VITE_DBIS_EXCHANGE_URL, - '/exchange', + 'https://exchange.omdnl.org/exchange', 'http://localhost:3012', ); export const SETTLEMENT_MIDDLEWARE_URL = prodOrLocal( import.meta.env.VITE_SETTLEMENT_MIDDLEWARE_URL, - '/settlement', + 'https://secure.omdnl.org/settlement', 'http://localhost:3011', ); -/** Production nginx maps /settlement/* → settlement-middleware /api/v1/settlement/* */ +/** Production nginx maps /settlement/* and /exchange/* to middleware; bare localhost needs /api/v1/... */ function proxiedServiceUrl(base: string, servicePrefix: string, path: string): string { const root = base.replace(/\/$/, ''); const suffix = path.startsWith('/') ? path : `/${path}`; if (root.startsWith('/')) { return `${root}${suffix}`; } - return `${root}${servicePrefix}${suffix}`; + const nginxPortal = /\/(settlement|exchange)$/.test(root); + return nginxPortal ? `${root}${suffix}` : `${root}${servicePrefix}${suffix}`; } export function settlementApi(path: string): string { @@ -41,11 +42,18 @@ export function exchangeApi(path: string): string { return proxiedServiceUrl(DBIS_EXCHANGE_URL, '/api/v1/exchange', path); } -/** Bearer auth for OMNL mutating APIs — production portals inject via nginx; never embed operator keys in browser. */ +/** Token aggregation OMNL routes (fiat LP positions, quotes). */ +export function tokenAggregationApi(path: string): string { + const root = TOKEN_AGGREGATION_URL.replace(/\/$/, ''); + const suffix = path.startsWith('/') ? path : `/${path}`; + if (root.endsWith('/api/v1')) return `${root}${suffix}`; + return `${root}/api/v1${suffix}`; +} + +/** Bearer auth for OMNL mutating APIs — set VITE_OMNL_API_KEY at build for tunnel deploy; nginx injects in prod portals. */ export function omnlApiHeaders(json = true): Record { const headers: Record = {}; if (json) headers['Content-Type'] = 'application/json'; - if (import.meta.env.PROD) return headers; const key = import.meta.env.VITE_OMNL_API_KEY?.trim(); if (key) headers.Authorization = `Bearer ${key}`; return headers; diff --git a/frontend-dapp/src/config/ecosystemLinkRouting.ts b/frontend-dapp/src/config/ecosystemLinkRouting.ts new file mode 100644 index 0000000..208a2c1 --- /dev/null +++ b/frontend-dapp/src/config/ecosystemLinkRouting.ts @@ -0,0 +1,16 @@ +import type { EcosystemLink } from './ecosystemLinks'; + +/** Paths served outside the React SPA (static HTML or proxied APIs). */ +export function isStaticPortalPath(href: string): boolean { + return ( + href.startsWith('/omnl') || + href.startsWith('/api') || + href.startsWith('/settlement') || + href.startsWith('/exchange') || + href.startsWith('/reserve/institutional') + ); +} + +export function ecosystemLinkUsesAnchor(link: EcosystemLink): boolean { + return Boolean(link.external) || link.href.startsWith('http') || isStaticPortalPath(link.href); +} diff --git a/frontend-dapp/src/config/ecosystemLinks.ts b/frontend-dapp/src/config/ecosystemLinks.ts new file mode 100644 index 0000000..a80c9e3 --- /dev/null +++ b/frontend-dapp/src/config/ecosystemLinks.ts @@ -0,0 +1,77 @@ +import type { NasaIconName } from '../components/icons/NasaIcon'; +import { defaultFrontendExplorerUrl } from './networks'; + +export type EcosystemLinkAccent = 'green' | 'teal' | 'blue' | 'gold' | 'purple' | 'slate'; + +export type EcosystemLink = { + id: string; + label: string; + description: string; + icon: NasaIconName; + href: string; + external?: boolean; + accent?: EcosystemLinkAccent; +}; + +/** In-app React routes (OMNL / DBIS — no Z products) */ +export const IN_APP_LINKS: EcosystemLink[] = [ + { id: 'bridge', label: 'Bridge', description: 'Cross-chain bridge, CCIP, XRPL, trustless', icon: 'bridge', href: '/bridge', accent: 'teal' }, + { id: 'swap', label: 'Digital Swap', description: 'M2 load, token swap, settlement rails', icon: 'swap', href: '/swap', accent: 'blue' }, + { id: 'trade', label: 'DBIS Trade', description: 'Markets, order book, spot trading', icon: 'trade', href: '/trade', accent: 'gold' }, + { id: 'central-bank', label: 'Central Bank', description: 'OMNL balances & payments', icon: 'central-bank', href: '/central-bank', accent: 'green' }, + { id: 'office-24', label: 'Office 24 Central Bank', description: '128-bank corridors · IPSAS settlement', icon: 'office', href: '/office-24', accent: 'purple' }, + { id: 'reserve', label: 'Reserve', description: 'Coverage, attestation, peg status', icon: 'money', href: '/reserve', accent: 'green' }, + { id: 'history', label: 'History', description: 'Bridge transfer lookup & tracking', icon: 'ledger', href: '/history', accent: 'slate' }, + { id: 'wallets', label: 'Wallets', description: 'OneX Wallet · email, social, passkey on Shiva', icon: 'wallet', href: '/wallets', accent: 'blue' }, + { id: 'admin', label: 'Admin', description: 'Multisig, channels, bridge operators', icon: 'admin', href: '/admin', accent: 'slate' }, + { id: 'docs', label: 'Developers', description: 'API reference & integration', icon: 'code', href: '/docs', accent: 'teal' }, + { id: 'hub', label: 'DBIS Dashboard', description: 'All apps, consoles & API links', icon: 'dashboard', href: '/hub', accent: 'green' }, +]; + +/** Primary apps shown in dashboard quick-access strip (OMNL only) */ +export const FEATURED_APP_LINKS: EcosystemLink[] = [ + IN_APP_LINKS.find((l) => l.id === 'central-bank')!, + IN_APP_LINKS.find((l) => l.id === 'bridge')!, + IN_APP_LINKS.find((l) => l.id === 'trade')!, + IN_APP_LINKS.find((l) => l.id === 'swap')!, + IN_APP_LINKS.find((l) => l.id === 'office-24')!, +]; + +/** Static operator consoles — served from /omnl/* (portal-serve or token-aggregation :3000) */ +export const OPERATOR_CONSOLE_LINKS: EcosystemLink[] = [ + { id: 'compliance', label: 'OMNL Compliance Console', description: 'Posture, pending actions, Safe tx', icon: 'shield', href: '/omnl/compliance', accent: 'purple' }, + { id: 'omnl-dash', label: 'OMNL API Snapshot', description: 'Registry and Fineract compare JSON', icon: 'chart', href: '/omnl/dashboard', accent: 'blue' }, + { id: 'reserve-dash', label: 'Reserve Dashboard', description: 'Proof rows, coverage, institutional view', icon: 'money', href: '/reserve', accent: 'green' }, + { id: 'settlement-term', label: 'Settlement Terminal', description: 'Operator settlement terminal UI', icon: 'terminal', href: '/omnl/terminal', accent: 'gold' }, + { id: 'token-admin', label: 'Token Aggregation API', description: 'API catalog JSON', icon: 'ledger', href: '/api/v1/omnl/catalog', accent: 'teal' }, +]; + +/** Public production portals (OMNL / DBIS) */ +export const PUBLIC_PORTAL_LINKS: EcosystemLink[] = [ + { id: 'pub-cb', label: 'Central Bank', description: 'secure.omdnl.org', icon: 'central-bank', href: 'https://secure.omdnl.org/central-bank', external: true, accent: 'green' }, + { id: 'pub-online', label: 'Online banking', description: 'online.omdnl.org', icon: 'globe', href: 'https://online.omdnl.org/central-bank', external: true, accent: 'green' }, + { id: 'pub-o24', label: 'Office 24 Central Bank', description: 'office24.omdnl.org', icon: 'office', href: 'https://office24.omdnl.org/office-24', external: true, accent: 'purple' }, + { id: 'pub-trade', label: 'Exchange', description: 'exchange.omdnl.org', icon: 'trade', href: 'https://exchange.omdnl.org/trade', external: true, accent: 'gold' }, + { id: 'pub-forex', label: 'Forex', description: 'forex.omdnl.org', icon: 'trade', href: 'https://forex.omdnl.org/trade', external: true, accent: 'gold' }, + { id: 'explorer', label: 'Chain 138 Explorer', description: 'Blocks, contracts, transactions', icon: 'globe', href: defaultFrontendExplorerUrl, external: true, accent: 'slate' }, +]; + +/** Public Z Ecosystem portal — separate from OMNL (zblockchainsystem.com) */ +export const Z_ECOSYSTEM_PORTAL_LINKS: EcosystemLink[] = [ + { id: 'z-hub', label: 'Z Ecosystem', description: 'zblockchainsystem.com', icon: 'globe', href: 'https://zblockchainsystem.com/z', external: true, accent: 'purple' }, + { id: 'z-wallet', label: 'International Z Wallet', description: 'Z Chain wallet + corridors', icon: 'wallet', href: 'https://zblockchainsystem.com/z-wallet', external: true, accent: 'blue' }, + { id: 'z-bot', label: 'Z Bot', description: 'In-app assistant', icon: 'terminal', href: 'https://zblockchainsystem.com/z-wallet#z-bot', external: true, accent: 'teal' }, + { id: 'z-swap', label: 'Digital Swap', description: 'zblockchainsystem.com', icon: 'swap', href: 'https://zblockchainsystem.com/swap', external: true, accent: 'teal' }, + { id: 'z-bank', label: 'Z Online Bank', description: 'M0–M4 settlement UI', icon: 'central-bank', href: 'https://zblockchainsystem.com/central-bank', external: true, accent: 'green' }, +]; + +/** API & docs endpoints — same-origin via portal-serve proxy on tunnel/local */ +export const API_LINKS: EcosystemLink[] = [ + { id: 'openapi', label: 'OpenAPI JSON', description: 'Machine-readable OMNL API spec', icon: 'code', href: '/api/v1/omnl/openapi.json', accent: 'teal' }, + { id: 'catalog', label: 'API Catalog', description: 'All OMNL routes and auth notes', icon: 'ledger', href: '/api/v1/omnl/catalog', accent: 'blue' }, + { id: 'integration', label: 'Integration Status', description: 'Configured env groups (no secrets)', icon: 'check', href: '/api/v1/omnl/integration-status', accent: 'green' }, + { id: 'registry', label: 'IPSAS Registry', description: 'GL registry JSON', icon: 'money', href: '/api/v1/omnl/ipsas/registry', accent: 'gold' }, + { id: 'settlement-health', label: 'Settlement health', description: '128-chain settlement middleware', icon: 'check', href: '/settlement/health', accent: 'green' }, + { id: 'exchange-health', label: 'Exchange health', description: 'DBIS exchange service', icon: 'check', href: '/exchange/health', accent: 'green' }, + { id: 'fineract-swagger', label: 'Fineract Swagger', description: 'omnl.hybxfinance.io', icon: 'chart', href: 'https://omnl.hybxfinance.io/fineract-provider/swagger-ui/index.html', external: true, accent: 'purple' }, +]; diff --git a/frontend-dapp/src/config/ecosystemMode.ts b/frontend-dapp/src/config/ecosystemMode.ts new file mode 100644 index 0000000..47a12e2 --- /dev/null +++ b/frontend-dapp/src/config/ecosystemMode.ts @@ -0,0 +1,9 @@ +/** Build profile: `z` = Z Blockchain System only; `omnl` = DBIS / OMNL only */ +export type EcosystemMode = 'z' | 'omnl'; + +const raw = (import.meta.env.VITE_ECOSYSTEM as string | undefined)?.trim().toLowerCase(); + +export const ECOSYSTEM_MODE: EcosystemMode = raw === 'z' ? 'z' : 'omnl'; + +export const isZEcosystem = ECOSYSTEM_MODE === 'z'; +export const isOmnlEcosystem = ECOSYSTEM_MODE === 'omnl'; diff --git a/frontend-dapp/src/config/guosmmNotices.ts b/frontend-dapp/src/config/guosmmNotices.ts new file mode 100644 index 0000000..cddf1e5 --- /dev/null +++ b/frontend-dapp/src/config/guosmmNotices.ts @@ -0,0 +1,46 @@ +/** GUOSMM gazette notices — SSOT: config/guosmm-notices.v1.json */ +export type GuosmmNoticeLink = { + documentId: string; + title: string; + url: string; + series: string; + entity?: string; + fineractOfficeId?: number; +}; + +export const GUOSMM_GAZETTE_BASE = 'https://gazette.shosjj.org/notices/2026'; + +export const GUOSMM_NOTICES: GuosmmNoticeLink[] = [ + { + documentId: 'GUOSMM-B-2026-001', + series: 'B', + title: 'Central Bank Charter — OMNL', + url: `${GUOSMM_GAZETTE_BASE}/GUOSMM-B-2026-001`, + }, + { + documentId: 'GUOSMM-C-2026-003', + series: 'C', + title: 'zBank — ISB Operations Charter', + url: `${GUOSMM_GAZETTE_BASE}/GUOSMM-C-2026-003`, + entity: 'zBank', + fineractOfficeId: 29, + }, + { + documentId: 'GUOSMM-C-2026-004', + series: 'C', + title: 'NovaBank — ISB Operations Charter', + url: `${GUOSMM_GAZETTE_BASE}/GUOSMM-C-2026-004`, + entity: 'NovaBank', + fineractOfficeId: 30, + }, + { + documentId: 'GUOSMM-C-2026-005', + series: 'C', + title: 'AnakaBank — ISB Operations Charter', + url: `${GUOSMM_GAZETTE_BASE}/GUOSMM-C-2026-005`, + entity: 'AnakaBank', + fineractOfficeId: 31, + }, +]; + +export const GUOSMM_API_CATALOG = '/api/v1/omnl/gazette/notices'; diff --git a/frontend-dapp/src/config/networks.ts b/frontend-dapp/src/config/networks.ts index aa1e91a..6627b64 100644 --- a/frontend-dapp/src/config/networks.ts +++ b/frontend-dapp/src/config/networks.ts @@ -13,6 +13,19 @@ import { optimism, } from 'wagmi/chains' +import { + SHIVA_ONEX_CHAIN_ID, + shivaOnexExplorerUrl, + shivaOnexRpcUrl, +} from './onexShiva' +import { + Z_CHAIN_ID, + zChainExplorerUrl, + zChainRpcUrl, +} from './zChain' + +const rpcUrlShivaOneX = shivaOnexRpcUrl +const rpcUrlZChain = zChainRpcUrl const rpcUrl138 = import.meta.env.VITE_RPC_URL_138 || 'https://rpc-http-pub.d-bis.org' const rpcUrl651940 = import.meta.env.VITE_RPC_URL_651940 || 'https://mainnet-rpc.alltra.global' const rpcUrl42793 = import.meta.env.VITE_RPC_URL_42793 || 'https://node.mainnet.etherlink.com' @@ -74,6 +87,54 @@ export const chain138 = defineChain({ }, }) +/** Chain 900001 - Shiva Blockchain (OneX Wallet / OneX Bank) */ +export const shivaOneXChain = defineChain({ + id: SHIVA_ONEX_CHAIN_ID, + name: 'Shiva Blockchain (OneX)', + network: 'shiva-onex', + nativeCurrency: { + decimals: 18, + name: 'Shiva', + symbol: 'SHIVA', + }, + rpcUrls: { + default: { http: [rpcUrlShivaOneX] }, + public: { http: [rpcUrlShivaOneX] }, + }, + blockExplorers: { + default: { + name: 'Shiva OneX Explorer', + url: shivaOnexExplorerUrl, + }, + }, +}) + +export const shivaOneXChainId = shivaOneXChain.id + +/** Chain 900002 - Z Chain (International Z Wallet) */ +export const zChain = defineChain({ + id: Z_CHAIN_ID, + name: 'Z Chain', + network: 'z-chain', + nativeCurrency: { + decimals: 18, + name: 'Z', + symbol: 'Z', + }, + rpcUrls: { + default: { http: [rpcUrlZChain] }, + public: { http: [rpcUrlZChain] }, + }, + blockExplorers: { + default: { + name: 'Z Chain Explorer', + url: zChainExplorerUrl, + }, + }, +}) + +export const zChainId = zChain.id + /** Chain 651940 - ALL Mainnet */ export const allMainnet = defineChain({ id: 651940, @@ -165,6 +226,8 @@ export const gnosis = defineChain({ const customChainsWithout2138 = [ chain138, + shivaOneXChain, + zChain, allMainnet, etherlink, bsc, @@ -175,6 +238,8 @@ const customChainsWithout2138 = [ const customChainsWith2138 = [ chain138, + shivaOneXChain, + zChain, chain2138Testnet, allMainnet, etherlink, @@ -191,8 +256,8 @@ export const customChains = chain2138TestnetEnabled /** Source chains the frontend can intentionally target for wallet UX. */ export const frontendSourceChains = chain2138TestnetEnabled - ? [chain138, chain2138Testnet] as const - : [chain138] as const + ? ([chain138, shivaOneXChain, zChain, chain2138Testnet] as const) + : ([chain138, shivaOneXChain, zChain] as const) /** Shared source-chain IDs for connect/switch prompts. */ export const frontendSourceChainIds: number[] = frontendSourceChains.map((chain) => chain.id) @@ -217,6 +282,8 @@ export const allNetworks = [...customChains, ...standardChains] as const /** Chain IDs we support (for CowSwap, Jumper, UI filters) - all deployed contract networks */ export const supportedChainIds = [ chain138.id, + shivaOneXChain.id, + zChain.id, ...(chain2138TestnetEnabled ? [chain2138Testnet.id] : []), allMainnet.id, etherlink.id, @@ -235,6 +302,8 @@ export const supportedChainIds = [ export const chainRpcUrls: Record = { [mainnet.id]: import.meta.env.VITE_MAINNET_RPC_URL || 'https://ethereum.publicnode.com', [chain138.id]: rpcUrl138, + [shivaOneXChain.id]: rpcUrlShivaOneX, + [zChain.id]: rpcUrlZChain, ...(chain2138TestnetEnabled ? { [chain2138Testnet.id]: rpcUrl2138 } : {}), [allMainnet.id]: rpcUrl651940, [etherlink.id]: rpcUrl42793, diff --git a/frontend-dapp/src/config/omnlInternalPaths.ts b/frontend-dapp/src/config/omnlInternalPaths.ts index 2302c1b..617b663 100644 --- a/frontend-dapp/src/config/omnlInternalPaths.ts +++ b/frontend-dapp/src/config/omnlInternalPaths.ts @@ -86,6 +86,8 @@ export const INTERNAL_PATH_GROUPS: InternalPathGroup[] = [ label: 'OMNL API', entries: [ { method: 'GET', path: '/api/v1/omnl/catalog', auth: 'none' }, + { method: 'GET', path: '/api/v1/omnl/gazette/notices', auth: 'none', notes: 'GUOSMM official gazette registry' }, + { method: 'GET', path: '/api/v1/omnl/gazette/notices/:documentId', auth: 'none' }, { method: 'GET', path: '/api/v1/omnl/openapi.json', auth: 'none' }, { method: 'GET', path: '/api/v1/omnl/health', auth: 'omnl-key-in-production' }, { method: 'GET', path: '/api/v1/omnl/ipsas/fineract-health', auth: 'omnl-key-in-production' }, diff --git a/frontend-dapp/src/config/onexShiva.ts b/frontend-dapp/src/config/onexShiva.ts new file mode 100644 index 0000000..12472af --- /dev/null +++ b/frontend-dapp/src/config/onexShiva.ts @@ -0,0 +1,24 @@ +import onexShivaConfig from '../../../config/onex-shiva-integration.v1.json'; + +export type OnexShivaConfig = typeof onexShivaConfig; + +export const onexShiva = onexShivaConfig; + +export const SHIVA_ONEX_CHAIN_ID = Number( + import.meta.env.VITE_SHIVA_ONEX_CHAIN_ID || onexShiva.chain.chainId, +); + +export const shivaOnexRpcUrl = + import.meta.env.VITE_RPC_URL_900001 || + import.meta.env.VITE_SHIVA_ONEX_RPC_URL || + onexShiva.chain.rpcDefault; + +export const shivaOnexExplorerUrl = + import.meta.env.VITE_SHIVA_ONEX_EXPLORER_URL || onexShiva.chain.explorerUrl; + +export const onexWalletEnabled = + import.meta.env.VITE_ENABLE_ONEX_SHIVA_WALLET !== 'false' && + import.meta.env.VITE_ENABLE_ONEX_SHIVA_WALLET !== '0'; + +export const ONEX_BANK_OFFICE_ID = onexShiva.fineract.officeId; +export const ONEX_HYBX_WALLET_ID = onexShiva.fineract.hybxWalletId; diff --git a/frontend-dapp/src/config/zChain.ts b/frontend-dapp/src/config/zChain.ts new file mode 100644 index 0000000..6bb7c69 --- /dev/null +++ b/frontend-dapp/src/config/zChain.ts @@ -0,0 +1,21 @@ +import zChainConfig from '../../../config/z-chain-integration.v1.json'; + +export type ZChainConfig = typeof zChainConfig; + +export const zChain = zChainConfig; + +export const Z_CHAIN_ID = Number(import.meta.env.VITE_Z_CHAIN_ID || zChain.chain.chainId); + +export const zChainRpcUrl = + import.meta.env.VITE_RPC_URL_900002 || + import.meta.env.VITE_Z_CHAIN_RPC_URL || + zChain.chain.rpcDefault; + +export const zChainExplorerUrl = + import.meta.env.VITE_Z_CHAIN_EXPLORER_URL || zChain.chain.explorerUrl; + +export const zWalletEnabled = + import.meta.env.VITE_ENABLE_Z_WALLET !== 'false' && import.meta.env.VITE_ENABLE_Z_WALLET !== '0'; + +export const Z_WALLET_OFFICE_ID = zChain.fineract.officeId; +export const Z_HYBX_WALLET_ID = zChain.fineract.hybxWalletId; diff --git a/frontend-dapp/src/config/zEcosystemDomain.ts b/frontend-dapp/src/config/zEcosystemDomain.ts new file mode 100644 index 0000000..c2e841c --- /dev/null +++ b/frontend-dapp/src/config/zEcosystemDomain.ts @@ -0,0 +1,11 @@ +import zProd from '../../../config/z-ecosystem-production-domains.v1.json'; + +/** Canonical public origin for Z Ecosystem (not OMNL). */ +export const Z_ECOSYSTEM_PUBLIC_URL = + import.meta.env.VITE_Z_ECOSYSTEM_URL?.trim() || zProd.publicUrl; + +export function zEcosystemUrl(path: string): string { + const base = Z_ECOSYSTEM_PUBLIC_URL.replace(/\/$/, ''); + const suffix = path.startsWith('/') ? path : `/${path}`; + return `${base}${suffix}`; +} diff --git a/frontend-dapp/src/features/central-bank/ChainMatrixPanel.tsx b/frontend-dapp/src/features/central-bank/ChainMatrixPanel.tsx index 4fa9b2b..15db1cf 100644 --- a/frontend-dapp/src/features/central-bank/ChainMatrixPanel.tsx +++ b/frontend-dapp/src/features/central-bank/ChainMatrixPanel.tsx @@ -1,109 +1,214 @@ -import { useEffect, useState } from 'react'; -import { fetchOmnlChainRegistry, type ChainRegistryResponse, type OmnlChainSummary } from '../../config/supported-chains'; -import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon'; - -type Filter = 'all' | 'active' | 'primary' | 'l2'; - -export default function ChainMatrixPanel() { - const [registry, setRegistry] = useState(null); - const [filter, setFilter] = useState('active'); - const [search, setSearch] = useState(''); - - useEffect(() => { - fetchOmnlChainRegistry().then(setRegistry); - }, []); - - const chains = (registry?.chains ?? []).filter((c) => { - if (filter === 'active' && c.status !== 'active') return false; - if (filter === 'primary' && c.omnlRole !== 'settlement-hub' && c.omnlRole !== 'mirror-target') return false; - if (filter === 'l2' && c.tier !== 'l2') return false; - if (search) { - const q = search.toLowerCase(); - return c.name.toLowerCase().includes(q) || String(c.chainId).includes(q) || c.nativeSymbol.toLowerCase().includes(q); - } - return true; - }); - - return ( -
-
-
-

- - 128-chain network matrix -

-

- {registry?.stats?.active ?? 0} active · {registry?.stats?.staged ?? 0} staged ·{' '} - {registry?.stats?.reserved ?? 0} reserved · primary {registry?.primaryChainId ?? 138} -

-
-
- - setSearch(e.target.value)} - className="nasa-input !min-h-[36px] !w-48 text-sm" - /> -
-
- -
- {(['all', 'active', 'primary', 'l2'] as Filter[]).map((f) => ( - - ))} -
- -
- - - - - - - - - - - - - {chains.map((c: OmnlChainSummary) => ( - - - - - - - - - ))} - -
IDNetworkRoleStatusSwapBridge
{c.chainId}{c.name}{c.omnlRole} - - {c.status} - -
-
- - ); -} +import { useEffect, useState } from 'react'; + +import { fetchOmnlChainRegistry, type ChainRegistryResponse, type OmnlChainSummary } from '../../config/supported-chains'; + +import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon'; + + + +type Filter = 'all' | 'active' | 'primary' | 'l2'; + + + +export default function ChainMatrixPanel() { + + const [registry, setRegistry] = useState(null); + + const [filter, setFilter] = useState('active'); + + const [search, setSearch] = useState(''); + + + + useEffect(() => { + + fetchOmnlChainRegistry().then(setRegistry); + + }, []); + + + + const chains = (registry?.chains ?? []).filter((c) => { + + if (filter === 'active' && c.status !== 'active') return false; + + if (filter === 'primary' && c.omnlRole !== 'settlement-hub' && c.omnlRole !== 'mirror-target') return false; + + if (filter === 'l2' && c.tier !== 'l2') return false; + + if (search) { + + const q = search.toLowerCase(); + + return c.name.toLowerCase().includes(q) || String(c.chainId).includes(q) || c.nativeSymbol.toLowerCase().includes(q); + + } + + return true; + + }); + + + + return ( + +
+ +
+ +
+ +

+ + + + 128-chain network matrix + +

+ +

+ + {registry?.stats?.active ?? 0} active · {registry?.stats?.staged ?? 0} staged ·{' '} + + {registry?.stats?.reserved ?? 0} reserved · primary {registry?.primaryChainId ?? 138} + +

+ +
+ +
+ + + + setSearch(e.target.value)} + + className="nasa-input !min-h-[36px] !w-48 text-sm" + + /> + +
+ +
+ + + +
+ + {(['all', 'active', 'primary', 'l2'] as Filter[]).map((f) => ( + + + + ))} + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + {chains.map((c: OmnlChainSummary) => ( + + + + + + + + + + + + + + + + + + ))} + + + +
IDNetworkRoleStatusSwapBridge
{c.chainId}{c.name}{c.omnlRole} + + + + {c.status} + + + +
+ +
+ +
+ + ); + +} + diff --git a/frontend-dapp/src/features/central-bank/useCentralBank.ts b/frontend-dapp/src/features/central-bank/useCentralBank.ts index a034c91..407a3e1 100644 --- a/frontend-dapp/src/features/central-bank/useCentralBank.ts +++ b/frontend-dapp/src/features/central-bank/useCentralBank.ts @@ -67,14 +67,9 @@ export function useCentralBank() { setOffice(o); setTokenCount(Array.isArray(m2?.tokens) ? m2.tokens.length : 0); setMonitor(mon); - setMoneySupply( - ms ?? - (h?.status === 'ok' - ? { fineractConnected: true, fineractLive: false, officeId: 24 } - : null), - ); + setMoneySupply(ms); if (!h && !o) { - setError('Settlement middleware unreachable'); + setError('Settlement middleware unreachable — check production API at explorer.d-bis.org'); } } catch (e) { setError(e instanceof Error ? e.message : 'Failed to load central bank data'); diff --git a/frontend-dapp/src/features/hub/DashboardSectionFold.tsx b/frontend-dapp/src/features/hub/DashboardSectionFold.tsx new file mode 100644 index 0000000..13f82cb --- /dev/null +++ b/frontend-dapp/src/features/hub/DashboardSectionFold.tsx @@ -0,0 +1,29 @@ +import type { ReactNode } from 'react'; +import type { NasaIconName } from '../../components/icons/NasaIcon'; +import NasaIcon from '../../components/icons/NasaIcon'; + +type Props = { + title: string; + subtitle?: string; + icon: NasaIconName; + defaultOpen?: boolean; + children: ReactNode; +}; + +export default function DashboardSectionFold({ title, subtitle, icon, defaultOpen = true, children }: Props) { + return ( +
+ + + + + + {title} + {subtitle && {subtitle}} + + + +
{children}
+
+ ); +} diff --git a/frontend-dapp/src/features/hub/EcosystemLinkGrid.tsx b/frontend-dapp/src/features/hub/EcosystemLinkGrid.tsx new file mode 100644 index 0000000..423fef4 --- /dev/null +++ b/frontend-dapp/src/features/hub/EcosystemLinkGrid.tsx @@ -0,0 +1,128 @@ +import { Link } from 'react-router-dom'; +import NasaIcon, { type NasaIconName } from '../../components/icons/NasaIcon'; +import type { EcosystemLink, EcosystemLinkAccent } from '../../config/ecosystemLinks'; +import { ecosystemLinkUsesAnchor } from '../../config/ecosystemLinkRouting'; + +type Props = { + title: string; + subtitle?: string; + links: EcosystemLink[]; + columns?: '2' | '3'; + centered?: boolean; + sectionIcon?: NasaIconName; + variant?: 'grid' | 'list'; + hideHeader?: boolean; +}; + +const ACCENT_STYLES: Record = { + green: { tile: 'bg-green-500/15 border-green-500/30 shadow-[0_0_20px_rgba(34,197,94,0.1)]', icon: 'text-green-400' }, + teal: { tile: 'bg-teal-500/15 border-teal-500/30 shadow-[0_0_20px_rgba(20,184,166,0.1)]', icon: 'text-teal-400' }, + blue: { tile: 'bg-sky-500/15 border-sky-500/30 shadow-[0_0_20px_rgba(56,189,248,0.1)]', icon: 'text-sky-400' }, + gold: { tile: 'bg-amber-500/15 border-amber-500/30 shadow-[0_0_20px_rgba(245,158,11,0.1)]', icon: 'text-amber-400' }, + purple: { tile: 'bg-violet-500/15 border-violet-500/30 shadow-[0_0_20px_rgba(139,92,246,0.1)]', icon: 'text-violet-400' }, + slate: { tile: 'bg-slate-500/15 border-slate-500/30', icon: 'text-slate-300' }, +}; + +function LinkCard({ link, variant }: { link: EcosystemLink; variant: 'grid' | 'list' }) { + const accent = link.accent ?? 'green'; + const styles = ACCENT_STYLES[accent]; + const isExternal = link.external || link.href.startsWith('http'); + const useAnchor = ecosystemLinkUsesAnchor(link); + const isList = variant === 'list'; + const className = [ + 'hub-link-card group flex no-underline text-inherit rounded-xl border border-white/10 bg-[var(--nasa-panel)]', + 'hover:border-green-500/40 hover:bg-[var(--nasa-panel-elevated)] active:scale-[0.98] transition-all duration-150 shadow-sm', + isList + ? 'hub-link-card--list flex-row items-center text-left gap-3 p-3 min-h-[60px]' + : 'flex-col items-center text-center gap-3 p-4 min-h-[148px] hover:-translate-y-0.5', + ].join(' '); + + const inner = ( + <> + + + +
+
+

{link.label}

+ {isExternal && } +
+

+ {link.description} +

+
+ {!isList && ( + + Open + + + )} + {isList && } + + ); + + if (isExternal) { + return ( + + {inner} + + ); + } + + if (useAnchor) { + return ( + + {inner} + + ); + } + + return ( + + {inner} + + ); +} + +export default function EcosystemLinkGrid({ + title, + subtitle, + links, + columns = '3', + centered = true, + sectionIcon = 'link', + variant = 'grid', + hideHeader = false, +}: Props) { + const gridClass = + variant === 'list' + ? 'grid grid-cols-1 gap-2' + : columns === '2' + ? 'grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4' + : 'grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 sm:gap-4'; + + return ( +
+ {!hideHeader && ( + + )} +
+ {links.map((link) => ( + + ))} +
+
+ ); +} diff --git a/frontend-dapp/src/features/hub/FeaturedAppsStrip.tsx b/frontend-dapp/src/features/hub/FeaturedAppsStrip.tsx new file mode 100644 index 0000000..b0d47ec --- /dev/null +++ b/frontend-dapp/src/features/hub/FeaturedAppsStrip.tsx @@ -0,0 +1,56 @@ +import { Link } from 'react-router-dom'; +import NasaIcon from '../../components/icons/NasaIcon'; +import { FEATURED_APP_LINKS, type EcosystemLink, type EcosystemLinkAccent } from '../../config/ecosystemLinks'; +import { ecosystemLinkUsesAnchor } from '../../config/ecosystemLinkRouting'; + +const ACCENT_TILE: Record = { + green: 'bg-green-500/15 border-green-500/30 text-green-400', + teal: 'bg-teal-500/15 border-teal-500/30 text-teal-400', + blue: 'bg-sky-500/15 border-sky-500/30 text-sky-400', + gold: 'bg-amber-500/15 border-amber-500/30 text-amber-400', + purple: 'bg-violet-500/15 border-violet-500/30 text-violet-400', + slate: 'bg-slate-500/15 border-slate-500/30 text-slate-300', +}; + +function FeaturedItem({ link }: { link: EcosystemLink }) { + const accent = link.accent ?? 'green'; + const tile = ACCENT_TILE[accent]; + const short = link.label.replace('DBIS ', '').replace('Digital ', ''); + + const className = 'dbis-featured__item'; + const inner = ( + <> + + + + {short} + + ); + + if (ecosystemLinkUsesAnchor(link)) { + return ( + + {inner} + + ); + } + + return ( + + {inner} + + ); +} + +export default function FeaturedAppsStrip() { + return ( +
+

Quick access

+
+ {FEATURED_APP_LINKS.map((link) => ( + + ))} +
+
+ ); +} diff --git a/frontend-dapp/src/features/office24/Office24BankCorridors.tsx b/frontend-dapp/src/features/office24/Office24BankCorridors.tsx new file mode 100644 index 0000000..9c87a16 --- /dev/null +++ b/frontend-dapp/src/features/office24/Office24BankCorridors.tsx @@ -0,0 +1,116 @@ +import { useEffect, useState } from 'react'; +import { fetchOmnlChainRegistry, type ChainRegistryResponse } from '../../config/supported-chains'; +import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon'; +import DashboardSection from '../omnl-dashboard/DashboardSection'; + +/** Office 24 Central Bank — 128-bank corridor status (settlement + SWIFT + M2 rails). */ +export default function Office24BankCorridors() { + const [registry, setRegistry] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetchOmnlChainRegistry() + .then(setRegistry) + .finally(() => setLoading(false)); + }, []); + + const chains = registry?.chains ?? []; + const active = chains.filter((c) => c.status === 'active'); + const openCorridors = active.length; + + return ( + + {loading ? 'Loading…' : `${openCorridors} / ${registry?.maxCapacity ?? 128} corridors open`} + + } + > +
+
+

{registry?.stats?.active ?? '—'}

+

Active chains

+
+
+

{registry?.primaryChainId ?? 138}

+

Settlement hub

+
+
+

{registry?.mirrorChainId ?? 651940}

+

Mirror chain

+
+
+

{registry?.stats?.staged ?? '—'}

+

Staged

+
+
+ +

+ + All active chains have full OMNL integration: Fineract posting, SWIFT MT102/MT103, trade finance (SBLC/BG/LC), M2 + token load, and bridge/swap when enabled in registry. +

+ +
+ + + + + + + + + + + + + + + {chains.slice(0, 128).map((c) => { + const open = c.status === 'active'; + return ( + + + + + + + + + + + ); + })} + {!chains.length && !loading && ( + + + + )} + +
ChainNetworkCorridorSWIFTSettlementM2SwapBridge
{c.chainId}{c.name} + + {open ? 'OPEN' : c.status} + + + + + + + + + + + +
+ Chain registry unavailable — settlement API /chains +
+
+
+ ); +} diff --git a/frontend-dapp/src/features/office24/Office24Dashboard.tsx b/frontend-dapp/src/features/office24/Office24Dashboard.tsx index 64e2c62..a1afec0 100644 --- a/frontend-dapp/src/features/office24/Office24Dashboard.tsx +++ b/frontend-dapp/src/features/office24/Office24Dashboard.tsx @@ -16,6 +16,12 @@ import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon'; import { formatUsd } from '../../utils/formatMoney'; +import ChainMatrixPanel from '../central-bank/ChainMatrixPanel'; + +import Office24BankCorridors from './Office24BankCorridors'; + +import Office24EcosystemSubjects from './Office24EcosystemSubjects'; + export default function Office24Dashboard() { @@ -44,43 +50,38 @@ export default function Office24Dashboard() { return ( -
+
-
+
-
+ - subtitle={profile?.externalId ?? 'HOSPITALLERS-ZARDASHT-IRAQ-IRAN'} + Office 24 Central Bank - badges={ + 128-bank corridors - <> + {profile?.locked && Single office · locked} - Office 24 + - {profile?.locked && Single office · locked} + } - + /> - } - - /> - -
- -
+
- Central Bank + Z Online Bank @@ -104,7 +105,7 @@ export default function Office24Dashboard() { -

+

@@ -146,7 +147,7 @@ export default function Office24Dashboard() { action={ - + + + Trade real USDT / USDC on Digital Swap → + +

+ + )} + + ); +} diff --git a/frontend-dapp/src/features/online-bank/useOnlineBank.ts b/frontend-dapp/src/features/online-bank/useOnlineBank.ts new file mode 100644 index 0000000..00d2f58 --- /dev/null +++ b/frontend-dapp/src/features/online-bank/useOnlineBank.ts @@ -0,0 +1,77 @@ +import { useMemo } from 'react'; +import { useCentralBank } from '../central-bank/useCentralBank'; +import { formatUsd } from '../../utils/formatMoney'; + +export type BankActivity = { + id: string; + title: string; + meta: string; + amount: string; + positive?: boolean; + kind: 'swap' | 'transfer' | 'system'; +}; + +export function useOnlineBank() { + const cb = useCentralBank(); + + const office = cb.office as { + officeId?: number; + name?: string; + externalId?: string; + } | null; + + const healthOk = (cb.health as { status?: string } | null)?.status === 'ok'; + const live = Boolean(cb.moneySupply?.fineractLive); + const connected = Boolean(cb.moneySupply?.fineractConnected) || healthOk; + + const primaryBalance = cb.moneySupply?.M1?.circulating ?? cb.moneySupply?.M2?.broadMoney; + const savingsBalance = cb.moneySupply?.M2?.broadMoney; + const reserveBalance = cb.moneySupply?.M0?.gl1050; + const m4Balance = cb.moneySupply?.M4?.nearMoney; + const m3Balance = cb.moneySupply?.M3?.tokenLiabilities; + + const activities = useMemo(() => { + const items: BankActivity[] = []; + const swaps = (cb.monitor as { swaps?: { status: string; tokenIn: string; tokenOut?: string; amountIn?: string; createdAt: string }[] } | null)?.swaps ?? []; + + for (const [i, s] of swaps.slice(0, 12).entries()) { + items.push({ + id: `swap-${i}`, + title: `Exchange · ${s.tokenIn?.slice(0, 8) ?? 'token'}`, + meta: `${s.status} · ${s.createdAt?.slice(0, 16) ?? 'recent'}`, + amount: s.amountIn ? formatUsd(s.amountIn) : '—', + positive: s.status?.toLowerCase() === 'completed', + kind: 'swap', + }); + } + + if (connected && items.length === 0) { + items.push({ + id: 'system-live', + title: 'Z Online Bank connected', + meta: live ? 'Fineract ledger live' : 'Settlement middleware online', + amount: formatUsd(primaryBalance), + positive: true, + kind: 'system', + }); + } + + return items; + }, [cb.monitor, connected, live, primaryBalance]); + + return { + ...cb, + office, + healthOk, + live, + connected, + primaryBalance, + savingsBalance, + reserveBalance, + m4Balance, + m3Balance, + activities, + displayName: office?.name?.split('—')[0]?.trim() ?? (import.meta.env.VITE_ECOSYSTEM === 'z' ? 'Z Account' : 'DBIS Account'), + officeLabel: office?.externalId ?? `Office ${office?.officeId ?? 24}`, + }; +} diff --git a/frontend-dapp/src/features/online-bank/useZBankSettlement.ts b/frontend-dapp/src/features/online-bank/useZBankSettlement.ts new file mode 100644 index 0000000..e3f5e45 --- /dev/null +++ b/frontend-dapp/src/features/online-bank/useZBankSettlement.ts @@ -0,0 +1,613 @@ +import { useCallback, useState } from 'react'; +import { exchangeApi, omnlApiHeaders, settlementApi, tokenAggregationApi } from '../../config/dex'; +import { onexShiva, SHIVA_ONEX_CHAIN_ID } from '../../config/onexShiva'; + +export type SettlementPhase = + | 'RECEIVED' + | 'VALIDATED' + | 'VERBIAGE_ROLLED' + | 'FINERACT_POSTED' + | 'HYBX_RAIL_DISPATCHED' + | 'ISO20022_ARCHIVED' + | 'CHAIN_MINT_REQUESTED' + | 'SETTLED' + | 'FAILED'; + +export type FiatLpSwapLeg = { + lpSymbol: string; + tokenIn: string; + tokenOut: string; + realSymbol: string; + amountIn: string; + amountOutQuoted?: string; + txHash?: string; + status: string; + error?: string; +}; + +export type FiatLpPosition = { + symbol: string; + tokenAddress: string; + balanceFormatted: string; + realSymbol: string; + amountOutQuoted?: string; + swappable: boolean; +}; + +export type SettlementResult = { + settlementId?: string; + phase?: SettlementPhase; + errors?: string[]; + fineractJournalRef?: string; + hybxPaymentId?: string; + chainTxHash?: string; + liquiditySwaps?: FiatLpSwapLeg[]; + error?: string; + layerSteps?: { + from: string; + to: string; + debitGl: string; + creditGl: string; + journalRef?: string; + status: 'pending' | 'posted' | 'skipped' | 'failed'; + }[]; +}; + +async function postJson(url: string, body: unknown): Promise<{ ok: boolean; status: number; data: T }> { + const res = await fetch(url, { + method: 'POST', + headers: omnlApiHeaders(), + body: JSON.stringify(body), + }); + const data = (await res.json().catch(() => ({ error: res.statusText || `HTTP ${res.status}` }))) as T; + return { ok: res.ok, status: res.status, data }; +} + +async function getJson(url: string): Promise { + try { + const res = await fetch(url, { headers: omnlApiHeaders(false) }); + return res.ok ? ((await res.json()) as T) : null; + } catch { + return null; + } +} + +function idem(prefix: string) { + return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; +} + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +const FIAT_LP_AUTO_EXECUTE = import.meta.env.VITE_FIAT_LP_AUTO_EXECUTE !== '0'; + +/** Nova Bank Online → Z Online Bank: inbound wire import to M4 fiat */ +export async function novaBankToZBankM4Fiat(params: { + amount: string; + creditorIban: string; + beneficiaryName?: string; + remittanceInfo?: string; +}) { + const base = { + idempotencyKey: idem('NOVA-Z-M4'), + officeId: 24, + currency: 'USD', + amount: params.amount, + creditorIban: params.creditorIban, + beneficiaryName: params.beneficiaryName ?? 'Z Online Bank', + orderingName: 'Nova Bank Online', + creditorBic: 'NOVAGB2LXXX', + debtorIban: 'GB29NOVA60161331926819', + remittanceInfo: params.remittanceInfo ?? 'Nova Bank Online → Z Online Bank · M4 fiat import', + moneyLayers: ['M1', 'M2', 'M4'] as const, + rail: 'SWIFT' as const, + swiftMessageKind: 'MT103' as const, + }; + + return postJson(settlementApi('/external-transfer'), base); +} + +/** OneX Bank (Shiva / zBank office 29) → Z Online Bank: inbound wire to M4 */ +export async function onexBankToZBankM4Fiat(params: { + amount: string; + creditorIban: string; + beneficiaryName?: string; + remittanceInfo?: string; +}) { + const corridor = onexShiva.bankCorridor; + const base = { + idempotencyKey: idem('ONEX-Z-M4'), + officeId: onexShiva.settlement.zBridgeOfficeId, + currency: 'USD', + amount: params.amount, + creditorIban: params.creditorIban, + beneficiaryName: params.beneficiaryName ?? 'Z Online Bank', + orderingName: corridor.orderingName, + creditorBic: corridor.creditorBic, + debtorIban: corridor.debtorIban, + remittanceInfo: params.remittanceInfo ?? corridor.remittanceTemplate, + moneyLayers: ['M1', 'M2', 'M4'] as const, + rail: 'HYBX' as const, + swiftMessageKind: corridor.swiftMessageKind as 'MT103', + metadata: { + sourceOfficeId: onexShiva.fineract.officeId, + sourceHybxWallet: onexShiva.fineract.hybxWalletId, + targetChainId: SHIVA_ONEX_CHAIN_ID, + integration: 'onex-shiva', + }, + }; + + return postJson(settlementApi('/external-transfer'), base); +} + +/** Office 24 → Z Online Bank: credit M1 then settle to M4 fiat rails */ +export async function office24ToZBankM4Fiat(params: { + amount: string; + creditorIban: string; + beneficiaryName?: string; + remittanceInfo?: string; +}) { + const base = { + idempotencyKey: idem('Z-O24-M4'), + officeId: 24, + currency: 'USD', + amount: params.amount, + creditorIban: params.creditorIban, + beneficiaryName: params.beneficiaryName ?? 'Z Online Bank', + orderingName: 'Office 24 Central Bank — HOSPITALLERS', + remittanceInfo: params.remittanceInfo ?? 'Office 24 → Z Online Bank · M4 fiat settlement', + moneyLayers: ['M1', 'M2', 'M4'] as const, + rail: 'SWIFT' as const, + swiftMessageKind: 'MT103' as const, + }; + + return postJson(settlementApi('/external-transfer'), base); +} + +export function useZBankSettlement() { + const [loading, setLoading] = useState(false); + const [statusLabel, setStatusLabel] = useState(null); + const [error, setError] = useState(null); + const [lastResult, setLastResult] = useState(null); + const [settlements, setSettlements] = useState([]); + const [autoLiquidityRan, setAutoLiquidityRan] = useState(false); + + const run = useCallback(async (label: string, fn: () => Promise<{ ok: boolean; status: number; data: SettlementResult }>) => { + setLoading(true); + setStatusLabel(label); + setError(null); + try { + const { ok, data, status } = await fn(); + setLastResult(data); + if (!ok || data.phase === 'FAILED') { + const errBody = data as SettlementResult & { message?: string }; + throw new Error( + errBody.error || errBody.errors?.join(', ') || errBody.message || `${label} failed (HTTP ${status})`, + ); + } + return data; + } catch (e) { + const msg = e instanceof Error ? e.message : `${label} failed`; + setError(msg); + throw e; + } finally { + setLoading(false); + setStatusLabel(null); + } + }, []); + + const transferNovaBankToZBank = useCallback( + (amount: string, creditorIban: string) => + run('Nova Bank Online → Z Online Bank M4 import', async () => { + const r = await novaBankToZBankM4Fiat({ amount, creditorIban }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const transferOffice24ToZBank = useCallback( + (amount: string, creditorIban: string) => + run('Office 24 → Z Online Bank M4 transfer', async () => { + const r = await office24ToZBankM4Fiat({ amount, creditorIban }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const transferOneXBankToZBank = useCallback( + (amount: string, creditorIban: string) => + run('OneX Bank → Z Online Bank M4 import (Shiva)', async () => { + const r = await onexBankToZBankM4Fiat({ amount, creditorIban }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const internalTransfer = useCallback( + (body: { + tokenAddress: string; + tokenSymbol: string; + amount: string; + recipientAddress: string; + senderAddress?: string; + }) => + run('Internal transfer', async () => { + const r = await postJson(exchangeApi('/transfer/internal'), { + idempotencyKey: idem('Z-INT'), + ...body, + moneyLayers: ['M2', 'M3'], + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const externalTransfer = useCallback( + (body: { + tokenAddress: string; + tokenSymbol: string; + amount: string; + recipientAddress: string; + creditorIban: string; + beneficiaryName?: string; + }) => + run('External M4 transfer', async () => { + const r = await postJson(exchangeApi('/transfer/external'), { + idempotencyKey: idem('Z-EXT'), + ...body, + moneyLayers: ['M2', 'M4'], + rail: 'SWIFT', + remittanceInfo: 'Z Online Bank M4 fiat outbound', + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const tokenLoad = useCallback( + (body: { + lineId: string; + symbol: string; + tokenAddress: string; + amount: string; + recipientAddress: string; + }) => + run('M2 token load', async () => { + const r = await postJson(exchangeApi('/token-load'), { + idempotencyKey: idem('Z-LOAD'), + currency: 'USD', + ...body, + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const fiatToCrypto = useCallback( + (body: { + amount: string; + creditorIban: string; + recipientAddress: string; + tokenLineId?: string; + targetChainId?: number; + }) => + run('Fiat to crypto', async () => { + const chainId = body.targetChainId ?? 138; + const r = await postJson(settlementApi('/convert/fiat-to-crypto'), { + idempotencyKey: idem('Z-F2C'), + officeId: 24, + currency: 'USD', + amount: body.amount, + creditorIban: body.creditorIban, + beneficiaryName: 'Z Online Bank', + recipientAddress: body.recipientAddress, + convertToCrypto: { + enabled: true, + targetChainId: chainId, + tokenLineId: body.tokenLineId ?? 'USD-M2', + recipientAddress: body.recipientAddress, + }, + moneyLayers: ['M2', 'M3'], + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const fiatToCryptoShiva = useCallback( + (body: { amount: string; creditorIban: string; recipientAddress: string; tokenLineId?: string }) => + fiatToCrypto({ ...body, targetChainId: SHIVA_ONEX_CHAIN_ID }), + [fiatToCrypto], + ); + + const tradeFinanceTransfer = useCallback( + (body: { + instrument: 'SBLC' | 'DLC' | 'BG' | 'LS'; + amount: string; + creditorIban: string; + reference?: string; + expiryDate?: string; + swiftMessageKind?: 'MT102' | 'MT103'; + }) => + run(`${body.instrument} trade finance`, async () => { + const r = await postJson(settlementApi(`/trade-finance/${body.instrument}`), { + idempotencyKey: idem(`Z-TF-${body.instrument}`), + officeId: 24, + currency: 'USD', + amount: body.amount, + creditorIban: body.creditorIban, + beneficiaryName: 'Z Online Bank', + orderingName: 'Z Online Bank — OMNL Office 24 Central Bank', + remittanceInfo: `Z Online Bank ${body.instrument} · ${body.swiftMessageKind ?? 'MT103'}`, + moneyLayers: ['M0', 'M1', 'M2', 'M3', 'M4'], + rail: 'SWIFT', + swiftMessageKind: body.swiftMessageKind ?? 'MT103', + tradeFinance: { + instrument: body.instrument, + reference: body.reference ?? idem('TF-REF'), + expiryDate: body.expiryDate, + }, + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const swiftMessageTransfer = useCallback( + (body: { + amount: string; + creditorIban: string; + swiftMessageKind: 'MT102' | 'MT103'; + moneyLayers?: string[]; + remittanceInfo?: string; + }) => + run(`SWIFT ${body.swiftMessageKind}`, async () => { + const layers = + body.moneyLayers ?? + (body.swiftMessageKind === 'MT102' ? ['M1', 'M2', 'M3', 'M4'] : ['M0', 'M1', 'M2', 'M4']); + const r = await postJson(settlementApi('/external-transfer'), { + idempotencyKey: idem(`Z-${body.swiftMessageKind}`), + officeId: 24, + currency: 'USD', + amount: body.amount, + creditorIban: body.creditorIban, + beneficiaryName: 'Z Online Bank', + orderingName: 'Z Online Bank — OMNL Office 24 Central Bank', + remittanceInfo: body.remittanceInfo ?? `Z Online Bank SWIFT ${body.swiftMessageKind}`, + moneyLayers: layers, + rail: 'SWIFT', + swiftMessageKind: body.swiftMessageKind, + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const mt103LcTransfer = useCallback( + (amount: string, creditorIban: string, reference?: string) => + tradeFinanceTransfer({ + instrument: 'DLC', + amount, + creditorIban, + reference, + swiftMessageKind: 'MT103', + }), + [tradeFinanceTransfer], + ); + + const fullExternalTransfer = useCallback( + (body: { + amount: string; + creditorIban: string; + beneficiaryName?: string; + moneyLayers?: string[]; + }) => + run('Full external transfer', async () => { + const r = await postJson(settlementApi('/external-transfer'), { + idempotencyKey: idem('Z-EXT-FULL'), + officeId: 24, + currency: 'USD', + amount: body.amount, + creditorIban: body.creditorIban, + beneficiaryName: body.beneficiaryName ?? 'Beneficiary', + moneyLayers: body.moneyLayers ?? ['M0', 'M1', 'M2', 'M3', 'M4'], + rail: 'SWIFT', + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + const refreshSettlements = useCallback(async () => { + const list = await getJson<{ settlements?: SettlementResult[]; items?: SettlementResult[] }>( + `${settlementApi('/settlements')}?limit=20`, + ); + const rows = list?.settlements ?? list?.items; + if (rows) setSettlements(rows); + }, []); + + const fetchFiatLpPositions = useCallback(async (holderAddress: string) => { + const q = new URLSearchParams({ holder: holderAddress }); + const data = await getJson<{ positions?: FiatLpPosition[] }>( + `${tokenAggregationApi('/omnl/settlement/fiat-lp-positions')}?${q}`, + ); + return data?.positions ?? []; + }, []); + + const requestLiquidityActivation = useCallback( + async (holderAddress: string, dryRun: boolean) => { + const r = await postJson(settlementApi('/convert/fiat-lp-to-liquidity'), { + idempotencyKey: idem('Z-LP-LIQ'), + officeId: 24, + holderAddress, + recipientAddress: holderAddress, + convertAll: true, + dryRun, + maxSlippageBps: 50, + }); + return r; + }, + [], + ); + + const autoActivateRealLiquidity = useCallback( + async (holderAddress: string): Promise => { + setStatusLabel('Waiting for LP tokens on Chain 138…'); + for (let attempt = 0; attempt < 12; attempt += 1) { + const positions = await fetchFiatLpPositions(holderAddress); + const hasLp = positions.some( + (p) => p.swappable && parseFloat(p.balanceFormatted) > 0, + ); + if (hasLp) break; + await sleep(2000); + } + + setStatusLabel('Activating real liquidity (c* → USDT/USDC/WETH)…'); + let live = FIAT_LP_AUTO_EXECUTE; + let r = await requestLiquidityActivation(holderAddress, !live); + if (!r.ok && live) { + r = await requestLiquidityActivation(holderAddress, true); + live = false; + } + + setAutoLiquidityRan(true); + if (r.data.liquiditySwaps?.length) { + setLastResult((prev) => ({ + ...(prev ?? {}), + ...r.data, + liquiditySwaps: r.data.liquiditySwaps, + })); + } + + if (!r.ok || r.data.phase === 'FAILED') { + const errBody = r.data as SettlementResult & { message?: string }; + setError( + errBody.error || + errBody.errors?.join(', ') || + errBody.message || + 'Automatic liquidity activation failed', + ); + return null; + } + + return r.data; + }, + [fetchFiatLpPositions, requestLiquidityActivation], + ); + + const zBankSettleM0ToM4 = useCallback( + (body: { + amount: string; + creditorIban: string; + recipientAddress?: string; + tokenLineId?: string; + autoActivateLiquidity?: boolean; + }) => + run('Z Online Bank M0→M4 settlement', async () => { + const payload = { + idempotencyKey: idem('Z-M0M4'), + officeId: 24, + currency: 'USD', + amount: body.amount, + creditorIban: body.creditorIban, + beneficiaryName: 'Z Online Bank', + remittanceInfo: 'Z Online Bank full-stack settlement M0→M1→M2→M3→M4', + recipientAddress: body.recipientAddress, + tokenLineId: body.tokenLineId ?? 'USD-M2', + }; + let r = await postJson(settlementApi('/z-bank/settle-m0-m4'), payload); + if (r.status === 404 || r.status === 405) { + r = await postJson(settlementApi('/external-transfer'), { + ...payload, + moneyLayers: ['M0', 'M1', 'M2', 'M3', 'M4'], + rail: 'SWIFT', + swiftMessageKind: 'MT103', + convertToCrypto: body.recipientAddress + ? { + enabled: true, + targetChainId: 138, + tokenLineId: body.tokenLineId ?? 'USD-M2', + recipientAddress: body.recipientAddress, + } + : undefined, + }); + } + + let data = r.data; + const autoOn = body.autoActivateLiquidity !== false; + if ( + autoOn && + body.recipientAddress?.startsWith('0x') && + r.ok && + data.phase !== 'FAILED' + ) { + setLoading(true); + const liq = await autoActivateRealLiquidity(body.recipientAddress); + if (liq?.liquiditySwaps) { + data = { ...data, liquiditySwaps: liq.liquiditySwaps }; + } + setLoading(false); + setStatusLabel(null); + } + + return { ok: r.ok, status: r.status, data }; + }), + [run, autoActivateRealLiquidity], + ); + + const activateRealLiquidity = useCallback( + (body: { + holderAddress: string; + recipientAddress?: string; + convertAll?: boolean; + symbol?: string; + amount?: string; + dryRun?: boolean; + }) => + run('Activate real liquidity', async () => { + const r = await postJson(settlementApi('/convert/fiat-lp-to-liquidity'), { + idempotencyKey: idem('Z-LP-LIQ'), + officeId: 24, + holderAddress: body.holderAddress, + recipientAddress: body.recipientAddress ?? body.holderAddress, + convertAll: body.convertAll ?? true, + symbol: body.symbol, + amount: body.amount, + dryRun: body.dryRun ?? true, + maxSlippageBps: 50, + }); + return { ok: r.ok, status: r.status, data: r.data }; + }), + [run], + ); + + return { + loading, + statusLabel, + error, + lastResult, + settlements, + autoLiquidityRan, + transferNovaBankToZBank, + transferOffice24ToZBank, + transferOneXBankToZBank, + internalTransfer, + externalTransfer, + tokenLoad, + fiatToCrypto, + fiatToCryptoShiva, + swiftMessageTransfer, + tradeFinanceTransfer, + mt103LcTransfer, + zBankSettleM0ToM4, + fullExternalTransfer, + refreshSettlements, + fetchFiatLpPositions, + activateRealLiquidity, + autoActivateRealLiquidity, + clearError: () => setError(null), + }; +} + +export type ZBankSettlementApi = ReturnType; diff --git a/frontend-dapp/src/features/swap/DBISSwapPanel.tsx b/frontend-dapp/src/features/swap/DBISSwapPanel.tsx index aa4a1fe..5cbdee9 100644 --- a/frontend-dapp/src/features/swap/DBISSwapPanel.tsx +++ b/frontend-dapp/src/features/swap/DBISSwapPanel.tsx @@ -42,12 +42,12 @@ function SidePanel({ children, }: { title: string; - icon: 'ledger' | 'rocket' | 'rail' | 'chart' | 'satellite'; + icon: 'ledger' | 'rocket' | 'rail' | 'chart'; children: ReactNode; }) { return (
-

+

{title}

@@ -75,13 +75,12 @@ export default function DBISSwapPanel() { : '—'; return ( -
+
Chain 138} + badges={Chain 138} />
@@ -116,16 +115,16 @@ export default function DBISSwapPanel() { /> -
- Slippage +
+ Slippage {[25, 50, 100].map((bps) => ( ); } @@ -44,7 +44,7 @@ function OrderBookMock({ price }: { price: string }) { }); return (
-
+
Price Amount
@@ -115,11 +115,37 @@ export default function BinanceTradePanel() { : '—'; return ( -
- {/* Markets sidebar */} -