From 448bade883d4414d9dd993a0c88ff9f5d0709af8 Mon Sep 17 00:00:00 2001 From: bakiberkay Date: Thu, 5 Feb 2026 22:42:55 +0100 Subject: [PATCH 1/3] feat: add linting infastructure and CI/CD pipeline Add Husky for precommit hooks Add Prettier with .prettierrrc config Add Solhint for Solidity linting with relaxed rules for now Update Node.js to 22.12.0 in Dockerfile Add detailed documentation (CI_CD.md) and modify existing ones to reflect the changes (BUILD.md , README.md) Integrate eslint-config-prettier to prevent rule conflicts --- .github/workflows/ci.yml | 99 ++ .husky/pre-commit | 1 + BUILD.md | 48 + CI_CD.md | 231 ++++ README.md | 7 + package-lock.json | 28 + package.json | 8 + packages/demo-app-frontend/.prettierignore | 26 + packages/demo-app-frontend/.prettierrc | 8 + packages/demo-app-frontend/Dockerfile | 2 +- packages/demo-app-frontend/eslint.config.js | 2 + packages/demo-app-frontend/package-lock.json | 457 +++++++ packages/demo-app-frontend/package.json | 14 +- packages/trust-anchor-did-ethr/.solhint.json | 15 + .../trust-anchor-did-ethr/package-lock.json | 1193 ++++++++++++++++- packages/trust-anchor-did-ethr/package.json | 1 + 16 files changed, 2130 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .husky/pre-commit create mode 100644 CI_CD.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 packages/demo-app-frontend/.prettierignore create mode 100644 packages/demo-app-frontend/.prettierrc create mode 100644 packages/trust-anchor-did-ethr/.solhint.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..451722f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,99 @@ +name: Test and Build + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.12.0' + + # Frontend Linting + - name: Install frontend dependencies + working-directory: packages/demo-app-frontend + run: npm ci + + - name: Run ESLint (Frontend) + working-directory: packages/demo-app-frontend + run: npm run lint + + - name: Check Prettier formatting (Frontend) + working-directory: packages/demo-app-frontend + run: npx prettier --check . + + # Solidity Linting + - name: Install contract dependencies + working-directory: packages/trust-anchor-did-ethr + run: npm ci + + - name: Run Solhint (Contracts) + working-directory: packages/trust-anchor-did-ethr + run: npx solhint 'contracts/**/*.sol' + + test: + name: Test + needs: lint + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.12.0' + cache: 'npm' + cache-dependency-path: packages/trust-anchor-did-ethr/package-lock.json + + - name: Install dependencies + working-directory: packages/trust-anchor-did-ethr + run: npm ci + + - name: Run Hardhat tests + working-directory: packages/trust-anchor-did-ethr + run: npx hardhat test + + build: + name: Build + needs: test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.12.0' + + # Compile Contracts + - name: Install contract dependencies + working-directory: packages/trust-anchor-did-ethr + run: npm ci + + - name: Compile smart contracts + working-directory: packages/trust-anchor-did-ethr + run: npx hardhat compile + + # Build Frontend + - name: Install frontend dependencies + working-directory: packages/demo-app-frontend + run: npm ci + + - name: Build frontend + working-directory: packages/demo-app-frontend + run: npm run build diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..236fe30 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +cd packages/demo-app-frontend && npx lint-staged diff --git a/BUILD.md b/BUILD.md index b63fd9a..960aa85 100644 --- a/BUILD.md +++ b/BUILD.md @@ -7,6 +7,7 @@ - [Installation](#installation) - [Running the Project](#running-the-project) - [Testing](#testing) +- [Development Workflow](#development-workflow) - [TypeScript Configuration](#typescript-configuration) - [Platform-Specific Notes](#platform-specific-notes) - [Deployment](#deployment) @@ -66,6 +67,14 @@ This project uses the following key dependencies: ## Installation +### Install Root Dependencies + +First, install root dependencies (required for Husky pre-commit hooks): + +```bash +npm install +``` + ### Install Dependencies for Each Package #### 1. Smart Contracts (trust-anchor-did-ethr) @@ -187,6 +196,45 @@ cd packages/demo-app-frontend npm run lint ``` +## Development Workflow + +### Pre-commit Hooks + +This project uses [Husky](https://typicode.github.io/husky/) and [lint-staged](https://github.com/okonet/lint-staged) to automatically lint your code before commits: + +**What happens when you commit:** +1. You stage files: `git add src/components/MyComponent.tsx` +2. You commit: `git commit -m "fix: update component"` +3. Pre-commit hook runs automatically: + - ESLint checks code quality and fixes issues + - Prettier formats code (indentation, spacing, etc.) + - Both tools auto-fix and re-stage files + - Unfixable errors block the commit +4. If successful, commit proceeds with clean, formatted code + +**Manual formatting:** +```bash +npm run format # Format all files with Prettier +npm run lint # Check code quality with ESLint +``` + +**Configuration:** +- Hook script: `.husky/pre-commit` +- Prettier rules: `packages/demo-app-frontend/.prettierrc` +- ESLint rules: `packages/demo-app-frontend/eslint.config.js` +- Staged file rules: `packages/demo-app-frontend/package.json` → `lint-staged` section + +### Continuous Integration (GitHub Actions) + +Pull requests and pushes to `main` trigger automated checks (`.github/workflows/ci.yml`): + +**CI Jobs:** +1. **Frontend Linting**: Runs ESLint on entire frontend codebase +2. **Frontend Formatting**: Checks Prettier formatting compliance +3. **Contract Validation**: Compiles smart contracts and runs full test suite + +**All checks must pass before merging.** The CI uses Node.js 22.12.0 to match local development. + ## TypeScript Configuration ### Smart Contracts (trust-anchor-did-ethr) diff --git a/CI_CD.md b/CI_CD.md new file mode 100644 index 0000000..78bb813 --- /dev/null +++ b/CI_CD.md @@ -0,0 +1,231 @@ +# CI/CD Pipeline Documentation + +This document describes the CI/CD pipeline for the On-Chain SSI project. + + +## Pipeline Structure + +The pipeline consists of three sequential stages: + +``` +Lint -> Test -> Build +``` + +Each stage must pass before the next stage runs. If any stage fails, the pipeline stops. + +### Workflow File + +Location: `.github/workflows/ci.yml` + +Triggers: +- On pull requests to `main` branch +- On pushes to `main` branch + +## Pipeline Stages + +### 1. Lint Stage + +**Purpose:** Validate code style and formatting + +**Checks:** +- **ESLint** - TypeScript/React code quality (frontend) +- **Prettier** - Code formatting consistency (frontend) +- **Solhint** - Solidity contract linting (contracts) + +**Configuration Files:** +- `packages/demo-app-frontend/eslint.config.js` - ESLint rules +- `packages/demo-app-frontend/.prettierrc` - Prettier formatting rules +- `packages/trust-anchor-did-ethr/.solhint.json` - Solhint rules (relaxed for practicality, it is work in progress to make them stricter) + +**Exit Criteria:** All linters pass with 0 errors + +### 2. Test Stage + +**Purpose:** Verify contract functionality + +**Requires:** Lint stage must pass + +**Tests:** +- Hardhat test suite (36 tests) +- Unit tests for all smart contracts +- Integration tests for DID workflows + +**Exit Criteria:** All tests pass + +### 3. Build Stage + +**Purpose:** Verify project can be compiled and built + +**Requires:** Test stage must pass + +**Builds:** +- Smart contract compilation (`npx hardhat compile`) +- Frontend production build (`npm run build`) + +**Exit Criteria:** Both contracts and frontend build successfully + +## Running Pipeline Stages Locally + +### Lint + +```bash +# Frontend +cd packages/demo-app-frontend +npm run lint +npx prettier --check . + +# Contracts +cd packages/trust-anchor-did-ethr +npx solhint 'contracts/**/*.sol' +``` + +### Test + +```bash +cd packages/trust-anchor-did-ethr +npx hardhat test +``` + +### Build + +```bash +# Contracts +cd packages/trust-anchor-did-ethr +npx hardhat compile + +# Frontend +cd packages/demo-app-frontend +npm run build +``` + +## Pre-commit Hooks + +The project uses [Husky](https://typicode.github.io/husky/) to enforce code quality before commits. + +**What runs on commit:** +1. ESLint checks staged TypeScript files +2. Prettier formats staged files +3. Changes are auto-fixed and staged again if possible +4. Commit is blocked if unfixable errors exist + +**Configuration:** +- `.husky/pre-commit` - Hook script +- `packages/demo-app-frontend/package.json` → `lint-staged` section + +**Bypass (emergency only):** +```bash +git commit --no-verify +``` + +## Expected Outputs + +### Successful Pipeline + +All three jobs show green checkmarks: +- Lint: All linters pass +- Test: All 36 tests pass +- Build: Contracts compile, frontend builds + +### Failed Pipeline + +Pipeline stops at the first failure: +- Lint fails: Test and Build stages do not run +- Test fails: Build stage does not run +- Build fails: PR cannot be merged + +## Troubleshooting + +### Linting Errors + +**ESLint fails:** +```bash +cd packages/demo-app-frontend +npm run lint +# Fix errors shown, or autofix: +npx eslint . --fix +``` + +**Prettier fails:** +```bash +cd packages/demo-app-frontend +npx prettier --check . # See what needs formatting +npm run format # Autoformat all files +``` + +**Solhint fails:** +```bash +cd packages/trust-anchor-did-ethr +npx solhint 'contracts/**/*.sol' +# Check .solhint.json if rules are too strict, probably not because they are relaxed currently. Can be checked with stricter rules +``` + +### Test Failures + +```bash +cd packages/trust-anchor-did-ethr +npx hardhat test +# Review error output +# Fix contracts or test files +``` + +### Build Failures + +**Contract compilation fails:** +```bash +cd packages/trust-anchor-did-ethr +npx hardhat compile +# Check Solidity syntax errors +``` + +**Frontend build fails:** +```bash +cd packages/demo-app-frontend +npm run build +# Usually TypeScript errors, so check terminal output +``` + +### CI Runs on Old Code + +**Issue:** CI fails on merged code + +**Cause:** Dependencies changed or Node.js version mismatch + +**Solution:** +```bash +# Update dependencies +npm install +# Ensure Node.js 22.12.0+ +node --version +``` + +## Future Enhancements + +**Note:** The following features are planned but not yet implemented. + +### Deployment Validation + +- Automated testnet deployments +- Contract verification on Etherscan/block explorers +- Deployment tests + +### Test Environments + +- Ethereum Sepolia testnet integration +- Secure secrets management using GitHub Secrets +- RPC endpoint configuration + + +## Contributing + +When contributing, ensure: +1. All pre-commit hooks pass +2. Run tests locally before pushing: `npm test` +3. Check CI status after pushing +4. Fix any CI failures before requesting review + +## Support + +For pipeline issues, check this document for troubleshooting steps, review GitHub Actions logs, and run commands locally to reproduce issues. + + +--- diff --git a/README.md b/README.md index 4156dde..52cddbd 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ Get the demo application running: **1. Install Dependencies** ```bash +# Install root dependencies (Husky for pre-commit hooks) +npm install + +# Install package dependencies cd packages/trust-anchor-did-ethr && npm install cd packages/demo-app-frontend && npm install ``` @@ -117,9 +121,12 @@ npm run dev Open http://localhost:5173/ in your browser. +**Note**: This project uses pre-commit hooks (Husky) and CI checks. See [Development Workflow](BUILD.md#development-workflow) in BUILD.md for details. + ## Documentation - **[BUILD.md](BUILD.md)** - Comprehensive build instructions, testing, troubleshooting, and deployment guides +- **[CI_CD.md](CI_CD.md)** - CI/CD pipeline documentation with workflow details and troubleshooting - **[Smart Contracts](packages/trust-anchor-did-ethr/README.md)** - Detailed smart contract documentation - **[Demo Application](packages/demo-app-frontend/README.md)** - Frontend application guide diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d612e75 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "on-chain-ssi", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "husky": "^9.1.7" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6b87ff3 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "husky": "^9.1.7" + }, + "scripts": { + "prepare": "husky" + } +} diff --git a/packages/demo-app-frontend/.prettierignore b/packages/demo-app-frontend/.prettierignore new file mode 100644 index 0000000..69dbd04 --- /dev/null +++ b/packages/demo-app-frontend/.prettierignore @@ -0,0 +1,26 @@ +# Dependencies +node_modules + +# Build outputs +dist +build +.vite +coverage + +# Logs +*.log +npm-debug.log* + +# Lock files +package-lock.json +pnpm-lock.yaml +yarn.lock + +# Environment files +.env +.env.local +.env.production + +# Minified files +*.min.js +*.min.css diff --git a/packages/demo-app-frontend/.prettierrc b/packages/demo-app-frontend/.prettierrc new file mode 100644 index 0000000..65f51e0 --- /dev/null +++ b/packages/demo-app-frontend/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100, + "arrowParens": "always" +} diff --git a/packages/demo-app-frontend/Dockerfile b/packages/demo-app-frontend/Dockerfile index 9b4fb92..bf3d3a9 100644 --- a/packages/demo-app-frontend/Dockerfile +++ b/packages/demo-app-frontend/Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Build the application -FROM node:20-alpine AS builder +FROM node:22-alpine AS builder WORKDIR /app diff --git a/packages/demo-app-frontend/eslint.config.js b/packages/demo-app-frontend/eslint.config.js index 5e6b472..7eb6f37 100644 --- a/packages/demo-app-frontend/eslint.config.js +++ b/packages/demo-app-frontend/eslint.config.js @@ -4,6 +4,7 @@ import reactHooks from 'eslint-plugin-react-hooks' import reactRefresh from 'eslint-plugin-react-refresh' import tseslint from 'typescript-eslint' import { defineConfig, globalIgnores } from 'eslint/config' +import eslintConfigPrettier from 'eslint-config-prettier' export default defineConfig([ globalIgnores(['dist']), @@ -14,6 +15,7 @@ export default defineConfig([ tseslint.configs.recommended, reactHooks.configs.flat.recommended, reactRefresh.configs.vite, + eslintConfigPrettier, ], languageOptions: { ecmaVersion: 2020, diff --git a/packages/demo-app-frontend/package-lock.json b/packages/demo-app-frontend/package-lock.json index 8bee798..d638e04 100644 --- a/packages/demo-app-frontend/package-lock.json +++ b/packages/demo-app-frontend/package-lock.json @@ -28,10 +28,14 @@ "@vitejs/plugin-react": "^5.1.1", "autoprefixer": "^10.4.23", "eslint": "^9.39.1", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "husky": "^9.1.7", + "lint-staged": "^16.2.7", "postcss": "^8.5.6", + "prettier": "^3.8.1", "tailwindcss": "^3.4.17", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", @@ -2016,6 +2020,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2301,6 +2334,39 @@ "node": ">= 6" } }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2330,6 +2396,13 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -2448,6 +2521,26 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/esbuild": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", @@ -2573,6 +2666,22 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", @@ -2895,6 +3004,19 @@ "node": ">=6.9.0" } }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2961,6 +3083,22 @@ "hermes-estree": "0.25.1" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3037,6 +3175,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3203,6 +3357,59 @@ "dev": true, "license": "MIT" }, + "node_modules/lint-staged": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3226,6 +3433,26 @@ "dev": true, "license": "MIT" }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3282,6 +3509,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3334,6 +3574,19 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nano-spawn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -3397,6 +3650,22 @@ "node": ">= 6" } }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3537,6 +3806,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -3744,6 +4026,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3911,6 +4209,23 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -3922,6 +4237,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "4.54.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", @@ -4033,6 +4355,49 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/sonner": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", @@ -4053,6 +4418,49 @@ "node": ">=0.10.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz", + "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4507,6 +4915,55 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", diff --git a/packages/demo-app-frontend/package.json b/packages/demo-app-frontend/package.json index 0fc0e38..1b29633 100644 --- a/packages/demo-app-frontend/package.json +++ b/packages/demo-app-frontend/package.json @@ -11,7 +11,15 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", - "preview": "vite preview" + "format": "prettier --write .", + "preview": "vite preview", + "prepare": "cd ../.. && husky packages/demo-app-frontend/.husky" + }, + "lint-staged": { + "*.{ts,tsx}": [ + "eslint --fix", + "prettier --write" + ] }, "dependencies": { "@tanstack/react-query": "^5.90.12", @@ -34,10 +42,14 @@ "@vitejs/plugin-react": "^5.1.1", "autoprefixer": "^10.4.23", "eslint": "^9.39.1", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "husky": "^9.1.7", + "lint-staged": "^16.2.7", "postcss": "^8.5.6", + "prettier": "^3.8.1", "tailwindcss": "^3.4.17", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", diff --git a/packages/trust-anchor-did-ethr/.solhint.json b/packages/trust-anchor-did-ethr/.solhint.json new file mode 100644 index 0000000..830591b --- /dev/null +++ b/packages/trust-anchor-did-ethr/.solhint.json @@ -0,0 +1,15 @@ +{ + "extends": "solhint:recommended", + "rules": { + "compiler-version": ["error", "^0.8.6"], + "func-visibility": "off", + "use-natspec": "off", + "gas-custom-errors": "off", + "gas-indexed-events": "off", + "gas-increment-by-one": "off", + "gas-strict-inequalities": "off", + "explicit-types": "off", + "immutable-vars-naming": "off", + "avoid-low-level-calls": "off" + } +} diff --git a/packages/trust-anchor-did-ethr/package-lock.json b/packages/trust-anchor-did-ethr/package-lock.json index 90f5fc4..4907394 100644 --- a/packages/trust-anchor-did-ethr/package-lock.json +++ b/packages/trust-anchor-did-ethr/package-lock.json @@ -13,8 +13,13 @@ "@types/node": "^22.19.3", "forge-std": "github:foundry-rs/forge-std#v1.9.4", "hardhat": "^3.1.0", + "solhint": "^6.0.3", "typescript": "~5.8.0", "viem": "^2.43.2" + }, + "engines": { + "node": ">=22.10.0", + "npm": ">=10.0.0" } }, "node_modules/@actions/core": { @@ -67,6 +72,31 @@ "dev": true, "license": "MIT" }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", @@ -932,6 +962,16 @@ "@ethersproject/strings": "^5.8.0" } }, + "node_modules/@humanwhocodes/momoa": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.10.0" + } + }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -1465,6 +1505,44 @@ "node": ">= 12" } }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/npm-conf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz", + "integrity": "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@scure/base": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", @@ -1548,6 +1626,26 @@ "license": "MIT", "peer": true }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@solidity-parser/parser": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.20.2.tgz", + "integrity": "sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA==", + "dev": true, + "license": "MIT" + }, "node_modules/@streamparser/json": { "version": "0.0.22", "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.22.tgz", @@ -1565,6 +1663,26 @@ "@streamparser/json": "^0.0.22" } }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.19.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", @@ -1614,6 +1732,33 @@ "dev": true, "license": "MIT" }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -1648,6 +1793,90 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/ast-parents": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz", + "integrity": "sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/better-ajv-errors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-2.0.3.tgz", + "integrity": "sha512-t1vxUP+vYKsaYi/BbKo2K98nEAZmfi4sjwvmRT8aOPDzPJeAtLurfoIDazVkLILxO4K+Sw4YrLYnBQ46l6pePg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@humanwhocodes/momoa": "^2.0.4", + "chalk": "^4.1.2", + "jsonpointer": "^5.0.1", + "leven": "^3.1.0 < 4" + }, + "engines": { + "node": ">= 18.20.6" + }, + "peerDependencies": { + "ajv": "4.11.8 - 8" + } + }, + "node_modules/better-ajv-errors/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/better-ajv-errors/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/bn.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", @@ -1655,6 +1884,16 @@ "dev": true, "license": "MIT" }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -1663,6 +1902,45 @@ "license": "MIT", "peer": true }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/cbor2": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/cbor2/-/cbor2-1.12.0.tgz", @@ -1708,7 +1986,6 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1721,8 +1998,55 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, "license": "MIT", - "peer": true + "engines": { + "node": ">=14" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, "node_modules/debug": { "version": "4.4.3", @@ -1742,6 +2066,55 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -1778,6 +2151,13 @@ "license": "MIT", "peer": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -1802,6 +2182,16 @@ "node": ">=6" } }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/esbuild": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", @@ -1949,6 +2339,20 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-equals": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", @@ -1959,12 +2363,53 @@ "node": ">=6.0.0" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/forge-std": { "version": "1.9.4", "resolved": "git+ssh://git@github.com/foundry-rs/forge-std.git#1eea5bae12ae557d589f9f0f0edae2faa47cb262", "dev": true, "license": "(Apache-2.0 OR MIT)" }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1980,10 +2425,23 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-tsconfig": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.1.tgz", - "integrity": "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.1.tgz", + "integrity": "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==", "dev": true, "license": "MIT", "dependencies": { @@ -1993,6 +2451,60 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, "node_modules/hardhat": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.6.tgz", @@ -2053,7 +2565,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -2083,6 +2594,37 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/immer": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz", @@ -2094,6 +2636,35 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2101,6 +2672,30 @@ "dev": true, "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/isows": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", @@ -2187,6 +2782,47 @@ "dev": true, "license": "MIT" }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/json-stream-stringify": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", @@ -2217,6 +2853,26 @@ "node": ">=6" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -2227,6 +2883,46 @@ "node": ">=6" } }, + "node_modules/latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-json": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", @@ -2234,6 +2930,26 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/micro-eth-signer": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz", @@ -2298,6 +3014,19 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -2314,6 +3043,19 @@ "license": "MIT", "peer": true }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -2351,6 +3093,29 @@ "node": ">=10" } }, + "node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/ox": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/ox/-/ox-0.11.3.tgz", @@ -2470,6 +3235,16 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/p-map": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", @@ -2483,6 +3258,101 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -2513,6 +3383,52 @@ "node": ">= 6" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -2550,6 +3466,62 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/registry-auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz", + "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^3.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -2570,6 +3542,22 @@ "node": ">=10" } }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -2618,6 +3606,106 @@ "dev": true, "license": "MIT" }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/solhint": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-6.0.3.tgz", + "integrity": "sha512-LYiy1bN8X9eUsti13mbS4fY6ILVxhP6VoOgqbHxCsHl5VPnxOWf7U1V9ZvgizxdInKBMW82D1FNJO+daAcWHbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@solidity-parser/parser": "^0.20.2", + "ajv": "^6.12.6", + "ajv-errors": "^1.0.1", + "ast-parents": "^0.0.1", + "better-ajv-errors": "^2.0.2", + "chalk": "^4.1.2", + "commander": "^10.0.0", + "cosmiconfig": "^8.0.0", + "fast-diff": "^1.2.0", + "glob": "^8.0.3", + "ignore": "^5.2.4", + "js-yaml": "^4.1.0", + "latest-version": "^7.0.0", + "lodash": "^4.17.21", + "pluralize": "^8.0.0", + "semver": "^7.5.2", + "table": "^6.8.1", + "text-table": "^0.2.0" + }, + "bin": { + "solhint": "solhint.js" + }, + "optionalDependencies": { + "prettier": "^2.8.3" + } + }, + "node_modules/solhint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/solhint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -2638,6 +3726,21 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2651,13 +3754,22 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -2665,6 +3777,54 @@ "node": ">=8" } }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/through2": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", @@ -2744,6 +3904,16 @@ "dev": true, "license": "MIT" }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2872,6 +4042,13 @@ } } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", diff --git a/packages/trust-anchor-did-ethr/package.json b/packages/trust-anchor-did-ethr/package.json index 7f82a10..269e9b6 100644 --- a/packages/trust-anchor-did-ethr/package.json +++ b/packages/trust-anchor-did-ethr/package.json @@ -12,6 +12,7 @@ "@types/node": "^22.19.3", "forge-std": "github:foundry-rs/forge-std#v1.9.4", "hardhat": "^3.1.0", + "solhint": "^6.0.3", "typescript": "~5.8.0", "viem": "^2.43.2" }, From 0eec8f82340940cd1b8ae4c98c6bef017f9befb4 Mon Sep 17 00:00:00 2001 From: bakiberkay Date: Thu, 5 Feb 2026 22:47:39 +0100 Subject: [PATCH 2/3] style: apply prettier formatting to frontend code (according to the github issue) --- packages/demo-app-frontend/README.md | 50 +- packages/demo-app-frontend/index.html | 13 +- packages/demo-app-frontend/postcss.config.js | 2 +- packages/demo-app-frontend/src/App.tsx | 58 +- .../src/components/auth/ProtectedRoute.tsx | 56 +- .../src/components/layout/AppLayout.tsx | 21 +- .../src/components/layout/CompanyLayout.tsx | 67 +-- .../components/layout/TrustAnchorLayout.tsx | 77 +-- .../src/components/ui/ProposalCard.tsx | 125 +++-- .../src/components/ui/StatCard.tsx | 2 +- .../src/components/ui/UserBadge.tsx | 57 +- .../src/hooks/useCRSetManagement.ts | 37 +- .../demo-app-frontend/src/hooks/useDIDSync.ts | 31 +- .../src/hooks/useGovernance.ts | 89 ++- .../src/hooks/useIdentity.ts | 42 +- .../src/hooks/useTrustAnchor.ts | 154 +++-- packages/demo-app-frontend/src/index.css | 2 +- .../demo-app-frontend/src/lib/contracts.ts | 291 +++++----- packages/demo-app-frontend/src/lib/ipfs.ts | 38 +- .../src/lib/proposalDecoder.ts | 90 +-- packages/demo-app-frontend/src/lib/wagmi.ts | 6 +- packages/demo-app-frontend/src/main.tsx | 2 +- packages/demo-app-frontend/src/pages/Home.tsx | 310 +++++----- .../src/pages/company/Onboarding.tsx | 394 +++++++------ .../src/pages/company/RevocationList.tsx | 322 ++++++----- .../src/pages/trust-anchor/Companies.tsx | 531 ++++++++++-------- .../src/pages/trust-anchor/Dashboard.tsx | 171 +++--- .../src/pages/trust-anchor/Governance.tsx | 279 ++++----- packages/demo-app-frontend/tailwind.config.ts | 7 +- packages/demo-app-frontend/tsconfig.json | 5 +- 30 files changed, 1753 insertions(+), 1576 deletions(-) diff --git a/packages/demo-app-frontend/README.md b/packages/demo-app-frontend/README.md index 117a362..acf8a29 100644 --- a/packages/demo-app-frontend/README.md +++ b/packages/demo-app-frontend/README.md @@ -34,7 +34,7 @@ You can containerize this application using the included Dockerfile (Nginx + Alp ### 1. Build the Image -*Note: The build process bakes the `.env` variables into the static files. Ensure your `.env` file is correct before building.* +_Note: The build process bakes the `.env` variables into the static files. Ensure your `.env` file is correct before building._ ```bash docker build -t ssi-frontend . @@ -75,51 +75,49 @@ The application supports two distinct user roles. Use MetaMask to switch between ### 🏛️ Role 1: Trust Anchor (Administrator) -*Use the wallet address that deployed the contracts (Account #0).* +_Use the wallet address that deployed the contracts (Account #0)._ 1. **Dashboard Overview:** -* Navigate to `/trust-anchor`. -* View real-time governance stats, quorum thresholds, and active proposals. +- Navigate to `/trust-anchor`. +- View real-time governance stats, quorum thresholds, and active proposals. 2. **Registering Companies:** -* Go to **Companies**. -* Search for a company's DID address (Wallet Address). -* If the company is "Not Managed", wait for them to delegate control. -* If they have delegated (Yellow status), scroll down to **"CRSet Admins"**. -* Paste the company's address and click **Add**. This completes the registration immediately (no proposal required). +- Go to **Companies**. +- Search for a company's DID address (Wallet Address). +- If the company is "Not Managed", wait for them to delegate control. +- If they have delegated (Yellow status), scroll down to **"CRSet Admins"**. +- Paste the company's address and click **Add**. This completes the registration immediately (no proposal required). 3. **Governance:** -* Go to **Governance**. -* Propose adding/removing admins or updating the multi-sig quorum threshold. - +- Go to **Governance**. +- Propose adding/removing admins or updating the multi-sig quorum threshold. ### 🏢 Role 2: Company (User) -*Use any other wallet address (Account #1, #2, etc.).* +_Use any other wallet address (Account #1, #2, etc.)._ 1. **Onboarding:** -* Navigate to `/company/onboarding`. -* **Step 1 (Delegate):** Sign the transaction to transfer identity ownership to the Trust Anchor. -* **Step 2 (Verification):** Wait for the Trust Anchor to approve your registration and add you as a CRSet Admin. -* **Step 3 (Complete):** Once verified, the dashboard will unlock. +- Navigate to `/company/onboarding`. +- **Step 1 (Delegate):** Sign the transaction to transfer identity ownership to the Trust Anchor. +- **Step 2 (Verification):** Wait for the Trust Anchor to approve your registration and add you as a CRSet Admin. +- **Step 3 (Complete):** Once verified, the dashboard will unlock. 2. **Revocation Management:** -* Navigate to `/company/revocations`. -* **Upload:** Drag & Drop a W3C-compliant JSON revocation list. -* **Publish:** The app uploads the file to IPFS (via Pinata) and updates the smart contract with the new CID. - +- Navigate to `/company/revocations`. +- **Upload:** Drag & Drop a W3C-compliant JSON revocation list. +- **Publish:** The app uploads the file to IPFS (via Pinata) and updates the smart contract with the new CID. --- ## 🛠 Tech Stack -* **Framework:** React + Vite -* **Language:** TypeScript -* **Web3 Integration:** Wagmi v2, Viem, TanStack Query -* **Styling:** Tailwind CSS, Lucide Icons -* **Deployment:** Docker, Nginx \ No newline at end of file +- **Framework:** React + Vite +- **Language:** TypeScript +- **Web3 Integration:** Wagmi v2, Viem, TanStack Query +- **Styling:** Tailwind CSS, Lucide Icons +- **Deployment:** Docker, Nginx diff --git a/packages/demo-app-frontend/index.html b/packages/demo-app-frontend/index.html index 1ea9bd4..3318988 100644 --- a/packages/demo-app-frontend/index.html +++ b/packages/demo-app-frontend/index.html @@ -2,13 +2,20 @@ - + On-Chain SSI | Decentralized Identity Governance - +
- \ No newline at end of file + diff --git a/packages/demo-app-frontend/postcss.config.js b/packages/demo-app-frontend/postcss.config.js index e99ebc2..2e7af2b 100644 --- a/packages/demo-app-frontend/postcss.config.js +++ b/packages/demo-app-frontend/postcss.config.js @@ -3,4 +3,4 @@ export default { tailwindcss: {}, autoprefixer: {}, }, -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/App.tsx b/packages/demo-app-frontend/src/App.tsx index 26a3adc..07f3a0e 100644 --- a/packages/demo-app-frontend/src/App.tsx +++ b/packages/demo-app-frontend/src/App.tsx @@ -23,14 +23,14 @@ function NetworkChecker() { const chainId = useChainId() const { switchChain } = useSwitchChain() // 11155111 is Sepolia, 31337 is Hardhat. Adjust as needed for Tezos Etherlink ID if different. - const supportedChains = [31337, 11155111] + const supportedChains = [31337, 11155111] if (!supportedChains.includes(chainId)) { return (
Unsupported Network. -
) @@ -63,26 +63,30 @@ export function ProtectedRoute({ children, requiredRole }: ProtectedRouteProps) // --- ERROR STATE (Blockchain Down) --- if (isError) { return ( -
-
- -
-

Network Connection Failed

-

- We couldn't fetch data from the blockchain. Please check if your wallet is connected to the correct network or if the RPC is reachable. -

- +
+
+
+

Network Connection Failed

+

+ We couldn't fetch data from the blockchain. Please check if your wallet is connected to + the correct network or if the RPC is reachable. +

+ +
) } if (!isConnected) return null return <>{children} -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/components/layout/AppLayout.tsx b/packages/demo-app-frontend/src/components/layout/AppLayout.tsx index 3b72dff..4d59b74 100644 --- a/packages/demo-app-frontend/src/components/layout/AppLayout.tsx +++ b/packages/demo-app-frontend/src/components/layout/AppLayout.tsx @@ -21,13 +21,10 @@ export function AppLayout({ children }: AppLayoutProps) { ] // Helper to shorten address (e.g. 0x1234...5678) - const shortAddress = address - ? `${address.slice(0, 6)}...${address.slice(-4)}` - : '' + const shortAddress = address ? `${address.slice(0, 6)}...${address.slice(-4)}` : '' return (
- {/* SIDEBAR */}
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/components/layout/CompanyLayout.tsx b/packages/demo-app-frontend/src/components/layout/CompanyLayout.tsx index b40ff83..228fa3e 100644 --- a/packages/demo-app-frontend/src/components/layout/CompanyLayout.tsx +++ b/packages/demo-app-frontend/src/components/layout/CompanyLayout.tsx @@ -18,20 +18,21 @@ export function CompanyLayout({ children }: CompanyLayoutProps) { return (
- {/* SIDEBAR (Dark Theme - Company Emerald Variant) */} - {/* MAIN CONTENT */} -
{/* Top Bar */}
- Company Portal - / - - {location.pathname.split('/').pop() || 'Dashboard'} - + Company Portal + / + + {location.pathname.split('/').pop() || 'Dashboard'} +
-
- {children} -
+
{children}
- +
- © 2026 ASC-S e.V. | Tezos Etherlink Testnet + © 2026 ASC-S e.V. | Tezos Etherlink Testnet
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/components/layout/TrustAnchorLayout.tsx b/packages/demo-app-frontend/src/components/layout/TrustAnchorLayout.tsx index 509dc59..7e9bd4c 100644 --- a/packages/demo-app-frontend/src/components/layout/TrustAnchorLayout.tsx +++ b/packages/demo-app-frontend/src/components/layout/TrustAnchorLayout.tsx @@ -1,6 +1,13 @@ import { type ReactNode, useState } from 'react' import { Link, useLocation } from 'react-router-dom' -import { LayoutDashboard, Building2, ShieldCheck, Settings, ChevronLeft, ChevronRight} from 'lucide-react' +import { + LayoutDashboard, + Building2, + ShieldCheck, + Settings, + ChevronLeft, + ChevronRight, +} from 'lucide-react' import { UserBadge } from '../ui/UserBadge' interface TrustAnchorLayoutProps { @@ -19,20 +26,21 @@ export function TrustAnchorLayout({ children }: TrustAnchorLayoutProps) { return (
- {/* TA SIDEBAR */} - {/* MAIN CONTENT AREA */} -
{/* Breadcrumbs / Top Bar (Optional Polish) */}
- Trust Anchor - / - - {location.pathname.split('/').pop() || 'Overview'} - + Trust Anchor + / + + {location.pathname.split('/').pop() || 'Overview'} +
-
- {children} -
+
{children}
- +
- © 2026 ASC-S e.V. | Tezos Etherlink Testnet + © 2026 ASC-S e.V. | Tezos Etherlink Testnet
-
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/components/ui/ProposalCard.tsx b/packages/demo-app-frontend/src/components/ui/ProposalCard.tsx index b9cea99..03fbdfb 100644 --- a/packages/demo-app-frontend/src/components/ui/ProposalCard.tsx +++ b/packages/demo-app-frontend/src/components/ui/ProposalCard.tsx @@ -23,86 +23,103 @@ export function ProposalCard({ isTrustAnchorAdmin, currentVotes, onApprove, - isPending + isPending, }: ProposalCardProps) { - const info = useMemo(() => decodeProposalData(proposal.rawInfo.data), [proposal.rawInfo.data]) - + const requiredVotes = proposal.rawInfo.requiresUnanimity ? totalAdmins : quorum const progressPercent = Math.min((currentVotes / requiredVotes) * 100, 100) const isReady = currentVotes >= requiredVotes const handleApprove = () => { - if(!isTrustAnchorAdmin) { - toast.error("Unauthorized") - return - } - // Ensure the ID is treated as a hex string - onApprove(proposal.id as `0x${string}`) + if (!isTrustAnchorAdmin) { + toast.error('Unauthorized') + return + } + // Ensure the ID is treated as a hex string + onApprove(proposal.id as `0x${string}`) } return ( -
+
-
- {info.type === 'governance' ? : } -
-
-

{info.title}

-

{info.details}

-
+
+ {info.type === 'governance' ? ( + + ) : ( + + )} +
+
+

{info.title}

+

{info.details}

+
- + {proposal.rawInfo.requiresUnanimity && ( - - Unanimity - + + Unanimity + )}
- Progress ({currentVotes}/{requiredVotes}) - {Math.round(progressPercent)}% + + Progress ({currentVotes}/{requiredVotes}) + + {Math.round(progressPercent)}%
-
+
- - ID: {proposal.id.slice(0, 6)}... - + ID: {proposal.id.slice(0, 6)}... - +
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/components/ui/StatCard.tsx b/packages/demo-app-frontend/src/components/ui/StatCard.tsx index a919da8..16a850a 100644 --- a/packages/demo-app-frontend/src/components/ui/StatCard.tsx +++ b/packages/demo-app-frontend/src/components/ui/StatCard.tsx @@ -21,4 +21,4 @@ export function StatCard({ title, value, icon, description }: StatCardProps) {
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/components/ui/UserBadge.tsx b/packages/demo-app-frontend/src/components/ui/UserBadge.tsx index d0e491c..cfedce1 100644 --- a/packages/demo-app-frontend/src/components/ui/UserBadge.tsx +++ b/packages/demo-app-frontend/src/components/ui/UserBadge.tsx @@ -5,7 +5,7 @@ import { useState } from 'react' // Add props interface interface UserBadgeProps { - isCollapsed?: boolean + isCollapsed?: boolean } export function UserBadge({ isCollapsed = false }: UserBadgeProps) { @@ -21,48 +21,51 @@ export function UserBadge({ isCollapsed = false }: UserBadgeProps) { } } - const avatarGradient = address + const avatarGradient = address ? `linear-gradient(135deg, #${address.slice(2, 8)} 0%, #${address.slice(address.length - 6)} 100%)` : 'bg-slate-700' - const badgeColor = role === 'admin' - ? 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20' - : 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20' + const badgeColor = + role === 'admin' + ? 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20' + : 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20' // COMPACT MODE (Collapsed) if (isCollapsed) { - return ( -
-
- -
- ) + return ( +
+
+ +
+ ) } // FULL MODE return (
-
- +
-
+
{roleLabel}
-
-
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/hooks/useCRSetManagement.ts b/packages/demo-app-frontend/src/hooks/useCRSetManagement.ts index dfea2ee..75fb06b 100644 --- a/packages/demo-app-frontend/src/hooks/useCRSetManagement.ts +++ b/packages/demo-app-frontend/src/hooks/useCRSetManagement.ts @@ -1,48 +1,53 @@ -import { useWriteContract, useWaitForTransactionReceipt } from "wagmi"; -import { encodeFunctionData } from "viem"; -import { TRUST_ANCHOR_ADDRESS, TRUST_ANCHOR_ABI, CRSET_REGISTRY_ADDRESS, CRSET_REGISTRY_ABI } from "../lib/contracts"; +import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi' +import { encodeFunctionData } from 'viem' +import { + TRUST_ANCHOR_ADDRESS, + TRUST_ANCHOR_ABI, + CRSET_REGISTRY_ADDRESS, + CRSET_REGISTRY_ABI, +} from '../lib/contracts' export function useCRSetManagement() { - const { writeContract, data: hash, error, isPending } = useWriteContract(); + const { writeContract, data: hash, error, isPending } = useWriteContract() const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash, - }); + }) // Add company admin using execCall (single TA admin is enough , no need to voting) const addCompanyAdmin = (companyDID: `0x${string}`, adminAddress: `0x${string}`) => { // Encode the addCompanyAdmin call const addAdminData = encodeFunctionData({ abi: CRSET_REGISTRY_ABI, - functionName: "addCompanyAdmin", + functionName: 'addCompanyAdmin', args: [companyDID, adminAddress], - }); + }) // Execute using multisig writeContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "execCall", + functionName: 'execCall', args: [CRSET_REGISTRY_ADDRESS, addAdminData], - }); - }; + }) + } // Remove company admin using execCall const removeCompanyAdmin = (companyDID: `0x${string}`, adminAddress: `0x${string}`) => { // Encode the removeCompanyAdmin call const removeAdminData = encodeFunctionData({ abi: CRSET_REGISTRY_ABI, - functionName: "removeCompanyAdmin", + functionName: 'removeCompanyAdmin', args: [companyDID, adminAddress], - }); + }) writeContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "execCall", + functionName: 'execCall', args: [CRSET_REGISTRY_ADDRESS, removeAdminData], - }); - }; + }) + } return { addCompanyAdmin, @@ -51,5 +56,5 @@ export function useCRSetManagement() { isSuccess, error, hash, - }; + } } diff --git a/packages/demo-app-frontend/src/hooks/useDIDSync.ts b/packages/demo-app-frontend/src/hooks/useDIDSync.ts index 3c2fa9f..dbb6a78 100644 --- a/packages/demo-app-frontend/src/hooks/useDIDSync.ts +++ b/packages/demo-app-frontend/src/hooks/useDIDSync.ts @@ -1,6 +1,13 @@ import { useWriteContract, useWaitForTransactionReceipt, useReadContract } from 'wagmi' import { encodeFunctionData, keccak256, toBytes, toHex } from 'viem' -import { TRUST_ANCHOR_ADDRESS, TRUST_ANCHOR_ABI, REGISTRY_ADDRESS, REGISTRY_ABI, CRSET_REGISTRY_ADDRESS, CRSET_REGISTRY_ABI } from '../lib/contracts' +import { + TRUST_ANCHOR_ADDRESS, + TRUST_ANCHOR_ABI, + REGISTRY_ADDRESS, + REGISTRY_ABI, + CRSET_REGISTRY_ADDRESS, + CRSET_REGISTRY_ABI, +} from '../lib/contracts' export function useDIDSync() { const { writeContract, data: hash, isPending, isSuccess, error } = useWriteContract() @@ -13,22 +20,22 @@ export function useDIDSync() { // Prepare setAttribute call - static endpoint (one-time setup, no manual syncing needed anymore) const attributeName = keccak256(toBytes('did/svc/CredentialRevocationList')) - + // JSON structure: blockchain native contract reference const endpointData = JSON.stringify({ contract: CRSET_REGISTRY_ADDRESS, chainId: 31337, // Localhost hardhat (change to 11155111 for Sepolia) - function: "getRevocationCID", - params: ["{{identity}}"] + function: 'getRevocationCID', + params: ['{{identity}}'], }) - + const attributeValue = toHex(toBytes(endpointData)) const validity = BigInt(31536000) // 1 year in seconds const setAttributeData = encodeFunctionData({ abi: REGISTRY_ABI, functionName: 'setAttribute', - args: [companyDID, attributeName, attributeValue, validity] + args: [companyDID, attributeName, attributeValue, validity], }) // Execute using multisig execCall @@ -36,7 +43,7 @@ export function useDIDSync() { address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, functionName: 'execCall', - args: [REGISTRY_ADDRESS, setAttributeData] + args: [REGISTRY_ADDRESS, setAttributeData], }) } @@ -45,17 +52,21 @@ export function useDIDSync() { isPending: isPending || isConfirming, isSuccess, error, - hash + hash, } } export function useReadCID(companyDID: `0x${string}` | undefined) { - const { data: cid, isLoading, refetch } = useReadContract({ + const { + data: cid, + isLoading, + refetch, + } = useReadContract({ address: CRSET_REGISTRY_ADDRESS, abi: CRSET_REGISTRY_ABI, functionName: 'getRevocationCID', args: companyDID ? [companyDID] : undefined, - query: { enabled: !!companyDID } + query: { enabled: !!companyDID }, }) return { cid: cid as string | undefined, isLoading, refetch } diff --git a/packages/demo-app-frontend/src/hooks/useGovernance.ts b/packages/demo-app-frontend/src/hooks/useGovernance.ts index c2a689c..e52e2f2 100644 --- a/packages/demo-app-frontend/src/hooks/useGovernance.ts +++ b/packages/demo-app-frontend/src/hooks/useGovernance.ts @@ -1,7 +1,7 @@ -import { useWriteContract, useWaitForTransactionReceipt } from "wagmi"; -import { TRUST_ANCHOR_ADDRESS, TRUST_ANCHOR_ABI } from "../lib/contracts"; -import { toast } from "sonner"; -import { useEffect } from "react"; +import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi' +import { TRUST_ANCHOR_ADDRESS, TRUST_ANCHOR_ABI } from '../lib/contracts' +import { toast } from 'sonner' +import { useEffect } from 'react' export function useGovernance() { const { @@ -12,92 +12,91 @@ export function useGovernance() { } = useWriteContract({ mutation: { onError: (err) => { - toast.error("Transaction Failed", { - description: err.message.split("\n")[0], // Short error - }); + toast.error('Transaction Failed', { + description: err.message.split('\n')[0], // Short error + }) }, }, - }); + }) const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash, - }); + }) useEffect(() => { if (isSuccess && hash) { - toast.success("Transaction Confirmed!", { - description: "The proposal has been executed or queued successfully.", + toast.success('Transaction Confirmed!', { + description: 'The proposal has been executed or queued successfully.', action: { - label: "View Explorer", - onClick: () => - window.open(`https://sepolia.etherscan.io/tx/${hash}`, "_blank"), + label: 'View Explorer', + onClick: () => window.open(`https://sepolia.etherscan.io/tx/${hash}`, '_blank'), }, - }); + }) } - }, [isSuccess, hash]); + }, [isSuccess, hash]) // 1. Propose changing owner of a company identity (Company Registration) const proposeCompanyRegistration = (companyAddress: `0x${string}`) => { - toast.info("Check your wallet", { - description: "Please sign the proposal transaction.", - }); + toast.info('Check your wallet', { + description: 'Please sign the proposal transaction.', + }) writeContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "proposeChangeOwner", + functionName: 'proposeChangeOwner', args: [companyAddress, TRUST_ANCHOR_ADDRESS], - }); - }; + }) + } // 2. Approve any proposal const approveProposal = (proposalId: `0x${string}`) => { - toast.info("Check your wallet", { description: "Signing approval..." }); + toast.info('Check your wallet', { description: 'Signing approval...' }) writeContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "approve", + functionName: 'approve', args: [proposalId], - }); - }; + }) + } // 3. Propose adding a new TA Admin const proposeAddOwner = (newAdmin: `0x${string}`) => { - toast.info("Check your wallet", { - description: "Signing owner addition proposal...", - }); + toast.info('Check your wallet', { + description: 'Signing owner addition proposal...', + }) writeContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "proposeAddOwner", + functionName: 'proposeAddOwner', args: [newAdmin], - }); - }; + }) + } // 4. Propose removing a TA Admin const proposeRemoveOwner = (adminToRemove: `0x${string}`) => { - toast.info("Check your wallet", { - description: "Signing owner removal proposal...", - }); + toast.info('Check your wallet', { + description: 'Signing owner removal proposal...', + }) writeContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "proposeRemoveOwner", + functionName: 'proposeRemoveOwner', args: [adminToRemove], - }); - }; + }) + } // 5. Propose updating Quorum const proposeQuorumUpdate = (newQuorum: number) => { - toast.info("Check your wallet", { - description: "Signing quorum update proposal...", - }); + toast.info('Check your wallet', { + description: 'Signing quorum update proposal...', + }) writeContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "proposeQuorumUpdate", + functionName: 'proposeQuorumUpdate', args: [BigInt(newQuorum)], - }); - }; + }) + } return { proposeCompanyRegistration, @@ -109,5 +108,5 @@ export function useGovernance() { isSuccess, error, hash, - }; + } } diff --git a/packages/demo-app-frontend/src/hooks/useIdentity.ts b/packages/demo-app-frontend/src/hooks/useIdentity.ts index c6330c0..acce258 100644 --- a/packages/demo-app-frontend/src/hooks/useIdentity.ts +++ b/packages/demo-app-frontend/src/hooks/useIdentity.ts @@ -1,44 +1,40 @@ -import { useAccount } from "wagmi"; -import { useTrustAnchorData } from "./useTrustAnchor"; -import { useMemo } from "react"; +import { useAccount } from 'wagmi' +import { useTrustAnchorData } from './useTrustAnchor' +import { useMemo } from 'react' -export type UserRole = "admin" | "company" | "guest"; +export type UserRole = 'admin' | 'company' | 'guest' export function useIdentity() { - const { address, isConnected } = useAccount(); - const { owners, isLoading } = useTrustAnchorData(); + const { address, isConnected } = useAccount() + const { owners, isLoading } = useTrustAnchorData() const role: UserRole = useMemo(() => { - if (!isConnected || !address) return "guest"; - if (isLoading) return "guest"; + if (!isConnected || !address) return 'guest' + if (isLoading) return 'guest' // Check if address is in owners list (case-insensitive) - const isTaAdmin = owners.some( - (owner) => owner.toLowerCase() === address.toLowerCase() - ); + const isTaAdmin = owners.some((owner) => owner.toLowerCase() === address.toLowerCase()) - return isTaAdmin ? "admin" : "company"; - }, [address, isConnected, owners, isLoading]); + return isTaAdmin ? 'admin' : 'company' + }, [address, isConnected, owners, isLoading]) const roleLabel = useMemo(() => { switch (role) { - case "admin": - return "Trust Anchor Admin"; - case "company": - return "Company User"; + case 'admin': + return 'Trust Anchor Admin' + case 'company': + return 'Company User' default: - return "Guest"; + return 'Guest' } - }, [role]); + }, [role]) return { address, - shortAddress: address - ? `${address.slice(0, 6)}...${address.slice(-4)}` - : "", + shortAddress: address ? `${address.slice(0, 6)}...${address.slice(-4)}` : '', isConnected, role, roleLabel, isLoading, - }; + } } diff --git a/packages/demo-app-frontend/src/hooks/useTrustAnchor.ts b/packages/demo-app-frontend/src/hooks/useTrustAnchor.ts index bd42922..a18a608 100644 --- a/packages/demo-app-frontend/src/hooks/useTrustAnchor.ts +++ b/packages/demo-app-frontend/src/hooks/useTrustAnchor.ts @@ -1,84 +1,78 @@ -import { - useReadContract, - useReadContracts, - useWatchContractEvent, - usePublicClient, -} from "wagmi"; -import { useState, useEffect } from "react"; -import { decodeFunctionData } from "viem"; -import { TRUST_ANCHOR_ADDRESS, TRUST_ANCHOR_ABI } from "../lib/contracts"; +import { useReadContract, useReadContracts, useWatchContractEvent, usePublicClient } from 'wagmi' +import { useState, useEffect } from 'react' +import { decodeFunctionData } from 'viem' +import { TRUST_ANCHOR_ADDRESS, TRUST_ANCHOR_ABI } from '../lib/contracts' // Helper type for decoded proposal export interface DecodedProposal { - id: string; + id: string rawInfo: { - data: `0x${string}`; - requiresUnanimity: boolean; - }; - description: string; - functionName: string; - args: unknown[]; + data: `0x${string}` + requiresUnanimity: boolean + } + description: string + functionName: string + args: unknown[] } export function useTrustAnchorData() { - const [proposals, setProposals] = useState([]); - const [approvals, setApprovals] = useState>({}); - const publicClient = usePublicClient(); + const [proposals, setProposals] = useState([]) + const [approvals, setApprovals] = useState>({}) + const publicClient = usePublicClient() // 1. Fetch history & Decode useEffect(() => { async function fetchHistory() { - if (!publicClient) return; + if (!publicClient) return const proposalLogs = await publicClient.getContractEvents({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - eventName: "ProposalCreated", + eventName: 'ProposalCreated', fromBlock: 0n, - }); + }) const decodedProposals = proposalLogs.map((log: unknown) => { - const { id, data, requiresUnanimity } = (log as { args: { id: bigint; data: `0x${string}`; requiresUnanimity: boolean } }).args; - let description = "Unknown Action"; - let funcName = "unknown"; - let decodedArgs: unknown[] = []; + const { id, data, requiresUnanimity } = ( + log as { args: { id: bigint; data: `0x${string}`; requiresUnanimity: boolean } } + ).args + let description = 'Unknown Action' + let funcName = 'unknown' + let decodedArgs: unknown[] = [] try { const decoded = decodeFunctionData({ abi: TRUST_ANCHOR_ABI, data: data, - }); - funcName = decoded.functionName; - decodedArgs = decoded.args as unknown as unknown[]; + }) + funcName = decoded.functionName + decodedArgs = decoded.args as unknown as unknown[] // Human readable description builder switch (funcName) { - case "_setQuorum": - description = `Update Quorum to ${decodedArgs[0]}`; - break; - case "_addOwner": + case '_setQuorum': + description = `Update Quorum to ${decodedArgs[0]}` + break + case '_addOwner': description = `Add Admin: ${(decodedArgs[0] as string).slice( 0, 6 - )}...${(decodedArgs[0] as string).slice(-4)}`; - break; - case "_removeOwner": + )}...${(decodedArgs[0] as string).slice(-4)}` + break + case '_removeOwner': description = `Remove Admin: ${(decodedArgs[0] as string).slice( 0, 6 - )}...${(decodedArgs[0] as string).slice(-4)}`; - break; - case "_executeChangeOwner": - description = `Register Company: ${(decodedArgs[0] as string).slice( - 0, - 6 - )}...`; - break; + )}...${(decodedArgs[0] as string).slice(-4)}` + break + case '_executeChangeOwner': + description = `Register Company: ${(decodedArgs[0] as string).slice(0, 6)}...` + break default: - description = `Execute ${funcName}`; + description = `Execute ${funcName}` } } catch (e) { - console.error("Failed to decode proposal data", e); + console.error('Failed to decode proposal data', e) } return { @@ -87,48 +81,48 @@ export function useTrustAnchorData() { description, functionName: funcName, args: decodedArgs, - }; - }); + } + }) - setProposals(decodedProposals); + setProposals(decodedProposals) // Fetch Approvals const approvalLogs = await publicClient.getContractEvents({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - eventName: "Approved", + eventName: 'Approved', fromBlock: 0n, - }); + }) - const newApprovals: Record = {}; + const newApprovals: Record = {} approvalLogs.forEach((log: unknown) => { - const { id: pid, owner } = (log as { args: { id: string; owner: string } }).args; - if (!newApprovals[pid]) newApprovals[pid] = []; - if (!newApprovals[pid].includes(owner)) newApprovals[pid].push(owner); - }); - setApprovals(newApprovals); + const { id: pid, owner } = (log as { args: { id: string; owner: string } }).args + if (!newApprovals[pid]) newApprovals[pid] = [] + if (!newApprovals[pid].includes(owner)) newApprovals[pid].push(owner) + }) + setApprovals(newApprovals) } - fetchHistory(); - }, [publicClient]); + fetchHistory() + }, [publicClient]) // 2. Watch for NEW approvals (Real-time updates) useWatchContractEvent({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - eventName: "Approved", + eventName: 'Approved', onLogs(logs: unknown[]) { setApprovals((prev) => { - const updated = { ...prev }; + const updated = { ...prev } logs.forEach((log: unknown) => { - const { id: pid, owner } = (log as { args: { id: string; owner: string } }).args; - if (!updated[pid]) updated[pid] = []; - if (!updated[pid].includes(owner)) updated[pid].push(owner); - }); - return updated; - }); + const { id: pid, owner } = (log as { args: { id: string; owner: string } }).args + if (!updated[pid]) updated[pid] = [] + if (!updated[pid].includes(owner)) updated[pid].push(owner) + }) + return updated + }) }, - }); + }) // Note: We should also watch for ProposalCreated to auto-update the list, // but for now history fetch on mount covers most cases. @@ -141,8 +135,8 @@ export function useTrustAnchorData() { } = useReadContract({ address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "quorum", - }); + functionName: 'quorum', + }) const { data: ownersData, @@ -153,41 +147,39 @@ export function useTrustAnchorData() { { address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "owners", + functionName: 'owners', args: [0n], }, { address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "owners", + functionName: 'owners', args: [1n], }, { address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "owners", + functionName: 'owners', args: [2n], }, { address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "owners", + functionName: 'owners', args: [3n], }, // Fetch a few more { address: TRUST_ANCHOR_ADDRESS, abi: TRUST_ANCHOR_ABI, - functionName: "owners", + functionName: 'owners', args: [4n], }, ], - }); + }) const owners = ownersData - ? ownersData - .filter((o) => o.status === "success") - .map((o) => o.result as string) - : []; + ? ownersData.filter((o) => o.status === 'success').map((o) => o.result as string) + : [] return { quorum: quorum ? Number(quorum) : 0, @@ -197,5 +189,5 @@ export function useTrustAnchorData() { totalAdmins: owners.length, isLoading: isQuorumLoading || isOwnersLoading, isError: isQuorumError || isOwnersError, - }; + } } diff --git a/packages/demo-app-frontend/src/index.css b/packages/demo-app-frontend/src/index.css index 2dca9fc..920a5bb 100644 --- a/packages/demo-app-frontend/src/index.css +++ b/packages/demo-app-frontend/src/index.css @@ -4,4 +4,4 @@ body { @apply bg-slate-50 text-slate-900 antialiased; -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/lib/contracts.ts b/packages/demo-app-frontend/src/lib/contracts.ts index 888f846..df0e00b 100644 --- a/packages/demo-app-frontend/src/lib/contracts.ts +++ b/packages/demo-app-frontend/src/lib/contracts.ts @@ -1,270 +1,263 @@ -import { type Address } from "viem"; +import { type Address } from 'viem' -const ENV_TRUST_ANCHOR_ADDRESS = import.meta.env.VITE_TRUST_ANCHOR_ADDRESS; -const ENV_REGISTRY_ADDRESS = import.meta.env.VITE_REGISTRY_ADDRESS; -const ENV_CRSET_REGISTRY_ADDRESS = import.meta.env.VITE_CRSET_REGISTRY_ADDRESS; +const ENV_TRUST_ANCHOR_ADDRESS = import.meta.env.VITE_TRUST_ANCHOR_ADDRESS +const ENV_REGISTRY_ADDRESS = import.meta.env.VITE_REGISTRY_ADDRESS +const ENV_CRSET_REGISTRY_ADDRESS = import.meta.env.VITE_CRSET_REGISTRY_ADDRESS -if (!ENV_TRUST_ANCHOR_ADDRESS || !ENV_TRUST_ANCHOR_ADDRESS.startsWith("0x")) { - console.error("CRITICAL: Trust Anchor Address not set in .env"); +if (!ENV_TRUST_ANCHOR_ADDRESS || !ENV_TRUST_ANCHOR_ADDRESS.startsWith('0x')) { + console.error('CRITICAL: Trust Anchor Address not set in .env') } -if (!ENV_REGISTRY_ADDRESS || !ENV_REGISTRY_ADDRESS.startsWith("0x")) { - console.error("CRITICAL: Registry Address not set in .env"); +if (!ENV_REGISTRY_ADDRESS || !ENV_REGISTRY_ADDRESS.startsWith('0x')) { + console.error('CRITICAL: Registry Address not set in .env') } -if (!ENV_CRSET_REGISTRY_ADDRESS || !ENV_CRSET_REGISTRY_ADDRESS.startsWith("0x")) { - console.error("CRITICAL: CRSet Registry Address not set in .env"); +if (!ENV_CRSET_REGISTRY_ADDRESS || !ENV_CRSET_REGISTRY_ADDRESS.startsWith('0x')) { + console.error('CRITICAL: CRSet Registry Address not set in .env') } export const TRUST_ANCHOR_ADDRESS: Address = - (ENV_TRUST_ANCHOR_ADDRESS as Address) || - "0x0000000000000000000000000000000000000000"; + (ENV_TRUST_ANCHOR_ADDRESS as Address) || '0x0000000000000000000000000000000000000000' export const REGISTRY_ADDRESS: Address = - (ENV_REGISTRY_ADDRESS as Address) || - "0x0000000000000000000000000000000000000000"; + (ENV_REGISTRY_ADDRESS as Address) || '0x0000000000000000000000000000000000000000' export const CRSET_REGISTRY_ADDRESS: Address = - (ENV_CRSET_REGISTRY_ADDRESS as Address) || - "0x0000000000000000000000000000000000000000"; + (ENV_CRSET_REGISTRY_ADDRESS as Address) || '0x0000000000000000000000000000000000000000' export const TRUST_ANCHOR_ABI = [ // --- EVENTS --- { anonymous: false, inputs: [ - { indexed: true, internalType: "bytes32", name: "id", type: "bytes32" }, - { indexed: false, internalType: "bytes", name: "data", type: "bytes" }, + { indexed: true, internalType: 'bytes32', name: 'id', type: 'bytes32' }, + { indexed: false, internalType: 'bytes', name: 'data', type: 'bytes' }, { indexed: false, - internalType: "bool", - name: "requiresUnanimity", - type: "bool", + internalType: 'bool', + name: 'requiresUnanimity', + type: 'bool', }, ], - name: "ProposalCreated", - type: "event", + name: 'ProposalCreated', + type: 'event', }, { anonymous: false, inputs: [ - { indexed: true, internalType: "bytes32", name: "id", type: "bytes32" }, + { indexed: true, internalType: 'bytes32', name: 'id', type: 'bytes32' }, { indexed: true, - internalType: "address", - name: "owner", - type: "address", + internalType: 'address', + name: 'owner', + type: 'address', }, ], - name: "Approved", - type: "event", + name: 'Approved', + type: 'event', }, { anonymous: false, - inputs: [ - { indexed: true, internalType: "bytes32", name: "id", type: "bytes32" }, - ], - name: "Executed", - type: "event", + inputs: [{ indexed: true, internalType: 'bytes32', name: 'id', type: 'bytes32' }], + name: 'Executed', + type: 'event', }, // --- FUNCTIONS --- { inputs: [], - name: "quorum", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", + name: 'quorum', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', }, { - inputs: [{ internalType: "uint256", name: "", type: "uint256" }], - name: "owners", - outputs: [{ internalType: "address", name: "", type: "address" }], - stateMutability: "view", - type: "function", + inputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + name: 'owners', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', }, { - inputs: [{ internalType: "bytes32", name: "id", type: "bytes32" }], - name: "approve", + inputs: [{ internalType: 'bytes32', name: 'id', type: 'bytes32' }], + name: 'approve', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, // Governance Proposals { inputs: [ - { internalType: "address", name: "identity", type: "address" }, - { internalType: "address", name: "newOwner", type: "address" }, + { internalType: 'address', name: 'identity', type: 'address' }, + { internalType: 'address', name: 'newOwner', type: 'address' }, ], - name: "proposeChangeOwner", - outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], - stateMutability: "nonpayable", - type: "function", + name: 'proposeChangeOwner', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function', }, { - inputs: [{ internalType: "address", name: "newOwner", type: "address" }], - name: "proposeAddOwner", - outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], - stateMutability: "nonpayable", - type: "function", + inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], + name: 'proposeAddOwner', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function', }, { - inputs: [{ internalType: "address", name: "owner", type: "address" }], - name: "proposeRemoveOwner", - outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], - stateMutability: "nonpayable", - type: "function", + inputs: [{ internalType: 'address', name: 'owner', type: 'address' }], + name: 'proposeRemoveOwner', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function', }, { - inputs: [{ internalType: "uint256", name: "newQuorum", type: "uint256" }], - name: "proposeQuorumUpdate", - outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], - stateMutability: "nonpayable", - type: "function", + inputs: [{ internalType: 'uint256', name: 'newQuorum', type: 'uint256' }], + name: 'proposeQuorumUpdate', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function', }, { inputs: [ - { internalType: "address", name: "target", type: "address" }, - { internalType: "bytes", name: "data", type: "bytes" }, + { internalType: 'address', name: 'target', type: 'address' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, ], - name: "execCall", + name: 'execCall', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, // --- INTERNAL TARGET FUNCTIONS (Needed for Decoding) --- { - inputs: [{ internalType: "uint256", name: "newQuorum", type: "uint256" }], - name: "_setQuorum", + inputs: [{ internalType: 'uint256', name: 'newQuorum', type: 'uint256' }], + name: '_setQuorum', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { - inputs: [{ internalType: "address", name: "newOwner", type: "address" }], - name: "_addOwner", + inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], + name: '_addOwner', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { - inputs: [{ internalType: "address", name: "owner", type: "address" }], - name: "_removeOwner", + inputs: [{ internalType: 'address', name: 'owner', type: 'address' }], + name: '_removeOwner', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { inputs: [ - { internalType: "address", name: "identity", type: "address" }, - { internalType: "address", name: "newOwner", type: "address" }, + { internalType: 'address', name: 'identity', type: 'address' }, + { internalType: 'address', name: 'newOwner', type: 'address' }, ], - name: "_executeChangeOwner", + name: '_executeChangeOwner', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, -] as const; +] as const export const REGISTRY_ABI = [ // Events { anonymous: false, inputs: [ - { indexed: true, internalType: "address", name: "identity", type: "address" }, - { indexed: false, internalType: "bytes32", name: "name", type: "bytes32" }, - { indexed: false, internalType: "bytes", name: "value", type: "bytes" }, - { indexed: false, internalType: "uint256", name: "validTo", type: "uint256" }, - { indexed: false, internalType: "uint256", name: "previousChange", type: "uint256" } + { indexed: true, internalType: 'address', name: 'identity', type: 'address' }, + { indexed: false, internalType: 'bytes32', name: 'name', type: 'bytes32' }, + { indexed: false, internalType: 'bytes', name: 'value', type: 'bytes' }, + { indexed: false, internalType: 'uint256', name: 'validTo', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'previousChange', type: 'uint256' }, ], - name: "DIDAttributeChanged", - type: "event", + name: 'DIDAttributeChanged', + type: 'event', }, // Functions { inputs: [ - { internalType: "address", name: "identity", type: "address" }, - { internalType: "address", name: "newOwner", type: "address" }, + { internalType: 'address', name: 'identity', type: 'address' }, + { internalType: 'address', name: 'newOwner', type: 'address' }, ], - name: "changeOwner", + name: 'changeOwner', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { - inputs: [{ internalType: "address", name: "identity", type: "address" }], - name: "identityOwner", - outputs: [{ internalType: "address", name: "", type: "address" }], - stateMutability: "view", - type: "function", + inputs: [{ internalType: 'address', name: 'identity', type: 'address' }], + name: 'identityOwner', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', }, { inputs: [ - { internalType: "address", name: "identity", type: "address" }, - { internalType: "bytes32", name: "name", type: "bytes32" }, - { internalType: "bytes", name: "value", type: "bytes" }, - { internalType: "uint256", name: "validity", type: "uint256" }, + { internalType: 'address', name: 'identity', type: 'address' }, + { internalType: 'bytes32', name: 'name', type: 'bytes32' }, + { internalType: 'bytes', name: 'value', type: 'bytes' }, + { internalType: 'uint256', name: 'validity', type: 'uint256' }, ], - name: "setAttribute", + name: 'setAttribute', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, -] as const; +] as const export const CRSET_REGISTRY_ABI = [ { inputs: [ - { internalType: "address", name: "companyDID", type: "address" }, - { internalType: "address", name: "admin", type: "address" }, + { internalType: 'address', name: 'companyDID', type: 'address' }, + { internalType: 'address', name: 'admin', type: 'address' }, ], - name: "addCompanyAdmin", + name: 'addCompanyAdmin', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { inputs: [ - { internalType: "address", name: "companyDID", type: "address" }, - { internalType: "address", name: "admin", type: "address" }, + { internalType: 'address', name: 'companyDID', type: 'address' }, + { internalType: 'address', name: 'admin', type: 'address' }, ], - name: "removeCompanyAdmin", + name: 'removeCompanyAdmin', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { inputs: [ - { internalType: "address", name: "companyDID", type: "address" }, - { internalType: "string", name: "newCID", type: "string" }, + { internalType: 'address', name: 'companyDID', type: 'address' }, + { internalType: 'string', name: 'newCID', type: 'string' }, ], - name: "updateRevocationCID", + name: 'updateRevocationCID', outputs: [], - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { - inputs: [ - { internalType: "address", name: "companyDID", type: "address" }, - ], - name: "getRevocationCID", - outputs: [{ internalType: "string", name: "", type: "string" }], - stateMutability: "view", - type: "function", + inputs: [{ internalType: 'address', name: 'companyDID', type: 'address' }], + name: 'getRevocationCID', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', }, { inputs: [ - { internalType: "address", name: "companyDID", type: "address" }, - { internalType: "address", name: "admin", type: "address" }, + { internalType: 'address', name: 'companyDID', type: 'address' }, + { internalType: 'address', name: 'admin', type: 'address' }, ], - name: "isCompanyAdmin", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "view", - type: "function", + name: 'isCompanyAdmin', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', }, { anonymous: false, inputs: [ - { indexed: true, internalType: "address", name: "companyDID", type: "address" }, - { indexed: false, internalType: "string", name: "newCID", type: "string" }, + { indexed: true, internalType: 'address', name: 'companyDID', type: 'address' }, + { indexed: false, internalType: 'string', name: 'newCID', type: 'string' }, ], - name: "RevocationCIDUpdated", - type: "event", + name: 'RevocationCIDUpdated', + type: 'event', }, -] as const; +] as const diff --git a/packages/demo-app-frontend/src/lib/ipfs.ts b/packages/demo-app-frontend/src/lib/ipfs.ts index 26b7cfb..695d79b 100644 --- a/packages/demo-app-frontend/src/lib/ipfs.ts +++ b/packages/demo-app-frontend/src/lib/ipfs.ts @@ -1,28 +1,28 @@ -const PINATA_JWT = import.meta.env.VITE_PINATA_JWT; +const PINATA_JWT = import.meta.env.VITE_PINATA_JWT // Upload file to IPFS with Pinata and return the CID export async function uploadToIPFS(file: File): Promise { try { - const formData = new FormData(); - formData.append('file', file); + const formData = new FormData() + formData.append('file', file) const response = await fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', { method: 'POST', headers: { - 'Authorization': `Bearer ${PINATA_JWT}` + Authorization: `Bearer ${PINATA_JWT}`, }, - body: formData - }); + body: formData, + }) if (!response.ok) { - throw new Error(`Pinata API error: ${response.statusText}`); + throw new Error(`Pinata API error: ${response.statusText}`) } - const data = await response.json(); - return data.IpfsHash; + const data = await response.json() + return data.IpfsHash } catch (error) { - console.error('Pinata upload error:', error); - throw new Error('Failed to upload to IPFS via Pinata. Please check your API key.'); + console.error('Pinata upload error:', error) + throw new Error('Failed to upload to IPFS via Pinata. Please check your API key.') } } @@ -33,19 +33,19 @@ export async function uploadJSONToIPFS(data: object): Promise { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${PINATA_JWT}` + Authorization: `Bearer ${PINATA_JWT}`, }, - body: JSON.stringify({ pinataContent: data }) - }); + body: JSON.stringify({ pinataContent: data }), + }) if (!response.ok) { - throw new Error(`Pinata API error: ${response.statusText}`); + throw new Error(`Pinata API error: ${response.statusText}`) } - const result = await response.json(); - return result.IpfsHash; + const result = await response.json() + return result.IpfsHash } catch (error) { - console.error('Pinata upload error:', error); - throw new Error('Failed to upload JSON to IPFS via Pinata'); + console.error('Pinata upload error:', error) + throw new Error('Failed to upload JSON to IPFS via Pinata') } } diff --git a/packages/demo-app-frontend/src/lib/proposalDecoder.ts b/packages/demo-app-frontend/src/lib/proposalDecoder.ts index b15800b..3bc2d60 100644 --- a/packages/demo-app-frontend/src/lib/proposalDecoder.ts +++ b/packages/demo-app-frontend/src/lib/proposalDecoder.ts @@ -1,71 +1,71 @@ -import { decodeFunctionData, type Hex } from "viem"; -import { TRUST_ANCHOR_ABI } from "./contracts"; +import { decodeFunctionData, type Hex } from 'viem' +import { TRUST_ANCHOR_ABI } from './contracts' export interface DecodedAction { - title: string; - details: string; - type: "governance" | "identity" | "unknown"; + title: string + details: string + type: 'governance' | 'identity' | 'unknown' } export function decodeProposalData(data: Hex): DecodedAction { - if (!data || data === "0x") { + if (!data || data === '0x') { return { - title: "Empty Proposal", - details: "No data provided", - type: "unknown", - }; + title: 'Empty Proposal', + details: 'No data provided', + type: 'unknown', + } } try { const taDecoded = decodeFunctionData({ abi: TRUST_ANCHOR_ABI, data: data, - }); + }) if (taDecoded) { // Cast to unknown first to handle readonly tuples from viem - const args = taDecoded.args as unknown as unknown[]; - const funcName = taDecoded.functionName as string; + const args = taDecoded.args as unknown as unknown[] + const funcName = taDecoded.functionName as string switch (funcName) { - case "_addOwner": + case '_addOwner': return { - title: "Add Admin", + title: 'Add Admin', details: formatAddress(args[0] as string), - type: "governance", - }; - case "_removeOwner": + type: 'governance', + } + case '_removeOwner': return { - title: "Remove Admin", + title: 'Remove Admin', details: formatAddress(args[0] as string), - type: "governance", - }; - case "_setQuorum": + type: 'governance', + } + case '_setQuorum': return { - title: "Update Quorum", + title: 'Update Quorum', details: `New Threshold: ${args[0]}`, - type: "governance", - }; - case "_executeChangeOwner": + type: 'governance', + } + case '_executeChangeOwner': return { - title: "Register Company", + title: 'Register Company', details: `Identity: ${formatAddress( args[0] as string )} → New Owner: ${formatAddress(args[1] as string)}`, - type: "identity", - }; - case "_setAttribute": + type: 'identity', + } + case '_setAttribute': return { - title: "Set Attribute", + title: 'Set Attribute', details: `Name: ${truncate(args[0] as string)}`, - type: "identity", - }; - case "_addDelegate": + type: 'identity', + } + case '_addDelegate': return { - title: "Add Delegate", + title: 'Add Delegate', details: `Delegate: ${formatAddress(args[1] as string)}`, - type: "identity", - }; + type: 'identity', + } } } } catch { @@ -73,19 +73,19 @@ export function decodeProposalData(data: Hex): DecodedAction { } return { - title: "Unknown Action", + title: 'Unknown Action', details: truncate(data, 20), - type: "unknown", - }; + type: 'unknown', + } } function formatAddress(addr: string): string { - if (!addr) return "?"; - return `${addr.slice(0, 6)}...${addr.slice(-4)}`; + if (!addr) return '?' + return `${addr.slice(0, 6)}...${addr.slice(-4)}` } function truncate(str: string, len: number = 10): string { - if (!str) return ""; - if (str.length <= len) return str; - return `${str.slice(0, len)}...`; + if (!str) return '' + if (str.length <= len) return str + return `${str.slice(0, len)}...` } diff --git a/packages/demo-app-frontend/src/lib/wagmi.ts b/packages/demo-app-frontend/src/lib/wagmi.ts index f6988f1..772a161 100644 --- a/packages/demo-app-frontend/src/lib/wagmi.ts +++ b/packages/demo-app-frontend/src/lib/wagmi.ts @@ -4,12 +4,10 @@ import { injected } from 'wagmi/connectors' export const config = createConfig({ chains: [hardhat, sepolia, mainnet], - connectors: [ - injected(), - ], + connectors: [injected()], transports: { [hardhat.id]: http(import.meta.env.VITE_HARDHAT_RPC_URL || 'http://127.0.0.1:8545'), [sepolia.id]: http(import.meta.env.VITE_SEPOLIA_RPC_URL), // Uses public RPC if not set [mainnet.id]: http(import.meta.env.VITE_MAINNET_RPC_URL), }, -}) \ No newline at end of file +}) diff --git a/packages/demo-app-frontend/src/main.tsx b/packages/demo-app-frontend/src/main.tsx index bef5202..2caec89 100644 --- a/packages/demo-app-frontend/src/main.tsx +++ b/packages/demo-app-frontend/src/main.tsx @@ -6,5 +6,5 @@ import App from './App.tsx' createRoot(document.getElementById('root')!).render( - , + ) diff --git a/packages/demo-app-frontend/src/pages/Home.tsx b/packages/demo-app-frontend/src/pages/Home.tsx index 50a6ae2..127b73b 100644 --- a/packages/demo-app-frontend/src/pages/Home.tsx +++ b/packages/demo-app-frontend/src/pages/Home.tsx @@ -6,33 +6,36 @@ import { Loader2, Shield, Globe, Lock, Cpu, AlertCircle, ArrowRight } from 'luci import { useTrustAnchorData } from '../hooks/useTrustAnchor' // --- SCROLL REVEAL COMPONENT --- -function RevealOnScroll({ children, delay = 0 }: { children: ReactNode, delay?: number }) { - const [isVisible, setIsVisible] = useState(false) - const ref = useRef(null) - - useEffect(() => { - const observer = new IntersectionObserver(([entry]) => { - if (entry.isIntersecting) { - setIsVisible(true) - observer.disconnect() // Reveal once - } - }, { threshold: 0.1 }) - - if (ref.current) observer.observe(ref.current) - return () => observer.disconnect() - }, []) - - return ( -
- {children} -
+function RevealOnScroll({ children, delay = 0 }: { children: ReactNode; delay?: number }) { + const [isVisible, setIsVisible] = useState(false) + const ref = useRef(null) + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true) + observer.disconnect() // Reveal once + } + }, + { threshold: 0.1 } ) + + if (ref.current) observer.observe(ref.current) + return () => observer.disconnect() + }, []) + + return ( +
+ {children} +
+ ) } export function HomePage() { @@ -63,153 +66,160 @@ export function HomePage() { return (
- {/* HEADER */}
-
-
- -
- Etherlink SSI +
+
+
- - {!isConnected && ( - - )} + Etherlink SSI +
+ + {!isConnected && ( + + )}
{/* HERO SECTION */}
- - {/* Left: Text Content */} - -
-
- - - - - Live on Tezos Etherlink + {/* Left: Text Content */} + +
+
+ + + + + Live on Tezos Etherlink +
+ +

+ The Future of
+ + Corporate Identity + +

+ +

+ A decentralized, on-chain governance framework for Verifiable Credentials. Establish + trust, manage DIDs, and secure your company's digital sovereignty with the power of + Etherlink. +

+ +
+ {isConnected ? ( + isTaError ? ( +
+ + + Network Error. Check Console/RPC. + +
+ ) : ( +
+ + Accessing Portal...
- -

- The Future of
- - Corporate Identity - -

- -

- A decentralized, on-chain governance framework for Verifiable Credentials. - Establish trust, manage DIDs, and secure your company's digital sovereignty - with the power of Etherlink. + ) + ) : ( + + )} +

+
+
+ + {/* Right: Feature Grid Visual */} +
+
+
+ +
+
+ +
+
+

Global Reach

+

+ Cross-border identity verification standard compliant with W3C.

+
+
+
-
- {isConnected ? ( - isTaError ? ( -
- - Network Error. Check Console/RPC. -
- ) : ( -
- - Accessing Portal... -
- ) - ) : ( - - )} -
+ +
+
+ +
+
+

Secure by Design

+

+ Multi-signature governance prevents single points of failure. +

+
-
- - {/* Right: Feature Grid Visual */} -
-
-
- - -
-
- -
-
-

Global Reach

-

Cross-border identity verification standard compliant with W3C.

-
-
-
- - -
-
- -
-
-

Secure by Design

-

Multi-signature governance prevents single points of failure.

-
-
-
- - -
-
- -
-
-

On-Chain Logic

-

Transparent execution via Smart Contracts on Etherlink.

-
-
-
- - -
-
- -
-
-

Self-Sovereign

-

Companies retain full cryptographic control of their data.

-
-
-
+ + + +
+
+ +
+
+

On-Chain Logic

+

+ Transparent execution via Smart Contracts on Etherlink. +

+
+
+ + +
+
+ +
+
+

Self-Sovereign

+

+ Companies retain full cryptographic control of their data. +

+
+
+
+
{/* FOOTER */}
-
- - Etherlink SSI -
- -
- © 2026 ASC-S e.V. All rights reserved. -
+
+ + Etherlink SSI +
+ +
+ © 2026 ASC-S e.V. All rights reserved. +
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/pages/company/Onboarding.tsx b/packages/demo-app-frontend/src/pages/company/Onboarding.tsx index 6bb4313..1b91d16 100644 --- a/packages/demo-app-frontend/src/pages/company/Onboarding.tsx +++ b/packages/demo-app-frontend/src/pages/company/Onboarding.tsx @@ -1,136 +1,185 @@ import { useAccount, useWriteContract, useWaitForTransactionReceipt, useReadContract } from 'wagmi' -import { REGISTRY_ADDRESS, REGISTRY_ABI, TRUST_ANCHOR_ADDRESS, CRSET_REGISTRY_ADDRESS, CRSET_REGISTRY_ABI } from '../../lib/contracts' -import { ArrowRight, CheckCircle2, Building, Loader2, ShieldCheck, Clock, RefreshCw } from 'lucide-react' +import { + REGISTRY_ADDRESS, + REGISTRY_ABI, + TRUST_ANCHOR_ADDRESS, + CRSET_REGISTRY_ADDRESS, + CRSET_REGISTRY_ABI, +} from '../../lib/contracts' +import { + ArrowRight, + CheckCircle2, + Building, + Loader2, + ShieldCheck, + Clock, + RefreshCw, +} from 'lucide-react' import { toast } from 'sonner' import { Link } from 'react-router-dom' import { useEffect } from 'react' interface StepProps { - num: number; - title: string; - desc: string; - status: 'pending' | 'current' | 'completed'; - isPending?: boolean; - isConfirming?: boolean; - isOwnerLoading?: boolean; - isAdminLoading?: boolean; - address?: `0x${string}`; - handOverControl?: () => void; - refetchAdmin?: () => void; + num: number + title: string + desc: string + status: 'pending' | 'current' | 'completed' + isPending?: boolean + isConfirming?: boolean + isOwnerLoading?: boolean + isAdminLoading?: boolean + address?: `0x${string}` + handOverControl?: () => void + refetchAdmin?: () => void } -const Step = ({ - num, - title, - desc, - status, - isPending, - isConfirming, - isOwnerLoading, - isAdminLoading, - address, - handOverControl, - refetchAdmin +const Step = ({ + num, + title, + desc, + status, + isPending, + isConfirming, + isOwnerLoading, + isAdminLoading, + address, + handOverControl, + refetchAdmin, }: StepProps) => ( -
-
- -
- {status === 'completed' ? : {num}} -
- -
-

{title}

-

{desc}

- - {status === 'current' && num === 1 && ( -
- -
- )} +
+
- {status === 'current' && num === 2 && ( -
- -
-

Verification Pending

-

Contact Trust Anchor to approve your registration.

-
- -
+
+ {status === 'completed' ? : {num}} +
+ +
+

+ {title} +

+

{desc}

+ + {status === 'current' && num === 1 && ( +
+
+ )} + + {status === 'current' && num === 2 && ( +
+ +
+

Verification Pending

+

+ Contact Trust Anchor to approve your registration. +

+
+ +
+ )}
+
) export function CompanyOnboardingPage() { const { address } = useAccount() - const { writeContract, data: hash, isPending } = useWriteContract({ + const { + writeContract, + data: hash, + isPending, + } = useWriteContract({ mutation: { - onError: (err) => toast.error("Delegation Failed", { description: err.message.split('\n')[0] }) - } + onError: (err) => + toast.error('Delegation Failed', { description: err.message.split('\n')[0] }), + }, }) - + const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash }) // 1. Check Identity Ownership - const { data: identityOwner, isLoading: isOwnerLoading, refetch: refetchOwner } = useReadContract({ - address: REGISTRY_ADDRESS, - abi: REGISTRY_ABI, - functionName: 'identityOwner', - args: address ? [address] : undefined, - query: { enabled: !!address, refetchInterval: 3000 } + const { + data: identityOwner, + isLoading: isOwnerLoading, + refetch: refetchOwner, + } = useReadContract({ + address: REGISTRY_ADDRESS, + abi: REGISTRY_ABI, + functionName: 'identityOwner', + args: address ? [address] : undefined, + query: { enabled: !!address, refetchInterval: 3000 }, }) // 2. Check CRSet Admin Status - const { data: isCompanyAdmin, isLoading: isAdminLoading, refetch: refetchAdmin } = useReadContract({ - address: CRSET_REGISTRY_ADDRESS, - abi: CRSET_REGISTRY_ABI, - functionName: 'isCompanyAdmin', - args: address ? [address, address] : undefined, - query: { enabled: !!address, refetchInterval: 3000 } + const { + data: isCompanyAdmin, + isLoading: isAdminLoading, + refetch: refetchAdmin, + } = useReadContract({ + address: CRSET_REGISTRY_ADDRESS, + abi: CRSET_REGISTRY_ABI, + functionName: 'isCompanyAdmin', + args: address ? [address, address] : undefined, + query: { enabled: !!address, refetchInterval: 3000 }, }) // Refetch data when transaction succeeds useEffect(() => { - if (isSuccess) { - refetchOwner() - setTimeout(refetchOwner, 2000) // Double check for indexing delay - } + if (isSuccess) { + refetchOwner() + setTimeout(refetchOwner, 2000) // Double check for indexing delay + } }, [isSuccess, refetchOwner]) // DERIVE STATE - const isManaged = identityOwner && TRUST_ANCHOR_ADDRESS && - identityOwner.toLowerCase() === TRUST_ANCHOR_ADDRESS.toLowerCase() + const isManaged = + identityOwner && + TRUST_ANCHOR_ADDRESS && + identityOwner.toLowerCase() === TRUST_ANCHOR_ADDRESS.toLowerCase() const isFullyOnboarded = isManaged && isCompanyAdmin const handOverControl = () => { if (!address) return - toast.info("Check your wallet", { description: "Please sign the delegation transaction." }) + toast.info('Check your wallet', { description: 'Please sign the delegation transaction.' }) writeContract({ address: REGISTRY_ADDRESS, abi: REGISTRY_ABI, functionName: 'changeOwner', - args: [address, TRUST_ANCHOR_ADDRESS], + args: [address, TRUST_ANCHOR_ADDRESS], }) } @@ -138,91 +187,96 @@ export function CompanyOnboardingPage() {

Company Onboarding

-

Secure your digital identity within the Trust Anchor ecosystem.

+

+ Secure your digital identity within the Trust Anchor ecosystem. +

- - {/* LEFT: STEPS (Non-blocking) */} -
-
- - - -
+ {/* LEFT: STEPS (Non-blocking) */} +
+
+ + +
+
+ + {/* RIGHT: STATUS CARD */} +
+
+
+
+ +
+
+

Your Identity

+

+ {address || 'Not Connected'} +

+
+
- {/* RIGHT: STATUS CARD */} -
-
-
-
- -
-
-

Your Identity

-

{address || "Not Connected"}

-
-
- -
-
- Identity Status - {isOwnerLoading ? ( - - ) : ( - - {isManaged ? 'Managed by TA' : 'Self-Sovereign'} - - )} -
-
- Service Access - {isAdminLoading ? ( - - ) : ( - - {isCompanyAdmin ? 'Authorized' : 'Restricted'} - - )} -
-
- - {isFullyOnboarded && ( -
- - Go to Revocation Manager - -
- )} +
+
+ Identity Status + {isOwnerLoading ? ( + + ) : ( + + {isManaged ? 'Managed by TA' : 'Self-Sovereign'} + + )}
+
+ Service Access + {isAdminLoading ? ( + + ) : ( + + {isCompanyAdmin ? 'Authorized' : 'Restricted'} + + )} +
+
+ + {isFullyOnboarded && ( +
+ + Go to Revocation Manager + +
+ )}
+
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/pages/company/RevocationList.tsx b/packages/demo-app-frontend/src/pages/company/RevocationList.tsx index ecc79ba..0a98a22 100644 --- a/packages/demo-app-frontend/src/pages/company/RevocationList.tsx +++ b/packages/demo-app-frontend/src/pages/company/RevocationList.tsx @@ -6,76 +6,81 @@ import { uploadToIPFS } from '../../lib/ipfs' import { toast } from 'sonner' interface StatusCardProps { - isReadingCID: boolean; - currentCID: string | undefined; + isReadingCID: boolean + currentCID: string | undefined } const StatusCard = ({ isReadingCID, currentCID }: StatusCardProps) => ( -
-

- - Current Version -

- -
- {isReadingCID ? ( -
-
-
-
- ) : currentCID ? ( -
-

Active IPFS CID

-

{currentCID}

- -
- - View Raw Data - - - Synced - -
-
- ) : ( -
- -

No revocation list published.

-
- )} +
+

+ + Current Version +

+ +
+ {isReadingCID ? ( +
+
+
+
+ ) : currentCID ? ( +
+

Active IPFS CID

+

{currentCID}

+ +
+ + View Raw Data + + + Synced + +
+
+ ) : ( +
+ +

No revocation list published.

+ )}
+
) export function RevocationListPage() { const { address } = useAccount() const fileInputRef = useRef(null) - + const [selectedFile, setSelectedFile] = useState(null) const [fileContent, setFileContent] = useState(null) const [isUploading, setIsUploading] = useState(false) - + // Read Current CID const { data: currentCID, isLoading: isReadingCID } = useReadContract({ address: CRSET_REGISTRY_ADDRESS, abi: CRSET_REGISTRY_ABI, functionName: 'getRevocationCID', args: address ? [address] : undefined, - query: { enabled: !!address } + query: { enabled: !!address }, }) // Write Contract - const { writeContract, data: hash, isPending } = useWriteContract({ - mutation: { - onError: (err) => toast.error("Transaction Failed", { description: err.message.split('\n')[0] }) - } + const { + writeContract, + data: hash, + isPending, + } = useWriteContract({ + mutation: { + onError: (err) => + toast.error('Transaction Failed', { description: err.message.split('\n')[0] }), + }, }) - + const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash }) // Handle File Selection @@ -84,25 +89,25 @@ export function RevocationListPage() { if (!file) return if (file.type !== 'application/json') { - toast.error("Invalid File", { description: "Please upload a .json file" }) + toast.error('Invalid File', { description: 'Please upload a .json file' }) return } // Client-side validation const text = await file.text() try { - JSON.parse(text) // Validate JSON syntax - setSelectedFile(file) - setFileContent(text) + JSON.parse(text) // Validate JSON syntax + setSelectedFile(file) + setFileContent(text) } catch { - toast.error("Invalid JSON", { description: "File content is not valid JSON" }) + toast.error('Invalid JSON', { description: 'File content is not valid JSON' }) } } const clearFile = () => { - setSelectedFile(null) - setFileContent(null) - if (fileInputRef.current) fileInputRef.current.value = '' + setSelectedFile(null) + setFileContent(null) + if (fileInputRef.current) fileInputRef.current.value = '' } const handlePublish = async () => { @@ -110,22 +115,22 @@ export function RevocationListPage() { setIsUploading(true) try { - // 1. Upload to IPFS - const cid = await uploadToIPFS(selectedFile) - setIsUploading(false) - - // 2. Update Contract - toast.info("IPFS Uploaded", { description: `CID: ${cid}. Confirming on-chain...` }) - - writeContract({ - address: CRSET_REGISTRY_ADDRESS, - abi: CRSET_REGISTRY_ABI, - functionName: 'updateRevocationCID', - args: [address, cid], - }) + // 1. Upload to IPFS + const cid = await uploadToIPFS(selectedFile) + setIsUploading(false) + + // 2. Update Contract + toast.info('IPFS Uploaded', { description: `CID: ${cid}. Confirming on-chain...` }) + + writeContract({ + address: CRSET_REGISTRY_ADDRESS, + abi: CRSET_REGISTRY_ABI, + functionName: 'updateRevocationCID', + args: [address, cid], + }) } catch { - setIsUploading(false) - toast.error("Upload Failed", { description: "Failed to upload to IPFS" }) + setIsUploading(false) + toast.error('Upload Failed', { description: 'Failed to upload to IPFS' }) } } @@ -133,95 +138,110 @@ export function RevocationListPage() {

Revocation Management

-

Publish updated Credential Revocation Lists (CRL) to IPFS and the blockchain.

+

+ Publish updated Credential Revocation Lists (CRL) to IPFS and the blockchain. +

- {/* LEFT: UPLOAD ZONE (2 Cols) */} -
-
+
+ {!selectedFile ? ( +
fileInputRef.current?.click()} > - {!selectedFile ? ( -
fileInputRef.current?.click()}> -
- -
-

Upload JSON List

-

- Drag and drop or click to upload your W3C Verifiable Credential revocation list. -

- -
- ) : ( -
-
- -
-

{selectedFile.name}

-

- {(selectedFile.size / 1024).toFixed(2)} KB • Ready to publish -

- - -
- )} +
+ +
+

Upload JSON List

+

+ Drag and drop or click to upload your W3C Verifiable Credential revocation list. +

+
+ ) : ( +
+
+ +
+

+ {selectedFile.name} +

+

+ {(selectedFile.size / 1024).toFixed(2)} KB • Ready to publish +

- {/* PREVIEW & ACTION */} - {selectedFile && ( -
-
-

Preview

- JSON -
-
-                          {fileContent}
-                      
-
- - {isSuccess && ( -

- ✓ Successfully published! -

- )} -
-
- )} + +
+ )}
- {/* RIGHT: CURRENT STATUS (1 Col) */} -
- -
+ {/* PREVIEW & ACTION */} + {selectedFile && ( +
+
+

+ Preview +

+ JSON +
+
+                {fileContent}
+              
+
+ + {isSuccess && ( +

+ ✓ Successfully published! +

+ )} +
+
+ )} +
+ + {/* RIGHT: CURRENT STATUS (1 Col) */} +
+ +
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/pages/trust-anchor/Companies.tsx b/packages/demo-app-frontend/src/pages/trust-anchor/Companies.tsx index c0766f6..a0373ed 100644 --- a/packages/demo-app-frontend/src/pages/trust-anchor/Companies.tsx +++ b/packages/demo-app-frontend/src/pages/trust-anchor/Companies.tsx @@ -1,16 +1,33 @@ -import { Loader2, CheckCircle2, AlertTriangle, Search, Building2, UserPlus, UserMinus, Link, Clock, Info } from 'lucide-react' +import { + Loader2, + CheckCircle2, + AlertTriangle, + Search, + Building2, + UserPlus, + UserMinus, + Link, + Clock, + Info, +} from 'lucide-react' import { useState, useEffect } from 'react' import { useReadContract, usePublicClient } from 'wagmi' import { useCRSetManagement } from '../../hooks/useCRSetManagement' import { useDIDSync, useReadCID } from '../../hooks/useDIDSync' -import { REGISTRY_ADDRESS, REGISTRY_ABI, TRUST_ANCHOR_ADDRESS, CRSET_REGISTRY_ADDRESS, CRSET_REGISTRY_ABI } from '../../lib/contracts' +import { + REGISTRY_ADDRESS, + REGISTRY_ABI, + TRUST_ANCHOR_ADDRESS, + CRSET_REGISTRY_ADDRESS, + CRSET_REGISTRY_ABI, +} from '../../lib/contracts' import { isAddress, keccak256, toBytes } from 'viem' import { toast } from 'sonner' export function CompaniesPage() { const [inputAddress, setInputAddress] = useState('') const [debouncedAddress, setDebouncedAddress] = useState<`0x${string}` | undefined>(undefined) - + // State for Admin Tools const [adminAddress, setAdminAddress] = useState('') const [hasStaticEndpoint, setHasStaticEndpoint] = useState(false) @@ -18,18 +35,23 @@ export function CompaniesPage() { // Hooks const publicClient = usePublicClient() - const { addCompanyAdmin, removeCompanyAdmin, isPending: isAdminPending, isSuccess: isAdminSuccess } = useCRSetManagement() + const { + addCompanyAdmin, + removeCompanyAdmin, + isPending: isAdminPending, + isSuccess: isAdminSuccess, + } = useCRSetManagement() const { syncCIDToDID, isPending: isSyncPending, isSuccess: isSyncSuccess } = useDIDSync() const { cid: companyCID, isLoading: isCIDLoading } = useReadCID(debouncedAddress) // Debounce Search useEffect(() => { const timer = setTimeout(() => { - if (isAddress(inputAddress)) { - setDebouncedAddress(inputAddress as `0x${string}`) - } else { - setDebouncedAddress(undefined) - } + if (isAddress(inputAddress)) { + setDebouncedAddress(inputAddress as `0x${string}`) + } else { + setDebouncedAddress(undefined) + } }, 500) return () => clearTimeout(timer) }, [inputAddress]) @@ -38,32 +60,34 @@ export function CompaniesPage() { useEffect(() => { async function checkEndpoint() { if (!debouncedAddress || !publicClient) return - + setIsCheckingEndpoint(true) try { const attributeName = keccak256(toBytes('did/svc/CredentialRevocationList')) - + // Use proper ABI from contracts.ts const didAttributeChangedEvent = REGISTRY_ABI.find( (item) => item.type === 'event' && item.name === 'DIDAttributeChanged' ) - + if (!didAttributeChangedEvent) { console.error('DIDAttributeChanged event not found in ABI') setHasStaticEndpoint(false) setIsCheckingEndpoint(false) return } - + const logs = await publicClient.getLogs({ address: REGISTRY_ADDRESS, event: didAttributeChangedEvent as typeof didAttributeChangedEvent, args: { identity: debouncedAddress }, - fromBlock: 0n + fromBlock: 0n, }) - + // check if any event matches the attribute name - const endpointSet = logs.some((log: unknown) => (log as { args: { name: string } }).args.name === attributeName) + const endpointSet = logs.some( + (log: unknown) => (log as { args: { name: string } }).args.name === attributeName + ) setHasStaticEndpoint(endpointSet) } catch (error) { console.error('Error checking endpoint:', error) @@ -72,17 +96,17 @@ export function CompaniesPage() { setIsCheckingEndpoint(false) } } - + checkEndpoint() }, [debouncedAddress, publicClient]) // Notifications useEffect(() => { - if (isAdminSuccess) toast.success("Admin Updated Successfully") - if (isSyncSuccess) { - toast.success("Static Endpoint Initialized") - setHasStaticEndpoint(true) - } + if (isAdminSuccess) toast.success('Admin Updated Successfully') + if (isSyncSuccess) { + toast.success('Static Endpoint Initialized') + setHasStaticEndpoint(true) + } }, [isAdminSuccess, isSyncSuccess]) // 1. READ: Identity Owner @@ -91,22 +115,24 @@ export function CompaniesPage() { abi: REGISTRY_ABI, functionName: 'identityOwner', args: debouncedAddress ? [debouncedAddress] : undefined, - query: { enabled: !!debouncedAddress } + query: { enabled: !!debouncedAddress }, }) // 2. READ: Is Already Admin (Registered) const { data: isRegisteredAdmin, isLoading: isCheckingAdmin } = useReadContract({ - address: CRSET_REGISTRY_ADDRESS, - abi: CRSET_REGISTRY_ABI, - functionName: 'isCompanyAdmin', - args: debouncedAddress ? [debouncedAddress, debouncedAddress] : undefined, // Check if company is admin of itself - query: { enabled: !!debouncedAddress } + address: CRSET_REGISTRY_ADDRESS, + abi: CRSET_REGISTRY_ABI, + functionName: 'isCompanyAdmin', + args: debouncedAddress ? [debouncedAddress, debouncedAddress] : undefined, // Check if company is admin of itself + query: { enabled: !!debouncedAddress }, }) const isLoading = isCheckingOwner || isCheckingAdmin // --- LOGIC: 3 STATES --- - const isManaged = currentOwner && TRUST_ANCHOR_ADDRESS && + const isManaged = + currentOwner && + TRUST_ANCHOR_ADDRESS && currentOwner.toLowerCase() === TRUST_ANCHOR_ADDRESS.toLowerCase() // State 1: Unmanaged (Needs Delegation) @@ -117,258 +143,287 @@ export function CompaniesPage() { // Admin Handlers const handleAddAdmin = () => { - if (!debouncedAddress || !adminAddress) return - addCompanyAdmin(debouncedAddress, adminAddress as `0x${string}`) + if (!debouncedAddress || !adminAddress) return + addCompanyAdmin(debouncedAddress, adminAddress as `0x${string}`) } const handleRemoveAdmin = () => { - if (!debouncedAddress || !adminAddress) return - removeCompanyAdmin(debouncedAddress, adminAddress as `0x${string}`) + if (!debouncedAddress || !adminAddress) return + removeCompanyAdmin(debouncedAddress, adminAddress as `0x${string}`) } const handleSync = () => { - if (!debouncedAddress) return - syncCIDToDID(debouncedAddress) + if (!debouncedAddress) return + syncCIDToDID(debouncedAddress) } // --- UI RENDERERS --- const renderStatus = () => { - if (!inputAddress) return ( -
- -

Enter a DID address to check status

-
- ) - - if (!isAddress(inputAddress)) return ( -
- -

Invalid Ethereum Address

-
+ if (!inputAddress) + return ( +
+ +

Enter a DID address to check status

+
) - if (isLoading) return ( -
- -

Verifying Identity Status...

-
+ if (!isAddress(inputAddress)) + return ( +
+ +

Invalid Ethereum Address

+
) - // STATE 1: Unmanaged (Red) - if (isUnmanaged) { - return ( -
-
-
-
- -
-
-

Action Required: Delegation

-

- This identity is NOT controlled by the Trust Anchor. The company must delegate control first via the Onboarding page. -

-
-
-
-
- ) - } + if (isLoading) + return ( +
+ +

Verifying Identity Status...

+
+ ) - // STATE 2: Pending Registration (Yellow) - if (isPendingRegistration) { - return ( -
-
-
-
- -
-
-

Identity Managed (Step 1 Complete)

-

- The Trust Anchor owns this identity, but the company wallet has no write access yet. -

-
- - Action Required: Add this address to "CRSet Admins" below. -
-
-
-
+ // STATE 1: Unmanaged (Red) + if (isUnmanaged) { + return ( +
+
+
+
+ +
+
+

Action Required: Delegation

+

+ This identity is NOT controlled by the Trust Anchor. The company must delegate + control first via the Onboarding page. +

+
- ) - } +
+
+ ) + } - // STATE 3: Fully Registered (Green) + // STATE 2: Pending Registration (Yellow) + if (isPendingRegistration) { return (
-
-
-
- -
-
-

Fully Verified

-

- Identity is managed and the company has full admin access to their CRSet. -

-
+
+
+
+ +
+
+

+ Identity Managed (Step 1 Complete) +

+

+ The Trust Anchor owns this identity, but the company wallet has no write access + yet. +

+
+ + Action Required: Add this address to "CRSet Admins" below.
+
- {/* No Register Button here - it's already done */} +
) + } + + // STATE 3: Fully Registered (Green) + return ( +
+
+
+
+ +
+
+

Fully Verified

+

+ Identity is managed and the company has full admin access to their CRSet. +

+
+
+
+ {/* No Register Button here - it's already done */} +
+ ) } // ... (Advanced Tools render remains the same) const renderAdvancedTools = () => { - if (!isManaged || !debouncedAddress) return null; + if (!isManaged || !debouncedAddress) return null - return ( -
- {/* TOOL 1: ADMIN MANAGEMENT */} -
-

- - CRSet Admins -

-
- setAdminAddress(e.target.value)} - className="w-full px-4 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500/20" - /> -
- - -
-
-
+ return ( +
+ {/* TOOL 1: ADMIN MANAGEMENT */} +
+

+ + CRSet Admins +

+
+ setAdminAddress(e.target.value)} + className="w-full px-4 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500/20" + /> +
+ + +
+
+
- {/* TOOL 2: STATIC ENDPOINT SETUP */} -
-

- - Static Endpoint Setup -

-
- {/* Info Banner */} -
- -

- One-time setup that links this company's DID to their revocation list contract. After initialization, verifiers read CID updates automatically. -

-
- - {/* Current Status */} -
-

Endpoint Status

- {isCheckingEndpoint ? ( -
- - Checking... -
- ) : hasStaticEndpoint ? ( -
- - Initialized -
- ) : ( -
- - Not Set -
- )} -
- - {/* Current CID Preview */} -
-

Latest Published CID

- {isCIDLoading ? ( - - ) : companyCID ? ( -
-

{companyCID}

-

Verifiers read this automatically via the static endpoint

-
- ) : ( -

No CID published yet

- )} -
- - {/* Action Button */} - - - {hasStaticEndpoint && ( -

- No further action needed. Verifiers will read CID updates automatically from the contract. -

- )} -
-
+ {/* TOOL 2: STATIC ENDPOINT SETUP */} +
+

+ + Static Endpoint Setup +

+
+ {/* Info Banner */} +
+ +

+ One-time setup that links this company's DID to their revocation list contract. + After initialization, verifiers read CID updates automatically. +

+
+ + {/* Current Status */} +
+

Endpoint Status

+ {isCheckingEndpoint ? ( +
+ + Checking... +
+ ) : hasStaticEndpoint ? ( +
+ + Initialized +
+ ) : ( +
+ + Not Set +
+ )} +
+ + {/* Current CID Preview */} +
+

+ Latest Published CID +

+ {isCIDLoading ? ( + + ) : companyCID ? ( +
+

{companyCID}

+

+ Verifiers read this automatically via the static endpoint +

+
+ ) : ( +

No CID published yet

+ )} +
+ + {/* Action Button */} + + + {hasStaticEndpoint && ( +

+ No further action needed. Verifiers will read CID updates automatically from the + contract. +

+ )}
- ) +
+
+ ) } return (
-

Company Registry

-

Lookup and manage decentralized identities.

+

Company Registry

+

Lookup and manage decentralized identities.

-

- - Identity Lookup -

- -
- setInputAddress(e.target.value)} - className="w-full pl-11 pr-4 py-3 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500/20 font-mono text-sm transition-all" - /> - -
+

+ + Identity Lookup +

-
- {renderStatus()} -
+
+ setInputAddress(e.target.value)} + className="w-full pl-11 pr-4 py-3 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500/20 font-mono text-sm transition-all" + /> + +
+ +
{renderStatus()}
{/* Advanced Tools */} {renderAdvancedTools()}
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/pages/trust-anchor/Dashboard.tsx b/packages/demo-app-frontend/src/pages/trust-anchor/Dashboard.tsx index b96df0b..39b1715 100644 --- a/packages/demo-app-frontend/src/pages/trust-anchor/Dashboard.tsx +++ b/packages/demo-app-frontend/src/pages/trust-anchor/Dashboard.tsx @@ -10,53 +10,52 @@ export function TrustAnchorDashboard() { const { quorum, owners, proposals, totalAdmins, approvals, isLoading } = useTrustAnchorData() const { isConnected, address } = useAccount() const { approveProposal, isPending } = useGovernance() - + const [filter, setFilter] = useState<'all' | 'needs_vote'>('needs_vote') - const isTrustAnchorAdmin = isConnected && address && owners.some( - (owner) => owner.toLowerCase() === address.toLowerCase() - ) + const isTrustAnchorAdmin = + isConnected && address && owners.some((owner) => owner.toLowerCase() === address.toLowerCase()) - const filteredProposals = proposals.filter(p => { - // In a real app, you might hide executed proposals or move them to a separate list - // For this demo, we assume the hook returns active proposals - if (filter === 'all') return true - - const hasVoted = address && approvals[p.id]?.includes(address) - return !hasVoted + const filteredProposals = proposals.filter((p) => { + // In a real app, you might hide executed proposals or move them to a separate list + // For this demo, we assume the hook returns active proposals + if (filter === 'all') return true + + const hasVoted = address && approvals[p.id]?.includes(address) + return !hasVoted }) return (
-

Trust Anchor Overview

-

Managing the root of trust on Etherlink.

+

Trust Anchor Overview

+

Managing the root of trust on Etherlink.

- +
-
- System Operational +
+ System Operational
- } description="Signatures required" trend="neutral" /> - } description="Governance members" /> - } description="Pending actions" /> @@ -64,71 +63,75 @@ export function TrustAnchorDashboard() {
-

- - Governance Proposals -

- -
- - -
+

+ + Governance Proposals +

+ +
+ + +
{filteredProposals.length > 0 ? ( -
- {filteredProposals.map((p) => { - const currentVotes = approvals[p.id]?.length || 0 - const hasVoted = address ? approvals[p.id]?.includes(address) : false +
+ {filteredProposals.map((p) => { + const currentVotes = approvals[p.id]?.length || 0 + const hasVoted = address ? approvals[p.id]?.includes(address) : false - return ( - - ) - })} -
+ return ( + + ) + })} +
) : ( -
-
- -
-

No proposals found

-

- {filter === 'needs_vote' - ? "You're all caught up! No pending votes." - : "There are no active proposals in the system."} -

+
+
+
+

No proposals found

+

+ {filter === 'needs_vote' + ? "You're all caught up! No pending votes." + : 'There are no active proposals in the system.'} +

+
)}

- Network Administrators + Network Administrators

@@ -138,11 +141,11 @@ export function TrustAnchorDashboard() { {owner} - {owner.toLowerCase() === address?.toLowerCase() && ( - - You - - )} + {owner.toLowerCase() === address?.toLowerCase() && ( + + You + + )} ))} @@ -152,4 +155,4 @@ export function TrustAnchorDashboard() {
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/src/pages/trust-anchor/Governance.tsx b/packages/demo-app-frontend/src/pages/trust-anchor/Governance.tsx index b4b5c81..4d30ba9 100644 --- a/packages/demo-app-frontend/src/pages/trust-anchor/Governance.tsx +++ b/packages/demo-app-frontend/src/pages/trust-anchor/Governance.tsx @@ -23,7 +23,7 @@ export function GovernancePage() { ) => { const error = validator() if (error) { - toast.error("Validation Error", { description: error }) + toast.error('Validation Error', { description: error }) return } toast.promise( @@ -37,16 +37,16 @@ export function GovernancePage() { }), { loading: `Proposing ${actionName}...`, - success: "Proposal Created!", - error: "Failed to create proposal" + success: 'Proposal Created!', + error: 'Failed to create proposal', } ) } const handleAddOwner = () => { handleAction( - "Add Admin", - () => !isAddress(newAdminAddress) ? "Invalid Ethereum Address" : null, + 'Add Admin', + () => (!isAddress(newAdminAddress) ? 'Invalid Ethereum Address' : null), () => { proposeAddOwner(newAdminAddress as `0x${string}`) setNewAdminAddress('') @@ -56,10 +56,11 @@ export function GovernancePage() { const handleRemoveOwner = () => { handleAction( - "Remove Admin", + 'Remove Admin', () => { - if (!isAddress(removeAdminAddress)) return "Invalid Ethereum Address" - if (quorum > totalAdmins - 1) return "Warning: Removing this admin will verify lock the contract (Quorum > Total). Update Quorum first!" + if (!isAddress(removeAdminAddress)) return 'Invalid Ethereum Address' + if (quorum > totalAdmins - 1) + return 'Warning: Removing this admin will verify lock the contract (Quorum > Total). Update Quorum first!' return null }, () => { @@ -71,11 +72,11 @@ export function GovernancePage() { const handleQuorumUpdate = () => { handleAction( - "Update Quorum", + 'Update Quorum', () => { const val = parseInt(newQuorumValue) - if (isNaN(val) || val <= 0) return "Quorum must be at least 1" - if (val > totalAdmins) return "Quorum cannot exceed total admins" + if (isNaN(val) || val <= 0) return 'Quorum must be at least 1' + if (val > totalAdmins) return 'Quorum cannot exceed total admins' return null }, () => { @@ -85,153 +86,155 @@ export function GovernancePage() { ) } - if (!isConnected) return ( -
- + if (!isConnected) + return ( +
+

Please connect your admin wallet to access governance.

-
- ) +
+ ) return (

Governance Actions

-

Execute critical changes to the Trust Anchor configuration.

+

+ Execute critical changes to the Trust Anchor configuration. +

- {/* SECTION: ADMIN MANAGEMENT */}
-

- Admin Management -

- - {/* ADD CARD */} -
-
-
-

Add New Admin

-

Grant full governance rights.

-
-
- -
-
- -
- setNewAdminAddress(e.target.value)} - className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500/20 font-mono text-sm" - /> - -
-
- -

Requires 100% consensus (Unanimity).

-
+

+ Admin Management +

+ + {/* ADD CARD */} +
+
+
+

Add New Admin

+

Grant full governance rights.

+
+
+ +
+
+ +
+ setNewAdminAddress(e.target.value)} + className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500/20 font-mono text-sm" + /> + +
+
+ +

Requires 100% consensus (Unanimity).

+
+
+ + {/* REMOVE CARD */} +
+
+
+

Remove Admin

+

Revoke all access rights.

+
+
+ +
- {/* REMOVE CARD */} -
-
-
-

Remove Admin

-

Revoke all access rights.

-
-
- -
-
- -
- setRemoveAdminAddress(e.target.value)} - className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500/20 font-mono text-sm" - /> - -
+
+ setRemoveAdminAddress(e.target.value)} + className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500/20 font-mono text-sm" + /> +
+
{/* SECTION: SECURITY CONFIG */}
-

- Security Configuration -

- - {/* QUORUM CARD */} -
-
-
-

Update Quorum

-

Change M-of-N threshold.

-
-
- -
-
- -
-
-

Current

-

{quorum}

-
-
-

Total Admins

-

{totalAdmins}

-
-
- -
-
- setNewQuorumValue(e.target.value)} - className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500/20 font-mono text-sm" - /> - {/* / {totalAdmins} */} -
- - -
- -
- -

- Warning: Setting a low quorum reduces security. Setting it too high might lock the system if admins lose access. -

-
+

+ Security Configuration +

+ + {/* QUORUM CARD */} +
+
+
+

Update Quorum

+

Change M-of-N threshold.

+
+
+ +
+
+ +
+
+

Current

+

{quorum}

+
+
+

Total Admins

+

{totalAdmins}

+
-
+
+
+ setNewQuorumValue(e.target.value)} + className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500/20 font-mono text-sm" + /> + {/* / {totalAdmins} */} +
+ + +
+ +
+ +

+ Warning: Setting a low quorum reduces security. Setting it too high + might lock the system if admins lose access. +

+
+
+
) -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/tailwind.config.ts b/packages/demo-app-frontend/tailwind.config.ts index 89a305e..7141e45 100644 --- a/packages/demo-app-frontend/tailwind.config.ts +++ b/packages/demo-app-frontend/tailwind.config.ts @@ -1,11 +1,8 @@ /** @type {import('tailwindcss').Config} */ export default { - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], theme: { extend: {}, }, plugins: [], -} \ No newline at end of file +} diff --git a/packages/demo-app-frontend/tsconfig.json b/packages/demo-app-frontend/tsconfig.json index 1ffef60..d32ff68 100644 --- a/packages/demo-app-frontend/tsconfig.json +++ b/packages/demo-app-frontend/tsconfig.json @@ -1,7 +1,4 @@ { "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] } From 4d10076d9f4b58320582c1029a21f06ee79ae56b Mon Sep 17 00:00:00 2001 From: siyovush-hamidov Date: Fri, 6 Feb 2026 11:25:49 +0100 Subject: [PATCH 3/3] Fix: Add missing lint scripts and dependencies --- packages/trust-anchor-did-ethr/package-lock.json | 6 +++--- packages/trust-anchor-did-ethr/package.json | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/trust-anchor-did-ethr/package-lock.json b/packages/trust-anchor-did-ethr/package-lock.json index 4907394..be357b7 100644 --- a/packages/trust-anchor-did-ethr/package-lock.json +++ b/packages/trust-anchor-did-ethr/package-lock.json @@ -2924,9 +2924,9 @@ "license": "MIT" }, "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", "dev": true, "license": "MIT" }, diff --git a/packages/trust-anchor-did-ethr/package.json b/packages/trust-anchor-did-ethr/package.json index 269e9b6..e5fb782 100644 --- a/packages/trust-anchor-did-ethr/package.json +++ b/packages/trust-anchor-did-ethr/package.json @@ -26,5 +26,8 @@ "@nomicfoundation/ignition-core": { "lodash-es": "^4.17.21" } + }, + "scripts": { + "lint": "solhint 'contracts/**/*.sol'" } }