decentralized-staking: The contract that settles itself

In sodofi/decentralized-staking, the important move is not staking. It is letting a deadline and a public `execute()` call decide whether pooled ETH gets forwarded or refunded.

12 min read • View on GitHub • More from sodofi

A communal pool of coins sits inside a clockwork vault with a deadline timer overhead and two possible exits. It shows how the contract can settle itself by either forwarding funds or reopening refunds once the deadline passes.
The core mechanic is not yield. It is settlement: one deadline, one public finalization call, two possible outcomes.
Key Takeaways

Most staking writeups start with rewards. This repo is better understood as a settlement machine. In sodofi/decentralized-staking, users pool ETH until a deadline, then a permissionless call decides whether the contract forwards everything to an external target or flips into refunds.

A deadline, not a manager

That is the threshold assurance pattern in its leanest form. The logic lives in Staker.sol, and it makes a simple promise: after the deadline, anyone can settle the round, but no one can change the outcome except the chain state itself.

This diagram makes the contract legible at a glance: collect, wait, then branch once.

How the contract settles itself

The state machine is tiny. `stake()` records balances, `receive()` routes plain ETH transfers into that path, and `execute()` checks only two things at the end of the round: time and total balance. If the threshold is met, the contract calls an external completion contract. If not, it opens withdrawals.

function execute() external {
    require(block.timestamp >= deadline, "too early");

    if (address(this).balance >= threshold) {
        exampleExternalContract.complete{value: address(this).balance}();
        status = Status.Executed;
    } else {
        openForWithdraw = true;
    }
}

function withdraw() external {
    require(openForWithdraw, "withdrawals closed");
    uint256 amount = balances[msg.sender];
    balances[msg.sender] = 0;
    payable(msg.sender).transfer(amount);
}

The small safety move is easy to miss: refunds zero the sender's balance before any ETH leaves the contract. That is the checks-effects-interactions pattern, and it is the difference between a clean refund flow and a reentrancy bug waiting to happen.

The interface mirrors the machine

The Next.js app is not a separate product layer. Scaffold-ETH 2 generates typed ABIs and network addresses into the frontend, so hooks like `useScaffoldReadContract` and `useScaffoldWriteContract` can read chain state without glue code. The result is a UI that stays in lockstep with the contract's visible phases: staking, executed, and open for withdraw. The Hardhat config also points at Celo by default, which fits the low-fee, consumer-adjacent feel of the project.

What it is, and what it is not

This is not ether.fi, and it is not Geode. Those systems solve production staking at protocol scale. This repo is smaller on purpose, which is why it is useful: it shows the mechanism before the machinery. If you want to explain non-custodial settlement in one sitting, this is a clean example.

ModelWho finalizesWhat the deadline doesTrust assumption
Custodial escrowA person or platformThey decide whether money moves or refundsTrust the middleman
Typical staking poolProtocol operators and validator stackRewards and state changes are mediated by infrastructureTrust the stack
sodofi/decentralized-stakingAnyone can call execute()The contract checks time and balance, then settles itselfTrust the code and chain state

The comparison is the point. Ordinary staking assumes operators and validator infrastructure. Custodial escrow assumes a trusted manager. This repo removes both assumptions and replaces them with a deadline, a balance check, and a public `execute()` call.


The real pedagogical move is the codebase shape. A monorepo with Hardhat on one side and Next.js on the other makes the contract, the UI, and the generated bindings feel like one artifact. That is useful if you want to teach the mechanics without burying them under protocol complexity.