From 280d530404a4b46d800dee139b348e9e3abc371a Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 5 Jun 2025 13:53:58 +0530 Subject: [PATCH 1/5] Add claude init file --- CLAUDE.md | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4914196 --- /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. + +## Project Overview + +This is a monorepo containing blockchain watchers for the Azimuth PKI system used in Urbit identities. It watches multiple Ethereum contracts (Azimuth, Censures, Claims, ConditionalStarRelease, DelegatedSending, Ecliptic, LinearStarRelease, Polls) and provides GraphQL APIs for querying their state. + +## Common Commands + +### Building and Development +```bash +# Build all packages +yarn build +# or +lerna run build --stream + +# Lint all packages (max warnings = 0) +yarn lint +# or +lerna run lint --stream -- --max-warnings=0 + +# Set versions across packages +yarn version:set +# or +lerna version --no-git-tag-version +``` + +### Individual Watcher Commands +Each watcher package supports these commands: +```bash +# Development server (with hot reload) +yarn server:dev + +# Production server +yarn server + +# Job runner (processes blockchain events) +yarn job-runner:dev # development +yarn job-runner # production + +# Watch contract for events +yarn watch:contract + +# Fill historical data +yarn fill + +# Reset operations +yarn reset + +# State management +yarn checkpoint:dev +yarn export-state:dev +yarn import-state:dev + +# Utilities +yarn inspect-cid +yarn index-block +``` + +### Gateway Server +```bash +# Development gateway server (proxies to all watchers) +yarn server:dev + +# Production gateway server +yarn server +``` + +## Architecture + +### Monorepo Structure +- **Lerna-managed** yarn workspaces with 8 watcher packages + 1 gateway server +- Each watcher is a standalone service with its own database and GraphQL endpoint +- **Gateway server** acts as a unified GraphQL proxy that routes queries to appropriate watchers + +### Watcher Packages +Each watcher follows identical structure: +- **Port allocation**: azimuth(3001), censures(3002), claims(3003), conditionalStarRelease(3004), delegatedSending(3005), ecliptic(3006), linearStarRelease(3007), polls(3008) +- **Database**: Individual PostgreSQL database per watcher +- **Configuration**: TOML files in `environments/` directory +- **Generated code**: Built from contract ABIs using `@cerc-io/codegen` + +### Key Components Per Watcher +- `src/server.ts` - GraphQL server +- `src/job-runner.ts` - Event processing worker +- `src/indexer.ts` - Blockchain event indexing logic +- `src/resolvers.ts` - GraphQL resolvers +- `src/entity/` - TypeORM entities for all contract methods +- `src/gql/queries/` - GraphQL query definitions +- `src/cli/` - Command-line utilities for management + +### Gateway Server Architecture +- **Schema stitching**: Combines all watcher schemas with prefixed field names +- **Health checking**: Monitors watcher availability before routing +- **Configuration**: `src/watchers.json` defines watcher endpoints and prefixes +- **GraphQL proxy**: Routes queries like `azimuthGetKeys` to azimuth-watcher at localhost:3001 + +### Data Flow +1. **Event Processing**: job-runner fetches Ethereum events → processes through indexer → stores in database +2. **Query Processing**: GraphQL queries → gateway server → appropriate watcher → database → response +3. **State Management**: Supports checkpointing, state export/import for data recovery + +## Configuration Notes + +### Environment Setup +- Each watcher requires PostgreSQL database (configurable in `environments/local.toml`) +- Requires Ethereum RPC endpoint (ipld-eth-server or standard RPC) +- Gateway server expects all watchers running on their designated ports + +### Development Workflow +- Start individual watchers with `yarn server:dev` and `yarn job-runner:dev` +- Start gateway server for unified GraphQL endpoint +- Use `yarn fill` to sync historical blockchain data +- Monitor with debug logs using `DEBUG=vulcanize:*` + +### Generated Watcher Creation +Watchers are generated using `@cerc-io/codegen` from contract ABIs. The process involves creating config.yaml files specifying contract paths, output folders, and generation modes (eth_call/storage/all). \ No newline at end of file -- 2.45.2 From 63d8ab41ca06c42c9a0bd2c26516c5a37fdf9116 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 5 Jun 2025 14:02:16 +0530 Subject: [PATCH 2/5] Move watcher generation instructions to a separate file --- README.md | 77 +------------------------------------------- generate-watchers.md | 76 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 76 deletions(-) create mode 100644 generate-watchers.md diff --git a/README.md b/README.md index 64eba30..142c0aa 100644 --- a/README.md +++ b/README.md @@ -108,79 +108,4 @@ It is also hosted at ## Generate Watchers -Steps to generate Azimuth watchers using the code generator ([`@cerc-io/codegen`](https://github.com/cerc-io/watcher-ts/tree/v0.2.76/packages/codegen)) - -* Clone the original Azimuth repo for required contracts: - - ```bash - git clone git@github.com:urbit/azimuth.git - - # Install dependencies - npm install - - # Contracts are located in the contracts folder - ``` - -* Setup `cerc-io/watcher-ts` repo: - - ```bash - git clone git@github.com:cerc-io/watcher-ts.git - - # Install dependencies and build packages - yarn install && yarn build - ``` - -* Create a folder to place all the generated watchers in: - - ```bash - mkdir -p azimuth-watcher-ts/packages - ``` - -* In `watcher-ts/packages/codegen`, create a `config.yaml` file with required codegen config for generating the watcher for a contract - - For example, for `Azimuth` contract: - - ```yaml - # Contracts to watch (required). - contracts: - # Contract name. - - name: Azimuth - # Contract file path or an url. - path: /home/user/azimuth/contracts/Azimuth.sol - # Contract kind - kind: Azimuth - - # Output folder path (logs output using `stdout` if not provided). - outputFolder: /home/user/azimuth-watcher-ts/packages/azimuth-watcher - - # Code generation mode [eth_call | storage | all | none] (default: none). - mode: eth_call - - # Kind of watcher [lazy | active] (default: active). - kind: active - - # Watcher server port (default: 3008). - port: 3001 - - # Solc version to use (optional) - # If not defined, uses solc version listed in dependencies - solc: v0.4.24+commit.e67f0147 - - # Flatten the input contract file(s) [true | false] (default: true). - flatten: true - ``` - - Note: Create `.sol` files with the contract code from Etherscan for [`ConditionalStarRelease`](https://etherscan.io/address/0x8C241098C3D3498Fe1261421633FD57986D74AeA#code), [`DelegatedSending`](https://etherscan.io/address/0xf6b461fe1ad4bd2ce25b23fe0aff2ac19b3dfa76#code), [`Ecliptic`](https://etherscan.io/address/ecliptic.eth#code) and [`LinearStarRelease`](https://etherscan.io/address/0x86cd9cd0992F04231751E3761De45cEceA5d1801#code) contracts and use the file path for `contracts.path` - -* Run codegen command to generate the watcher: - - ```bash - # In watcher-ts/packages/codegen - yarn codegen --config-file ./config.yaml - ``` - -* Update `contracts`, `outputFolder` and `port` fields in the config and re-run the codegen command for all other contracts - -* Setup the parent folder `/home/user/azimuth-watcher-ts` where all the generated watchers are placed as a monorepo - -* The [gateway GQL server](packages/gateway-server) can be used to proxy queries to their respective watchers +Follow [instructions](./generate-watchers.md) to generate Azimuth watchers using the code generator ([`@cerc-io/codegen`](https://github.com/cerc-io/watcher-ts/tree/v0.2.76/packages/codegen)) diff --git a/generate-watchers.md b/generate-watchers.md new file mode 100644 index 0000000..67e39a8 --- /dev/null +++ b/generate-watchers.md @@ -0,0 +1,76 @@ +# Generate Azimuth Watchers + +* Clone the original Azimuth repo for required contracts: + + ```bash + git clone git@github.com:urbit/azimuth.git + + # Install dependencies + npm install + + # Contracts are located in the contracts folder + ``` + +* Setup `cerc-io/watcher-ts` repo: + + ```bash + git clone git@github.com:cerc-io/watcher-ts.git + + # Install dependencies and build packages + yarn install && yarn build + ``` + +* Create a folder to place all the generated watchers in: + + ```bash + mkdir -p azimuth-watcher-ts/packages + ``` + +* In `watcher-ts/packages/codegen`, create a `config.yaml` file with required codegen config for generating the watcher for a contract + + For example, for `Azimuth` contract: + + ```yaml + # Contracts to watch (required). + contracts: + # Contract name. + - name: Azimuth + # Contract file path or an url. + path: /home/user/azimuth/contracts/Azimuth.sol + # Contract kind + kind: Azimuth + + # Output folder path (logs output using `stdout` if not provided). + outputFolder: /home/user/azimuth-watcher-ts/packages/azimuth-watcher + + # Code generation mode [eth_call | storage | all | none] (default: none). + mode: eth_call + + # Kind of watcher [lazy | active] (default: active). + kind: active + + # Watcher server port (default: 3008). + port: 3001 + + # Solc version to use (optional) + # If not defined, uses solc version listed in dependencies + solc: v0.4.24+commit.e67f0147 + + # Flatten the input contract file(s) [true | false] (default: true). + flatten: true + ``` + + Note: Create `.sol` files with the contract code from Etherscan for [`ConditionalStarRelease`](https://etherscan.io/address/0x8C241098C3D3498Fe1261421633FD57986D74AeA#code), [`DelegatedSending`](https://etherscan.io/address/0xf6b461fe1ad4bd2ce25b23fe0aff2ac19b3dfa76#code), [`Ecliptic`](https://etherscan.io/address/ecliptic.eth#code) and [`LinearStarRelease`](https://etherscan.io/address/0x86cd9cd0992F04231751E3761De45cEceA5d1801#code) contracts and use the file path for `contracts.path` + +* Run codegen command to generate the watcher: + + ```bash + # In watcher-ts/packages/codegen + yarn codegen --config-file ./config.yaml + ``` + +* Update `contracts`, `outputFolder` and `port` fields in the config and re-run the codegen command for all other contracts + +* Setup the parent folder `/home/user/azimuth-watcher-ts` where all the generated watchers are placed as a monorepo + +* The [gateway GQL server](packages/gateway-server) can be used to proxy queries to their respective watchers -- 2.45.2 From d796dde1a84c9ce4f664de5c984b730e14407714 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 5 Jun 2025 14:51:04 +0530 Subject: [PATCH 3/5] Generate documentation --- README.md | 366 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 275 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 142c0aa..63ae9c8 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,294 @@ # azimuth-watcher-ts -Watcher for the Azimuth PKI on Ethereum, used in Urbit identities. Read more about Azimuth: +A comprehensive blockchain indexing and querying system for [Azimuth](https://developers.urbit.org/reference/azimuth/azimuth), Urbit's identity layer on Ethereum. This system monitors multiple Ethereum smart contracts that make up the Azimuth PKI and provides a unified GraphQL API for querying Urbit identity information. -* [https://developers.urbit.org/reference/azimuth/azimuth](https://developers.urbit.org/reference/azimuth/azimuth) +## What is Azimuth? -This app can be run using Stack Orchestrator: +Azimuth is Urbit's public key infrastructure (PKI) that lives on Ethereum. It's a set of smart contracts that manage Urbit identities called "points" (similar to usernames), their ownership, cryptographic keys, and hierarchical relationships. Each Urbit identity is represented as an NFT on Ethereum, making it decentralized and censorship-resistant. -* [Azimuth stack](https://git.vdb.to/cerc-io/stack-orchestrator/src/branch/main/app/data/stacks/azimuth) +## What are Blockchain Watchers? -It is also hosted at +Blockchain watchers are services that continuously monitor smart contracts on Ethereum, index their events and state changes, and provide efficient APIs for querying blockchain data. Instead of directly querying the Ethereum blockchain (which is slow and expensive), applications can query watchers for fast, indexed access to current and historical blockchain state. -## Usage +## System Architecture -* Query public keys for a point: +This system consists of **8 specialized watchers** that each monitor different Azimuth smart contracts: - ```bash - # Example - curl 'https://azimuth.dev.vdb.to/graphql' \ - -H 'Content-Type: application/json' \ - --data-raw '{"query":"{ azimuthGetKeys(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 58213) { value { encryptionKey: value0 authenticationKey: value1 cryptoSuiteVersion: value2 keyRevisionNumber: value3 } } }"}' \ - | jq +- **azimuth-watcher** - Core identity operations (ownership, keys, sponsorship) +- **censures-watcher** - Reputation and censure system +- **claims-watcher** - On-chain claims and metadata +- **ecliptic-watcher** - Galaxy operations and governance +- **polls-watcher** - Voting and governance proposals +- **conditional-star-release-watcher** - Conditional star distribution +- **linear-star-release-watcher** - Linear star distribution +- **delegated-sending-watcher** - Delegated operations - # Response - # { - # "data": { - # "azimuthGetKeys": { - # "value": { - # "encryptionKey": "0xc248f759474b16192bd8bdca0bff1b8bff555cd3d118022095331d6d98690c6d", - # "authenticationKey": "0x21188bac08542730e1c4697636d6fa25968f404470ccf917756f05e28c69045a", - # "cryptoSuiteVersion": "1", - # "keyRevisionNumber": "1" - # } - # } - # } - # } - ``` +Plus a **gateway server** that provides a unified GraphQL endpoint routing queries to the appropriate watcher. - * API params: - * `contractAddress`: Azimuth contract address - * `blockHash`: block hash at which you want to query the contract state +## Hosted Service -* Example GQL queries: +A public instance is available at: **** - ```gql - { - azimuthIsActive( - blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" - contractAddress: "0x223c067F8CF28ae173EE5CafEa60cA44C335fecB" - _point: 1 - ) { - value +You can also run the system locally using [Stack Orchestrator](https://git.vdb.to/cerc-io/stack-orchestrator/src/branch/main/app/data/stacks/azimuth). + +## Common Use Cases + +### Querying Urbit Point Information + +The **azimuth-watcher** is the primary service for querying Urbit identity data. Here are the most common operations: + +#### 1. Get Point Owner and Basic Info + +```bash +# Check who owns a specific Urbit point +curl 'https://azimuth.dev.vdb.to/graphql' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "query": "{ + azimuthGetOwner( + blockHash: \"latest\", + contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", + _point: 1234 + ) { + value + } + }" + }' | jq +``` + +#### 2. Get Cryptographic Keys for a Point + +```bash +# Get encryption and authentication keys for networking +curl 'https://azimuth.dev.vdb.to/graphql' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "query": "{ + azimuthGetKeys( + blockHash: \"latest\", + contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", + _point: 58213 + ) { + value { + encryptionKey: value0, + authenticationKey: value1, + cryptoSuiteVersion: value2, + keyRevisionNumber: value3 + } + } + }" + }' | jq + +# Example Response: +# { +# "data": { +# "azimuthGetKeys": { +# "value": { +# "encryptionKey": "0xc248f759474b16192bd8bdca0bff1b8bff555cd3d118022095331d6d98690c6d", +# "authenticationKey": "0x21188bac08542730e1c4697636d6fa25968f404470ccf917756f05e28c69045a", +# "cryptoSuiteVersion": "1", +# "keyRevisionNumber": "1" +# } +# } +# } +# } +``` + +#### 3. Check Point Status + +```bash +# Check if a point is active (booted) and has a sponsor +curl 'https://azimuth.dev.vdb.to/graphql' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "query": "{ + azimuthIsActive(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } + azimuthHasSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } + azimuthGetSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } + }" + }' | jq +``` + +#### 4. Get All Points Owned by an Address + +```bash +# Find all Urbit points owned by an Ethereum address +curl 'https://azimuth.dev.vdb.to/graphql' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "query": "{ + azimuthGetOwnedPoints( + blockHash: \"latest\", + contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", + _whose: \"0x1234567890123456789012345678901234567890\" + ) { + value + } + }" + }' | jq +``` + +### Understanding Query Parameters + +All queries require these standard parameters: + +- **`blockHash`**: Use `"latest"` for current state, or a specific block hash for historical queries +- **`contractAddress`**: Azimuth contract address (`0x223c067F8CF28ae173EE5CafEa60cA44C335fecB`) +- **`_point`**: The Urbit point number you're querying +- **`_whose`**: Ethereum address when querying by owner + +## Available Query Types + +### Azimuth Watcher (Core Identity Operations) + +**Point Ownership & Information:** +- `azimuthGetOwner` - Get point owner address +- `azimuthGetOwnedPoints` - Get all points owned by an address +- `azimuthIsActive` - Check if point is activated +- `azimuthIsLive` - Check if point is online + +**Cryptographic Keys:** +- `azimuthGetKeys` - Get encryption/authentication keys +- `azimuthGetKeyRevisionNumber` - Get key version number + +**Sponsorship Hierarchy:** +- `azimuthGetSponsor` - Get point's sponsor +- `azimuthGetSponsoring` - Get points sponsored by a point +- `azimuthHasSponsor` - Check if point has sponsor + +**Proxies & Permissions:** +- `azimuthGetManagementProxy` - Get management proxy address +- `azimuthCanManage` - Check management permissions +- `azimuthGetSpawnProxy` - Get spawn proxy address +- `azimuthCanSpawnAs` - Check spawn permissions + +### Other Watchers + +**Censures** - Reputation system queries with prefix `censures` +**Claims** - Metadata and claims queries with prefix `claims` +**Ecliptic** - Galaxy operations queries with prefix `ecliptic` +**Polls** - Governance voting queries with prefix `polls` +**Star Release** - Token distribution queries with prefixes `linearStarRelease` and `conditionalStarRelease` +**Delegated Sending** - Delegation queries with prefix `delegatedSending` + +### Multi-Watcher Query Example + +```graphql +{ + # Check point status (azimuth-watcher) + azimuthIsActive(blockHash: "latest", contractAddress: "0x223c067F8CF28ae173EE5CafEa60cA44C335fecB", _point: 1) { + value + } + + # Check censure count (censures-watcher) + censuresGetCensuredByCount(blockHash: "latest", contractAddress: "0x325f68d32BdEe6Ed86E7235ff2480e2A433D6189", _who: 1) { + value + } + + # Find a claim (claims-watcher) + claimsFindClaim(blockHash: "latest", contractAddress: "0xe7e7f69b34D7d9Bd8d61Fb22C33b22708947971A", _whose: 1234, _protocol: "text", _claim: "hello world") { + value + } +} + +## Real-time Updates + +Subscribe to blockchain events in real-time: + +```graphql +subscription { + onEvent { + event { + __typename + ... on OwnerChangedEvent { + point + owner + } + ... on ActivatedEvent { + point + } + ... on ChangedKeysEvent { + point + encryptionKey + authenticationKey + } } - censuresGetCensuredByCount( - blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" - contractAddress: "0x325f68d32BdEe6Ed86E7235ff2480e2A433D6189" - _who: 6054 - ) { - value - } - claimsFindClaim( - blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" - contractAddress: "0xe7e7f69b34D7d9Bd8d61Fb22C33b22708947971A" - _whose: 1967913144 - _protocol: "text" - _claim: "Shrek is NOT Drek!" - ) { - value - } - linearStarReleaseVerifyBalance( - blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" - contractAddress: "0x86cd9cd0992F04231751E3761De45cEceA5d1801" - _participant: "0xbD396c580d868FBbE4a115DD667E756079880801" - ) { - value - } - conditionalStarReleaseWithdrawLimit( - blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" - contractAddress: "0x8C241098C3D3498Fe1261421633FD57986D74AeA" - _participant: "0x7F0584938E649061e80e45cF88E6d8dDDb22f2aB" - _batch: 2 - ) { - value - } - pollsGetUpgradeProposalCount( - blockHash: "0xeaf611fabbe604932d36b97c89955c091e9582e292b741ebf144962b9ff5c271" - contractAddress: "0x7fEcaB617c868Bb5996d99D95200D2Fa708218e4" - ) { - value - } - eclipticBalanceOf( - blockHash: "0x5e82abbe6474caf7b5325022db1d1287ce352488b303685493289770484f54f4" - contractAddress: "0x33EeCbf908478C10614626A9D304bfe18B78DD73" - _owner: "0x4b5E239C1bbb98d44ea23BC9f8eC7584F54096E8" - ) { - value - } - delegatedSendingCanSend( - blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" - contractAddress: "0xf6b461fE1aD4bd2ce25B23Fe0aff2ac19B3dFA76" - _as: 1 - _point: 1 - ) { - value + block { + hash + number + timestamp } } - ``` +} +``` + +## Development Setup + +### Prerequisites +- Node.js 16+ +- PostgreSQL database +- Ethereum RPC endpoint (or ipld-eth-server) + +### Quick Start + +1. **Clone and install dependencies:** + ```bash + git clone https://github.com/cerc-io/azimuth-watcher-ts.git + cd azimuth-watcher-ts + yarn install + ``` + +2. **Build all packages:** + ```bash + yarn build + ``` + +3. **Configure environment:** + - Copy `packages/azimuth-watcher/environments/local.toml` and update database/RPC settings + - Set up PostgreSQL databases for each watcher + +4. **Start the azimuth-watcher:** + ```bash + cd packages/azimuth-watcher + yarn server:dev # GraphQL server + yarn job-runner:dev # Blockchain indexer (separate terminal) + ``` + +5. **Start the gateway server (optional):** + ```bash + cd packages/gateway-server + yarn server:dev + ``` + +### Commands + +**Build and lint:** +```bash +yarn build # Build all packages +yarn lint # Lint with zero warnings tolerance +``` + +**Individual watcher operations:** +```bash +yarn server:dev # Development GraphQL server +yarn job-runner:dev # Event processing worker +yarn fill # Backfill historical data +yarn reset # Reset watcher state +yarn checkpoint # Create state checkpoint +``` + +## Architecture Details + +### Data Flow +1. **Indexing**: Job runners continuously fetch Ethereum events and blocks +2. **Processing**: Events are processed and state changes stored in PostgreSQL +3. **Querying**: GraphQL servers provide indexed data with historical query support +4. **Gateway**: Unified endpoint routes queries to appropriate specialized watchers + +### Storage +- Each watcher maintains its own PostgreSQL database +- State is checkpointed periodically for recovery +- IPLD (Content-addressed storage) support for cryptographic proofs ## Generate Watchers -- 2.45.2 From 2f87f3c7ea668405446e0819ba557b81e68a55f1 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 5 Jun 2025 15:48:08 +0530 Subject: [PATCH 4/5] Remove unnecessary info and update examples --- README.md | 336 +++++++++++++++++++++++------------------------------- 1 file changed, 143 insertions(+), 193 deletions(-) diff --git a/README.md b/README.md index 63ae9c8..b731bd1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # azimuth-watcher-ts -A comprehensive blockchain indexing and querying system for [Azimuth](https://developers.urbit.org/reference/azimuth/azimuth), Urbit's identity layer on Ethereum. This system monitors multiple Ethereum smart contracts that make up the Azimuth PKI and provides a unified GraphQL API for querying Urbit identity information. +A comprehensive blockchain indexing and querying system for [Azimuth](https://docs.urbit.org/system/identity), Urbit's identity layer on Ethereum. This system monitors multiple Ethereum smart contracts that make up the Azimuth PKI and provides a unified GraphQL API for querying Urbit identity information. ## What is Azimuth? -Azimuth is Urbit's public key infrastructure (PKI) that lives on Ethereum. It's a set of smart contracts that manage Urbit identities called "points" (similar to usernames), their ownership, cryptographic keys, and hierarchical relationships. Each Urbit identity is represented as an NFT on Ethereum, making it decentralized and censorship-resistant. +Azimuth is Urbit's public key infrastructure (PKI) that lives on Ethereum. It's a set of smart contracts that manage Urbit identities called "points" (similar to usernames), their ownership, cryptographic keys, and hierarchical relationships. By storing identity data on Ethereum, the system is decentralized and censorship-resistant. ## What are Blockchain Watchers? @@ -41,45 +41,33 @@ The **azimuth-watcher** is the primary service for querying Urbit identity data. ```bash # Check who owns a specific Urbit point +# Example: curl 'https://azimuth.dev.vdb.to/graphql' \ -H 'Content-Type: application/json' \ - --data-raw '{ - "query": "{ - azimuthGetOwner( - blockHash: \"latest\", - contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", - _point: 1234 - ) { - value - } - }" - }' | jq + --data-raw '{"query":"{ azimuthGetOwner(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } }"}' \ + | jq + +# Response: +# { +# "data": { +# "azimuthGetOwner": { +# "value": "0x4b22764F2Db640aB4d0Ecfd0F84344F3CB5C3715" +# } +# } +# } ``` #### 2. Get Cryptographic Keys for a Point ```bash # Get encryption and authentication keys for networking +# Example: curl 'https://azimuth.dev.vdb.to/graphql' \ -H 'Content-Type: application/json' \ - --data-raw '{ - "query": "{ - azimuthGetKeys( - blockHash: \"latest\", - contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", - _point: 58213 - ) { - value { - encryptionKey: value0, - authenticationKey: value1, - cryptoSuiteVersion: value2, - keyRevisionNumber: value3 - } - } - }" - }' | jq + --data-raw '{"query":"{ azimuthGetKeys(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 58213) { value { encryptionKey: value0 authenticationKey: value1 cryptoSuiteVersion: value2 keyRevisionNumber: value3 } } }"}' \ + | jq -# Example Response: +# Response: # { # "data": { # "azimuthGetKeys": { @@ -98,34 +86,132 @@ curl 'https://azimuth.dev.vdb.to/graphql' \ ```bash # Check if a point is active (booted) and has a sponsor +# Example: curl 'https://azimuth.dev.vdb.to/graphql' \ -H 'Content-Type: application/json' \ - --data-raw '{ - "query": "{ - azimuthIsActive(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } - azimuthHasSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } - azimuthGetSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } - }" - }' | jq + --data-raw '{"query":"{ azimuthIsActive(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } azimuthHasSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } azimuthGetSponsor(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _point: 1234) { value } }"}' \ + | jq + +# Response: +# { +# "data": { +# "azimuthIsActive": { +# "value": true +# }, +# "azimuthHasSponsor": { +# "value": true +# }, +# "azimuthGetSponsor": { +# "value": "210" +# } +# } +# } ``` #### 4. Get All Points Owned by an Address ```bash # Find all Urbit points owned by an Ethereum address +# Example: curl 'https://azimuth.dev.vdb.to/graphql' \ -H 'Content-Type: application/json' \ - --data-raw '{ - "query": "{ - azimuthGetOwnedPoints( - blockHash: \"latest\", - contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", - _whose: \"0x1234567890123456789012345678901234567890\" - ) { - value - } - }" - }' | jq + --data-raw '{"query":"{ azimuthGetOwnedPoints(blockHash: \"latest\", contractAddress: \"0x223c067F8CF28ae173EE5CafEa60cA44C335fecB\", _whose: \"0x1234567890123456789012345678901234567890\") { value } }"}' \ + | jq + +# Response: +# { +# "data": { +# "azimuthGetOwnedPoints": { +# "value": [ +# "57965", +# "1234" +# ] +# } +# } +# } +``` + +#### 5. Multi-Watcher Queries + +The gateway server allows querying multiple watchers in a single request: + +```graphql +{ + # Check point status (azimuth-watcher) + azimuthIsActive( + blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" + contractAddress: "0x223c067F8CF28ae173EE5CafEa60cA44C335fecB" + _point: 1 + ) { + value + } + + # Check censure count (censures-watcher) + censuresGetCensuredByCount( + blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" + contractAddress: "0x325f68d32BdEe6Ed86E7235ff2480e2A433D6189" + _who: 6054 + ) { + value + } } + + # Find a claim (claims-watcher) + claimsFindClaim( + blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" + contractAddress: "0xe7e7f69b34D7d9Bd8d61Fb22C33b22708947971A" + _whose: 1967913144 + _protocol: "text" + _claim: "Shrek is NOT Drek!" + ) { + value + } + + # Check star release balance (linear-star-release-watcher) + linearStarReleaseVerifyBalance( + blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" + contractAddress: "0x86cd9cd0992F04231751E3761De45cEceA5d1801" + _participant: "0xbD396c580d868FBbE4a115DD667E756079880801" + ) { + value + } + + # Check conditional star release (conditional-star-release-watcher) + conditionalStarReleaseWithdrawLimit( + blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" + contractAddress: "0x8C241098C3D3498Fe1261421633FD57986D74AeA" + _participant: "0x7F0584938E649061e80e45cF88E6d8dDDb22f2aB" + _batch: 2 + ) { + value + } + + # Check governance proposals (polls-watcher) + pollsGetUpgradeProposalCount( + blockHash: "0xeaf611fabbe604932d36b97c89955c091e9582e292b741ebf144962b9ff5c271" + contractAddress: "0x7fEcaB617c868Bb5996d99D95200D2Fa708218e4" + ) { + value + } + + # Check NFT balance (ecliptic-watcher) + eclipticBalanceOf( + blockHash: "0x5e82abbe6474caf7b5325022db1d1287ce352488b303685493289770484f54f4" + contractAddress: "0x33EeCbf908478C10614626A9D304bfe18B78DD73" + _owner: "0x4b5E239C1bbb98d44ea23BC9f8eC7584F54096E8" + ) { + value + } + + # Check delegation permissions (delegated-sending-watcher) + delegatedSendingCanSend( + blockHash: "0x2461e78f075e618173c524b5ab4309111001517bb50cfd1b3505aed5433cf5f9" + contractAddress: "0xf6b461fE1aD4bd2ce25B23Fe0aff2ac19B3dFA76" + _as: 1 + _point: 1 + ) { + value + } +} ``` ### Understanding Query Parameters @@ -137,158 +223,22 @@ All queries require these standard parameters: - **`_point`**: The Urbit point number you're querying - **`_whose`**: Ethereum address when querying by owner -## Available Query Types +## How It Works -### Azimuth Watcher (Core Identity Operations) +### Data Source -**Point Ownership & Information:** -- `azimuthGetOwner` - Get point owner address -- `azimuthGetOwnedPoints` - Get all points owned by an address -- `azimuthIsActive` - Check if point is activated -- `azimuthIsLive` - Check if point is online - -**Cryptographic Keys:** -- `azimuthGetKeys` - Get encryption/authentication keys -- `azimuthGetKeyRevisionNumber` - Get key version number - -**Sponsorship Hierarchy:** -- `azimuthGetSponsor` - Get point's sponsor -- `azimuthGetSponsoring` - Get points sponsored by a point -- `azimuthHasSponsor` - Check if point has sponsor - -**Proxies & Permissions:** -- `azimuthGetManagementProxy` - Get management proxy address -- `azimuthCanManage` - Check management permissions -- `azimuthGetSpawnProxy` - Get spawn proxy address -- `azimuthCanSpawnAs` - Check spawn permissions - -### Other Watchers - -**Censures** - Reputation system queries with prefix `censures` -**Claims** - Metadata and claims queries with prefix `claims` -**Ecliptic** - Galaxy operations queries with prefix `ecliptic` -**Polls** - Governance voting queries with prefix `polls` -**Star Release** - Token distribution queries with prefixes `linearStarRelease` and `conditionalStarRelease` -**Delegated Sending** - Delegation queries with prefix `delegatedSending` - -### Multi-Watcher Query Example - -```graphql -{ - # Check point status (azimuth-watcher) - azimuthIsActive(blockHash: "latest", contractAddress: "0x223c067F8CF28ae173EE5CafEa60cA44C335fecB", _point: 1) { - value - } - - # Check censure count (censures-watcher) - censuresGetCensuredByCount(blockHash: "latest", contractAddress: "0x325f68d32BdEe6Ed86E7235ff2480e2A433D6189", _who: 1) { - value - } - - # Find a claim (claims-watcher) - claimsFindClaim(blockHash: "latest", contractAddress: "0xe7e7f69b34D7d9Bd8d61Fb22C33b22708947971A", _whose: 1234, _protocol: "text", _claim: "hello world") { - value - } -} - -## Real-time Updates - -Subscribe to blockchain events in real-time: - -```graphql -subscription { - onEvent { - event { - __typename - ... on OwnerChangedEvent { - point - owner - } - ... on ActivatedEvent { - point - } - ... on ChangedKeysEvent { - point - encryptionKey - authenticationKey - } - } - block { - hash - number - timestamp - } - } -} -``` - -## Development Setup - -### Prerequisites -- Node.js 16+ -- PostgreSQL database -- Ethereum RPC endpoint (or ipld-eth-server) - -### Quick Start - -1. **Clone and install dependencies:** - ```bash - git clone https://github.com/cerc-io/azimuth-watcher-ts.git - cd azimuth-watcher-ts - yarn install - ``` - -2. **Build all packages:** - ```bash - yarn build - ``` - -3. **Configure environment:** - - Copy `packages/azimuth-watcher/environments/local.toml` and update database/RPC settings - - Set up PostgreSQL databases for each watcher - -4. **Start the azimuth-watcher:** - ```bash - cd packages/azimuth-watcher - yarn server:dev # GraphQL server - yarn job-runner:dev # Blockchain indexer (separate terminal) - ``` - -5. **Start the gateway server (optional):** - ```bash - cd packages/gateway-server - yarn server:dev - ``` - -### Commands - -**Build and lint:** -```bash -yarn build # Build all packages -yarn lint # Lint with zero warnings tolerance -``` - -**Individual watcher operations:** -```bash -yarn server:dev # Development GraphQL server -yarn job-runner:dev # Event processing worker -yarn fill # Backfill historical data -yarn reset # Reset watcher state -yarn checkpoint # Create state checkpoint -``` - -## Architecture Details +The watchers continuously monitor Ethereum smart contracts by connecting to Ethereum RPC endpoint(s), indexing blockchain events and state changes. ### Data Flow -1. **Indexing**: Job runners continuously fetch Ethereum events and blocks -2. **Processing**: Events are processed and state changes stored in PostgreSQL -3. **Querying**: GraphQL servers provide indexed data with historical query support + +1. **Indexing**: Job runners fetch Ethereum events and blocks from RPC endpoint(s) +2. **Processing**: Events are processed and state changes stored in PostgreSQL databases +3. **Querying**: GraphQL servers provide fast, indexed access to current and historical blockchain state 4. **Gateway**: Unified endpoint routes queries to appropriate specialized watchers ### Storage -- Each watcher maintains its own PostgreSQL database -- State is checkpointed periodically for recovery -- IPLD (Content-addressed storage) support for cryptographic proofs + +Each watcher maintains its own PostgreSQL database for efficient querying and data isolation. ## Generate Watchers -- 2.45.2 From fc7cde6c27841147a5257432017b2384e5115d39 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 5 Jun 2025 18:22:40 +0530 Subject: [PATCH 5/5] Handle nitpicks --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b731bd1..bc80a1c 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ A comprehensive blockchain indexing and querying system for [Azimuth](https://do Azimuth is Urbit's public key infrastructure (PKI) that lives on Ethereum. It's a set of smart contracts that manage Urbit identities called "points" (similar to usernames), their ownership, cryptographic keys, and hierarchical relationships. By storing identity data on Ethereum, the system is decentralized and censorship-resistant. -## What are Blockchain Watchers? +## What are Watchers? -Blockchain watchers are services that continuously monitor smart contracts on Ethereum, index their events and state changes, and provide efficient APIs for querying blockchain data. Instead of directly querying the Ethereum blockchain (which is slow and expensive), applications can query watchers for fast, indexed access to current and historical blockchain state. +Watchers are services that continuously monitor smart contracts on Ethereum, index their events and state changes, and provide efficient APIs for querying blockchain data. Instead of directly querying the Ethereum blockchain (which is slow and expensive), applications can query watchers for fast, indexed access to current and historical blockchain state. ## System Architecture @@ -239,7 +239,3 @@ The watchers continuously monitor Ethereum smart contracts by connecting to Ethe ### Storage Each watcher maintains its own PostgreSQL database for efficient querying and data isolation. - -## Generate Watchers - -Follow [instructions](./generate-watchers.md) to generate Azimuth watchers using the code generator ([`@cerc-io/codegen`](https://github.com/cerc-io/watcher-ts/tree/v0.2.76/packages/codegen)) -- 2.45.2