PR E: SWIFT gateway (MT760, pacs.009, MT202, camt.025/054) #9
Reference in New Issue
Block a user
Delete Branch "devin/1776875929-swift-gateway"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements step 6 from the architecture gap-analysis — a SWIFT gateway module covering the issuance and payment legs plus the ingest path for bank confirmations. Also fixes the pacs.008 → pacs.009 mis-routing flagged by the gap-analysis for the interbank leg. Stacks on PR D.
What lands (all under
src/services/swift/)Outbound generators
mt760.tsInstrumentTerms(12 tags, URDG 758 / UCP 600 aware).messageHash()is deterministic so the on-chain plan anchor can be reproduced from the terms.pacs009.tspacs.009.001.08)mt202.tsInbound parsers
camt.ts#parseCamt025— Receipt / status of a prior instruction.camt.ts#parseCamt054— Bank-to-customer credit/debit notification.camt.ts#reconcileCamt054— diffs(amount, currency, creditDebitIndicator, endToEndId)and returns the exact mismatches soVALIDATINGcan feed them intoData.valueMismatch()from PR B.camt.ts#parseCamt— dispatcher on xmlns marker.Channel policy (arch §9.2 — accepted ≠ settled)
Documented in
swift/index.ts:pacs.008stays on the customer-initiated PSP channel (services/iso20022.ts). Acceptance alone does not allow COMMIT.pacs.009/MT202is the interbank channel. COMMIT requires eithercamt.025 ACSCorcamt.054 CRDTevidence.Tests
tests/unit/swift.test.ts— 14 cases: MT760 tag layout, MT760 rejects malformed date + negative amount,messageHashdeterminism, pacs.009 XML + BIC validation + pay-step requirement, MT202 tag layout, camt.025 + camt.054 happy path, xmlns dispatcher, unknown-xmlns rejection,reconcileCamt054both match-and-mismatch cases.Verification
Not in this PR
issueInstrumentstep in PR A still executes a mocked dispatch; wiring it togenerateMt760(terms)+eventBus.publish('instrument.dispatched', …)is a one-file change that lands in the next coordinator-focused PR so this PR stays reviewable.Series order
A → B → C → D → E → F → G → H.
Base:
devin/1776875718-event-bus-sse(PR D). The diff here is E-only.- services/notaryChain.ts: new ethers-v6 adapter speaking to the deployed NotaryRegistry.sol via CHAIN_138_RPC_URL + NOTARY_REGISTRY_ADDRESS + ORCHESTRATOR_PRIVATE_KEY. Exposes anchorPlan(plan) -> { mode, txHash, planHash, blockNumber } and finalizeAnchor(planId, success) -> { mode, txHash, receiptHash } with deterministic mock fallback when envs are absent. - services/notary.ts: refactored to delegate to notaryChain; preserves the prior signature and returns extra on-chain fields (mode, txHash, blockNumber, contractAddress) when the anchor lands. - config/env.ts: add CHAIN_138_RPC_URL, CHAIN_138_CHAIN_ID, NOTARY_REGISTRY_ADDRESS, ORCHESTRATOR_PRIVATE_KEY (all optional, validated via regex where applicable). - package.json: add ethers@^6.11.0 dependency. - tests/unit/notaryChain.test.ts: 6 tests covering deterministic hashing helpers and the mock fallback path. tsc clean. 51 tests pass (45 pre-existing + 6 new).- db/migrations/003_events.ts: append-only events table with payload_hash, prev_hash, HMAC signature, indexed by plan_id + type - services/eventBus.ts: EVENT_TYPES union (all 15 arch §7.2 categories), publish() with hash-chain + HMAC signing, verifyChain() for tamper detection, subscribe() via in-process EventEmitter - api/plans.ts: - GET /api/plans/:planId/events (?verify=1 returns chain_valid) - GET /api/plans/:planId/events/stream (SSE with history replay + live push, 15s keep-alive, clean unsubscribe on client disconnect) - index.ts: register the two new endpoints - tests/unit/eventBus.test.ts: 9 tests covering publish, hash chain, per-plan isolation, and three tamper-detection scenarios (payload, signature, prev_hash) 60 tests pass. tsc clean.8dcdb4531cto632f309ffc