Chain Labs

A compute job that turns DAO contribution data into a reward payout

At Curve Labs, on a Protocol Labs grant, Mihir built the compute half of Impact Evaluator: a Docker job on Bacalhau that runs a DAO's own scoring function and turns the result into a Merkle tree a contract can pay out from. It is a prototype, demonstrated on the Goerli testnet.

Founder’s experience at Curve Labs

Client
Protocol Labs, through a grant to Curve Labs
Employer
Curve Labs
Services
Blockchain, Data
When
December 2022 to January 2023
Team
Mihir
Status
Prototype

The problem

A DAO can agree to reward its contributors by measured impact. Someone then has to run the scoring calculation, and the other members have to trust the result. If one person runs the scoring on their own machine, the payout depends on that person. A DAO paying for contributions needs a calculation that anyone can rerun.

Impact Evaluator is a design set out in a public research spec. The design has three parts. Contributors record their metrics on Ceramic, a shared data network that every member of the DAO can read. A DAO's own scoring function runs as a job on Bacalhau, a network that runs containers on request. The result settles on-chain as a Merkle airdrop. Protocol Labs funded the build with a $15k grant to Curve Labs.

Mihir built the compute half at Curve Labs, from December 2022 to January 2023. Other engineers built the Ceramic data model, the settlement contracts and the app.

What Mihir built

Mihir kept the DAO's scoring function separate from the job runner that runs it. The runner is a Docker image. It reads one input file. The file holds contribution data, a list of trusted accounts and, optionally, the previous rewards. The runner calls the scoring function. Then it writes three files: the new rewards, the new trusted accounts and a Merkle tree of the rewards. The runner builds the tree with OpenZeppelin's library. The leaves are address-and-amount pairs that match what the settlement contract checks. The contract commits to one root, and each contributor claims with their own proof.

A DAO never edits the runner. The DAO forks a template repository Mihir created. It edits one file that returns the rewards and the next trusted accounts. It builds its own image on top of the shared one. A script looks up the image's exact digest on Docker Hub, runs that digest on Bacalhau against an IPFS or HTTPS input, and fetches the results. Running a pinned digest means a rerun runs the same code.

Keeping the function apart let a DAO change its reward policy without learning the job orchestration. The split had two costs. The function must return its result synchronously, because the runner never waits on a promise. The data the function scores is therefore meant to arrive in the input file. A DAO also tests a change to the function by building and running the Docker image, because the template has no faster test path.

What was hard

Every DAO writes its own function, so the runner treats each function as untrusted outside code. The runner loads the file at run time. When the function throws, returns nothing or returns rewards that are not an object, the job stops with an explicit error and writes no tree. This check catches a broken function before its output becomes an allocation. The check does not judge whether a DAO's scoring is fair.

The input format changed late in the build. The runner first looped over rounds of data spread across separate files. In January 2023 Mihir changed the runner to run once on a single input file. The new input has the same shape as the single IPFS file the app side produced by then. Mihir also made the previous rewards optional, so the template worked beyond its example.

The result

Impact Evaluator stayed a prototype, demonstrated on the Goerli testnet. The settlement contracts were deployed there and wired to a test Safe through a Zodiac Reality module. The module executes a payout only after a Reality.eth question about it resolves yes.

The prototype proved that a DAO can supply its own scoring function. It also proved that the runner turns the function's output into a Merkle tree in the format the settlement contract checks claims against. Anyone can rerun the image and the input. Mihir also wrote the template's authoring guide and recorded the walkthrough linked in the facts panel.

Built with

Job runnerTypeScript on Node.js
PackagingDocker, with images published to Docker Hub
ComputeBacalhau
InputsOne JSON file, from IPFS or over HTTPS
Reward treeOpenZeppelin Merkle tree library
Run scriptsShell