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.

Data Table
Network
Address
Status
Sepolia
<SEPOLIA_V5_ADDRESS>
Not recorded or verified
Mainnet
TBD
Not deployed; infrastructure is a stub

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:

Data Table
Constant
Value
Applies to
WRITE_PRICE_ETH
0.001 ether
Every settleWrite call
DELETE_PRICE_ETH
0.001 ether
Every settleDelete call

There 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:

Data Table
Flag
Value
Applies to
CAPABILITY_NONE
0
Standard write or delete
CAPABILITY_VALIDATOR_HOSTED_BINARY
1 << 0
Write whose binary is hosted by the validator (per ADR 083)

Unknown flag values revert. The validator-hosted-binary flag is restricted to writes; using it on a delete reverts.

Behaviors worth knowing

Measured gas profile

Baseline captured 2026-05-08 via hardhat run scripts/eval-v5-gas.js:

Data Table
Operation
Gas
settleWrite (ETH)
74,030
settleWrite with validator-hosted binary capability
57,007
settleDelete
56,939
withdrawClusterFees
34,617
setClusterTreasury
30,643
setPaused (true, first toggle)
46,845
setPaused (false, warm slot)
24,933
recoverExcessETH
39,721
Deployment
987,855

Reproduce 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

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:

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