Payroll that pays by the block.
Salaries shouldn't arrive once a month. OPN Flow streams pay every second on OPN Chain and lets people withdraw whenever they need it — after a shift, before rent, mid-afternoon.
- Live demo: https://nayemlengta.github.io/opn-flow/ (no wallet needed to watch)
- Chain: OPN Chain Testnet ·
984
Payment streaming is an old idea. It has never been usable on Ethereum mainnet for an unglamorous reason: withdrawing costs more than the stream pays.
Someone earning the equivalent of $50 a day cannot spend several dollars of gas to collect it, so in practice they withdraw monthly — and a "stream" you touch once a month is just payroll with extra steps.
OPN Chain changes the arithmetic:
| Ethereum mainnet | OPN Chain | |
|---|---|---|
| Cost to withdraw | ~$3–15 | ~0.0000000000005 OPN |
| Block time | ~12s | ~1.25s |
| Collecting daily | costs more than you earn | free |
Gas on OPN Chain is 7 wei — measured, not quoted from the docs, which state 7 Gwei. That factor of 10⁹ is the entire product thesis. Streaming only becomes real when withdrawal is free, and this is the first chain where it is.
| Contract | Address |
|---|---|
StreamHub |
0xded0a09979beb42681c9134ab928590b78c0723e |
Payroll |
0xe83147c793919f4071ec012485b4b88362e6d3e8 |
A three-person payroll is running live, funded for 30 days. The demo page reads those streams straight from the chain and ticks every 100 ms.
Fund it once. Every stream is fully funded at creation — the deposit must cover the entire term up front. There is no insolvency state, no liquidation, and no moment where a recipient discovers the money stopped arriving. If a stream exists, it will pay in full.
This costs the payer capital efficiency and buys the recipient an unconditional guarantee. For payroll, that is the right way round.
It accrues every second. The balance is a function of the clock, not of anyone remembering to run a job.
Anyone can trigger the payment — but only the recipient can receive it.
withdraw is callable by any address and always sends to the stream's
recipient. That is safe because the destination is fixed, and it is useful
precisely because gas is negligible here: an employer can push wages to someone
who holds no OPN at all and therefore couldn't pay for their own withdrawal.
Either side can stop it. On cancellation the recipient keeps every second already worked — including time behind a cliff, because that time was genuinely worked — and the sender gets back only what was never earned.
employer StreamHub employee
────────── ─────────── ──────────
runPayroll() ──────────▶ deposit locked
accrues per second
│
│ withdraw() ◀──── anytime, ~free
└──────────────────▶ paid to employee
cancelStream() ─────────▶ earned → employee
unearned → employer
Two contracts with a clean split.
StreamHub — the primitive. It moves money between two addresses over time
and knows nothing about employment. Fully-funded streams, per-second accrual,
optional cliff, partial withdrawals, top-ups, fair cancellation, and batch
creation.
Payroll — the product. An employer keeps a roster, quotes the cost of a
run before committing, and funds the whole team in one transaction. It is
all-or-nothing: if the value sent doesn't cover everyone, the run reverts, so
nobody ends up half-paid.
Payroll never takes custody. It forwards value straight into StreamHub
in the same transaction, and the resulting streams name the employee as
recipient — so it cannot withdraw them, cannot redirect them, and holds no
balance between calls. The test suite asserts its balance is zero after a run.
- Fully funded by construction.
createStreamreverts unless the deposit coversrate × duration. Excess is refunded, not absorbed. - Cliffs withhold, they don't confiscate. Pre-cliff time still accrues and is paid out on cancellation.
- Reentrancy. Withdraw, cancel and top-up use an EIP-1153 transient guard under strict checks-effects-interactions. TSTORE/TLOAD are verified working on OPN Chain.
- No partial payroll. A batch that can't fund everyone reverts entirely.
- Rosters are per-employer. One employer cannot read or edit another's.
23 tests pass, covering accrual maths, cliff behaviour, fair cancellation, third-party withdrawal, batch atomicity, and roster isolation.
Some of this cost real debugging time and isn't in the official docs. Written up
in full in docs/OPN_CHAIN_NOTES.md.
- Set explicit gas limits.
eth_estimateGascan return a limit that consumes only intrinsic gas and reverts before entering the contract — which looks exactly like an unsupported opcode and will send you hunting the wrong bug. - Compile for
cancun, notprague. Cancun is the highest fork honoured end to end. EIP-7702 transactions are accepted and silently discarded. - Don't infer fork support from block headers. They omit Cancun/Prague fields even though TSTORE and MCOPY execute fine. Probe opcodes instead.
- Real gas price is 7 wei, not the documented 7 Gwei.
npm install
npx hardhat compile
npx hardhat test # 23 tests
cp .env.example .env # add DEPLOYER_KEY
npx hardhat run scripts/deploy.js --network opn
npx hardhat run scripts/demo.js --network opn # stands up a live payroll
npx serve web -l 4174 # the dashboardNow — testnet, live. Fully-funded native-OPN streams, cliffs, batch payroll, employer rosters, live dashboard, 23 passing tests.
Next. ERC-20 streams so teams can pay in stablecoins. Stream NFTs, so a salary can be transferred or borrowed against. An invoice mode where the recipient proposes terms and the payer signs.
Later. Withdrawal scheduling, so wages land in a wallet automatically without anyone pressing a button — cheap only because gas here is what it is.
Mainnet. Security review, then payroll for real teams building on OPN Chain.
MIT