Skip to content

Commit dc830f1

Browse files
devsimzecursoragent
andcommitted
feat: implement issues #473#476 — archival, upgrade safety, bridge monitor, i18n
Add IPFS on-chain archival with daily batches and restore workflow, contract upgrade validation service, cross-chain bridge monitoring with alerts, and next-intl localization across five locales with admin dashboards and CI checks. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f8e6d09 commit dc830f1

99 files changed

Lines changed: 5502 additions & 241 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/contracts-evm.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ jobs:
6060
- name: Compile contracts
6161
run: npx hardhat compile
6262

63+
- name: Validate upgrade safety (OZ dry-run)
64+
run: npx hardhat run scripts/upgrade.ts --network hardhat
65+
env:
66+
PROPOSE_ONLY: 'true'
67+
SPLITTER_CONTRACT: SplitterV2
68+
continue-on-error: true
69+
6370
- name: Type-check scripts & tests
6471
run: npm run lint
6572

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
name: Platform Services (#473–476)
2+
3+
on:
4+
push:
5+
branches: [main, dev]
6+
pull_request:
7+
branches: [main, dev]
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
backend-services:
16+
name: Archival / Upgrade / Bridge tests
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 15
19+
defaults:
20+
run:
21+
working-directory: backend
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- uses: actions/setup-node@v4
26+
with:
27+
node-version: 22
28+
cache: npm
29+
cache-dependency-path: package-lock.json
30+
31+
- name: Install dependencies
32+
run: npm ci --ignore-scripts
33+
working-directory: .
34+
35+
- name: Generate Prisma client
36+
run: npm run db:generate
37+
working-directory: backend
38+
39+
- name: Run service tests (#473–475)
40+
run: npm test -- src/services/__tests__/issues-473-475.test.ts
41+
working-directory: backend
42+
43+
- name: Validate Prisma schema drift
44+
run: npm run db:migrate:check
45+
working-directory: backend
46+
env:
47+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/agenticpay_ci
48+
49+
i18n:
50+
name: Translation parity check
51+
runs-on: ubuntu-latest
52+
timeout-minutes: 10
53+
defaults:
54+
run:
55+
working-directory: frontend
56+
steps:
57+
- uses: actions/checkout@v4
58+
59+
- uses: actions/setup-node@v4
60+
with:
61+
node-version: 22
62+
cache: npm
63+
cache-dependency-path: package-lock.json
64+
65+
- name: Install dependencies
66+
run: npm ci --ignore-scripts
67+
working-directory: .
68+
69+
- name: Check translation key parity
70+
run: npm run i18n:check
71+
working-directory: frontend
72+
73+
i18n-sync:
74+
name: DeepL translation sync (manual)
75+
if: github.event_name == 'workflow_dispatch'
76+
runs-on: ubuntu-latest
77+
timeout-minutes: 10
78+
defaults:
79+
run:
80+
working-directory: frontend
81+
steps:
82+
- uses: actions/checkout@v4
83+
84+
- uses: actions/setup-node@v4
85+
with:
86+
node-version: 22
87+
88+
- name: Install dependencies
89+
run: npm ci --ignore-scripts
90+
working-directory: .
91+
92+
- name: Sync missing translations via DeepL
93+
run: npm run i18n:sync
94+
working-directory: frontend
95+
env:
96+
DEEPL_API_KEY: ${{ secrets.DEEPL_API_KEY }}
97+
98+
- name: Verify parity after sync
99+
run: npm run i18n:check
100+
working-directory: frontend
101+
102+
upgrade-validation:
103+
name: Contract upgrade safety gate
104+
runs-on: ubuntu-latest
105+
timeout-minutes: 15
106+
defaults:
107+
run:
108+
working-directory: contracts/evm
109+
steps:
110+
- uses: actions/checkout@v4
111+
112+
- uses: actions/setup-node@v4
113+
with:
114+
node-version: 22
115+
cache: npm
116+
cache-dependency-path: contracts/evm/package-lock.json
117+
118+
- name: Install EVM contract dependencies
119+
run: npm ci --prefer-offline
120+
121+
- name: Compile contracts
122+
run: npx hardhat compile
123+
124+
- name: Run OZ upgrade validation (dry-run)
125+
run: npx hardhat run scripts/upgrade.ts --network hardhat
126+
env:
127+
PROPOSE_ONLY: 'true'
128+
SPLITTER_CONTRACT: SplitterV2
129+
continue-on-error: true
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
-- Issue #473, #474, #475: Archival, upgrade validation, and bridge monitoring models
2+
3+
-- CreateEnum
4+
CREATE TYPE "ArchivalBatchStatus" AS ENUM ('pending', 'collecting', 'compressing', 'uploading', 'completed', 'failed', 'restoring');
5+
CREATE TYPE "ArchivalChain" AS ENUM ('stellar', 'ethereum', 'polygon', 'base', 'arbitrum', 'soroban');
6+
CREATE TYPE "UpgradeValidationStatus" AS ENUM ('pending', 'running', 'passed', 'failed', 'rolled_back');
7+
CREATE TYPE "ContractPlatform" AS ENUM ('evm', 'soroban');
8+
CREATE TYPE "BridgeProvider" AS ENUM ('wormhole', 'layerzero', 'axelar', 'custom');
9+
CREATE TYPE "BridgeMessageStatus" AS ENUM ('initiated', 'source_confirmed', 'relayed', 'destination_executed', 'failed', 'stuck', 'expired');
10+
CREATE TYPE "BridgeAlertSeverity" AS ENUM ('info', 'warning', 'critical');
11+
12+
-- CreateTable
13+
CREATE TABLE "archival_batches" (
14+
"id" TEXT NOT NULL,
15+
"batch_date" DATE NOT NULL,
16+
"status" "ArchivalBatchStatus" NOT NULL DEFAULT 'pending',
17+
"chain" "ArchivalChain" NOT NULL,
18+
"record_count" INTEGER NOT NULL DEFAULT 0,
19+
"uncompressed_bytes" BIGINT NOT NULL DEFAULT 0,
20+
"compressed_bytes" BIGINT NOT NULL DEFAULT 0,
21+
"content_hash" TEXT,
22+
"ipfs_cid" TEXT,
23+
"ipfs_url" TEXT,
24+
"verified_hash" TEXT,
25+
"error_message" TEXT,
26+
"retention_until" TIMESTAMP(3) NOT NULL,
27+
"started_at" TIMESTAMP(3),
28+
"completed_at" TIMESTAMP(3),
29+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
30+
"updated_at" TIMESTAMP(3) NOT NULL,
31+
32+
CONSTRAINT "archival_batches_pkey" PRIMARY KEY ("id")
33+
);
34+
35+
CREATE TABLE "data_archives" (
36+
"id" TEXT NOT NULL,
37+
"batch_id" TEXT NOT NULL,
38+
"chain" "ArchivalChain" NOT NULL,
39+
"tx_hash" TEXT NOT NULL,
40+
"block_number" BIGINT NOT NULL,
41+
"block_hash" TEXT,
42+
"payload" JSONB NOT NULL,
43+
"payload_hash" TEXT NOT NULL,
44+
"proof_of_inclusion" JSONB NOT NULL,
45+
"indexed_at" TIMESTAMP(3),
46+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
47+
48+
CONSTRAINT "data_archives_pkey" PRIMARY KEY ("id")
49+
);
50+
51+
CREATE TABLE "contract_upgrades" (
52+
"id" TEXT NOT NULL,
53+
"contract_name" TEXT NOT NULL,
54+
"platform" "ContractPlatform" NOT NULL,
55+
"network" TEXT NOT NULL,
56+
"proxy_address" TEXT NOT NULL,
57+
"previous_implementation" TEXT,
58+
"new_implementation" TEXT NOT NULL,
59+
"deployer_address" TEXT,
60+
"timelock_address" TEXT,
61+
"status" "UpgradeValidationStatus" NOT NULL DEFAULT 'pending',
62+
"deployed_at" TIMESTAMP(3),
63+
"rolled_back_at" TIMESTAMP(3),
64+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
65+
"updated_at" TIMESTAMP(3) NOT NULL,
66+
67+
CONSTRAINT "contract_upgrades_pkey" PRIMARY KEY ("id")
68+
);
69+
70+
CREATE TABLE "upgrade_validation_reports" (
71+
"id" TEXT NOT NULL,
72+
"upgrade_id" TEXT NOT NULL,
73+
"status" "UpgradeValidationStatus" NOT NULL,
74+
"storage_layout_diff" JSONB,
75+
"simulation_passed" BOOLEAN NOT NULL DEFAULT false,
76+
"smoke_tests_passed" BOOLEAN NOT NULL DEFAULT false,
77+
"admin_preserved" BOOLEAN NOT NULL DEFAULT false,
78+
"proxy_admin_valid" BOOLEAN NOT NULL DEFAULT false,
79+
"implementation_verified" BOOLEAN NOT NULL DEFAULT false,
80+
"failures" JSONB,
81+
"warnings" JSONB,
82+
"fork_block_number" BIGINT,
83+
"duration_ms" INTEGER,
84+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
85+
86+
CONSTRAINT "upgrade_validation_reports_pkey" PRIMARY KEY ("id")
87+
);
88+
89+
CREATE TABLE "bridge_messages" (
90+
"id" TEXT NOT NULL,
91+
"provider" "BridgeProvider" NOT NULL,
92+
"source_chain" TEXT NOT NULL,
93+
"destination_chain" TEXT NOT NULL,
94+
"message_id" TEXT NOT NULL,
95+
"tx_hash_source" TEXT,
96+
"tx_hash_destination" TEXT,
97+
"status" "BridgeMessageStatus" NOT NULL DEFAULT 'initiated',
98+
"amount" TEXT,
99+
"token_address" TEXT,
100+
"sender" TEXT,
101+
"recipient" TEXT,
102+
"gas_cost_source" TEXT,
103+
"gas_cost_destination" TEXT,
104+
"initiated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
105+
"source_confirmed_at" TIMESTAMP(3),
106+
"relayed_at" TIMESTAMP(3),
107+
"executed_at" TIMESTAMP(3),
108+
"expected_delivery_ms" INTEGER NOT NULL DEFAULT 300000,
109+
"metadata" JSONB,
110+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
111+
"updated_at" TIMESTAMP(3) NOT NULL,
112+
113+
CONSTRAINT "bridge_messages_pkey" PRIMARY KEY ("id")
114+
);
115+
116+
CREATE TABLE "bridge_alerts" (
117+
"id" TEXT NOT NULL,
118+
"message_id" TEXT NOT NULL,
119+
"severity" "BridgeAlertSeverity" NOT NULL,
120+
"alert_type" TEXT NOT NULL,
121+
"message" TEXT NOT NULL,
122+
"acknowledged" BOOLEAN NOT NULL DEFAULT false,
123+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
124+
125+
CONSTRAINT "bridge_alerts_pkey" PRIMARY KEY ("id")
126+
);
127+
128+
CREATE TABLE "bridge_retries" (
129+
"id" TEXT NOT NULL,
130+
"message_id" TEXT NOT NULL,
131+
"attempt" INTEGER NOT NULL DEFAULT 1,
132+
"status" TEXT NOT NULL DEFAULT 'pending',
133+
"tx_hash" TEXT,
134+
"error_message" TEXT,
135+
"initiated_by" TEXT,
136+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
137+
"completed_at" TIMESTAMP(3),
138+
139+
CONSTRAINT "bridge_retries_pkey" PRIMARY KEY ("id")
140+
);
141+
142+
-- CreateIndex
143+
CREATE UNIQUE INDEX "archival_batches_batch_date_chain_key" ON "archival_batches"("batch_date", "chain");
144+
CREATE INDEX "archival_batches_status_idx" ON "archival_batches"("status");
145+
CREATE INDEX "archival_batches_batch_date_idx" ON "archival_batches"("batch_date");
146+
CREATE INDEX "archival_batches_ipfs_cid_idx" ON "archival_batches"("ipfs_cid");
147+
148+
CREATE INDEX "data_archives_batch_id_idx" ON "data_archives"("batch_id");
149+
CREATE INDEX "data_archives_chain_tx_hash_idx" ON "data_archives"("chain", "tx_hash");
150+
CREATE INDEX "data_archives_block_number_idx" ON "data_archives"("block_number");
151+
152+
CREATE INDEX "contract_upgrades_network_contract_name_idx" ON "contract_upgrades"("network", "contract_name");
153+
CREATE INDEX "contract_upgrades_status_idx" ON "contract_upgrades"("status");
154+
155+
CREATE INDEX "upgrade_validation_reports_upgrade_id_idx" ON "upgrade_validation_reports"("upgrade_id");
156+
CREATE INDEX "upgrade_validation_reports_status_idx" ON "upgrade_validation_reports"("status");
157+
158+
CREATE UNIQUE INDEX "bridge_messages_provider_message_id_key" ON "bridge_messages"("provider", "message_id");
159+
CREATE INDEX "bridge_messages_status_idx" ON "bridge_messages"("status");
160+
CREATE INDEX "bridge_messages_provider_idx" ON "bridge_messages"("provider");
161+
CREATE INDEX "bridge_messages_source_chain_destination_chain_idx" ON "bridge_messages"("source_chain", "destination_chain");
162+
CREATE INDEX "bridge_messages_initiated_at_idx" ON "bridge_messages"("initiated_at");
163+
164+
CREATE INDEX "bridge_alerts_message_id_idx" ON "bridge_alerts"("message_id");
165+
CREATE INDEX "bridge_alerts_severity_idx" ON "bridge_alerts"("severity");
166+
167+
CREATE INDEX "bridge_retries_message_id_idx" ON "bridge_retries"("message_id");
168+
169+
-- AddForeignKey
170+
ALTER TABLE "data_archives" ADD CONSTRAINT "data_archives_batch_id_fkey" FOREIGN KEY ("batch_id") REFERENCES "archival_batches"("id") ON DELETE CASCADE ON UPDATE CASCADE;
171+
ALTER TABLE "upgrade_validation_reports" ADD CONSTRAINT "upgrade_validation_reports_upgrade_id_fkey" FOREIGN KEY ("upgrade_id") REFERENCES "contract_upgrades"("id") ON DELETE CASCADE ON UPDATE CASCADE;
172+
ALTER TABLE "bridge_alerts" ADD CONSTRAINT "bridge_alerts_message_id_fkey" FOREIGN KEY ("message_id") REFERENCES "bridge_messages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
173+
ALTER TABLE "bridge_retries" ADD CONSTRAINT "bridge_retries_message_id_fkey" FOREIGN KEY ("message_id") REFERENCES "bridge_messages"("id") ON DELETE CASCADE ON UPDATE CASCADE;

0 commit comments

Comments
 (0)