-
+
VERIFIED RENTAL
{machine.metadata?.name}
diff --git a/apps/web/src/wallet/config.ts b/apps/web/src/wallet/config.ts
index 71390c6..52611f4 100644
--- a/apps/web/src/wallet/config.ts
+++ b/apps/web/src/wallet/config.ts
@@ -4,6 +4,7 @@ import { createConfig, http } from 'wagmi';
import { sepolia } from 'wagmi/chains';
import { coinbaseWallet, injected } from 'wagmi/connectors';
import { defineChain } from 'viem';
+import { liveTestnetConfig } from '../live-config.js';
export const creditcoinTestnet = defineChain({
id: 102031,
@@ -13,7 +14,7 @@ export const creditcoinTestnet = defineChain({
default: {
http: [
import.meta.env.VITE_CREDITCOIN_RPC_URL?.trim() ||
- 'https://rpc.cc3-testnet.creditcoin.network',
+ liveTestnetConfig.creditcoinRpcUrl,
],
},
},
@@ -40,10 +41,12 @@ const connectors = [
] as const;
const transports = {
[sepolia.id]: http(
- import.meta.env.VITE_ETHEREUM_SEPOLIA_RPC_URL?.trim() || undefined,
+ import.meta.env.VITE_ETHEREUM_SEPOLIA_RPC_URL?.trim() ||
+ liveTestnetConfig.sepoliaRpcUrl,
),
[creditcoinTestnet.id]: http(
- import.meta.env.VITE_CREDITCOIN_RPC_URL?.trim() || undefined,
+ import.meta.env.VITE_CREDITCOIN_RPC_URL?.trim() ||
+ liveTestnetConfig.creditcoinRpcUrl,
),
};
diff --git a/apps/web/tsconfig.test.json b/apps/web/tsconfig.test.json
index c0fd39a..08f568c 100644
--- a/apps/web/tsconfig.test.json
+++ b/apps/web/tsconfig.test.json
@@ -21,6 +21,8 @@
"src/rental.ts",
"src/rental.test.ts",
"src/contracts.ts",
+ "src/live-config.ts",
+ "src/live-config.test.ts",
"src/vite-env.d.ts",
"src/activity.ts",
"src/activity.test.ts",
diff --git a/apps/worker/package.json b/apps/worker/package.json
index 76b7509..37125d7 100644
--- a/apps/worker/package.json
+++ b/apps/worker/package.json
@@ -8,6 +8,7 @@
"live:mvp": "npm run build && node dist/live-mvp.js",
"live:verify": "npm run build && node dist/verify-live.js",
"relay": "npm run build && node dist/cli.js",
+ "seed:marketplace": "npm run build && node dist/seed-marketplace.js",
"serve": "npm run build && node dist/server.js",
"test": "npm run build && node --test dist/evidence.test.js dist/executor.test.js dist/http.test.js dist/queue.test.js dist/relay.test.js dist/store.test.js dist/usage-receipt.test.js",
"typecheck": "tsc -p tsconfig.json --noEmit"
diff --git a/apps/worker/src/seed-marketplace.ts b/apps/worker/src/seed-marketplace.ts
new file mode 100644
index 0000000..daa4cb4
--- /dev/null
+++ b/apps/worker/src/seed-marketplace.ts
@@ -0,0 +1,337 @@
+import { existsSync } from 'node:fs';
+import { readFile, writeFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import {
+ Contract,
+ JsonRpcProvider,
+ Wallet,
+ getAddress,
+ keccak256,
+ toUtf8Bytes,
+ type ContractTransactionResponse,
+} from 'ethers';
+
+const SEPOLIA_CHAIN_ID = 11_155_111n;
+const CREDITCOIN_CHAIN_ID = 102_031n;
+const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
+
+interface CatalogMachine {
+ label: string;
+ tariff: string;
+ uri: string;
+ name: string;
+ operator: { wallet: string };
+}
+
+interface DeploymentRecord {
+ address: string;
+ blockNumber: number;
+}
+
+interface SepoliaDeployment {
+ chainId: number;
+ usagePaymentRegistry: DeploymentRecord;
+}
+
+interface CreditcoinDeployment {
+ chainId: number;
+ machineRegistry: DeploymentRecord;
+}
+
+const machineRegistryAbi = [
+ 'function machines(bytes32) view returns (address owner,address controller,bytes32 metadataHash,uint128 tariff,bool active)',
+ 'function registerMachine(bytes32,address,bytes32,uint128,bool)',
+ 'function updateController(bytes32,address)',
+ 'function updateMetadataHash(bytes32,bytes32)',
+ 'function updateTariff(bytes32,uint128)',
+ 'function setMachineActive(bytes32,bool)',
+] as const;
+const paymentRegistryAbi = [
+ 'function owner() view returns (address)',
+ 'function machineOffers(bytes32) view returns (address beneficiary,uint128 pricePerSecond,bool active)',
+ 'function setMachineOffer(bytes32,address,uint128,bool)',
+] as const;
+
+async function main() {
+ const root = resolve(import.meta.dirname, '../../..');
+ const output = resolve(
+ root,
+ 'packages/contracts/deployments/machine-catalog-live.json',
+ );
+ const environmentPath = resolve(root, '.env');
+ if (existsSync(environmentPath)) process.loadEnvFile(environmentPath);
+
+ const [catalog, sepoliaDeployment, creditcoinDeployment] = await Promise.all([
+ readJson(
+ resolve(root, 'apps/web/src/machine-catalog.json'),
+ ),
+ readJson(
+ resolve(root, 'packages/contracts/deployments/sepolia.json'),
+ ),
+ readJson(
+ resolve(root, 'packages/contracts/deployments/cc3-testnet.json'),
+ ),
+ ]);
+ assertCatalog(catalog);
+ const previous = existsSync(output)
+ ? await readJson<{
+ machines?: Array<{
+ machineId: string;
+ updates?: Array>;
+ }>;
+ }>(output)
+ : undefined;
+ if (BigInt(sepoliaDeployment.chainId) !== SEPOLIA_CHAIN_ID)
+ throw new Error('Sepolia deployment manifest has the wrong chain ID.');
+ if (BigInt(creditcoinDeployment.chainId) !== CREDITCOIN_CHAIN_ID)
+ throw new Error('Creditcoin deployment manifest has the wrong chain ID.');
+
+ const privateKey = requiredPrivateKey('DEPLOYER_PRIVATE_KEY');
+ const sepoliaProvider = new JsonRpcProvider(
+ required('ETHEREUM_SEPOLIA_RPC_URL'),
+ Number(SEPOLIA_CHAIN_ID),
+ { staticNetwork: true },
+ );
+ const creditcoinProvider = new JsonRpcProvider(
+ required('CREDITCOIN_TESTNET_RPC_URL'),
+ Number(CREDITCOIN_CHAIN_ID),
+ { staticNetwork: true },
+ );
+ const [sepoliaNetwork, creditcoinNetwork] = await Promise.all([
+ sepoliaProvider.getNetwork(),
+ creditcoinProvider.getNetwork(),
+ ]);
+ if (sepoliaNetwork.chainId !== SEPOLIA_CHAIN_ID)
+ throw new Error(`Sepolia RPC returned chain ${sepoliaNetwork.chainId}.`);
+ if (creditcoinNetwork.chainId !== CREDITCOIN_CHAIN_ID)
+ throw new Error(
+ `Creditcoin RPC returned chain ${creditcoinNetwork.chainId}.`,
+ );
+
+ const sepoliaWallet = new Wallet(privateKey, sepoliaProvider);
+ const creditcoinWallet = new Wallet(privateKey, creditcoinProvider);
+ const owner = getAddress(sepoliaWallet.address);
+ if (owner !== getAddress(creditcoinWallet.address))
+ throw new Error('The configured deployer must control both networks.');
+ const paymentRegistry = new Contract(
+ sepoliaDeployment.usagePaymentRegistry.address,
+ paymentRegistryAbi,
+ sepoliaWallet,
+ );
+ const machineRegistry = new Contract(
+ creditcoinDeployment.machineRegistry.address,
+ machineRegistryAbi,
+ creditcoinWallet,
+ );
+ if (
+ getAddress((await paymentRegistry.getFunction('owner')()) as string) !==
+ owner
+ )
+ throw new Error('DEPLOYER_PRIVATE_KEY is not the Sepolia registry owner.');
+
+ const records = [];
+ for (const machine of catalog) {
+ if (getAddress(machine.operator.wallet) !== owner)
+ throw new Error(`${machine.label} operator does not match the deployer.`);
+ const machineId = keccak256(toUtf8Bytes(machine.label));
+ const metadataHash = keccak256(toUtf8Bytes(machine.uri));
+ const tariff = BigInt(machine.tariff);
+ const updates: Array> = [];
+ const current = await machineRegistry.getFunction('machines')(machineId);
+ if (getAddress(current.owner as string) === ZERO_ADDRESS) {
+ updates.push(
+ await submit(
+ 'creditcoin:register',
+ machine.label,
+ machineRegistry.getFunction('registerMachine')(
+ machineId,
+ owner,
+ metadataHash,
+ tariff,
+ true,
+ ),
+ ),
+ );
+ } else {
+ if (getAddress(current.owner as string) !== owner)
+ throw new Error(`${machine.label} is owned by another account.`);
+ if (getAddress(current.controller as string) !== owner)
+ updates.push(
+ await submit(
+ 'creditcoin:controller',
+ machine.label,
+ machineRegistry.getFunction('updateController')(machineId, owner),
+ ),
+ );
+ if ((current.metadataHash as string).toLowerCase() !== metadataHash)
+ updates.push(
+ await submit(
+ 'creditcoin:metadata',
+ machine.label,
+ machineRegistry.getFunction('updateMetadataHash')(
+ machineId,
+ metadataHash,
+ ),
+ ),
+ );
+ if ((current.tariff as bigint) !== tariff)
+ updates.push(
+ await submit(
+ 'creditcoin:tariff',
+ machine.label,
+ machineRegistry.getFunction('updateTariff')(machineId, tariff),
+ ),
+ );
+ if (!(current.active as boolean))
+ updates.push(
+ await submit(
+ 'creditcoin:activate',
+ machine.label,
+ machineRegistry.getFunction('setMachineActive')(machineId, true),
+ ),
+ );
+ }
+
+ const offer = await paymentRegistry.getFunction('machineOffers')(machineId);
+ if (
+ getAddress(offer.beneficiary as string) !== owner ||
+ (offer.pricePerSecond as bigint) !== tariff ||
+ !(offer.active as boolean)
+ )
+ updates.push(
+ await submit(
+ 'sepolia:offer',
+ machine.label,
+ paymentRegistry.getFunction('setMachineOffer')(
+ machineId,
+ owner,
+ tariff,
+ true,
+ ),
+ ),
+ );
+
+ const [verifiedMachine, verifiedOffer] = await Promise.all([
+ machineRegistry.getFunction('machines')(machineId),
+ paymentRegistry.getFunction('machineOffers')(machineId),
+ ]);
+ if (
+ getAddress(verifiedMachine.owner as string) !== owner ||
+ getAddress(verifiedMachine.controller as string) !== owner ||
+ (verifiedMachine.metadataHash as string).toLowerCase() !== metadataHash ||
+ (verifiedMachine.tariff as bigint) !== tariff ||
+ !(verifiedMachine.active as boolean) ||
+ getAddress(verifiedOffer.beneficiary as string) !== owner ||
+ (verifiedOffer.pricePerSecond as bigint) !== tariff ||
+ !(verifiedOffer.active as boolean)
+ )
+ throw new Error(`${machine.label} failed post-transaction verification.`);
+ records.push({
+ label: machine.label,
+ name: machine.name,
+ machineId,
+ metadataUri: machine.uri,
+ metadataHash,
+ tariff: machine.tariff,
+ owner,
+ active: true,
+ updates: [
+ ...(previous?.machines?.find(
+ ({ machineId: previousId }) =>
+ previousId.toLowerCase() === machineId.toLowerCase(),
+ )?.updates ?? []),
+ ...updates,
+ ],
+ });
+ console.log(
+ `${machine.name}: synchronized${updates.length ? ` (${updates.length} update${updates.length === 1 ? '' : 's'})` : ' (no changes)'}`,
+ );
+ }
+
+ await writeFile(
+ output,
+ `${JSON.stringify(
+ {
+ provenance: {
+ kind: 'live-testnet-catalog',
+ note: 'Public machine identity and synchronization evidence only; no secret material.',
+ },
+ verifiedAt: new Date().toISOString(),
+ owner,
+ networks: {
+ creditcoin: Number(CREDITCOIN_CHAIN_ID),
+ sepolia: Number(SEPOLIA_CHAIN_ID),
+ },
+ machines: records,
+ },
+ null,
+ 2,
+ )}\n`,
+ 'utf8',
+ );
+ console.log(`Verified ${records.length} synchronized machine listings.`);
+}
+
+async function submit(
+ action: string,
+ label: string,
+ pending: Promise,
+) {
+ const transaction = await pending;
+ console.log(`${action} ${label}: ${transaction.hash}`);
+ const receipt = await transaction.wait();
+ if (!receipt || receipt.status !== 1)
+ throw new Error(`${action} failed for ${label}.`);
+ return {
+ action,
+ transactionHash: transaction.hash,
+ blockNumber: receipt.blockNumber,
+ };
+}
+
+async function readJson(path: string): Promise {
+ return JSON.parse(await readFile(path, 'utf8')) as T;
+}
+
+function assertCatalog(value: CatalogMachine[]) {
+ if (!Array.isArray(value) || value.length < 5)
+ throw new Error('Machine catalog must contain at least five entries.');
+ const labels = new Set();
+ for (const machine of value) {
+ if (
+ !machine ||
+ typeof machine.label !== 'string' ||
+ typeof machine.name !== 'string' ||
+ typeof machine.uri !== 'string' ||
+ !machine.uri.startsWith('ipfs://proofkey/') ||
+ !/^\d+$/.test(machine.tariff) ||
+ BigInt(machine.tariff) <= 0n ||
+ labels.has(machine.label)
+ )
+ throw new Error(
+ 'Machine catalog contains an invalid or duplicate entry.',
+ );
+ labels.add(machine.label);
+ }
+}
+
+function required(name: string) {
+ const value = process.env[name]?.trim();
+ if (!value) throw new Error(`Missing required environment variable ${name}.`);
+ return value;
+}
+
+function requiredPrivateKey(name: string) {
+ const configured = required(name);
+ const value = configured.startsWith('0x') ? configured : `0x${configured}`;
+ if (!/^0x[0-9a-fA-F]{64}$/.test(value))
+ throw new Error(`${name} must be a 32-byte hexadecimal value.`);
+ return value;
+}
+
+main().catch((error: unknown) => {
+ console.error(
+ error instanceof Error ? error.message : 'Catalog seed failed.',
+ );
+ process.exitCode = 1;
+});
diff --git a/package.json b/package.json
index 123cd2a..3c19b2a 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"format": "prettier --write .",
"live:mvp": "npm run live:mvp --workspace @proofkey/worker",
"live:verify": "npm run live:verify --workspace @proofkey/worker",
+ "seed:marketplace": "npm run seed:marketplace --workspace @proofkey/worker",
"lint": "prettier --check .",
"test": "npm run test --workspaces --if-present",
"test:contracts": "npm run test --workspace @proofkey/contracts",
diff --git a/packages/contracts/deployments/README.md b/packages/contracts/deployments/README.md
index 6a37319..d08db71 100644
--- a/packages/contracts/deployments/README.md
+++ b/packages/contracts/deployments/README.md
@@ -7,3 +7,5 @@
Review the output before committing a deployment record. Never commit deployment keys or RPC credentials.
After both deployments exist, `npm run live:mvp --workspace @proofkey/worker` performs the real cross-chain demo and writes `live-mvp.json`. That evidence file and `../fixtures/recorded-live-proof.json` are emitted only after Creditcoin reports the payer as authorized. Both are public, secret-scanned records and are explicitly labeled as historical recorded-live evidence.
+
+`npm run seed:marketplace` idempotently synchronizes the committed five-machine catalog across the CC3 `MachineRegistry` and Sepolia `UsagePaymentRegistry`, verifies the resulting state, and writes `machine-catalog-live.json`. The file contains public transaction evidence only and does not certify the physical existence or condition of a represented machine.
diff --git a/packages/contracts/deployments/machine-catalog-live.json b/packages/contracts/deployments/machine-catalog-live.json
new file mode 100644
index 0000000..7b690b1
--- /dev/null
+++ b/packages/contracts/deployments/machine-catalog-live.json
@@ -0,0 +1,113 @@
+{
+ "provenance": {
+ "kind": "live-testnet-catalog",
+ "note": "Public machine identity and synchronization evidence only; no secret material."
+ },
+ "verifiedAt": "2026-09-13T20:13:22.429Z",
+ "owner": "0x1114eeaFEB92B71bABf860E64e4575433a734B6A",
+ "networks": {
+ "creditcoin": 102031,
+ "sepolia": 11155111
+ },
+ "machines": [
+ {
+ "label": "proofkey.excavator.001",
+ "name": "Industrial Excavator",
+ "machineId": "0xc04beae61beb9471c4f24c8788a4624988d2948a5c3d3dd0b6ba1b7602875bcc",
+ "metadataUri": "ipfs://proofkey/excavator/001",
+ "metadataHash": "0x24f58d3fcaa80aa0cbe4c88b0ce7d4a23312fa95ca201300a7313894970e883e",
+ "tariff": "2500",
+ "owner": "0x1114eeaFEB92B71bABf860E64e4575433a734B6A",
+ "active": true,
+ "updates": []
+ },
+ {
+ "label": "proofkey.tractor.001",
+ "name": "Autonomous Field Tractor",
+ "machineId": "0x4d673d80ca7d15bb870d8df7397e3faac88f60b2a918beb5c215e5cc1a8aff0c",
+ "metadataUri": "ipfs://proofkey/tractor/001",
+ "metadataHash": "0xff85fc745528ac9c10083098d766792ced398c012a4892b86ccbf00e79eccd3a",
+ "tariff": "1800",
+ "owner": "0x1114eeaFEB92B71bABf860E64e4575433a734B6A",
+ "active": true,
+ "updates": [
+ {
+ "action": "creditcoin:register",
+ "transactionHash": "0x140a6598169755a984a85c0885f39dc26a3878b59bea78f6a2400cc59eb0799f",
+ "blockNumber": 5482431
+ },
+ {
+ "action": "sepolia:offer",
+ "transactionHash": "0x29e31c180c2fde8a959f7ff926d49ca5b143528c3074e7ee456b57c3219e8265",
+ "blockNumber": 11697952
+ }
+ ]
+ },
+ {
+ "label": "proofkey.energy.001",
+ "name": "Mobile Solar Power Unit",
+ "machineId": "0x8dbcb34d2311b1fd5d86e56650dc2c71daa7f194d3bb1e8f798e108cbef2611f",
+ "metadataUri": "ipfs://proofkey/energy/001",
+ "metadataHash": "0xea6041cc1b15985e03bb886760429c739a0a62867e33d72f7b05fe3a0fa6f9a6",
+ "tariff": "3200",
+ "owner": "0x1114eeaFEB92B71bABf860E64e4575433a734B6A",
+ "active": true,
+ "updates": [
+ {
+ "action": "creditcoin:register",
+ "transactionHash": "0x1fa7f6acfb9e0d815870076163f42ed70bcd7008eadee3fa7157b385c713e002",
+ "blockNumber": 5482433
+ },
+ {
+ "action": "sepolia:offer",
+ "transactionHash": "0x7404ffb1653587505f7c0b5fd173a58ef84fb26e8b4ebe753303bee430e45963",
+ "blockNumber": 11697955
+ }
+ ]
+ },
+ {
+ "label": "proofkey.coldchain.001",
+ "name": "Refrigerated Cargo Pod",
+ "machineId": "0xdc40bf68b56301609cebb894644c36d415df694098cde5e435f00398df4af53b",
+ "metadataUri": "ipfs://proofkey/coldchain/001",
+ "metadataHash": "0xfec650583b0f2a52a4b2fb82b684f48aa34231b55ce2db4c607c81cbd3c69af7",
+ "tariff": "1400",
+ "owner": "0x1114eeaFEB92B71bABf860E64e4575433a734B6A",
+ "active": true,
+ "updates": [
+ {
+ "action": "creditcoin:register",
+ "transactionHash": "0x16c0f1bef2eb9ade0977358b3f25b790f497a06485d7b290e7191a1cbbeb9fba",
+ "blockNumber": 5482435
+ },
+ {
+ "action": "sepolia:offer",
+ "transactionHash": "0xeee447b5c47d08ea0efd049e39b76741c823c59eac905c0129e88c74e0033ab1",
+ "blockNumber": 11697957
+ }
+ ]
+ },
+ {
+ "label": "proofkey.cnc.001",
+ "name": "Precision CNC Milling Cell",
+ "machineId": "0xb3c404059df1545ade309532a431b2d826296994c1670f9a14a0c5959d271a25",
+ "metadataUri": "ipfs://proofkey/cnc/001",
+ "metadataHash": "0xf11e2437777ff8a23462d1b4a170d8c0bf57e98163952c3d3607f3e8bd1e304c",
+ "tariff": "4100",
+ "owner": "0x1114eeaFEB92B71bABf860E64e4575433a734B6A",
+ "active": true,
+ "updates": [
+ {
+ "action": "creditcoin:register",
+ "transactionHash": "0x52627fde773a3b472e6562a8f14f1665f6a3f4b429b92eb5da8810193ddf09ba",
+ "blockNumber": 5482437
+ },
+ {
+ "action": "sepolia:offer",
+ "transactionHash": "0xd22b9e29b54ac4c7f87e21091e84f9d931f4e5ff2109f3d23845d3d13b35ec01",
+ "blockNumber": 11697960
+ }
+ ]
+ }
+ ]
+}
diff --git a/scripts/audit-web-build.mjs b/scripts/audit-web-build.mjs
index fac438a..7723491 100644
--- a/scripts/audit-web-build.mjs
+++ b/scripts/audit-web-build.mjs
@@ -1,6 +1,7 @@
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
import { gzipSync } from 'node:zlib';
import { extname, join, relative } from 'node:path';
+import { liveTestnetConfig } from '../apps/web/src/live-config.ts';
const root = join(import.meta.dirname, '..');
const output = join(root, 'apps', 'web', 'dist');
@@ -45,18 +46,20 @@ for (const check of forbidden)
if (check.pattern.test(combined))
failures.push(`Client bundle contains a forbidden ${check.label}.`);
-for (const name of [
- 'VITE_ETHEREUM_SEPOLIA_RPC_URL',
- 'VITE_CREDITCOIN_RPC_URL',
- 'VITE_PROOF_WORKER_URL',
- 'VITE_USAGE_PAYMENT_REGISTRY_ADDRESS',
- 'VITE_MACHINE_REGISTRY_ADDRESS',
- 'VITE_ACCESS_PASS_ADDRESS',
- 'VITE_PROOFKEY_ASC_ADDRESS',
- 'VITE_DEMO_MACHINE_ID',
-]) {
- const value = process.env[name];
- if (!value || !combined.toLowerCase().includes(value.toLowerCase()))
+const publicDefaults = {
+ VITE_ETHEREUM_SEPOLIA_RPC_URL: liveTestnetConfig.sepoliaRpcUrl,
+ VITE_CREDITCOIN_RPC_URL: liveTestnetConfig.creditcoinRpcUrl,
+ VITE_PROOF_WORKER_URL: liveTestnetConfig.workerUrl,
+ VITE_USAGE_PAYMENT_REGISTRY_ADDRESS:
+ liveTestnetConfig.usagePaymentRegistryAddress,
+ VITE_MACHINE_REGISTRY_ADDRESS: liveTestnetConfig.machineRegistryAddress,
+ VITE_ACCESS_PASS_ADDRESS: liveTestnetConfig.accessPassAddress,
+ VITE_PROOFKEY_ASC_ADDRESS: liveTestnetConfig.proofKeyAscAddress,
+ VITE_DEMO_MACHINE_ID: liveTestnetConfig.machineId,
+};
+for (const [name, fallback] of Object.entries(publicDefaults)) {
+ const value = process.env[name]?.trim() || fallback;
+ if (!combined.toLowerCase().includes(value.toLowerCase()))
failures.push(`${name} was not emitted into the production bundle.`);
}
diff --git a/scripts/validate-production-config.mjs b/scripts/validate-production-config.mjs
index 33bb9eb..d0c7e96 100644
--- a/scripts/validate-production-config.mjs
+++ b/scripts/validate-production-config.mjs
@@ -1,3 +1,5 @@
+import { liveTestnetConfig } from '../apps/web/src/live-config.ts';
+
const requiredUrls = [
'VITE_ETHEREUM_SEPOLIA_RPC_URL',
'VITE_CREDITCOIN_RPC_URL',
@@ -10,9 +12,25 @@ const requiredAddresses = [
'VITE_PROOFKEY_ASC_ADDRESS',
];
+const defaults = {
+ VITE_ETHEREUM_SEPOLIA_RPC_URL: liveTestnetConfig.sepoliaRpcUrl,
+ VITE_CREDITCOIN_RPC_URL: liveTestnetConfig.creditcoinRpcUrl,
+ VITE_PROOF_WORKER_URL: liveTestnetConfig.workerUrl,
+ VITE_USAGE_PAYMENT_REGISTRY_ADDRESS:
+ liveTestnetConfig.usagePaymentRegistryAddress,
+ VITE_MACHINE_REGISTRY_ADDRESS: liveTestnetConfig.machineRegistryAddress,
+ VITE_ACCESS_PASS_ADDRESS: liveTestnetConfig.accessPassAddress,
+ VITE_PROOFKEY_ASC_ADDRESS: liveTestnetConfig.proofKeyAscAddress,
+ VITE_DEMO_MACHINE_ID: liveTestnetConfig.machineId,
+};
+
+function configuredValue(name) {
+ return process.env[name]?.trim() || defaults[name];
+}
+
const failures = [];
for (const name of requiredUrls) {
- const value = process.env[name]?.trim();
+ const value = configuredValue(name);
try {
const url = new URL(value);
if (url.protocol !== 'https:') failures.push(`${name} must use HTTPS.`);
@@ -25,10 +43,10 @@ for (const name of requiredUrls) {
}
}
for (const name of requiredAddresses) {
- if (!/^0x[0-9a-fA-F]{40}$/.test(process.env[name]?.trim() ?? ''))
+ if (!/^0x[0-9a-fA-F]{40}$/.test(configuredValue(name)))
failures.push(`${name} must be a 20-byte address.`);
}
-if (!/^0x[0-9a-fA-F]{64}$/.test(process.env.VITE_DEMO_MACHINE_ID?.trim() ?? ''))
+if (!/^0x[0-9a-fA-F]{64}$/.test(configuredValue('VITE_DEMO_MACHINE_ID')))
failures.push('VITE_DEMO_MACHINE_ID must be a 32-byte machine ID.');
if (failures.length) {