Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions indexer/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# PrivacyLayer Event Indexer Configuration

# Server Configuration
PORT=4000

# Database Configuration
DATABASE_URL="postgresql://user:password@localhost:5432/privacylayer_indexer?schema=public"

# Stellar/Soroban Configuration
SOROBAN_RPC_URL=https://soroban-testnet.stellar.org:443
NETWORK_PASSPHRASE=Test SDF Network ; September 2015
CONTRACT_ID=your_contract_id_here

# Indexer Configuration
POLL_INTERVAL=5000

# For mainnet, use:
# SOROBAN_RPC_URL=https://soroban.stellar.org:443
# NETWORK_PASSPHRASE=Public Global Stellar Network ; September 2015
32 changes: 32 additions & 0 deletions indexer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Dependencies
node_modules/

# Build output
dist/

# Environment files
.env
.env.local
.env.*.local

# IDE
.idea/
.vscode/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Logs
logs/
*.log
npm-debug.log*

# Test coverage
coverage/

# Prisma
prisma/*.db
prisma/*.db-journal
20 changes: 20 additions & 0 deletions indexer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM node:18-alpine

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy Prisma schema
COPY prisma ./prisma/
RUN npx prisma generate

# Copy source code
COPY dist ./dist/

# Expose port
EXPOSE 4000

# Start the server
CMD ["node", "dist/index.js"]
239 changes: 239 additions & 0 deletions indexer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
# PrivacyLayer Event Indexer

A high-performance event indexer for the PrivacyLayer smart contract on Stellar/Soroban.

## Features

- **Real-time Event Indexing**: Automatically indexes all PrivacyLayer contract events
- **GraphQL API**: Query deposits, withdrawals, and admin events efficiently
- **WebSocket Subscriptions**: Real-time updates via GraphQL subscriptions
- **PostgreSQL Storage**: Persistent, indexed storage for historical queries
- **Docker Support**: Easy deployment with Docker Compose

## Events Indexed

| Event | Description | Fields |
|-------|-------------|--------|
| `DepositEvent` | Emitted on deposit | commitment, leafIndex, root |
| `WithdrawEvent` | Emitted on withdrawal | nullifierHash, recipient, relayer, fee, amount |
| `PoolPausedEvent` | Emitted when pool is paused | admin |
| `PoolUnpausedEvent` | Emitted when pool is unpaused | admin |
| `VkUpdatedEvent` | Emitted when verifying key is updated | admin |

## Quick Start

### Prerequisites

- Node.js 18+
- PostgreSQL 15+
- npm or yarn

### Installation

```bash
# Clone the repository
git clone https://github.com/ANAVHEOBA/PrivacyLayer.git
cd PrivacyLayer/indexer

# Install dependencies
npm install

# Copy environment file
cp .env.example .env

# Edit .env with your configuration
# - DATABASE_URL: PostgreSQL connection string
# - CONTRACT_ID: Your deployed PrivacyLayer contract ID
# - SOROBAN_RPC_URL: Soroban RPC endpoint
```

### Database Setup

```bash
# Run migrations
npx prisma migrate dev --name init

# (Optional) Open Prisma Studio to view data
npx prisma studio
```

### Running the Indexer

```bash
# Development mode with auto-reload
npm run dev

# Production build
npm run build
npm start
```

### Docker Deployment

```bash
# Build and run with Docker Compose
docker-compose up -d

# View logs
docker-compose logs -f indexer
```

## API Documentation

### GraphQL Endpoint

The indexer exposes a GraphQL API at `http://localhost:4000/graphql`.

### Example Queries

#### Get recent deposits

```graphql
query {
deposits(pagination: { skip: 0, take: 10 }) {
id
commitment
leafIndex
root
txHash
timestamp
ledger
}
}
```

#### Get withdrawals by recipient

```graphql
query {
withdrawals(filter: { recipient: "G..." }) {
id
nullifierHash
recipient
amount
timestamp
}
}
```

#### Check if commitment is used

```graphql
query {
isCommitmentUsed(commitment: "0x...")
}
```

#### Get current Merkle tree state

```graphql
query {
merkleTreeState {
currentRoot
leafCount
lastUpdated
}
}
```

### Real-time Subscriptions

#### Subscribe to new deposits

```graphql
subscription {
onDeposit {
commitment
leafIndex
root
timestamp
}
}
```

#### Subscribe to Merkle tree updates

```graphql
subscription {
onMerkleTreeUpdate {
currentRoot
leafCount
lastUpdated
}
}
```

## Configuration

| Environment Variable | Description | Default |
|---------------------|-------------|---------|
| `PORT` | API server port | `4000` |
| `DATABASE_URL` | PostgreSQL connection string | Required |
| `SOROBAN_RPC_URL` | Soroban RPC endpoint | `https://soroban-testnet.stellar.org:443` |
| `NETWORK_PASSPHRASE` | Stellar network passphrase | Testnet |
| `CONTRACT_ID` | PrivacyLayer contract ID | Required |
| `POLL_INTERVAL` | Event polling interval (ms) | `5000` |

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│ GraphQL API │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Queries │ │ Mutations │ │ Subscriptions │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Event Indexer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Parser │ │ Processor │ │ Merkle Sync │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Soroban RPC │ │ PostgreSQL │ │ PubSub │
│ (Events) │ │ (Storage) │ │ (WebSocket) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```

## Development

### Project Structure

```
indexer/
├── src/
│ ├── index.ts # Main entry point
│ ├── indexer.ts # Event indexing logic
│ ├── parsers.ts # Event parsing utilities
│ ├── resolvers.ts # GraphQL resolvers
│ └── schema.graphql # GraphQL schema
├── prisma/
│ └── schema.prisma # Database schema
├── package.json
├── tsconfig.json
├── Dockerfile
├── docker-compose.yml
└── README.md
```

### Running Tests

```bash
npm test
```

## License

MIT License - See LICENSE file for details.

## Contributing

Contributions are welcome! Please see the main [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines.

## Support

For issues and feature requests, please open a GitHub issue.
37 changes: 37 additions & 0 deletions indexer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
version: '3.8'

services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: privacylayer
POSTGRES_PASSWORD: privacylayer_secret
POSTGRES_DB: privacylayer_indexer
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U privacylayer -d privacylayer_indexer"]
interval: 5s
timeout: 5s
retries: 5

indexer:
build: .
environment:
DATABASE_URL: "postgresql://privacylayer:privacylayer_secret@postgres:5432/privacylayer_indexer?schema=public"
SOROBAN_RPC_URL: ${SOROBAN_RPC_URL:-https://soroban-testnet.stellar.org:443}
NETWORK_PASSPHRASE: ${NETWORK_PASSPHRASE:-Test SDF Network ; September 2015}
CONTRACT_ID: ${CONTRACT_ID}
PORT: 4000
POLL_INTERVAL: 5000
ports:
- "4000:4000"
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped

volumes:
postgres_data:
Loading