Skip to content
50 changes: 15 additions & 35 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,49 +131,15 @@ jobs:
# bundle workflow can analyse it without re-building.
# ───────────────────────────────────────────────────────────────────────────
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: ./.github/actions/setup

# Restore the Next.js incremental build cache. Key includes a hash of
# package-lock.json so the cache is invalidated when deps change.
- name: Restore Next.js cache
uses: actions/cache@v4
with:
path: .next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-${{ hashFiles('src/**/*.ts', 'src/**/*.tsx') }}
restore-keys: |
nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-
nextjs-${{ runner.os }}-

- name: Build
run: npm run build

# Upload the production build so the bundle job can measure it without
# running next build a second time.
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: next-build-${{ github.sha }}
path: |
.next/
!.next/cache
retention-days: 1

e2e:
name: Playwright (desktop, iPhone 13, Pixel 7)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 20
cache: npm
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build
env:
NEXT_TELEMETRY_DISABLED: "1"
Expand Down Expand Up @@ -225,6 +191,20 @@ jobs:
test-results
retention-days: 14

contract-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Verify contract registry against on-chain code hashes
env:
STELLAR_RPC_URL: ${{ secrets.STELLAR_RPC_URL }}
run: npm run verify:contracts

lighthouse:
name: Lighthouse (mobile perf ≥ 0.85, CLS < 0.05)
runs-on: ubuntu-latest
Expand Down
38 changes: 38 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,44 @@ export function contractEnvVarName(key: string): string {
return `NEXT_PUBLIC_CONTRACT_${key.toUpperCase()}_${stellarNetworkSuffix}`;
}

/**
* Verified-build registry entry for a deployed contract (issue #264).
*
* Mirrors the versioned machine-readable registry consumed by the SDK and
* docs. `verified` is false while a freshly deployed revision is pending
* audit sign-off — such entries must be surfaced as unverified, never
* silently trusted.
*/
export interface VerifiedContractEntry {
/** Canonical on-chain contract ID (Strkey `C...`). */
contractId: string;
/** Hex-encoded SHA-256 of the reproducible WASM build. */
wasmHash: string;
/** Audited source revision (git SHA) the WASM was built from. */
auditRevision: string;
/** Whether the audit sign-off has been recorded for this revision. */
verified: boolean;
}

/**
* Registry lookup for the active network's vault contract.
*
* Returns `undefined` when no vault contract is configured. When a contract ID
* is configured but has no matching verified registry entry, the returned
* entry is marked `verified: false` so consumers can visibly flag it.
*/
export function getVaultRegistryEntry(
registry: Partial<Record<"testnet" | "mainnet", VerifiedContractEntry[]>>,
): VerifiedContractEntry | undefined {
const contractId = governableContractIds.vault;
if (!contractId) return undefined;
const network = env.NEXT_PUBLIC_STELLAR_NETWORK === "mainnet" ? "mainnet" : "testnet";
const entries = registry[network] ?? [];
const match = entries.find((entry) => entry.contractId === contractId);
if (match) return match;
return { contractId, wasmHash: "", auditRevision: "", verified: false };
}

/**
* Soroban RPC resilience configuration (issue #282).
*
Expand Down
Loading
Loading