Smart Contract
Oak Chain settles write authorization on Ethereum through a single contract per Aeron cluster: ValidatorPaymentV5. In the V5 reference flow, a write or delete proposal is settled on-chain before it becomes eligible for the validator queue. The contract carries the minimum proof surface needed to verify that a proposal was paid for: a unique proposal id, the kind of operation, the payer, the amount, and any capability flags. Nothing else is on chain.
The contract is intentionally small: 180 lines, ~3.9 KB runtime bytecode, two flat prices, no validator registry, no tier system, no token logic. It is a settlement primitive, not a scheduling engine. Release timing, packing priority, and queue semantics are all runtime concerns governed by the validator, not the contract.
Deployment
Deployment status: V5 source, tests, and deployment tooling exist, but this project does not currently record a verified V5 deployment on Sepolia or Mainnet. The addresses below are deliberately unresolved.
<SEPOLIA_V5_ADDRESS>One contract instance is deployed per Aeron cluster. The constructor binds the contract to a single clusterTreasury address that receives accumulated fees. The contract does not split fees among validators. Any distribution from the treasury is an off-chain operator policy, and none is defined today.
Pricing
Two flat prices, both denominated in ETH:
WRITE_PRICE_ETH0.001 ethersettleWrite callDELETE_PRICE_ETH0.001 ethersettleDelete callThere are no tiers, classes, or per-validator pricing in V5. Release semantics (priority, batching, packing) live in the validator runtime, not the contract.
The prices are compile-time constants, so changing them means deploying a new contract. There is no on-chain fee governance. The flat 0.001 ETH is a starting hypothesis meant to make writes cost something, which filters spam, while staying simple. It has not been validated against production costs.
Function surface
settleWrite(bytes32 proposalId, uint32 capabilityFlags)
Pays for a write proposal. Caller sends at least WRITE_PRICE_ETH; any excess is refunded immediately. Records the proposal id in usedProposals for replay protection. Emits ProposalSettledV5.
settleDelete(bytes32 proposalId, uint32 capabilityFlags)
Same shape as settleWrite, charges DELETE_PRICE_ETH. Capability flags must be CAPABILITY_NONE for deletes; the validator-hosted-binary capability applies only to writes.
quotePriceETH(ProposalKind proposalKind, uint32 capabilityFlags) (view)
Returns the required ETH amount for a given proposal kind and capability set. Validates the capability combination is supported. Use this off-chain to compute exact payment before calling settleWrite or settleDelete.
withdrawClusterFees()
Drains accumulatedFeesETH to the configured clusterTreasury. Callable only by the cluster treasury address. Emits ClusterFeesWithdrawn.
setClusterTreasury(address newTreasury) (owner only)
Rotates the cluster treasury address. Emits ClusterTreasuryUpdated. Cannot be set to the zero address.
setPaused(bool _paused) (owner only)
Emergency pause. When paused, settleWrite and settleDelete revert. Read-only functions and treasury withdrawals continue to work. Emits EmergencyPause.
recoverExcessETH(address recipient, uint256 amount) (owner only)
Recovers ETH that arrived at the contract outside the settlement path (which should be zero, since direct transfers revert). Bounded by address(this).balance - accumulatedFeesETH so it cannot drain cluster fees. Emits ExcessETHRecovered.
Events
event ProposalSettledV5(
bytes32 indexed proposalId,
address indexed payer,
ProposalKind proposalKind,
uint256 amount,
uint32 capabilityFlags
);
event ClusterTreasuryUpdated(
address indexed previousTreasury,
address indexed newTreasury
);
event ClusterFeesWithdrawn(address indexed treasury, uint256 amount);
event ExcessETHRecovered(address indexed recipient, uint256 amount);
event EmergencyPause(bool paused);
ProposalSettledV5 is the load-bearing event. In sepolia mode, validators watch for it (filtered by contract address and proposal id) to verify that a proposal was paid for before releasing the queued write to consensus. Chain-backed payments are not yet validated. In mock mode, the default, payments are simulated.
Capability flags
The capabilityFlags bitfield extends the settlement payload without changing the contract for additional capabilities:
CAPABILITY_NONE0CAPABILITY_VALIDATOR_HOSTED_BINARY1 << 0Unknown flag values revert. The validator-hosted-binary flag is restricted to writes; using it on a delete reverts.
Behaviors worth knowing
- Replay protection. A given
proposalIdcan be settled exactly once. Re-using a settled proposal id reverts with"Already paid". - Excess refund. If the caller sends more ETH than required, the excess is refunded in the same transaction. Validators do not need to charge exact amounts; they can over-fund and let the contract return change.
- Reentrancy protection. All state-changing external functions use OpenZeppelin's
ReentrancyGuard. The treasury withdrawal and excess-refund paths are safe against reentry from receiving contracts. - No direct ETH transfers. Both
receive()andfallback()revert with"Direct ETH unsupported". The only way ETH enters the contract is through a settlement call. - Two-step ownership. Owner transfer uses
Ownable2Step. A pending owner must accept the transfer before it takes effect. Single-step ownership transfers are not possible.
Measured gas profile
Baseline captured 2026-05-08 via hardhat run scripts/eval-v5-gas.js:
settleWrite (ETH)settleWrite with validator-hosted binary capabilitysettleDeletewithdrawClusterFeessetClusterTreasurysetPaused (true, first toggle)setPaused (false, warm slot)recoverExcessETHReproduce with:
cd oak-chain-smart-contracts
npx hardhat run scripts/eval-v5-gas.js
The plain settleWrite row is the first settlement on a fresh contract, so it also pays the one-time zero-to-nonzero storage write of accumulatedFeesETH (about 17,000 gas). Later settlements, including the capability write, cost about 57,000 gas. The capability flag itself does not reduce gas.
The local Hardhat baseline places the sampled V5 settlement near the gas envelope of an ERC-20 transfer. Production network and facilitator economics have not been validated.
Source and verification
- Source:
oak-chain-smart-contracts/contracts/ValidatorPaymentV5.solon thecodex/archive-legacy-validator-paymentbranch (themainbranch still carries V4) - License: Apache-2.0
- Solidity version:
^0.8.24 - Dependencies:
@openzeppelin/contracts(Ownable2Step, ReentrancyGuard) - Compilation and tests:
npm run test:v5 - Sepolia deploy:
npm run deploy:sepolia - Block explorer verification: pending a recorded deployment address, deployment transaction, constructor arguments, and address-specific verification link
Integration pattern
Authoring clients call the contract directly with their wallet, then submit the resulting transaction hash to the validator alongside the proposal:
import { ethers } from 'ethers';
const PRICE = ethers.parseEther('0.001');
const CAPABILITY_NONE = 0;
// 1. Settle on chain
const tx = await contract.settleWrite(proposalId, CAPABILITY_NONE, { value: PRICE });
const receipt = await tx.wait();
// 2. Submit proposal to validator with the tx hash
await fetch('http://validator:8090/v1/propose-write', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
proposalId,
walletAddress: signer.address,
organization: 'MyBrand',
message: JSON.stringify(content),
ethereumTxHash: receipt.hash,
signature: await signer.signMessage(JSON.stringify(content)),
}),
});
In sepolia mode, the validator queues the proposal (202) and verifies the on-chain settlement before releasing it to consensus. Mock mode simulates the settlement, and chain-backed payments are not yet validated. See Authentication for the signing surface and Write Pipeline for the validator-side acceptance pipeline.
Iteration history
V5 is the result of a brutal simplification pass from V3 → V4 → V5:
- V3 / V3_1 / V3_2: 3-tier pricing (Standard / Express / Priority), per-validator settlement records, USDC settlement option, validator registry on chain.
- V4: Full validator registry, time-locked claims, batched checkpointing, multi-token settlement. ~720 lines.
- V5: Single contract per cluster, single treasury, no on-chain validator registry, no token logic, no tiers. ~180 lines. Settlement reduced to a single
SSTOREfor the replay flag plus the fee accumulator.
The simplification was the gas-optimization pass. V5's per-settlement cost is dominated by unavoidable storage operations; further optimization would require trading off security or correctness.
Next Steps
- Authentication - Wallet signing for write proposals
- Write Pipeline - How a paid proposal moves through the validator
- API Reference - Validator HTTP surface for proposal submission