Ledger — Verified Milestone Escrow Escrow that pays for what actually shipped.
Inspiration
This idea came from a problem I’ve actually dealt with.
I co-founded a web development agency working with SMEs, mostly helping small businesses replace outdated websites. Building the websites was usually the easy part.
Getting paid was a different story.
A typical project would go something like this: a client agrees to pay when the website is delivered, we finish the work and put the site live, and then suddenly there’s a conversation about whether it’s really finished.
Is the website good enough? Does “delivery” mean the site is live? What if one page is missing? What if the client thinks something still needs to be changed?
At that point, getting paid becomes its own job.
That got me thinking about what smart contracts and Web3 could actually do for service work. We already have great infrastructure for trustless value transfer, but most of it is built around things that are relatively easy to measure: token prices, oracle data, physical shipments, and so on.
Service work is different.
With freelance or agency work, the question usually isn’t “what is this worth?” It’s “was this actually done?”
And most of the time, that question comes down to someone saying yes or no.
So we asked:
What if an AI agent could act as a neutral verifier, checking a deliverable against a clearly defined specification and paying the freelancer based on what was actually completed?
That became Ledger.
What it does
Ledger is a smart contract escrow designed around service milestones.
Instead of defining a milestone as something vague like “finish the website,” the client defines a few things that can actually be checked.
For example:
The website is live Required pages exist Performance score is at least 85
The client deposits the milestone amount into the escrow.
The freelancer or agency submits their deliverable, usually just a live URL.
Then the verification agent checks the site against the agreed conditions and reports the result on-chain.
The important part is that the payout isn’t simply pass or fail.
If 2 out of 3 conditions pass, the freelancer gets 2/3 of the milestone and the remaining 1/3 goes back to the client.
Payout = amount × (conditions passed / total conditions)
That means a freelancer who completed most of the work doesn’t automatically walk away with nothing just because one requirement wasn’t met.
And the client doesn’t have to manually argue over every milestone either.
How we built it
The system is split into three main pieces, which are also the three tabs in our demo.
- MilestoneEscrow.sol
This is the Solidity contract that handles the escrow and milestone state.
The flow is:
Created → Submitted → Verified / Cancelled
The contract exposes functions for creating a milestone, submitting a deliverable, and submitting the verification result.
Once the authorized verification agent submits the result, the contract calculates the proportional payout and sends the freelancer and client their respective amounts in the same transaction.
- The verification agent
The verification agent is a Node script that treats the milestone specification as the source of truth.
For a website milestone, it can:
Fetch the submitted URL Check that required pages exist Run a Lighthouse performance check Report which conditions passed
One small problem we ran into was Lighthouse itself.
It requires headless Chrome, which isn’t necessarily available on every machine, especially in a hackathon environment. We didn’t want to quietly make up a performance score if Lighthouse wasn’t available.
So we added a fallback timing-based check and clearly label it as a proxy in the output.
It isn’t as rigorous as Lighthouse, and we don’t pretend that it is.
- The demo frontend
The frontend is a simple, dependency-free HTML/JS interface that follows the same sequence as the actual contract:
createMilestone → submitDeliverable → submitVerification
For the hackathon demo, it uses mocked chain state so we don’t have to depend on a funded wallet, RPC connection, gas, or a blockchain network behaving perfectly while we’re presenting.
The goal was to demonstrate the actual mechanism without making the demo fragile.
Challenges we ran into
Binary vs. proportional payouts
Our first version was simple:
Pass means get paid. Fail means don’t get paid.
But pretty quickly, we realized that this recreated the exact problem we were trying to solve.
If someone finishes 90% of a project and misses one requirement, should they really get 0%?
Probably not.
That’s why we moved to proportional payouts. It’s a much better fit for real-world service work, where milestones aren’t always perfectly binary.
Where do we put the trust?
This is probably the biggest limitation of the current version.
Right now, the contract trusts one authorized agent address to submit the verification result.
That’s obviously a centralization point.
We’ve intentionally kept it this way for the hackathon because it makes the system much simpler, but we don’t want to pretend we’ve solved the oracle problem.
The next step would be something like a multi-agent quorum or a client challenge period where the client can dispute a result before the payment becomes final.
Making the performance check honest
We didn’t want the demo to look more reliable than it actually was.
Lighthouse isn’t always available, so instead of silently falling back to a fake score, the agent tells you when it’s using the timing-based proxy.
That felt like the right tradeoff for the demo: better to be transparent about a limitation than hide it.
Keeping the demo reliable
Live blockchain demos can be unpredictable.
RPC issues, wallet popups, gas problems, network congestion — any of those can turn a five-minute demo into a very awkward five-minute wait.
So we made the frontend mirror the actual contract state machine and call sequence, but run against mocked chain state for the presentation.
The underlying contract and verification logic are real and independently testable. The demo just removes the unnecessary points of failure.
What we learned
The most interesting part of building Ledger wasn’t actually the smart contract.
It was figuring out what “verifiable” really means.
At first, it’s easy to say something like:
“The AI should check whether the website is good.”
But that’s not something you can reliably put on-chain.
So we had to break that vague requirement into things a script can actually verify:
Is the site live? Does this page exist? Does it meet this performance threshold?
That decomposition ended up being one of the biggest insights for us.
The hard part of building trustless infrastructure for service work isn’t moving the money. It’s defining the work in a way that can actually be checked.
Once the deliverable can be expressed as an auditable specification, the smart contract part becomes much more straightforward.
What’s next
There are a few directions we’d like to take Ledger next.
Weighted conditions. Not every requirement should necessarily be worth the same amount. A website being live probably matters more than hitting a specific performance score.
A challenge window. Give the client a short period to challenge an agent’s verification before the funds become final, without bringing back a completely manual arbitration process.
More types of deliverables. Websites are just the starting point. The same system could eventually verify things like API uptime, automated test coverage, code changes, content requirements, or even design matching using vision models.
That’s what makes the idea interesting to us.
We didn’t build Ledger because we wanted another escrow contract.
We built it because we’ve experienced firsthand how messy getting paid for completed service work can be, and we think there’s a way to make that process more objective.
If the work can be clearly specified, maybe getting paid for it can be automated too.
Built With
- base
- base-sepolia
- css3
- evm
- html5
- javascript
- node-jsethereum
- smart-contracts
- solidity
- testnet
- web3
Log in or sign up for Devpost to join the conversation.