From 89011ce46528f476a1b8920f160d367fd2fef1cf Mon Sep 17 00:00:00 2001 From: Lukasz Kosiak Date: Thu, 16 Apr 2026 18:47:00 +0200 Subject: [PATCH 1/3] delivering evm for governance portal --- .../generate-copilot-instructions/SKILL.md | 28 + .github/copilot-instructions.md | 113 + .github/workflows/cicd-prd.yml | 8 +- CLAUDE.md | 118 + package.json | 3 +- products/.env.governance.example | 22 + products/GOVERNANCE.md | 246 + products/docker-compose.yml | 107 + products/governance-api/.dockerignore | 28 + products/governance-api/.nvmrc | 1 + products/governance-api/Dockerfile | 17 +- products/governance-api/lib/routes/message.ts | 53 +- products/governance-api/lib/utils/pin-ipfs.ts | 55 +- .../lib/utils/verify-evm-signature.ts | 19 + products/governance-api/package.json | 3 +- products/governance-api/yarn.lock | 335 +- products/governance-snapshot/.dockerignore | 37 + products/governance-snapshot/.gitignore | 2 +- products/governance-snapshot/.nvmrc | 1 + products/governance-snapshot/Dockerfile | 19 +- products/governance-snapshot/README.md | 113 +- products/governance-snapshot/config.docker.js | 3 + products/governance-snapshot/package.json | 123 +- .../governance-snapshot/src/assets/zlp.png | Bin 0 -> 5997 bytes products/governance-snapshot/src/auth.ts | 4 +- .../src/components/Badges.vue | 4 +- .../src/components/Block/Results.vue | 6 +- .../src/components/Modal/Account.vue | 9 +- .../src/components/Modal/Confirm.vue | 1 + .../src/components/Ui/Markdown.vue | 1 - .../src/components/User.vue | 16 +- .../src/helpers/connectors.json | 4 + .../src/helpers/content.ts | 2 +- .../src/helpers/get-scores.ts | 26 +- .../governance-snapshot/src/helpers/ipfs.ts | 2 +- .../src/helpers/plugins/LockPlugin.ts | 24 +- .../src/helpers/plugins/aragon.ts | 2 +- .../governance-snapshot/src/helpers/web3.ts | 31 +- .../src/helpers/zilliqa.ts | 3 + products/governance-snapshot/src/main.ts | 11 + .../src/store/modules/app.ts | 29 +- .../src/store/modules/web3.ts | 22 + .../governance-snapshot/src/views/Create.vue | 7 +- .../src/views/Delegate.vue | 4 +- .../src/views/Proposal.vue | 12 +- .../src/views/Proposals.vue | 9 +- .../src/views/Settings.vue | 2 +- products/governance-snapshot/vue.config.js | 5 - products/governance-snapshot/yarn.lock | 12187 +++++++--------- products/ipfs/init.sh | 16 + ...evm_wallet_support_to_governance_portal.md | 348 + 51 files changed, 7061 insertions(+), 7180 deletions(-) create mode 100644 .claude/skills/generate-copilot-instructions/SKILL.md create mode 100644 .github/copilot-instructions.md create mode 100644 CLAUDE.md create mode 100644 products/.env.governance.example create mode 100644 products/GOVERNANCE.md create mode 100644 products/docker-compose.yml create mode 100644 products/governance-api/.dockerignore create mode 100644 products/governance-api/.nvmrc create mode 100644 products/governance-api/lib/utils/verify-evm-signature.ts create mode 100644 products/governance-snapshot/.dockerignore create mode 100644 products/governance-snapshot/.nvmrc create mode 100644 products/governance-snapshot/config.docker.js create mode 100644 products/governance-snapshot/src/assets/zlp.png create mode 100644 products/governance-snapshot/src/helpers/zilliqa.ts create mode 100755 products/ipfs/init.sh create mode 100644 products/task_adding_evm_wallet_support_to_governance_portal.md diff --git a/.claude/skills/generate-copilot-instructions/SKILL.md b/.claude/skills/generate-copilot-instructions/SKILL.md new file mode 100644 index 000000000..5e2854512 --- /dev/null +++ b/.claude/skills/generate-copilot-instructions/SKILL.md @@ -0,0 +1,28 @@ +--- +name: generate-copilot-instructions +description: Generate `.github/copilot-instructions.md` based on the `CLAUDE.md` +--- + +Translate the project-specific rules and coding standards from `CLAUDE.md` into a format optimized for GitHub Copilot. + +## Usage +- When requested to sync or generate Copilot instructions. +- After significant updates to `CLAUDE.md`. + +## Instructions + +1. **Locate Source**: Read the `CLAUDE.md` file in the project root. +2. **Identify Target**: Prepare to write to `.github/copilot-instructions.md`. +3. **Analyze Content**: + - Extract **Coding Standards**: Language-specific rules, naming conventions, and formatting. + - Extract **Project Architecture**: Folder structures, design patterns, and tech stack details. + - Extract **Documentation Requirements**: JSDoc, TypeDoc, or README update rules. + - **Filter Out**: Remove agent-specific terminal commands (like "Build command: npm run build") as Copilot handles these differently than agentic tools like Cline. +4. **Transform**: + - Reformat the instructions into a clear, hierarchical Markdown structure. + - Use imperative language (e.g., "Always use...", "Avoid...", "Ensure..."). + - Group instructions into logical sections: `Code Style`, `Architecture`, `Testing`, and `Patterns`. +5. **Execution**: + - Create the `.github` directory if it does not exist. + - Write the refined content to `.github/copilot-instructions.md`. + - Provide a brief summary of the changes made to the user. \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..a8e41cb44 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,113 @@ +# GitHub Copilot Instructions + +> Project-specific coding standards and architecture guidance for the `zilliqa-developer` monorepo. + +--- + +## Architecture + +### Repository Structure + +- This is a **Bazel-based monorepo** using Bazelisk as the build wrapper. +- JS packages are managed as a **pnpm workspace** under `zilliqa/js/`. +- Products are self-contained applications under `products/`. +- Contracts live under `contracts/` — `audited/` for production, `experimental/` for in-development. + +### JS SDK Packages (`zilliqa/js/`) + +All packages are published under the `@zilliqa-js/*` namespace: + +- `core` — HTTP/WebSocket provider, JSON-RPC +- `account` — wallet and account management +- `blockchain` — Blockchain API queries +- `contract` — Scilla contract deployment and interaction +- `crypto` — key generation, schnorr signing, address formats +- `util` — BN.js utilities, unit conversion, validation +- `subscriptions` — WebSocket event subscriptions +- `proto` — protobuf definitions +- `zilliqa` — main entry point, re-exports all sub-packages + +### Products + +Each product under `products/` is self-contained. Key products: + +| Product | Stack | +|---|---| +| `devex` | React (CRA) | +| `neo-savant` | Vue 2 + Vite | +| `governance-snapshot` | Vue 2 | +| `governance-api` | Node.js/Express + Sequelize + PostgreSQL | +| `zillion` | React | +| `pdt` | Rust | +| `faucet-service` | Go | +| `eth-spout` | Rust | +| `laksaj` | Java (Maven via Bazel) | +| `bridge` | Solidity smart contracts (pnpm workspace) | +| `devex-apollo` | Node.js + Apollo + MongoDB | + +### Governance System + +- `governance-api` — Express REST API backend (TypeScript, Sequelize ORM, PostgreSQL). Uses IPFS (Pinata) for proposal storage. +- `governance-snapshot` — Vue 2 SPA frontend connecting to `governance-api` and integrating `@snapshot-labs` libraries. + +### Block Explorer (`devex`) + +React SPA using Apollo Client for GraphQL queries against the `devex-apollo` backend (Node.js + MongoDB). + +--- + +## Code Style + +### General + +- Follow language-idiomatic conventions for each file's language (TypeScript, Go, Rust, Java, Solidity). +- Use **imperative style** for commit messages and documentation. +- Respect the linting rules enforced by [trunk.io](https://trunk.io) — it runs eslint, prettier, black, flake8, gofmt, rustfmt, shellcheck, and others. + +### TypeScript / JavaScript + +- Use TypeScript for all JS SDK and product code where applicable. +- Each JS SDK package targets three distribution formats: **cjs**, **esm**, and **umd** (via `tsc` and `rollup`). +- Shared TypeScript configuration is in `tsconfig.base.json` — extend it rather than duplicating settings. +- Tests match the Jest pattern: `zilliqa/js/**/test/?(*.)+(spec|test).(ts|js)`. + +### Bazel + +- Use `BUILD` files consistently for all Bazel targets. +- Prefer `bazelisk` over `bazel` to ensure consistent Bazel version usage. +- Use `bazelisk query` to inspect targets and dependencies before making structural changes. + +--- + +## Testing + +- JS SDK tests run via **Jest** (config at `jest.config.js` in the repo root). +- Bazel is the canonical test runner for all targets: `bazelisk test //zilliqa/js/...`. +- Always run tests after modifying SDK packages or contracts. +- For Go and Rust, use their native test frameworks (`go test`, `cargo test`). + +--- + +## Patterns + +### Dependency Management + +- JS/TS packages: use **pnpm** (not npm or yarn) for the monorepo workspace. +- Install workspace deps from `zilliqa/js/`: `pnpm i`. +- Do not mix package managers within the same product. + +### Linting + +- Linting is managed by **trunk**. Avoid bypassing trunk checks. +- Some directories are intentionally excluded from trunk (e.g., `products/governance-api`, `products/governance-snapshot`, `products/zillion`, `products/multisig`). +- Do not add those directories back to trunk linting unless intentional. + +### Deployment + +- Products are deployed via the internal `z` CLI tool. +- Each deployable product has a `z.yaml` configuration — do not remove or rename it. + +### Contracts + +- Production-deployed contracts live in `contracts/audited/` — treat these as immutable references. +- Experimental contracts in `contracts/experimental/` may be modified, but changes must be reviewed carefully. diff --git a/.github/workflows/cicd-prd.yml b/.github/workflows/cicd-prd.yml index 48211fce8..1611e6e9d 100644 --- a/.github/workflows/cicd-prd.yml +++ b/.github/workflows/cicd-prd.yml @@ -22,13 +22,13 @@ jobs: matrix: application: [ - bluebell-playground, - eth-spout, + # bluebell-playground, + # eth-spout, governance-api, governance-snapshot, multisig, - scilla-server, - zillion, + # scilla-server, + # zillion, ] include: - application: bluebell-playground diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..92832bfd9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,118 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository Overview + +`zilliqa-developer` is a Bazel-based monorepo containing SDKs, documentation, and products for the Zilliqa blockchain ecosystem. The build system is Bazel (via Bazelisk), with pnpm workspaces managing JS packages. + +## Build System + +The primary build tool is **Bazelisk** (Bazel wrapper). Most targets are built, tested, and run via Bazel: + +```sh +bazelisk build [target] # Build a target +bazelisk run [target] # Run an executable target +bazelisk test [target] # Test a target +bazelisk query "//..." # List all targets +bazelisk query "//zilliqa/..." # List targets in a subfolder +bazelisk query "deps(//zilliqa/js/util:pkg)" # Find target dependencies +``` + +Useful flags: `--verbose_failures`, `--test_output=all`, `--sandbox_debug` + +Set `DISABLE_WORKSPACE_STATUS=1` (or `--workspace_status_command=echo`) to skip git queries and avoid key/password prompts on every build. + +## JS SDK (zilliqa/js) + +The JS SDK packages live in `zilliqa/js/` and are managed as a pnpm workspace. Packages: `account`, `blockchain`, `contract`, `core`, `crypto`, `proto`, `subscriptions`, `typings`, `util`, `zilliqa` — all published as `@zilliqa-js/*`. + +```sh +# Install dependencies +cd zilliqa/js && pnpm i + +# Build all packages +pnpm -r build + +# Run tests (via Bazel) +bazelisk test //zilliqa/js/... + +# Run tests via Jest (from repo root) +npx jest +``` + +Jest config is at `jest.config.js` — tests match `zilliqa/js/**/test/?(*.)+(spec|test).(ts|js)`. + +Each package builds three distribution formats (cjs, esm, umd) via `tsc` and `rollup`. + +## Linting & Formatting + +Uses [trunk.io](https://trunk.io) to manage multiple linters (eslint, prettier, black, flake8, gofmt, rustfmt, shellcheck, etc.): + +```sh +trunk check # Check code style +trunk fmt # Auto-format where possible +``` + +Trunk is enforced by CI. Several directories are excluded from linting (see `.trunk/trunk.yaml`), including `products/governance-api`, `products/governance-snapshot`, `products/zillion`, `products/multisig`, and others. + +## Products + +Each product under `products/` is largely self-contained. Common setup patterns: + +| Product | Stack | Dev command | +|---|---|---| +| `devex` | React (CRA) | `yarn start` (port 3000) | +| `neo-savant` | Vue 2 + Vite | `yarn dev` | +| `governance-snapshot` | Vue 2 | `yarn serve` (NODE_OPTIONS=--openssl-legacy-provider) | +| `governance-api` | Node.js/Express + Sequelize + PostgreSQL | `npm start` (ts-node) | +| `zillion` | React | `yarn` then configure `public/config.js` | +| `pdt` | Rust | `cargo run` | +| `faucet-service` | Go | `make deps && make build` | +| `eth-spout` | Rust | Configured via environment variables | +| `laksaj` | Java (Maven via Bazel) | Bazel build | +| `isolated-server` | Docker | `docker build` / `docker run` | +| `bridge` | Solidity smart contracts | pnpm workspace | +| `devex-apollo` | Node.js + Apollo + MongoDB | docker-compose | + +## Architecture + +### JS SDK Architecture + +The `@zilliqa-js/zilliqa` package is the main entry point, re-exporting from these sub-packages: +- `@zilliqa-js/core` — HTTP/WebSocket provider, JSON-RPC +- `@zilliqa-js/account` — wallet, account management +- `@zilliqa-js/blockchain` — Blockchain API queries +- `@zilliqa-js/contract` — Scilla contract deployment and interaction +- `@zilliqa-js/crypto` — key generation, signing (schnorr), address formats +- `@zilliqa-js/util` — BN.js utilities, unit conversion, validation +- `@zilliqa-js/subscriptions` — WebSocket event subscriptions +- `@zilliqa-js/proto` — protobuf definitions + +### Governance System + +Two related products: +- `governance-api` — Express REST API backend (TypeScript, Sequelize ORM, PostgreSQL). Entry: `index.ts` → `lib/server.ts`. Uses IPFS (Pinata) for proposal storage. +- `governance-snapshot` — Vue 2 SPA frontend. Connects to `governance-api` and integrates with `@snapshot-labs` libraries. + +### Devex (Block Explorer) + +React SPA (`products/devex`) using Apollo Client for GraphQL queries against `devex-apollo` (Node.js + MongoDB backend that crawls Zilliqa chain data). + +### Deployment + +Products are deployed via the `z` CLI tool (Zilliqa internal DevOps tool). See each product's README for `z.yaml` configuration and staging/production deployment instructions. + +## Contracts + +- `contracts/audited/` — production-deployed contracts +- `contracts/experimental/` — contracts under development, including `ERC20ProxyForZRC2` (pnpm workspace package) +- `contracts/gaming_contracts/`, `contracts/reward_control/` — domain-specific contracts + +## Key Configuration Files + +- `WORKSPACE` / `BUILD` — Bazel workspace and build rules +- `pnpm-workspace.yaml` — pnpm workspace package list +- `tsconfig.base.json` — shared TypeScript base config +- `.trunk/trunk.yaml` — linter configuration and ignore rules +- `jest.config.js` — root Jest config for JS SDK tests diff --git a/package.json b/package.json index a03752db0..a76169a41 100644 --- a/package.json +++ b/package.json @@ -194,5 +194,6 @@ } } } - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/products/.env.governance.example b/products/.env.governance.example new file mode 100644 index 000000000..7d16db0d1 --- /dev/null +++ b/products/.env.governance.example @@ -0,0 +1,22 @@ +# Copy this file to .env and fill in the required values. +# docker compose reads .env automatically. + +# ── Required ───────────────────────────────────────────────────────────────── + +# PostgreSQL password (any non-empty string works for a local setup) +POSTGRES_PASSWORD=changeme + +# ── Optional overrides ──────────────────────────────────────────────────────── + +# Database name and user (defaults shown) +# POSTGRES_DB=snapshot +# POSTGRES_USER=postgres + +# IPFS gateway URL as seen from the user's browser. +# Change this if you run the stack on a remote server. +# VUE_APP_IPFS_NODE=http://localhost:8080 + +# Use Pinata instead of the local IPFS node. +# Set IPFS_API_URL= (empty) in docker-compose.yml and supply credentials here. +# PINATA_API_KEY= +# PINATA_SECRET_API_KEY= diff --git a/products/GOVERNANCE.md b/products/GOVERNANCE.md new file mode 100644 index 000000000..728822cb0 --- /dev/null +++ b/products/GOVERNANCE.md @@ -0,0 +1,246 @@ +# Zilliqa Governance Portal + +This document explains how `governance-api` and `governance-snapshot` work together to deliver the Zilliqa governance system — a gasless, off-chain, signature-based voting platform. + +## Products + +| Product | Stack | Purpose | +|---|---|---| +| `governance-api` | Node.js + TypeScript + Express + Sequelize + PostgreSQL | REST API backend | +| `governance-snapshot` | Vue 2 + Vuex + TypeScript | Single-page frontend | + +## High-Level Architecture + +``` +User's browser (governance-snapshot) + │ + │ POST /api/message (submit proposal or vote) + │ GET /api/spaces (space configs) + │ GET /api/:space/proposals + │ GET /api/:space/proposal/:id + ▼ +governance-api ──── Zilliqa blockchain (balance queries, signature verification) + │ + ├── IPFS / Pinata (immutable storage for proposals and votes) + └── PostgreSQL (indexed proposal and vote records) +``` + +The frontend never writes directly to a database or blockchain. Every proposal and vote is: +1. Signed locally in the user's ZilPay wallet (Schnorr signature — no gas). +2. Sent to the API, which validates, pins to IPFS, and stores a database record. +3. Fetched back for display by reading the database index and IPFS content. + +## Wallet Authentication + +The frontend uses `@snapshot-labs/lock` wrapping the **ZilPay** browser extension: + +1. User clicks "Connect Wallet". +2. `LockPlugin.login()` calls `window.zilPay.wallet.connect()`. +3. ZilPay prompts for permission; on approval it returns a provider. +4. The Vuex `web3` module subscribes to account/network change events and stores `{ base16, bech32 }` addresses. +5. The connector name is persisted in `localStorage` so the session survives page reloads. + +No session token or server-side auth exists. Identity is proven entirely by cryptographic signature on each action. + +## Governance Spaces + +A **space** is a named governance context tied to a specific ZRC-2 token. Space metadata (name, token address, voting strategies, quorum default) comes from the `@snapshot-labs/snapshot-spaces` npm package and is served verbatim by `GET /api/spaces`. + +The frontend can also be deployed on a custom domain that maps directly to a single space (via `domains.json` in snapshot-spaces), in which case the UI defaults to that space without showing a space selector. + +## Proposal Lifecycle + +### 1. Creation + +The user fills in a form in the `Create.vue` view: + +| Field | Constraint | +|---|---| +| Title | ≤ 256 characters | +| Body | ≤ 40,000 characters | +| Choices | ≥ 2 | +| Snapshot block | > 0, must exist | +| Voting window | `start < end`, both Unix timestamps | +| Quorum | 0–100% | + +On submit, the frontend constructs a JSON message envelope: + +```json +{ + "address": "", + "msg": "{\"version\":\"0.1.2\",\"timestamp\":,\"token\":\"\",\"type\":\"proposal\",\"payload\":{...}}", + "sig": { "message": "...", "publicKey": "...", "signature": "..." } +} +``` + +`signMessage()` calls `zilPay.wallet.sign(msg)` to produce a Schnorr signature without any on-chain transaction. + +The API (`POST /api/message`) then: + +1. **Validates the envelope** — timestamp within 30 s, protocol version, payload field count. +2. **Verifies the signature** — SHA-256 hashes the message, derives address from public key, calls `schnorr.verify()`. +3. **Checks the minimum balance** — the proposer must hold ≥ 30 gZIL at the current block. +4. **Snapshots token balances** — queries the Zilliqa blockchain for every holder's balance, including: + - Direct ZRC-2 token holdings + - Proportional stake in Zilswap liquidity pools + - Proportional stake in XCAD pools +5. **Pins to IPFS** via Pinata — the pinned object includes `{ balances, totalSupply, address, msg, sig, version }`. The resulting IPFS hash becomes the proposal's canonical identifier. +6. **Writes a database record** — `Message { type: 'proposal', author_ipfs_hash, space, token, payload, ... }`. +7. **Returns** `{ ipfsHash }` to the frontend, which shows a success notification. + +### 2. Viewing Proposals + +`GET /api/:space/proposals` returns all proposals for a space ordered by timestamp. For each proposal, the frontend also calls `getScores()` to compute the proposer's voting power at the snapshot block. + +The `Proposals.vue` view renders a card per proposal showing: title, author, voting window, current vote distribution, and quorum progress. + +### 3. Voting + +#### Browsing a Proposal + +`Proposal.vue` loads a proposal by its IPFS hash: + +1. Fetch proposal content from IPFS (`https://gateway.pinata.cloud/ipfs/`) — this returns the immutable object pinned at creation time, including the balance snapshot. +2. `GET /api/:space/proposal/:id` returns all votes (keyed by voter address). +3. For each voter, `getScores()` resolves their balance from the IPFS-stored snapshot (no live blockchain query needed). +4. Results are aggregated: vote count, total balance, and total score per choice. + +#### Casting a Vote + +1. User selects a choice; a confirmation modal (`Modal/Confirm.vue`) shows the choice, snapshot block, and the user's voting power. +2. User confirms; the frontend calls `send({ type: 'vote', payload: { proposal: , choice: <1-indexed int>, metadata: {} } })`. +3. ZilPay signs the vote message locally. +4. `POST /api/message` validates: + - Payload has exactly 3 fields. + - The referenced proposal exists in the database. + - Current time is within the proposal's `[start, end)` window. +5. The vote is pinned to IPFS and a `Message { type: 'vote', proposal_id: }` record is inserted. +6. The frontend reloads the proposal to reflect the new totals. + +## Voting Power & Balance Snapshots + +Voting power is calculated by the `zrc2-balance-of` strategy implemented in `helpers/get-scores.ts`. + +The balance snapshot taken at proposal creation time is the authoritative source. This prevents vote buying: a user who acquires tokens after the snapshot block cannot use them on that proposal. + +**Resolution order:** +1. Check `proposal.balances[address]` (from the IPFS-pinned object). +2. If missing, fall back to a live blockchain query for `getSmartContractSubState(token, 'balances', [address])` at the snapshot block. + +**DEX-adjusted balance formula:** + +``` +total_balance = direct_balance + + (user_zilswap_lp / total_zilswap_lp) × pool_token_reserve + + (user_xcad_lp / total_xcad_lp) × xcad_token_reserve +``` + +This ensures users who provide liquidity on Zilswap or XCAD retain governance power proportional to their pooled tokens. + +## Signature Verification + +All messages use Zilliqa's Schnorr signature scheme: + +| Step | Detail | +|---|---| +| Hash | SHA-256 of the raw message string | +| Sign | `zilPay.wallet.sign(msg)` → `{ message, publicKey, signature }` | +| Verify | `schnorr.verify(hash, sig_bytes, pubkey)` + address derivation check | + +The backend (`utils/verify-signature.ts`) performs both checks: the cryptographic signature and that the public key maps to the claimed address. + +## Data Storage + +### PostgreSQL — `Message` table + +Indexed, queryable records for listing and lookup. + +| Column | Description | +|---|---| +| `address` | Proposer/voter wallet address | +| `type` | `"proposal"` or `"vote"` | +| `space` | Space key | +| `token` | Token address | +| `author_ipfs_hash` | IPFS hash (primary identifier) | +| `payload` | JSON payload (name, choices, etc.) | +| `proposal_id` | For votes: IPFS hash of parent proposal | +| `sig` | Signature object | +| `timestamp` | Unix seconds | + +### IPFS / Pinata — immutable content + +Proposals and votes are pinned as JSON objects. The IPFS hash is content-addressed, making it tamper-evident: any change to the content would produce a different hash. + +- **Proposal pin:** includes the balance snapshot — serves as the voting-power source of truth. +- **Vote pin:** includes the signed vote — provides a censorship-resistant audit trail. + +## API Reference + +| Method | Path | Description | +|---|---|---| +| `GET` | `/api/` | Health check; returns network, version, relayer address | +| `POST` | `/api/message` | Submit a proposal or vote | +| `GET` | `/api/spaces/:key?` | Fetch all spaces, or a single space by key | +| `GET` | `/api/:space/proposals` | List all proposals for a space | +| `GET` | `/api/:space/proposal/:id` | List all votes on a proposal | + +## Environment Configuration + +### governance-api + +| Variable | Required | Description | +|---|---|---| +| `PINATA_API_KEY` | Yes | Pinata API key for IPFS pinning | +| `PINATA_SECRET_API_KEY` | Yes | Pinata secret | +| `POSTGRES_DB` | Yes (prod) | Database name | +| `POSTGRES_USER` | Yes (prod) | Database user | +| `POSTGRES_PASSWORD` | Yes (prod) | Database password | +| `POSTGRES_HOST` | Yes (prod) | Database host | +| `NODE_ENV` | Yes | `development` → SQLite; `production` → PostgreSQL + mainnet | +| `PORT` | No | HTTP port (default 3000) | +| `RELAYER_PK` | No | Private key for optional transaction relaying | + +### governance-snapshot (`public/config.js`) + +| Variable | Description | +|---|---| +| `VUE_APP_HUB_URL` | Base URL of `governance-api` (e.g. `https://governance-api.zilliqa.com`) | +| `VUE_APP_IPFS_NODE` | IPFS gateway hostname (default `gateway.pinata.cloud`) | + +## Local Development + +```sh +# governance-api +cd products/governance-api +cp example.env .env # fill in credentials +npm install +npm start # ts-node, port 3000, SQLite in dev mode + +# governance-snapshot +cd products/governance-snapshot +yarn install +yarn serve # NODE_OPTIONS=--openssl-legacy-provider; port 8080 +``` + +The frontend expects `governance-api` at the URL set in `public/config.js`. For local development, point `VUE_APP_HUB_URL` to `http://localhost:3000`. + +## Deployment + +Both products are deployed to Kubernetes via Kustomize overlays and the internal `z` CLI tool: + +``` +products/governance-api/cd/overlays/staging +products/governance-api/cd/overlays/production +products/governance-snapshot/cd/overlays/staging +products/governance-snapshot/cd/overlays/production +``` + +The API is stateless aside from the PostgreSQL database and external IPFS pins, making horizontal scaling straightforward. + +## Key Design Properties + +- **Gasless** — no on-chain transactions required to vote or propose. +- **Snapshot-based** — voting power is locked at proposal creation; token transfers after the snapshot do not affect voting. +- **DEX-aware** — liquidity providers on Zilswap and XCAD retain proportional governance rights. +- **Tamper-evident** — proposals and votes are content-addressed on IPFS; the database is an index, not the source of truth. +- **Minimum stake to propose** — 30 gZIL required to submit a proposal, preventing spam. diff --git a/products/docker-compose.yml b/products/docker-compose.yml new file mode 100644 index 000000000..49bad2e39 --- /dev/null +++ b/products/docker-compose.yml @@ -0,0 +1,107 @@ +# Standalone Zilliqa Governance Portal +# +# Services: +# postgres — relational store for proposal/vote index +# ipfs — kubo node for immutable proposal/vote storage +# governance-api — REST API (validates, pins, indexes) +# governance-snapshot — Vue 2 SPA served by nginx +# +# Quick start: +# cp .env.governance.example .env # fill in POSTGRES_PASSWORD +# docker compose up --build +# +# Frontend: http://localhost:8000 +# API: http://localhost:3000 +# IPFS GW: http://localhost:8080 + +services: + + # ── PostgreSQL ──────────────────────────────────────────────────────────── + postgres: + image: postgres:15-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-snapshot} + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"] + interval: 5s + timeout: 5s + retries: 10 + + # ── IPFS (kubo) ────────────────────────────────────────────────────────── + ipfs: + image: ipfs/kubo:latest + restart: unless-stopped + environment: + # Disable MDNS discovery; use the server profile for a headless node. + IPFS_PROFILE: server + ports: + - "4001:4001" # Swarm (P2P) — omit if not joining the public DHT + - "5001:5001" # RPC API — do not expose publicly in production + - "8080:8080" # HTTP Gateway — browser IPFS reads + volumes: + - ipfs_data:/data/ipfs + # Init script: opens API/gateway to 0.0.0.0 so governance-api can reach + # it via the docker network before the daemon starts. + - ./ipfs/init.sh:/container-init.d/001-init.sh:ro + healthcheck: + test: ["CMD", "ipfs", "id"] + interval: 10s + timeout: 5s + retries: 10 + + # ── governance-api ──────────────────────────────────────────────────────── + governance-api: + build: + context: ./governance-api + dockerfile: Dockerfile + restart: unless-stopped + ports: + - "3000:3000" + environment: + NODE_ENV: production + POSTGRES_DB: ${POSTGRES_DB:-snapshot} + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + POSTGRES_HOST: postgres + # Point at the local kubo node instead of Pinata. + # Remove this variable (and set PINATA_* below) to use Pinata instead. + IPFS_API_URL: http://ipfs:5001 + # Pinata credentials — leave empty when IPFS_API_URL is set. + PINATA_API_KEY: ${PINATA_API_KEY:-} + PINATA_SECRET_API_KEY: ${PINATA_SECRET_API_KEY:-} + depends_on: + postgres: + condition: service_healthy + ipfs: + condition: service_healthy + + # ── governance-snapshot ─────────────────────────────────────────────────── + governance-snapshot: + build: + context: ./governance-snapshot + dockerfile: Dockerfile + args: + # VUE_APP_IPFS_NODE is baked in at build time (process.env in compiled JS). + # Must be reachable from the user's browser, not from inside docker. + VUE_APP_IPFS_NODE: ${VUE_APP_IPFS_NODE:-http://localhost:8080} + # Set to \"production\" for an optimised production build. + # Defaults to \"development\" for unminified JS with source maps (readable stack traces). + BUILD_MODE: ${BUILD_MODE:-development} + restart: unless-stopped + ports: + - "8000:80" + volumes: + # Override runtime config so the browser points at the local API. + # VUE_APP_HUB_URL is read as window['VUE_APP_HUB_URL'] at runtime. + - ./governance-snapshot/config.docker.js:/usr/share/nginx/html/config.js:ro + depends_on: + - governance-api + +volumes: + postgres_data: + ipfs_data: diff --git a/products/governance-api/.dockerignore b/products/governance-api/.dockerignore new file mode 100644 index 000000000..3ee5de21b --- /dev/null +++ b/products/governance-api/.dockerignore @@ -0,0 +1,28 @@ +# Dependencies (reinstalled inside the image) +node_modules + +# Version control +.git + +# Secrets +.env +*.sql + +# Docker artifacts +docker-compose.yml +Dockerfile + +# Deployment / CI configs +cd/ + +# Editor / OS artifacts +.DS_Store +.idea +.editorconfig + +# Docs and tooling not needed at runtime +*.md +LICENSE +Makefile +tslint.json +example.env diff --git a/products/governance-api/.nvmrc b/products/governance-api/.nvmrc new file mode 100644 index 000000000..2edeafb09 --- /dev/null +++ b/products/governance-api/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/products/governance-api/Dockerfile b/products/governance-api/Dockerfile index c2a990b73..27153cffc 100644 --- a/products/governance-api/Dockerfile +++ b/products/governance-api/Dockerfile @@ -1,11 +1,20 @@ -FROM node:14.17.3 +FROM node:20 WORKDIR /app -COPY . . + +COPY package.json yarn.lock ./ RUN npm install +COPY . . + +RUN groupadd -r app \ + && useradd -r -g app app \ + && chown -R app:app /app + +USER app + +ENV SCRIPT=start + EXPOSE 3000 -ARG SCRIPT="start" -ENV SCRIPT=${SCRIPT} ENTRYPOINT ["bash", "run.sh"] diff --git a/products/governance-api/lib/routes/message.ts b/products/governance-api/lib/routes/message.ts index 75fc0e8d3..58fd9798d 100644 --- a/products/governance-api/lib/routes/message.ts +++ b/products/governance-api/lib/routes/message.ts @@ -1,25 +1,22 @@ import { Router } from "express"; import BN from "bn.js"; -import fromentries from "object.fromentries"; import spaces from "@snapshot-labs/snapshot-spaces"; import { verifySignature, pinJson } from "../utils"; +import { verifyEVMSignature } from "../utils/verify-evm-signature"; import { Message } from "../models"; import { blockchain } from "../zilliqa/custom-fetch"; import pkg from "../../package.json"; import { ErrorCodes } from "../config"; -import { fromBech32Address, validation } from "@zilliqa-js/zilliqa"; +import { fromBech32Address } from "@zilliqa-js/zilliqa"; +import { validation } from "@zilliqa-js/util"; export const message = Router(); const gZIL = "zil14pzuzq6v6pmmmrfjhczywguu0e97djepxt8g3e"; const blk = new blockchain(); -const tokens = fromentries( - Object.entries(spaces).map((space: any) => { - return [space[1].token, space[0]]; - }) -); + const proposal = (res: any, msg: any) => { if (msg.type !== "proposal") { @@ -149,13 +146,21 @@ message.post("/message", async (req, res) => { }); } - if (!tokens[msg.token]) { + const spaceKey = body.space; + if (!spaceKey || !spaces[spaceKey]) { return res.status(400).json({ code: ErrorCodes.UNKNOWN_SPACE, error_description: "unknown space", }); } + if (spaces[spaceKey].token !== msg.token) { + return res.status(400).json({ + code: ErrorCodes.UNKNOWN_SPACE, + error_description: "token does not match space", + }); + } + msg.timestamp = Number(msg.timestamp); if (!msg.timestamp || isNaN(msg.timestamp) || msg.timestamp > ts + 30) { @@ -179,17 +184,29 @@ message.post("/message", async (req, res) => { }); } - try { - const checked = verifySignature( - body.sig.message, - body.sig.publicKey, - body.sig.signature, - body.address - ); + // Normalise EVM addresses to always have the 0x prefix + if (body.sigType === 'evm' && body.address && !body.address.startsWith('0x')) { + body.address = '0x' + body.address; + } - if (!checked) { - throw new Error(); + try { + let checked: boolean; + if (body.sigType === 'evm') { + checked = verifyEVMSignature( + body.sig.message, + body.sig.signature, + body.address + ); + } else { + // Default to Schnorr for ZilPay and legacy submissions + checked = verifySignature( + body.sig.message, + body.sig.publicKey, + body.sig.signature, + body.address + ); } + if (!checked) throw new Error('signature mismatch'); } catch (err) { return res.status(400).json({ code: ErrorCodes.INCORRECT_SIGNATURE, @@ -200,7 +217,7 @@ message.post("/message", async (req, res) => { proposal(res, msg); await vote(res, msg, ts); - const space = tokens[msg.token]; + const space = spaceKey; let authorIpfsRes: any | null = null; if (msg.type === "proposal") { diff --git a/products/governance-api/lib/utils/pin-ipfs.ts b/products/governance-api/lib/utils/pin-ipfs.ts index 712b61b50..d7549ba54 100644 --- a/products/governance-api/lib/utils/pin-ipfs.ts +++ b/products/governance-api/lib/utils/pin-ipfs.ts @@ -1,21 +1,50 @@ import fetch from "node-fetch"; import pinataSDK from "@pinata/sdk"; -const pinata = pinataSDK( - String(process.env.PINATA_API_KEY), - String(process.env.PINATA_SECRET_API_KEY) -); - -export async function pinJson(body: object) { - let ipfsHash: string; +export async function pinJson(body: object): Promise { + if (process.env.IPFS_API_URL) { + return pinToLocalNode(body); + } + const pinata = pinataSDK( + String(process.env.PINATA_API_KEY), + String(process.env.PINATA_SECRET_API_KEY) + ); const result = await pinata.pinJSONToIPFS(body); - ipfsHash = result.IpfsHash; + return result.IpfsHash; +} + +async function pinToLocalNode(body: object): Promise { + const json = JSON.stringify(body); + const boundary = `----IpfsBoundary${Date.now().toString(16)}`; + const crlf = "\r\n"; + const multipart = [ + `--${boundary}`, + 'Content-Disposition: form-data; name="file"; filename="data.json"', + "Content-Type: application/json", + "", + json, + `--${boundary}--`, + ].join(crlf); + + const response = await fetch( + `${process.env.IPFS_API_URL}/api/v0/add?pin=true`, + { + method: "POST", + headers: { + "Content-Type": `multipart/form-data; boundary=${boundary}`, + }, + body: multipart, + } + ); - fetch(`https://gateway.pinata.cloud/ipfs/${ipfsHash}`) - .then((res) => res.json()) - .then((json) => console.log("Arweave success", ipfsHash)) - .catch((e) => console.error("Arweave error", e)); + if (!response.ok) { + throw new Error( + `IPFS add failed: ${response.status} ${await response.text()}` + ); + } - return ipfsHash; + const data = (await response.json()) as { Hash: string }; + console.log("IPFS pin success", data.Hash); + return data.Hash; } diff --git a/products/governance-api/lib/utils/verify-evm-signature.ts b/products/governance-api/lib/utils/verify-evm-signature.ts new file mode 100644 index 000000000..866f67f39 --- /dev/null +++ b/products/governance-api/lib/utils/verify-evm-signature.ts @@ -0,0 +1,19 @@ +import { ethers } from 'ethers'; + +/** + * Verifies an EIP-191 personal_sign signature. + * + * @param message - The raw string that was passed to personal_sign (msg.msg from the request). + * @param signature - The 0x-prefixed hex signature returned by personal_sign. + * @param address - The expected signer address (with or without 0x prefix). + */ +export function verifyEVMSignature( + message: string, + signature: string, + address: string +): boolean { + const recovered = ethers.verifyMessage(message, signature); + // Normalise both to lowercase hex without 0x for comparison + const normalised = address.startsWith('0x') ? address.slice(2) : address; + return recovered.slice(2).toLowerCase() === normalised.toLowerCase(); +} diff --git a/products/governance-api/package.json b/products/governance-api/package.json index 273e62960..336c5c5ab 100644 --- a/products/governance-api/package.json +++ b/products/governance-api/package.json @@ -15,11 +15,12 @@ "@pinata/sdk": "^1.1.23", "@snapshot-labs/snapshot-spaces": "github:Zilliqa/snapshot-spaces#master", "@types/bluebird": "^3.5.32", - "@zilliqa-js/zilliqa": " ^3.3.0", + "@zilliqa-js/zilliqa": "3.5.0", "bignumber.js": "^9.0.1", "bluebird": "^3.7.2", "body-parser": "^1.19.0", "dotenv": "^10.0.0", + "ethers": "^6.16.0", "express": "^4.19.2", "js-sha256": "^0.9.0", "lodash": "^4.17.20", diff --git a/products/governance-api/yarn.lock b/products/governance-api/yarn.lock index ed1fc705b..cb4ad254a 100644 --- a/products/governance-api/yarn.lock +++ b/products/governance-api/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adraffy/ens-normalize@1.10.1": + version "1.10.1" + resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + "@babel/code-frame@^7.0.0": version "7.15.8" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503" @@ -55,6 +60,18 @@ semver "^7.3.5" tar "^6.1.11" +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@npmcli/fs@^1.0.0": version "1.1.1" resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" @@ -164,25 +181,11 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== -"@types/bip39@^2.4.0": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@types/bip39/-/bip39-2.4.2.tgz#f5d6617212be496bb998d3969f657f77a10c5287" - integrity sha512-Vo9lqOIRq8uoIzEVrV87ZvcIM0PN9t0K3oYZ/CS61fIYKCBdOIM7mlWzXuRvSXrDtVa1uUO2w1cdfufxTC0bzg== - dependencies: - "@types/node" "*" - "@types/bluebird@^3.5.32": version "3.5.36" resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652" integrity sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q== -"@types/bn.js@^4.11.3": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== - dependencies: - "@types/node" "*" - "@types/body-parser@*": version "1.19.1" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" @@ -217,14 +220,7 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/hdkey@^0.7.0": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@types/hdkey/-/hdkey-0.7.1.tgz#9bc63ebbe96b107b277b65ea7a95442a677d0d61" - integrity sha512-4Kkr06hq+R8a9EzVNqXGOY2x1xA7dhY6qlp6OvaZ+IJy1BCca1Cv126RD9X7CMJoXoLo8WvAizy8gQHpqW6K0Q== - dependencies: - "@types/node" "*" - -"@types/long@^4.0.0", "@types/long@^4.0.1": +"@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== @@ -239,6 +235,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5" integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ== +"@types/node@22.7.5": + version "22.7.5" + resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" + "@types/node@^14.14.5": version "14.17.21" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.21.tgz#6359d8cf73481e312a43886fa50afc70ce5592c6" @@ -267,114 +270,120 @@ "@types/mime" "^1" "@types/node" "*" -"@zilliqa-js/account@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/account/-/account-3.3.0.tgz#40fc84fc827166f3eb8fbda0a3af525e84038cf1" - integrity sha512-+DXCe2XBpR8XdGRV+40ZeOGsn+1lieAAXtt6fNWo+toJ7mq4qfxL07QpY5XGPetmdHR67xyBDZsh7VjRdvQeNw== - dependencies: - "@types/bip39" "^2.4.0" - "@types/hdkey" "^0.7.0" - "@zilliqa-js/core" "3.3.0" - "@zilliqa-js/crypto" "3.3.0" - "@zilliqa-js/proto" "3.2.0" - "@zilliqa-js/util" "3.3.0" +"@zilliqa-js/account@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/account/-/account-3.5.0.tgz#a4c0b30edb97d0f441aedddc4433af838f0632f7" + integrity sha512-ojy+YjofL6CnuqAYjcf7gkqOMJzDSWsqaR64UF6gQshcPl76RitFbC6Udw0cmkJqStycqiEQS/OzjFRoiqvE7w== + dependencies: + "@zilliqa-js/core" "3.5.0" + "@zilliqa-js/crypto" "3.5.0" + "@zilliqa-js/proto" "3.5.0" + "@zilliqa-js/util" "3.5.0" bip39 "^2.5.0" + buffer "^6.0.3" + hash.js "^1.1.7" hdkey "^1.1.0" tslib "2.3.1" -"@zilliqa-js/blockchain@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/blockchain/-/blockchain-3.3.0.tgz#8db2cbf1bb1117c1b9899436d680c03320f2416c" - integrity sha512-AYUYBCY1DMJpMBRLG+Y172+FiOrC8pDjMrMG+GGhaQEDgas+bTAOB6m0cu3nPL3bqqB3uTKKSgWghkCSfQyFVQ== +"@zilliqa-js/blockchain@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/blockchain/-/blockchain-3.5.0.tgz#86c2ee387859144c289e70c2b63867aad7405bd1" + integrity sha512-YuWfmt5mAGTpWuSAgY0jHxQFxWkKplyC7+ebUo+wpkw45gzT4gc2X75cAckgt5nps5c9dO85OruWNZoKynoQ3g== dependencies: - "@zilliqa-js/account" "3.3.0" - "@zilliqa-js/contract" "3.3.0" - "@zilliqa-js/core" "3.3.0" - "@zilliqa-js/crypto" "3.3.0" - "@zilliqa-js/util" "3.3.0" + "@zilliqa-js/account" "3.5.0" + "@zilliqa-js/core" "3.5.0" + "@zilliqa-js/crypto" "3.5.0" + "@zilliqa-js/util" "3.5.0" tslib "2.3.1" - utility-types "^3.4.1" - -"@zilliqa-js/contract@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/contract/-/contract-3.3.0.tgz#121541ba166efaf38d54360682b8457c85ea73bf" - integrity sha512-nCs3mvJ08EFx2Jekd9FYfGwKMjtRO42goVsef0oX0yjY9psxY1fMginKd0FxPntz+BYbkIpf28fvioC531n7rw== - dependencies: - "@zilliqa-js/account" "3.3.0" - "@zilliqa-js/blockchain" "3.3.0" - "@zilliqa-js/core" "3.3.0" - "@zilliqa-js/crypto" "3.3.0" - "@zilliqa-js/util" "3.3.0" + +"@zilliqa-js/contract@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/contract/-/contract-3.5.0.tgz#a398acc72dfa40ef710823800a19ddeb72b32324" + integrity sha512-eojquTPaMTGy74fDV0w5KMjyvb4uIopcND2u3dGAx2aYoZzafuvBzw8sevu1hqUQhKSxJUZEcHIeeEK53PffMw== + dependencies: + "@zilliqa-js/account" "3.5.0" + "@zilliqa-js/blockchain" "3.5.0" + "@zilliqa-js/core" "3.5.0" + "@zilliqa-js/crypto" "3.5.0" + "@zilliqa-js/util" "3.5.0" + bn.js "^4.11.8" + buffer-from "^1.1.2" + cross-fetch "2.2.5" hash.js "^1.1.5" + node-fetch "^3.2.10" tslib "2.3.1" utility-types "^2.1.0" -"@zilliqa-js/core@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/core/-/core-3.3.0.tgz#0aecca37b5c2ec8e3e83921d7474ad89c3f5fe6c" - integrity sha512-KQFoA01gCOvCoQdU452lpcIiXkyB+7p57XKqVjM/bSpTQKzlellmUMiZEzlvynwRNrEU00jdj/A43xLKm2cuYw== +"@zilliqa-js/core@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/core/-/core-3.5.0.tgz#dc537d81705f52fc82e1e2f0e59269a4467187c5" + integrity sha512-sc3RaF7W4bwnLrOffuVhzmHGmXcfLGnCHxVkhJRNNkGzgjwjV9EhumtbNLinDTosvmaZY68mvSLlPkyyYEP1Yg== dependencies: - cross-fetch "2.2.5" + buffer "^6.0.3" + cross-fetch "2.2.6" mitt "^1.1.3" tslib "2.3.1" -"@zilliqa-js/crypto@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/crypto/-/crypto-3.3.0.tgz#6df86444d52e48e61380339ca38f3318f617fae8" - integrity sha512-Bpx8xaZQ6hJFTJlpapWM7mt/JWNNSZSm5k4Gf5uB9Uu66s/Kdlui7iCCNhZt78OzPHHFxqyXVnU2TVqA0zO7Ww== +"@zilliqa-js/crypto@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/crypto/-/crypto-3.5.0.tgz#62288e660d737f8d256288f58d2d5cde39db29b7" + integrity sha512-KMTY4hREh706k0oqCJ7KTFCEgPvgWuckv7z1SkOc9UDjJnnfOD8KxGWrleaKMZOw+EjKJRybxgewPUvSZ+o7Mw== dependencies: - "@zilliqa-js/util" "3.3.0" + "@zilliqa-js/util" "3.5.0" aes-js "^3.1.1" - bsert "^0.0.4" + buffer "^6.0.3" + crypto-js "^4.2.0" elliptic "^6.5.0" hash.js "^1.1.5" hmac-drbg "^1.0.1" pbkdf2 "^3.0.16" - randombytes "^2.0.6" scrypt-js "^3.0.1" scryptsy "^2.1.0" - sodium-native "^3.2.1" tslib "2.3.1" - uuid "^3.3.2" + uuid "8.3.2" -"@zilliqa-js/proto@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/proto/-/proto-3.2.0.tgz#1fa6330b109f6cc856c67bc620113446a11ea0ea" - integrity sha512-HFDEXvnT+DUGs0wvRaWs7evlTjgk8AmyoL+Zg3QXx1+vjBRjUtoVwYq5P1r68aOhmbgt2hEgcFdBgqUVMmk6wA== +"@zilliqa-js/proto@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/proto/-/proto-3.5.0.tgz#3d491352287596a46b2fa3f7207676a8c86f6f8d" + integrity sha512-Ids/iS+lYYseC0g1lzkLVRzrsVnB/6QQdDIxbqXzMQwGEjJVwX+UJqGV5eCREQ9w04bI9SS0lmeaNZ3KmN8CdA== dependencies: protobufjs "^6.8.8" -"@zilliqa-js/subscriptions@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/subscriptions/-/subscriptions-3.3.0.tgz#5fb41a600f9f55dd86ee0a658641f81d3456f225" - integrity sha512-RoO6QkHAmUoff0usYWI/7E52GPmT7vgXKtPKG9HqUj4k4mjrpop2i22NDXxXB73bKCxtYJ6uCXaKU1/kkt0lDA== +"@zilliqa-js/subscriptions@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/subscriptions/-/subscriptions-3.5.0.tgz#f8b4e1cf516ff21659723461d1b6142caf92edfe" + integrity sha512-K7qN03xu71C8fMdweMThvsHWG1yj5aQCtbincVqiCYSrKeMTLViNFHRK6th/FOhoWF2AFgJzMap6/Pv2tFbQ4w== dependencies: + buffer "^6.0.3" + camelcase "5.0.0" + mitt "^1.1.3" tslib "2.3.1" websocket "^1.0.28" -"@zilliqa-js/util@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/util/-/util-3.3.0.tgz#8593ac6f666903eb708f9bccd4ecef7d573f1a75" - integrity sha512-8q7S98ndUiigRWrjTosEUmMxrpNj8ULhCHdx9K0U44xKWjM17XTbFH/FYKoY3r03tBDtNgb/YjsyDLBkg3iCww== +"@zilliqa-js/util@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/util/-/util-3.5.0.tgz#ee76b7129dc4754ed754923b2c4e2f64b42630eb" + integrity sha512-YT8OhYAv2nCIrRTMMwXLDEqyV/O0jbtfc5Uvlb0qkIx56a4OeneebIJtBlTwf9ld7MZlU5LvvDOEJyljQErz6w== dependencies: - "@types/bn.js" "^4.11.3" - "@types/long" "^4.0.0" bn.js "^4.11.8" + camelcase "^5.0.0" long "^4.0.0" tslib "2.3.1" -"@zilliqa-js/zilliqa@ ^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@zilliqa-js/zilliqa/-/zilliqa-3.3.0.tgz#cc6e9fcaec178dcb6d13256aebb0eaa8a6ab6fad" - integrity sha512-MwkylgNzvjvUCkCzcDfUOwB1NTNj7TgeTa+bNkLLECxe2jznsC/wDs7jfu/lSUFVNt8DUp8C+rmN3a5+jjhUng== - dependencies: - "@zilliqa-js/account" "3.3.0" - "@zilliqa-js/blockchain" "3.3.0" - "@zilliqa-js/contract" "3.3.0" - "@zilliqa-js/core" "3.3.0" - "@zilliqa-js/crypto" "3.3.0" - "@zilliqa-js/subscriptions" "3.3.0" - "@zilliqa-js/util" "3.3.0" +"@zilliqa-js/zilliqa@3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@zilliqa-js/zilliqa/-/zilliqa-3.5.0.tgz#7666256291f2843037c0b35e15452699b3726f0f" + integrity sha512-CQ9HG16wtKOBFSPfSf8ZLOpyJy1+qXJOk7gvhE+fkBuR/pCdi3IKIxWH7bo/hCwBr+bEFo+Pi4fY22/8LsNN7Q== + dependencies: + "@zilliqa-js/account" "3.5.0" + "@zilliqa-js/blockchain" "3.5.0" + "@zilliqa-js/contract" "3.5.0" + "@zilliqa-js/core" "3.5.0" + "@zilliqa-js/crypto" "3.5.0" + "@zilliqa-js/proto" "3.5.0" + "@zilliqa-js/subscriptions" "3.5.0" + "@zilliqa-js/util" "3.5.0" + buffer "^6.0.3" tslib "2.3.1" abbrev@1: @@ -408,6 +417,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== +aes-js@4.0.0-beta.5: + version "4.0.0-beta.5" + resolved "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== + aes-js@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" @@ -657,10 +671,10 @@ bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" -bsert@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/bsert/-/bsert-0.0.4.tgz#2b3c236357923f0625f948802a5f9b66c0383d0b" - integrity sha512-VReLe1aTaHRmf80YLOHUk8ONQ48SjseZP76GlNIDheD5REYByn/Mm9yrtI0/ZCaFrcfxzgpiw1/hMMCUOSMa3w== +buffer-from@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer-writer@2.0.0: version "2.0.0" @@ -680,6 +694,14 @@ buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + bufferutil@^4.0.1: version "4.0.4" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.4.tgz#ab81373d313a6ead0d734e98c448c722734ae7bb" @@ -729,6 +751,11 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +camelcase@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -952,6 +979,14 @@ cross-fetch@2.2.5: node-fetch "2.6.1" whatwg-fetch "2.0.4" +cross-fetch@2.2.6: + version "2.2.6" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.6.tgz#2ef0bb39a24ac034787965c457368a28730e220a" + integrity sha512-9JZz+vXCmfKUZ68zAptS7k4Nu8e2qcibe7WVZYps7sAgk5R8GYTc+T1WR0v1rlP9HxgARmOX1UTIJZFytajpNA== + dependencies: + node-fetch "^2.6.7" + whatwg-fetch "^2.0.4" + cross-spawn@^7.0.0: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -966,6 +1001,11 @@ crypt@0.0.2: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -974,6 +1014,11 @@ d@1, d@^1.0.1: es5-ext "^0.10.50" type "^1.0.1" +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + debug@2.6.9, debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1225,6 +1270,19 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +ethers@^6.16.0: + version "6.16.0" + resolved "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz#fff9b4f05d7a359c774ad6e91085a800f7fccf65" + integrity sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "22.7.5" + aes-js "4.0.0-beta.5" + tslib "2.7.0" + ws "8.17.1" + event-emitter@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" @@ -1305,6 +1363,14 @@ fast-safe-stringify@^2.0.6: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -1351,6 +1417,13 @@ form-data@^2.3.3: combined-stream "^1.0.6" mime-types "^2.1.12" +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -1546,7 +1619,7 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.5: +hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.5, hash.js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -1636,7 +1709,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -1674,7 +1747,7 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.4, ini@^1.3.5: +ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -2323,6 +2396,11 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-fetch@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -2342,6 +2420,15 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-fetch@^3.2.10: + version "3.3.2" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + node-gyp-build@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" @@ -2686,7 +2773,7 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -randombytes@^2.0.1, randombytes@^2.0.6: +randombytes@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -2986,14 +3073,6 @@ socks@^2.6.2: ip-address "^9.0.5" smart-buffer "^4.2.0" -sodium-native@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-3.2.1.tgz#91cd6a175d4b58b038eeccc7078ebc33202328be" - integrity sha512-EgDZ/Z7PxL2kCasKk7wnRkV8W9kvwuIlHuHXAxkQm3FF0MgVsjyLBXGjSRGhjE6u7rhSpk3KaMfFM23bfMysIQ== - dependencies: - ini "^1.3.5" - node-gyp-build "^4.2.0" - split2@^3.1.1: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" @@ -3199,6 +3278,11 @@ tslib@2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + tslib@^1.13.0, tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -3282,6 +3366,11 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -3328,22 +3417,12 @@ utility-types@^2.1.0: resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-2.1.0.tgz#0c78fc9f7eb424d14302222b4ddd13fdb17f44ab" integrity sha512-/nP2gqavggo6l38rtQI/CdeV+2fmBGXVvHgj9kV2MAnms3TIi77Mz9BtapPFI0+GZQCqqom0vACQ+VlTTaCovw== -utility-types@^3.4.1: - version "3.10.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" - integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.2: +uuid@8.3.2, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -3363,6 +3442,11 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -3380,9 +3464,9 @@ websocket@^1.0.28: utf-8-validate "^5.0.2" yaeti "^0.0.6" -whatwg-fetch@2.0.4: +whatwg-fetch@2.0.4, whatwg-fetch@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== whatwg-url@^5.0.0: @@ -3444,6 +3528,11 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +ws@8.17.1: + version "8.17.1" + resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + xmlcreate@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.3.tgz#df9ecd518fd3890ab3548e1b811d040614993497" diff --git a/products/governance-snapshot/.dockerignore b/products/governance-snapshot/.dockerignore new file mode 100644 index 000000000..96efd422f --- /dev/null +++ b/products/governance-snapshot/.dockerignore @@ -0,0 +1,37 @@ +# Dependencies (reinstalled inside the image) +node_modules + +# Version control +.git + +# Build output (produced inside the image) +dist + +# Local env overrides +.env.local +.env.*.local + +# Docker artifacts +Dockerfile + +# Editor / OS artifacts +.DS_Store +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.nvmrc + +# Docs and tooling not needed at build time +*.md +LICENSE +Makefile + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* diff --git a/products/governance-snapshot/.gitignore b/products/governance-snapshot/.gitignore index 4b92c763d..0aa696276 100644 --- a/products/governance-snapshot/.gitignore +++ b/products/governance-snapshot/.gitignore @@ -1,6 +1,6 @@ .DS_Store node_modules -!dist +dist/ # local env files .env.local diff --git a/products/governance-snapshot/.nvmrc b/products/governance-snapshot/.nvmrc new file mode 100644 index 000000000..2edeafb09 --- /dev/null +++ b/products/governance-snapshot/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/products/governance-snapshot/Dockerfile b/products/governance-snapshot/Dockerfile index 196f7c8a9..0140b33e6 100644 --- a/products/governance-snapshot/Dockerfile +++ b/products/governance-snapshot/Dockerfile @@ -1,13 +1,20 @@ -FROM node:14.17.3 as build-stage +FROM node:20 AS build-stage -ENV VUE_APP_IPFS_NODE="gateway.pinata.cloud" +ARG VUE_APP_IPFS_NODE=https://gateway.pinata.cloud +ENV VUE_APP_IPFS_NODE=$VUE_APP_IPFS_NODE -WORKDIR app/ -COPY . ./ +# Set to "development" to produce unminified code with source maps for debugging. +ARG BUILD_MODE=production + +WORKDIR /app + +COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile -RUN yarn build -FROM nginx:stable-alpine as production-stage +COPY . . +RUN yarn build --mode $BUILD_MODE + +FROM nginx:stable-alpine AS production-stage COPY --from=build-stage /app/dist /usr/share/nginx/html EXPOSE 80 ENTRYPOINT ["nginx", "-g", "daemon off;"] diff --git a/products/governance-snapshot/README.md b/products/governance-snapshot/README.md index 342636410..bef083e27 100644 --- a/products/governance-snapshot/README.md +++ b/products/governance-snapshot/README.md @@ -5,24 +5,92 @@ Snapshot is an off-chain gasless multi-governance client with easy to verify and This project is a fork of https://github.com/balancer-labs/snapshot. Do support the Snapshot team @ https://gitcoin.co/grants/1093/snapshot -## Project setup +## Voting flow + +### Overview +The voting system is an **off-chain, signature-based** governance model (similar to Snapshot.org). Instead of sending a transaction on the blockchain (which costs gas), users sign a message with their wallet. This signature proves the authenticity of the vote. The "weight" or power of the vote is calculated based on the user's token balance at a specific block height (snapshot) on the Zilliqa blockchain. + +### 1. Wallet Identification +To participate, a user must connect their Zilliqa wallet (e.g., ZilPay). +- **Source**: [src/store/modules/web3.ts](src/store/modules/web3.ts) +- **Mechanism**: The application initializes the wallet provider and subscribes to account changes. +- **Address**: The application retrieves the connected **Bech32** or **Base16** address. This address serves as the voter's identity throughout the session. + +### 2. Voting Power Calculation (The "Weight") +A vote is not just "one person, one vote"; it is weighted by the number of tokens held. +- **Source**: [src/helpers/get-scores.ts](src/helpers/get-scores.ts) (specifically the `getScores` function) +- **Strategy**: The application primarily uses the `zrc2-balance-of` strategy. +- **Snapshot Logic**: + - Each proposal has a `snapshot` block number. + - When a user views a proposal, the app calculates their voting power by querying the Zilliqa blockchain. + - It requests the **balance of the governance token** for the specific wallet address **at that specific block height**. + - This ensures users cannot buy tokens *after* a proposal starts to influence the vote. + +### 3. Casting the Vote +When a particular wallet address initiates a vote, the following process occurs: + +#### A. Interaction +- The user selects a choice (e.g., "For", "Against") in the [Proposal.vue](src/views/Proposal.vue) view. +- Upon clicking "Vote", a confirmation modal ([Modal/Confirm.vue](src/components/Modal/Confirm.vue)) appears. + +#### B. Message Construction +When confirmed, the application ([src/store/modules/app.ts](src/store/modules/app.ts)) constructs a message envelope (`msg` variable) . This includes the **user's wallet address** (identifying the voter) and the JSON-stringified vote payload. Including the address allows the backend to know which account is performing the action, which will be verified against the signature. + +```json +{ + "address": "", + "msg": JSON.stringify({ + "version": "...", + "timestamp": "", + "token": "", + "type": "vote", + "payload": { + "proposal": "", + "choice": , + "metadata": {} + } + }) +} ``` -npm install + +#### C. Cryptographic Signature +- **Action**: `signMessage` in [src/helpers/web3.ts](src/helpers/web3.ts). +- The application requests the connected wallet (e.g., ZilPay) to **sign** this message. +- This action occurs off-chain. It does not broadcast a transaction to the network. +- The resulting signature is a cryptographic proof that the owner of the `particular wallet address` authorized this specific choice. +- The signature is saved in `msg.sign` field. + +#### D. Submission +- The application bundles the **Message**, the **Address**, and the **Signature**. +- It sends this package via an HTTP POST request to the backend hub (API) using [src/helpers/client.ts](src/helpers/client.ts). + +### Summary +1. **Identify**: The address is identified by the connected wallet. +2. **Quantify**: The system queries the blockchain to see how many tokens that address held at the snapshot block. +3. **Authorize**: The address owner calculates the vote intention locally and cryptographically signs it using their private key (managed by the wallet software). +4. **Submit**: The signed intent is relayed to a centralized hub for aggregation. + +## Development + + +### Project setup +``` +yarn install ``` ### Compiles and hot-reloads for development ``` -npm run serve +yarn run serve ``` ### Compiles and minifies for production ``` -npm run build +yarn run build ``` ### Lints and fixes files ``` -npm run lint +yarn run lint ``` ## Pre-built app directory notes @@ -39,18 +107,14 @@ RUN yarn build ## Snapshot Spaces Commit https://github.com/Zilliqa/snapshot-spaces/commit/238e87aad231351a51727b06208ab407f0de1dcc -## License +## Deployment -[MIT](LICENSE). - -20220216 - -## Deploying applications with z +### Deploying applications with z `z` is the one-stop shop for the Zilliqa provisioning and deployment operations. To deploy applications with z ensure the `z` binary is installed in your operative system PATH environment variable. For more details about `z` please refer to the [documentation](https://github.com/Zilliqa/devops/blob/main/docs/z2.md). -## Deploying applications to staging +### Deploying applications to staging To deploy the staging environment we need to clone the devops repository and execute `z` from there: @@ -60,7 +124,7 @@ cd devops source setenv ``` -### Set the following environment variables +#### Set the following environment variables - `Z_ENV` to the path in which your `z.yaml` resides. - `ZQ_USER` to your username (the bit before `@` in your email address) @@ -74,13 +138,13 @@ export ZQ_USER=@zilliqa.com export GITHUB_PAT= ``` -### Login to Google Cloud +#### Login to Google Cloud ```sh z login ``` -### Add the application to the staging `z.yaml` file. Skip this step if it is an existing application +#### Add the application to the staging `z.yaml` file. Skip this step if it is an existing application 1. Create a branch: @@ -130,7 +194,7 @@ z login z apply ``` -### Deploy the application +#### Deploy the application ```sh z app sync --cache-dir=.cache governance-snapshot @@ -138,7 +202,7 @@ z app sync --cache-dir=.cache governance-snapshot Verify your application is running correct from the staging URL and with `kubectl` commands (if required). -## Deploying applications to production +### Deploying applications to production To deploy the production environment we need to clone the devops repository and execute `z` from there: @@ -148,7 +212,7 @@ cd devops source setenv ``` -### Set the following environment variables +#### Set the following environment variables - `Z_ENV` to the path in which your `z.yaml` resides. - `ZQ_USER` to your username (the bit before `@` in your email address) @@ -162,13 +226,13 @@ export ZQ_USER=@zilliqa.com export GITHUB_PAT= ``` -### Login to Google Cloud +#### Login to Google Cloud ```sh z login ``` -### Add the application to the production `z.yaml` file. Skip this step if it is an existing application +#### Add the application to the production `z.yaml` file. Skip this step if it is an existing application 1. Create a branch: @@ -218,10 +282,17 @@ z login z apply ``` -### Deploy the application +#### Deploy the application ```sh z app sync --cache-dir=.cache governance-snapshot ``` Verify your application is running correct from the production URL and with `kubectl` commands (if required). + + +## License + +[MIT](LICENSE). + +20220216 \ No newline at end of file diff --git a/products/governance-snapshot/config.docker.js b/products/governance-snapshot/config.docker.js new file mode 100644 index 000000000..424dd9214 --- /dev/null +++ b/products/governance-snapshot/config.docker.js @@ -0,0 +1,3 @@ +// Runtime config injected by docker-compose. +// Mounted over /usr/share/nginx/html/config.js inside the governance-snapshot container. +window['VUE_APP_HUB_URL'] = 'http://localhost:3000'; diff --git a/products/governance-snapshot/package.json b/products/governance-snapshot/package.json index bd71cce1e..f1e573f79 100644 --- a/products/governance-snapshot/package.json +++ b/products/governance-snapshot/package.json @@ -1,80 +1,73 @@ { "name": "snapshot", "version": "0.1.2", - "repository": "zilpay/snapshot", "license": "MIT", "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", + "serve": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve", + "build": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { - "@ensdomains/content-hash": "^2.5.3", - "@ethersproject/abi": "^5.0.1", - "@ethersproject/address": "^5.0.1", - "@ethersproject/constants": "^5.0.1", - "@ethersproject/contracts": "^5.0.1", - "@ethersproject/providers": "^5.0.12", - "@ethersproject/units": "^5.0.1", - "@ethersproject/wallet": "^5.0.1", - "@portis/web3": "2.0.0-beta.49", - "@primer/css": "^14.4.0", + "@zilliqa-js/zilliqa": "3.5.0", + "@zilliqa-js/util": "3.5.0", + "@ensdomains/content-hash": "2.5.7", + "@ethersproject/abi": "5.0.13", + "@ethersproject/address": "5.0.11", + "@ethersproject/constants": "5.0.10", + "@ethersproject/contracts": "5.0.12", + "@ethersproject/providers": "5.0.24", + "@ethersproject/units": "5.0.11", + "@ethersproject/wallet": "5.0.12", + "@primer/css": "14.4.0", "@snapshot-labs/lock": "github:snapshot-labs/lock#5b9f0592731c0105bb47f8b7bf78b4f164e27c3e", "@snapshot-labs/snapshot-spaces": "github:Zilliqa/snapshot-spaces#master", "@snapshot-labs/snapshot.js": "github:snapshot-labs/snapshot.js#master", - "@vue/cli-plugin-babel": "^4.4.0", - "@vue/cli-plugin-eslint": "^4.4.0", - "@vue/cli-plugin-typescript": "^4.4.0", - "@vue/cli-service": "^4.4.0", - "@walletconnect/web3-provider": "^1.3.1", - "ajv": "^7.0.0-beta.1", - "big.js": "^6.0.3", - "bluebird": "^3.7.2", - "body-parser": "^1.19.0", - "bs58": "^4.0.1", - "core-js": "^3.6.5", - "cors": "^2.8.5", - "eslint": "^6.7.2", - "fortmatic": "^2.0.6", - "jsonexport": "^3.0.1", - "lodash": "^4.17.15", - "numeral": "^2.0.4", - "pretty-ms": "^7.0.0", - "primer-support": "^5.0.0", - "remarkable": "^2.0.1", - "require-context": "^1.1.0", - "sanitize-html": "^2.12.1", - "serve-static": "^1.14.1", - "stylus": "^0.54.8", - "stylus-loader": "^3.0.2", - "typescript": "~3.9.3", - "vue": "^2.6.11", - "vue-autofocus-directive": "^1.0.4", - "vue-cli-plugin-webpack-bundle-analyzer": "^2.0.0", - "vue-clipboard2": "^0.3.1", - "vue-i18n": "^8.18.1", - "vue-infinite-scroll": "^2.0.2", - "vue-jazzicon": "^0.1.3", - "vue-router": "^3.2.0", - "vue-textarea-autosize": "^1.1.1", - "vuedraggable": "^2.24.1", - "vuex": "^3.4.0", - "walletlink": "^2.0.2", - "web3-eth-abi": "^1.3.0" + "@vue/cli-plugin-babel": "4.4.6", + "@vue/cli-plugin-eslint": "4.4.6", + "@vue/cli-plugin-typescript": "4.4.6", + "@vue/cli-service": "4.4.6", + "big.js": "6.0.3", + "body-parser": "1.19.2", + "bs58": "4.0.1", + "core-js": "3.49.0", + "jsonexport": "3.0.1", + "lodash": "4.18.1", + "numeral": "2.0.6", + "pretty-ms": "7.0.1", + "primer-support": "5.0.0", + "remarkable": "2.0.1", + "stylus": "0.54.8", + "stylus-loader": "3.0.2", + "typescript": "3.9.10", + "vue": "2.6.14", + "vue-autofocus-directive": "1.0.4", + "vue-clipboard2": "0.3.3", + "vue-i18n": "8.18.1", + "vue-infinite-scroll": "2.0.2", + "vue-jazzicon": "0.1.3", + "vue-router": "3.2.0", + "vue-textarea-autosize": "1.1.1", + "vuedraggable": "2.24.1", + "vuex": "3.4.0", + "web3-eth-abi": "1.3.0" + }, + "resolutions": { + "@achrinza/node-ipc": "9.2.10" }, "devDependencies": { - "@types/node": "^14.0.13", - "@typescript-eslint/eslint-plugin": "^2.33.0", - "@typescript-eslint/parser": "^2.33.0", - "@vue/cli-plugin-router": "^4.4.0", - "@vue/cli-plugin-vuex": "^4.4.0", - "@vue/eslint-config-prettier": "^6.0.0", - "@vue/eslint-config-typescript": "^5.0.2", - "eslint-plugin-prettier": "^3.1.3", - "eslint-plugin-vue": "^6.2.2", - "node-sass": "^4.12.0", - "prettier": "^1.19.1", - "sass-loader": "^8.0.2", - "vue-template-compiler": "^2.6.11" + "@types/node": "^14.0.27", + "@typescript-eslint/eslint-plugin": "2.33.0", + "@typescript-eslint/parser": "2.33.0", + "@vue/cli-plugin-router": "4.4.6", + "@vue/cli-plugin-vuex": "4.4.6", + "@vue/eslint-config-prettier": "6.0.0", + "@vue/eslint-config-typescript": "5.0.2", + "eslint": "6.7.2", + "eslint-plugin-prettier": "3.1.4", + "eslint-plugin-vue": "6.2.2", + "sass": "1.32.13", + "prettier": "1.19.1", + "sass-loader": "10.5.2", + "vue-template-compiler": "2.6.14" } } \ No newline at end of file diff --git a/products/governance-snapshot/src/assets/zlp.png b/products/governance-snapshot/src/assets/zlp.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2aa98a5f8807beff92609110bfd96d0083f89b GIT binary patch literal 5997 zcmV-z7n10SP)XR19%>abIFIn23rRWlM}n zBr)zVqbAY3y!V`E7RAVp#*joM$peMBqC|bhC2^0$l@P@!K4CUMaAle9Q}2)2db!EGwWhwgjpo;uT2r%u&5b?QO|6;x0`1r=0KK?N05P(cM1R8T<$6;!a}L$-GZ ziUpxy+ZXfNC7aEy*YQB8R5hXPRtfOv73{9SwAArbL2Xo^m`L^Fe z-60Nr4yg9I?SLN*V)irv&6P(3Qx7%BuiL*aSLF9YnDs=i?G+m(r2q=jOzkH?&-m*C z%Z1jlh49{+nfjlKT$_R9PRHhN*D(LnTzNE7`wM}Kx~}M>27B>9X1002pK);h44!93N(l%w)#e-!oG1ao;7vco*QCtA2d%Ze86> z-OOCtECxzW0URc+0<@!-;vQ(G?(BT{C^9V@jUfA3Vt;OC%0Ag>{V<4rw*mk3;a=NJ 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 bg7El%gmVAp9jJkU00000NkvXXu0mjfbr_7> literal 0 HcmV?d00001 diff --git a/products/governance-snapshot/src/auth.ts b/products/governance-snapshot/src/auth.ts index 1e65a5d9b..8d120cc1e 100644 --- a/products/governance-snapshot/src/auth.ts +++ b/products/governance-snapshot/src/auth.ts @@ -1,10 +1,10 @@ import Vue from 'vue'; -import { LockPlugin, ZilPay as zlp } from '@/helpers/plugins/LockPlugin'; +import { LockPlugin, ZilPay as zlp, EVMConnector as evm } from '@/helpers/plugins/LockPlugin'; import config from '@/helpers/config'; const options: any = { connectors: [] }; -const connectors = { zlp }; +const connectors = { zlp, evm }; Object.entries(config.connectors).forEach((connector: any) => { options.connectors.push({ diff --git a/products/governance-snapshot/src/components/Badges.vue b/products/governance-snapshot/src/components/Badges.vue index c391c44b4..144cf195b 100644 --- a/products/governance-snapshot/src/components/Badges.vue +++ b/products/governance-snapshot/src/components/Badges.vue @@ -6,13 +6,13 @@