Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
7fe3b51
MMR-based model with final root commits & Errors libs + fixes
Apr 25, 2025
84d2982
adding comments
Apr 25, 2025
bf6e2ad
allowing approve existing roots via commit function
Apr 25, 2025
0b628a4
Update Readme.md
Gradonsky Apr 25, 2025
754e9e9
add: merkle- dist + generator & commiter
May 3, 2025
2405d0b
deploy script + fix naming
May 3, 2025
6d75be6
add: local arb fork w/ docker & full testing env
May 27, 2025
fd90a94
add: new backend for merkle distribution
Gradonsky Jun 25, 2025
59a85de
fix: reward calc, back to unmocked data, add: gas optimization simula…
Gradonsky Jun 26, 2025
35ee253
package json BUMP
Gradonsky Jun 26, 2025
7df1eb7
feat: add structured JSON logging (match reward bot format) & docker…
Gradonsky Jul 1, 2025
deb1623
Merge pull request #67 from subsquid/feat/merkle-distribution
Gradonsky Jul 1, 2025
1032055
Merge pull request #70 from subsquid/feat/nest-backend
Gradonsky Jul 1, 2025
cc071fb
Switch foundry image to latest tag
kalabukdima Jul 1, 2025
2050b5a
ref: cleanup + lint + updated lockfile
Gradonsky Jul 1, 2025
e64380a
ref: updated Dockerfile to pnpm + fixed type safety + del foundry
Gradonsky Jul 1, 2025
a1d098e
Fix docker workflow
kalabukdima Jul 1, 2025
6f1dcde
Enhance logging and error handling across services
Gradonsky Jul 8, 2025
ef1235d
"crypto" fix
Gradonsky Jul 9, 2025
eaf7a29
ref: logs
Gradonsky Jul 9, 2025
1ca0535
fix: old-backend calc
Gradonsky Jul 9, 2025
ca6017c
bump clickhouse, update from HOST to URL
Gradonsky Jul 9, 2025
131b149
Merge branch 'dev' into feat/logging
Gradonsky Jul 9, 2025
e5d0140
Merge pull request #71 from subsquid/feat/logging
Gradonsky Jul 9, 2025
bd39bf2
fix: merge logging fix + added logs and calc fix
Gradonsky Jul 10, 2025
8af8a43
fix: clickhouse connector
Gradonsky Jul 10, 2025
b108041
refactor + simplification + cleanup & added better logging
Gradonsky Jul 11, 2025
1c60795
fix: distribution + logging
Gradonsky Jul 15, 2025
3b14b34
fix: public api endpoints, fix: requestErrorRate & validRequests + l…
Gradonsky Jul 21, 2025
388a436
feat: startup-recovery, fixes
Gradonsky Jul 22, 2025
37bce7b
ver bump
Gradonsky Jul 22, 2025
7ea1a5d
ref: improve contract interaction & error handling
Gradonsky Jul 23, 2025
bf38755
v0.0.6 bump
Gradonsky Jul 23, 2025
d96c10f
enhanced error handling
Gradonsky Jul 24, 2025
5f93bcd
fix:targetcapacity & add: lastCommitmentKey + fixes & ver bump
Gradonsky Jul 28, 2025
6d4f590
add: error decoding +
Gradonsky Jul 31, 2025
43a00dc
fixed BN issue, abi-fix, cleanup, default scheduling to 520
Gradonsky Jul 31, 2025
01dbc76
chore: bump version
Gradonsky Jul 31, 2025
3f00e0a
refactor + split approval
Gradonsky Aug 4, 2025
fbfe436
version bump + fix: approval on comitmentKey
Gradonsky Aug 4, 2025
fbb498e
add: logs to approved epoch
Gradonsky Aug 4, 2025
e70e566
add: S3 + fixes on approval system & merkle adjust
Gradonsky Aug 8, 2025
b3d228b
feat/fix: integrate S3 module and enhance configuration management
Gradonsky Aug 10, 2025
1c8101f
Cleanup
Gradonsky Aug 10, 2025
7b8a030
chore: bump version to 0.1.3
Gradonsky Aug 10, 2025
ecc95ee
fix: worker_reward logs
Gradonsky Aug 11, 2025
f8be840
change: distribution cron interval from 1 min to 5 mins
Gradonsky Aug 11, 2025
d59c2d4
bump version to 0.1.5
Gradonsky Aug 11, 2025
a79ea5d
refactor + cleanup + added comments & PREFIX
Gradonsky Aug 14, 2025
73536e6
Resolve merge conflict in block-scheduler.service.ts - fix cron comme…
Gradonsky Aug 14, 2025
d456a3f
version bump
Gradonsky Aug 14, 2025
b0f787d
epoch fixes
Gradonsky Aug 25, 2025
cc41d4b
epoch range correction
Gradonsky Aug 26, 2025
1472f43
add: commitment status, fix: streaming instead promise & cleanup
Gradonsky Sep 7, 2025
1058499
fmt + ci fix
Gradonsky Sep 8, 2025
d3413f0
bump recovery check
Gradonsky Sep 10, 2025
6dad903
fix reporting on recovery process & update lockfile
Gradonsky Sep 10, 2025
5859d6a
del: table add: debugging
Gradonsky Sep 10, 2025
c84aab6
bump version
Gradonsky Sep 10, 2025
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
32 changes: 32 additions & 0 deletions .github/workflows/docker-rewards-backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: docker-rewards-backend
on:
workflow_dispatch: # manually run
inputs:
tag:
description: image tag
required: true

env:
CI: true

jobs:
publish:
name: Build & publish docker image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Docker login
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_LOGIN }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Build & publish subsquid-rewards-backend image
uses: docker/build-push-action@v3
with:
push: true
context: .
file: packages/subsquid-rewards-backend/Dockerfile
tags: subsquid/rewards-backend:${{ inputs.tag }}
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
cache/
out/
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/
node_modules
.idea
.env
.env*
.turbo
.lockb
56 changes: 56 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,62 @@

This is a monorepo that contains contracts and utils that enable [Subsquid](https://subsquid.io/) decentralised network

-----------

### 🔍 What Is a Merkle Mountain Range (MMR)

A Merkle Mountain Range is like a “forest” of perfect Merkle trees (the “mountains”), each one packed as tightly as possible, side by side.
Instead of rebuilding one giant Merkle tree every time we append new data:
* Append a new leaf
* Find the rightmost “mountain” whose height matches our new leaf count (a perfect power-of-two subtree)
* Hash the new leaf with its immediate sibling(s) to climb up that mountain, creating new parent nodes as needed

* “Bag the peaks”
* After we’ve added all the leaves, we take the root of each perfect subtree (each “mountain peak”)
then we hash those peaks pairwise—left to right—until we collapse them into a single, final MMR root
* Those two steps let us grow the data set one leaf at a time (append-only) without ever touching old leaves, yet still end up with a single 32-byte root that cryptographically commits to
every leaf ever added

### 🛠 How This Powers DistributedRewardsDistribution

Our rewards pipeline leverages MMR to securely batch-and-release thousands of worker payouts with minimal on-chain footprint:

1. **Off-chain: Collect & Batch thousands of per-worker rewards**
- Pull each worker’s reward slice from ClickHouse.
- Group them into fixed-size batches (e.g. 200 workers per batch).
- For each batch, compute a “batch leaf hash”:
```ts
keccak256(abi.encode(recipients[], workerRewards[], stakerRewards[]))
```
- Assemble a list of `BatchInput = { batchId, recipients[], workerRewards[], stakerRewards[], leafHash }`.

2. **Off-chain: Build the MMR**
- **Append** each `leafHash` into our in-memory MMR structure, creating only the minimal new parent hashes.
- **Compute peaks** via bit-twiddling on the leaf count to isolate perfect subtrees.
- **Bag peaks** into one final MMR root and record each leaf’s inclusion proof (an array of sibling hashes + cross-peak bag-hashes).

3. **On-chain: Commit & Approve the MMR Root**
- We only store a single bytes32 finalRoot and a 64-bit totalLeaves on-chain per block-range key

4. **On-chain: Distribute Each Batch**
```
rewardsContract.distributeBatch(
[fromBlock, toBlock],
kIndex, // “mountain k-index” inside its peak
leafIndex, // sequential batch ID
recipients[], // worker IDs
workerRewards[],
stakerRewards[],
merkleProof[] // sibling-hash proof + bagged peaks
);
```
- The contract recomputes the batch’s leaf hash, then calls:
`MerkleMountainRange.VerifyProof(finalRoot, merkleProof, [MmrLeaf(kIndex, leafIndex, leafHash)], totalLeaves)`

On success, it marks the batch as processed, updates each worker’s accumulatedRewards, and triggers `router.staking().distribute(…)`

-------------

Subsquid uses [pnpm](https://pnpm.io/) as a package and monorepo manager.
To install `pnpm`, run `npm install -g pnpm` or consult with [pnpm installation guide](https://pnpm.io/installation).

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"build": "turbo build",
"lint": "turbo lint",
"test": "turbo test",
"build-reward-image": "docker build -f packages/rewards-calculator/Dockerfile -t rewards-simulation ."
"build-reward-image": "docker build -f packages/rewards-calculator/Dockerfile -t rewards-simulation .",
"generate": "node packages/contracts/generate_merkle.mjs"
},
"devDependencies": {
"turbo": "^1.9.9"
Expand Down
2 changes: 1 addition & 1 deletion packages/contract-admin-state/src/config/contracts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Deployments from "../../../contracts/deployments/42161.json";
import rewardsDistributionAbi from "../../../contracts/artifacts/DistributedRewardDistribution.sol/DistributedRewardsDistribution";
import rewardsDistributionAbi from "../../../contracts/artifacts/DistributedRewardsDistribution.sol/DistributedRewardsDistribution";
import stakingAbi from "../../../contracts/artifacts/Staking.sol/Staking";
import networkControllerAbi from "../../../contracts/artifacts/NetworkController.sol/NetworkController";
import gatewayRegistryAbi from "../../../contracts/artifacts/GatewayRegistry.sol/GatewayRegistry";
Expand Down
61 changes: 61 additions & 0 deletions packages/contracts/Dockerfile.arbitrum-fork
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
curl \
git \
build-essential \
jq \
python3 \
python3-pip \
bash \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs

RUN pip3 install base58

RUN npm install -g pnpm@latest

RUN curl -L https://foundry.paradigm.xyz | bash
ENV PATH="/root/.foundry/bin:${PATH}"
RUN foundryup

WORKDIR /usr/src/app

COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY packages/contracts/package.json ./packages/contracts/
COPY packages/rewards-calculator/package.json ./packages/rewards-calculator/

RUN pnpm install

COPY packages/contracts/foundry.toml ./packages/contracts/foundry.toml

WORKDIR /usr/src/app/packages/contracts
RUN forge install foundry-rs/forge-std OpenZeppelin/openzeppelin-contracts@v5.0.1 OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.2 PaulRBerg/prb-math@release-v4 --no-git

COPY packages/contracts/src ./src
COPY packages/contracts/script/DeployDistributedRewards.s.sol ./script/DeployDistributedRewards.s.sol
#COPY packages/contracts/test ./test
COPY packages/contracts/lib ./lib
COPY packages/contracts/generate_merkle.mjs ./generate_merkle.mjs
COPY packages/contracts/b58.py ./b58.py

RUN forge build

COPY packages/contracts/deploy ./deploy
COPY packages/contracts/deployments ./deployments

RUN mkdir -p /usr/src/app/packages/contracts/docker-scripts

COPY packages/contracts/docker-scripts/ ./docker-scripts/

RUN chmod +x docker-scripts/*.sh

EXPOSE 8545

WORKDIR /usr/src/app/packages/contracts

CMD ["./docker-scripts/run_arbitrum_fork.sh"]
35 changes: 35 additions & 0 deletions packages/contracts/docker-compose.arbitrum-fork.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: '3.8'

services:
arbitrum-fork:
build:
context: ../../
dockerfile: packages/contracts/Dockerfile.arbitrum-fork
ports:
- "8545:8545"
environment:
- ADMIN_KEY=${ADMIN_KEY:-}
- DISTRIBUTOR1_KEY=${DISTRIBUTOR1_KEY:-}
- DISTRIBUTOR2_KEY=${DISTRIBUTOR2_KEY:-}

- NUM_WORKERS=${NUM_WORKERS:-500}
- BATCH_SIZE=${BATCH_SIZE:-100}
- IPFS_LINK=${IPFS_LINK:-ipfs://test}

- SKIP_COMMIT=${SKIP_COMMIT:-false}
- SKIP_APPROVE=${SKIP_APPROVE:-false}
- SKIP_DISTRIBUTE=${SKIP_DISTRIBUTE:-false}

- FORK_URL=${FORK_URL:-https://arb1.arbitrum.io/rpc}
- FORK_BLOCK_NUMBER=${FORK_BLOCK_NUMBER:-latest}
volumes:
- ./broadcast:/usr/src/app/packages/contracts/broadcast
- ./cache:/usr/src/app/packages/contracts/cache
- ./out:/usr/src/app/packages/contracts/out
networks:
- arbitrum-fork-network
command: ["./docker-scripts/run_arbitrum_fork.sh"]

networks:
arbitrum-fork-network:
driver: bridge
Loading
Loading